Files
docs/悦/货代SAAS平台后台/tests/verify-dispatch-bol-print.cjs
T
2026-08-22 09:50:50 +08:00

98 lines
7.8 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 moduleSource = fs.readFileSync(path.join(root, 'prototype-src/modules/04-overseas-delivery.html'), 'utf8');
const overlaySource = fs.readFileSync(path.join(root, 'prototype-src/overlays.html'), 'utf8');
const shellSource = fs.readFileSync(path.join(root, 'prototype-src/shell.html'), 'utf8');
const appSource = fs.readFileSync(path.join(root, 'prototype-src/app.js'), 'utf8');
const completeSource = fs.readFileSync(path.join(root, 'index.html'), 'utf8');
const dispatchTable = moduleSource.match(/id="dispatch-table-scroll"[\s\S]*?<tbody id="tbody-outbound-dispatch">([\s\S]*?)<\/tbody>/);
assert.ok(dispatchTable, '缺少派送单列表主表');
const staticRows = [...dispatchTable[1].matchAll(/<tr data-dispatch-no="(YWBHK20061500[1-5])">([\s\S]*?)<\/tr>/g)];
assert.equal(staticRows.length, 5, '派送单静态示例行数量异常');
staticRows.forEach((row, index) => {
const bolNo = `BOL2026061500${index + 1}`;
assert.match(row[2], new RegExp(`openDispatchBolPrint\\('${bolNo}'\\)`), `${row[1]} 缺少对应的打印 BOL 调用`);
assert.match(row[2], /class="dispatch-bol-print-action"[^>]*>打印 BOL<\/a>/, `${row[1]} 的“打印 BOL”入口缺少防重复标识`);
assert.match(row[2], />编辑车队<\/a>/, `${row[1]} 的“编辑车队”操作被移除`);
});
assert.match(overlaySource, /id="modal-dispatch-bol-print"/, '缺少 BOL 打印预览弹窗');
assert.match(overlaySource, /id="dispatch-bol-print-title"/, 'BOL 打印预览缺少标题');
assert.match(overlaySource, /id="dispatch-bol-print-sheet"/, 'BOL 打印预览缺少单据容器');
assert.match(overlaySource, /onclick="printDispatchBol\(\)"/, 'BOL 打印预览缺少打印按钮');
const printDataBlock = appSource.match(/const dispatchBolPrintData\s*=\s*\[([\s\S]*?)\n\];/);
assert.ok(printDataBlock, '缺少派送单 BOL 打印数据');
const dispatchBolPrintData = vm.runInNewContext(`[${printDataBlock[1]}]`);
assert.equal(dispatchBolPrintData.length, 5, 'BOL 打印示例数据不是 5 条');
dispatchBolPrintData.forEach((record, index) => {
const dispatchNo = `YWBHK20061500${index + 1}`;
const bolNo = `BOL2026061500${index + 1}`;
assert.equal(record.dispatchNo, dispatchNo, `${dispatchNo} BOL 数据缺少派送单号`);
assert.equal(record.bolNo, bolNo, `${dispatchNo} BOL 数据缺少对应内部 bolNo`);
['trailerNo', 'sealNo', 'displayBolNo', 'shipmentId', 'poNo', 'fbaAddress'].forEach((field) => {
assert.equal(record[field], '', `${dispatchNo} BOL 数据 ${field} 必须为空白`);
});
assert.ok(Array.isArray(record.lines) && record.lines.length > 0, `${dispatchNo} BOL 数据必须保留明细`);
record.lines.forEach((line) => {
assert.ok(typeof line.poNo === 'string' && line.poNo.length > 0, `${dispatchNo} 每条 BOL 明细必须保留非空 poNo`);
});
});
assert.match(appSource, /function dispatchBolPrintBlankText\(value\)/, '缺少 BOL 空白字段格式化函数');
const blankTextBlock = appSource.match(/function dispatchBolPrintBlankText\(value\)\{([\s\S]*?)\n\}/);
assert.ok(blankTextBlock, '无法读取 BOL 空白字段格式化函数');
assert.ok(blankTextBlock[1].includes("value===undefined||value===null||String(value).trim()===''?'':String(value)"), 'BOL 空白字段必须保持为空字符串');
assert.match(blankTextBlock[1], /return lmoEsc\(text\)/, 'BOL 空白字段仍需经过 HTML 转义');
const renderDispatchBolPrintBlock = appSource.match(/function renderDispatchBolPrint\(data\)\{([\s\S]*?)\n\}\nfunction openDispatchBolPrint\(bolNo\)/);
assert.ok(renderDispatchBolPrintBlock, '缺少 BOL 模板渲染函数');
const renderDispatchBolPrintSource = renderDispatchBolPrintBlock[1];
assert.match(renderDispatchBolPrintSource, /const blank=dispatchBolPrintBlankText;/, 'BOL 模板未绑定空白字段格式化函数');
['trailerNo', 'sealNo', 'displayBolNo', 'shipmentId', 'poNo', 'fbaAddress'].forEach((field) => {
assert.match(renderDispatchBolPrintSource, new RegExp(`blank\\(data\\.${field}\\)`), `BOL 模板 ${field} 未使用 blank(...)`);
});
assert.match(renderDispatchBolPrintSource, /<td>\$\{t\(line\.poNo\)\}<\/td>/, 'BOL 明细 poNo 必须继续使用 t(line.poNo)');
assert.doesNotMatch(renderDispatchBolPrintSource, /t\(data\.bolNo\)/, '模板不得显示内部 bolNo');
['Container #', 'Pallet #', 'FBA Warehouse #', 'Warehouse No #', 'FBA #', 'PO #', 'CTNS'].forEach((header) => {
assert.ok(appSource.includes(header), `BOL 模板缺少表头 ${header}`);
});
['BILL OF LADING', 'SHIP FROM', 'CARRIER', 'SHIP TO', 'REFERENCE'].forEach((heading) => {
assert.ok(appSource.includes(heading), `BOL 模板缺少标题 ${heading}`);
});
const lineCount = (printDataBlock[1].match(/containerNo:/g) || []).length;
const ctnsCount = (printDataBlock[1].match(/ctns:\d+/g) || []).length;
assert.ok(lineCount > 0, 'BOL 打印示例数据缺少明细');
assert.equal(ctnsCount, lineCount, '每条 BOL 明细必须独立维护非负整数 ctns');
assert.match(printDataBlock[1], /palletNo:'MSKU-01'[\s\S]*?palletNo:'MSKU-01'/, '示例数据缺少连续相同托盘,无法验收纵向合并');
assert.match(appSource, /function getDispatchBolPalletRowspans\(lines\)/, '缺少连续托盘分组函数');
assert.match(appSource, /rowspan="\$\{palletRowspans\[index\]\}"/, '连续相同托盘未通过 rowspan 纵向合并');
assert.match(appSource, /function normalizeDispatchBolCtns\(value\)[\s\S]*?Number\.isFinite\(Number\(value\)\)/, 'CTNS 未按独立数值字段规范化');
assert.match(appSource, /reduce\(\(sum,line\)=>sum\+normalizeDispatchBolCtns\(line\.ctns\),0\)/, '总箱数未对全部明细 CTNS 求和');
assert.match(appSource, /总箱数 \/ Total CTNS:/, 'BOL 模板缺少总箱数标签');
assert.match(appSource, /colspan="7" class="dispatch-bol-empty"/, '空明细未适配七列表格');
assert.match(appSource, /function openDispatchBolPrint\(bolNo\)/, '缺少打开 BOL 打印预览函数');
assert.match(appSource, /未找到该派送单的 BOL 打印数据/, '缺少 BOL 打印数据错误提示');
assert.match(appSource, /class="dispatch-bol-print-action"/, '动态派送单未增加打印 BOL 入口');
assert.match(appSource, /document\.body\.classList\.add\('dispatch-bol-printing'\)/, '打印前未启用 BOL 打印隔离状态');
assert.match(appSource, /window\.print\(\)/, 'BOL 打印未调用浏览器打印');
assert.match(shellSource, /\.dispatch-bol-sheet/, '缺少 BOL 单据样式');
assert.match(shellSource, /@page dispatchBol\{size:A4 portrait/, '缺少 BOL A4 纵向页面规则');
assert.match(shellSource, /body\.dispatch-bol-printing/, '缺少 BOL 打印隔离样式');
assert.match(shellSource, /\.dispatch-bol-document-title\{[^}]*background:#000[^}]*color:#fff/, 'BOL 主标题未使用黑底白字');
assert.match(shellSource, /\.dispatch-bol-section-title\{[^}]*background:#000[^}]*color:#fff/, 'BOL 分区标题未使用黑底白字');
assert.match(shellSource, /\.dispatch-bol-lines th\{[^}]*background:#fff[^}]*color:#000/, 'BOL 明细表头未保持模板白底黑字');
assert.match(shellSource, /\.dispatch-bol-lines td\{[^}]*white-space:nowrap/, 'BOL 明细值未保持单行排版');
assert.match(shellSource, /\.dispatch-bol-lines th:nth-child\(7\)\{width:7%\}/, 'BOL 明细未设置 CTNS 列宽');
assert.match(shellSource, /\.dispatch-bol-total\{[^}]*display:grid[^}]*grid-template-columns:1fr auto/, '总箱数未按左标签、右数字布局');
assert.match(completeSource, /id="modal-dispatch-bol-print"/, '完整原型未包含 BOL 打印预览弹窗,请重新构建');
assert.match(completeSource, /function openDispatchBolPrint\(bolNo\)/, '完整原型未包含 BOL 打印逻辑,请重新构建');
console.log('验证通过:派送单列表已接入基于模板的 BOL 预览与 A4 打印。');