Files
docs/悦/货代SAAS平台后台/tests/verify-overseas-pda.cjs
T
2026-08-21 09:20:26 +08:00

102 lines
8.2 KiB
JavaScript

const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const root = path.resolve(__dirname, '..');
const htmlPath = path.join(root, 'PDA(海外)', 'index.html');
assert.ok(fs.existsSync(htmlPath), 'PDA(海外)/index.html must exist');
const html = fs.readFileSync(htmlPath, 'utf8');
assert.match(html, /const I18N\s*=\s*\{/, 'three-language dictionary must exist');
assert.match(html, /zh\s*:\s*\{/, 'Chinese dictionary must exist');
assert.match(html, /en\s*:\s*\{/, 'English dictionary must exist');
assert.match(html, /es\s*:\s*\{/, 'Spanish dictionary must exist');
for (const metric of ['待卸货集装箱', '今日已入库', '今日待出库']) {
assert.ok(html.includes(metric), `workbench metric missing: ${metric}`);
}
assert.ok(!html.includes('入库上架'), 'overseas PDA must not expose inbound shelving');
assert.ok(!html.includes('批次号'), 'overseas PDA must not expose batch fields');
for (const photoKey of ['licensePlate', 'containerSeal', 'doorOpen', 'unloading', 'unloadComplete']) {
assert.ok(html.includes(photoKey), `unload photo step missing: ${photoKey}`);
}
assert.match(html, /capture=["']environment["']/, 'unload photos must support the rear camera');
assert.match(html, /mergeFlag|isMerged/, 'pallet lines must expose a merge marker');
assert.ok(html.includes('托数'), 'inbound plans must use pallet count rather than carton count');
assert.match(html, /startPlanPallet|开始打托/, 'inbound plan must start palletizing');
assert.match(html, /startConsolidation|合托/, 'consolidation must have a separate entry');
assert.match(html, /targetPallets/, 'palletizing must carry the inbound plan pallet target');
assert.match(html, /palletMode\s*===\s*['"]consolidate['"]/, 'palletizing and consolidation modes must be distinct');
assert.match(html, /expectedPallets/, 'palletizing progress must use planned pallet count');
assert.match(html, /palletTargetReached/, 'submitting more pallets than the plan target must be blocked');
assert.match(html, /receiveListView/, 'unload receiving must open a pending-container list first');
assert.match(html, /page==='receive-list'/, 'pending-container list must be a separate page');
assert.ok(html.includes("container:'D2222'"), 'container codes must demonstrate the D2222 format');
assert.match(html, /-TC-/, 'pallet codes must follow the container-TC-sequence format');
assert.ok(!html.includes('打托 / 合托'), 'palletize and consolidation must not use a combined visible label');
assert.match(html, /inboundPallet|入库打托/, 'inbound palletizing must be the single inbound pallet entry');
assert.doesNotMatch(html, /<h3>\$\{t\('plan'\)\}<\/h3>[\s\S]*?<h3>\$\{t\('pallet'\)\}/, 'workbench must not expose duplicate plan and pallet entries');
assert.match(html, /palletCount|托盘数量/, 'outbound orders must expose pallet quantity');
assert.match(html, /orderInfo|关联订单/, 'outbound orders must expose linked order information');
for (const outboundField of ['inboundCode','plateNo','driver','outboundTime','totalPallets']) {
assert.ok(html.includes(outboundField), `outbound picking field missing: ${outboundField}`);
}
assert.match(html, /totalPalletBoxes|总托数\s*\/\s*总箱数/, 'outbound picking must combine total pallets and total cartons');
assert.match(html, /injectInboundPalletUnits[\s\S]*entries\?\.\[1\]/, 'unload receiving must remain immediately after inbound palletizing');
assert.match(html, /injectInboundPalletUnits[\s\S]*insertBefore/, 'workbench inbound entries must order unload before palletizing');
for (const unloadField of ['tractorDriver','tractorPlate']) {
assert.ok(html.includes(unloadField), `unload list field missing: ${unloadField}`);
}
assert.match(html, /receiveListView[\s\S]*inboundCode/, 'unload list must label ticket as inbound code');
assert.match(html, /destinationWarehouse/, 'unload list must use destination warehouse label');
assert.match(html, /destinationWarehouses|slice\(0,3\)/, 'unload list must support up to three destination warehouse codes');
assert.match(html, /navigate\('receive-list'\)/, 'workbench must expose a separate unload receiving entry');
assert.match(html, /page\s*=\s*['"]pallet-list['"]|palletListView/, 'inbound palletizing must open a pallet list before pallet detail');
assert.match(html, /selectPallet|openPalletDetail|pallet-list/, 'pallet list must provide a pallet detail selection entry');
assert.match(html, /viewPalletDetails|选择查看明细/, 'inbound order list must use the view details label');
assert.match(html, /startPalletWork|t\('startPallet'\)/, 'inbound order list must provide a palletizing action');
assert.match(html, /btn ghost[\s\S]*viewPalletDetails|viewPalletDetails[\s\S]*btn ghost/, 'view details must be the secondary left action');
assert.match(html, /directEnter|直接进入/, 'pallet list must directly enter pallet detail');
assert.match(html, /palletLabelSelector|selectPalletLabel|选择托盘标/, 'pallet detail must provide a pallet label selector');
assert.match(html, /palletLabelOption[\s\S]*idle|occupied/, 'pallet label options must show pallet number and status');
assert.match(html, /t\('boxMark'\)|箱唛/, 'pallet detail must show box mark');
assert.match(html, /t\('inboundCode'\)|入仓码/, 'pallet detail must show inbound code');
assert.match(html, /t\('productName'\)|产品名称/, 'pallet detail must show product name');
assert.match(html, /removePalletItem/, 'pallet detail must keep a delete action');
const orderListView = html.match(/function plansPalletView\(\)\{[^\n]+\}/)?.[0] || '';
assert.equal((orderListView.match(/t\('container'\)/g) || []).length, 1, 'inbound order card must show container number only once');
assert.match(orderListView, /esc\(p\.container\)/, 'inbound order cards must render each plan container number');
assert.match(html, /cartonCount|箱数/, 'pallet list must show carton count');
assert.match(html, /idle|occupied|闲置|占用/, 'pallet list must show idle or occupied status');
const consolidationFn = html.match(/function startConsolidation\(\)\{[^\n]+\}/)?.[0] || '';
assert.match(consolidationFn, /page\s*=\s*['"]pallet['"]/, 'consolidation must open detail directly');
assert.doesNotMatch(consolidationFn, /pallet-list/, 'consolidation must not open pallet list first');
assert.match(html, /injectReservedEntries[\s\S]*more\.style\.display='none'/, 'more operations must be hidden');
assert.doesNotMatch(html, /injectReservedEntries[\s\S]*t\('stocktake'\)/, 'stocktake must be hidden');
assert.doesNotMatch(html, /injectReservedEntries[\s\S]*t\('transfer'\)/, 'transfer must be hidden');
for (const handoverPhoto of ['licensePlatePhoto','vehicleCabPhoto','driverLicense','loadingSealPhoto','lockSealPhoto','bolDriverSigned','amazonAppointment']) {
assert.ok(html.includes(handoverPhoto), `handover photo step missing: ${handoverPhoto}`);
}
assert.match(html, /HANDOVER_PHOTO_STEPS/, 'handover must require a seven-photo checklist');
assert.match(html, /completed===7|completed<7|7 \/ 7/, 'handover completion must require seven photos');
assert.match(html, /function relabelView|page==='relabel'/, 'relabel operation must be implemented');
assert.match(html, /function exceptionView|page==='exception'/, 'exception operation must be implemented');
const fnMatch = html.match(/function canMergePallet\(pallet,\s*cargo\)\s*\{[\s\S]*?\n\s*\}/);
assert.ok(fnMatch, 'canMergePallet(pallet, cargo) must exist');
const canMergePallet = vm.runInNewContext(`(${fnMatch[0].replace('function canMergePallet', 'function')})`);
const emptyPallet = { destinationCode: '', destinationAddress: '' };
const laxA = { customer: 'A', planNo: 'IN-A', destinationCode: 'ONT8', destinationAddress: '24300 Nandina Ave, Moreno Valley, CA' };
const laxB = { customer: 'B', planNo: 'IN-B', destinationCode: 'ONT8', destinationAddress: '24300 Nandina Ave, Moreno Valley, CA' };
const conflict = { customer: 'B', planNo: 'IN-B', destinationCode: 'LGB8', destinationAddress: '1568 N Linden Ave, Rialto, CA' };
assert.equal(canMergePallet(emptyPallet, laxA), true, 'first cargo must lock an empty pallet');
assert.equal(canMergePallet({ ...laxA }, laxB), true, 'same destination must merge across customers and plans');
assert.equal(canMergePallet({ ...laxA }, conflict), false, 'different destination must be blocked');
console.log('verify-overseas-pda: all assertions passed');