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

154 lines
8.0 KiB
JavaScript

#!/usr/bin/env node
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const root = path.resolve(__dirname, '..');
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'prototype-src', 'manifest.json'), 'utf8'));
const html = fs.readFileSync(path.join(root, manifest.source), 'utf8');
const failures = [];
function expect(condition, message) {
if (!condition) failures.push(message);
}
function section(startMarker, endMarker) {
const start = html.indexOf(startMarker);
const end = html.indexOf(endMarker, start + startMarker.length);
return start >= 0 && end > start ? html.slice(start, end) : '';
}
function functionSource(name) {
const start = html.indexOf(`function ${name}(`);
if (start < 0) return '';
const braceStart = html.indexOf('{', start);
let depth = 0;
let quote = '';
let escaped = false;
for (let index = braceStart; index < html.length; index += 1) {
const char = html[index];
if (quote) {
if (escaped) escaped = false;
else if (char === '\\') escaped = true;
else if (char === quote) quote = '';
continue;
}
if (char === "'" || char === '"' || char === '`') {
quote = char;
continue;
}
if (char === '{') depth += 1;
if (char === '}' && --depth === 0) return html.slice(start, index + 1);
}
return '';
}
const ctPage = section('<!-- ==================== CONTAINER LIST ==================== -->', '<!-- ==================== CONTAINER MANIFEST ==================== -->');
const lastMilePage = section('<div class="page" id="page-last-mile-orders">', '<!-- ==================== OVERSEAS WAREHOUSE - 尾程订单详情');
const abnormalModal = section('<div class="modal-overlay" id="modal-ct-abnormal">', '<!-- ==================== 新增货柜费用 MODAL');
expect(/>货柜状态<\/th>\s*<th[^>]*>是否异常<\/th>/.test(ctPage), '货柜列表应在货柜状态后显示“是否异常”列');
expect(/>集装箱号<\/th>\s*<th[^>]*>货柜状态<\/th>\s*<th[^>]*>是否异常<\/th>/.test(lastMilePage), '尾程订单列顺序应为“集装箱号、货柜状态、是否异常”');
expect(/id="ct-abnormal-remark"[^>]*maxlength="200"/.test(abnormalModal), '异常处理弹窗应提供最多 200 字的备注输入');
expect(/id="ct-abnormal-submit"/.test(abnormalModal), '异常处理弹窗应提供动态提交按钮');
expect(/id="ct-abnormal-hint"/.test(abnormalModal), '异常处理弹窗应说明备注规则');
const names = [
'findContainerByNo',
'renderContainerStatusTag',
'getContainerAbnormalState',
'renderAbnormalTag',
'openCtAbnormalModal',
'saveCtAbnormalState'
];
const sources = Object.fromEntries(names.map(name => [name, functionSource(name)]));
names.forEach(name => expect(Boolean(sources[name]), `缺少 ${name} 函数`));
const ctRenderer = functionSource('renderCtList');
const lastMileRenderer = functionSource('renderLastMileOrders');
const detailRenderer = functionSource('renderLastMileOrderDetail');
expect(/openCtAbnormalModal/.test(ctRenderer), '货柜操作栏应提供异常标记或取消异常入口');
expect(/renderContainerStatusTag/.test(ctRenderer), '货柜列表应复用公共货柜状态标签');
expect(/findContainerByNo/.test(lastMileRenderer) && /renderContainerStatusTag/.test(lastMileRenderer), '尾程列表应实时查找并渲染货柜状态');
expect(/getContainerAbnormalState/.test(lastMileRenderer), '尾程列表应按集装箱号读取货柜异常状态');
expect(/renderAbnormalTag/.test(lastMileRenderer), '尾程列表应渲染异常状态标签');
expect(/colspan="33"/.test(lastMileRenderer), '尾程列表空状态应覆盖新增后的 33 列');
expect(/货柜异常/.test(detailRenderer) && /异常原因/.test(detailRenderer), '尾程详情应显示货柜异常状态和原因');
expect(/containerNo:'FFAU6685194'/.test(html), '应提供一条与货柜列表可见关联的尾程样例');
if (names.every(name => sources[name])) {
const elements = {
'ct-abnormal-title': {textContent: ''},
'ct-abnormal-container': {textContent: ''},
'ct-abnormal-label': {textContent: ''},
'ct-abnormal-required': {textContent: ''},
'ct-abnormal-hint': {textContent: ''},
'ct-abnormal-remark': {value: '', placeholder: '', focus() {}},
'ct-abnormal-submit': {textContent: '', className: ''}
};
const calls = {ct: 0, lmo: 0, toast: [], shown: [], hidden: []};
const context = {
ctListData: [{no: 'CT-TEST', status: '待装柜', isAbnormal: false, abnormalReason: '', abnormalLogs: []}],
lastMileOrderData: [{no: 'LMO-TEST', containerNo: 'CT-TEST'}],
currentCtAbnormalNo: '',
currentCtAbnormalAction: '',
document: {getElementById: id => elements[id] || null},
showModal: id => calls.shown.push(id),
hideModal: id => calls.hidden.push(id),
showToast: (message, type) => calls.toast.push({message, type}),
renderCtList: () => { calls.ct += 1; },
renderLastMileOrders: () => { calls.lmo += 1; },
getLastMileFilteredOrders: () => context.lastMileOrderData,
lmoNow: () => '2026-08-11 20:30',
lmoEsc: value => String(value == null ? '' : value)
.replace(/[&<>"']/g, char => ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'}[char]))
};
vm.createContext(context);
try {
vm.runInContext(names.map(name => sources[name]).join('\n'), context, {timeout: 1000});
expect(context.findContainerByNo(' ct-test ') === context.ctListData[0], '货柜查找应忽略首尾空格和大小写');
expect(/待装柜/.test(context.renderContainerStatusTag(context.findContainerByNo('CT-TEST').status)), '关联货柜应显示同步状态');
const unknownContainer = context.findContainerByNo('UNKNOWN');
expect(context.renderContainerStatusTag(unknownContainer && unknownContainer.status) === '—', '未关联货柜应显示“—”');
context.openCtAbnormalModal('CT-TEST');
expect(context.currentCtAbnormalAction === 'mark', '正常货柜应进入标记异常模式');
elements['ct-abnormal-remark'].value = '';
expect(context.saveCtAbnormalState() === true, '标记异常时空原因应允许提交');
expect(context.ctListData[0].isAbnormal === true, '标记后货柜应为异常状态');
expect(context.getContainerAbnormalState('CT-TEST').isAbnormal === true, '关联尾程应读取到异常状态');
expect(context.ctListData[0].abnormalLogs.length === 1, '标记异常应新增一条历史记录');
context.openCtAbnormalModal('CT-TEST');
expect(context.currentCtAbnormalAction === 'cancel', '异常货柜应进入取消异常模式');
elements['ct-abnormal-remark'].value = ' ';
expect(context.saveCtAbnormalState() === false, '取消异常时空备注应被拦截');
expect(context.ctListData[0].isAbnormal === true, '取消备注为空时应保持异常状态');
expect(calls.toast.some(item => /取消备注/.test(item.message)), '取消备注为空时应显示明确提示');
elements['ct-abnormal-remark'].value = '问题已核实并解除';
expect(context.saveCtAbnormalState() === true, '填写取消备注后应允许提交');
expect(context.ctListData[0].isAbnormal === false, '取消后货柜应恢复正常');
expect(context.getContainerAbnormalState('CT-TEST').isAbnormal === false, '关联尾程应同步恢复正常');
expect(context.ctListData[0].abnormalLogs.length === 2, '取消异常应保留第二条历史记录');
expect(calls.ct === 2 && calls.lmo === 2, '成功变更后应同时刷新货柜和尾程列表');
context.ctListData[0].isAbnormal = true;
context.ctListData[0].abnormalReason = '<异常原因>';
const tag = context.renderAbnormalTag(context.getContainerAbnormalState('CT-TEST'));
expect(/异常/.test(tag) && /&lt;异常原因&gt;/.test(tag), '异常标签应安全显示异常原因提示');
} catch (error) {
failures.push(`异常状态行为验证执行失败:${error.message}`);
}
}
if (failures.length) {
console.error('CONTAINER_ABNORMAL_SYNC_CHECK_FAIL');
failures.forEach(failure => console.error(`- ${failure}`));
process.exitCode = 1;
} else {
console.log('CONTAINER_ABNORMAL_SYNC_CHECK_PASS');
}