112 lines
6.7 KiB
JavaScript
112 lines
6.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
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 overlays = fs.readFileSync(path.join(root, 'prototype-src/overlays.html'), 'utf8');
|
|
const general = fs.readFileSync(path.join(root, 'prototype-src/modules/10-general.html'), 'utf8');
|
|
const app = fs.readFileSync(path.join(root, 'prototype-src/app.js'), 'utf8');
|
|
const shell = fs.readFileSync(path.join(root, 'prototype-src/shell.html'), 'utf8');
|
|
|
|
function extractFunction(source, name) {
|
|
const start = source.indexOf(`function ${name}(`);
|
|
assert.notEqual(start, -1, `缺少 ${name} 函数`);
|
|
const bodyStart = source.indexOf('{', start);
|
|
let depth = 0;
|
|
let quote = '';
|
|
let escaped = false;
|
|
for (let index = bodyStart; index < source.length; index += 1) {
|
|
const char = source[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 -= 1;
|
|
if (depth === 0) return source.slice(start, index + 1);
|
|
}
|
|
}
|
|
throw new Error(`${name} 函数未正确闭合`);
|
|
}
|
|
|
|
assert.match(overlays, /id="ef-current-vehicle"/, '编辑车队弹窗缺少当前车辆摘要');
|
|
assert.match(overlays, /id="ef-vehicle-id"[^>]*onchange="updateEditFleetReasonState\(\)"/, '编辑车队弹窗缺少外派车辆下拉框');
|
|
assert.match(overlays, /id="ef-change-reason"[^>]*maxlength="200"/, '编辑车队弹窗缺少更换原因字段或长度限制');
|
|
assert.match(overlays, /id="ef-change-reason-hint"/, '编辑车队弹窗缺少原因规则提示');
|
|
assert.doesNotMatch(overlays, /id="ef-fleet-name"|id="ef-fleet-phone"|id="ef-fleet-email"/, '编辑车队弹窗仍保留自由输入字段');
|
|
|
|
const dispatchPanelStart = general.indexOf('id="panel-vehicle-dispatch"');
|
|
const dispatchPanelEnd = general.indexOf('<div class="pagination">', dispatchPanelStart);
|
|
const dispatchPanel = general.slice(dispatchPanelStart, dispatchPanelEnd);
|
|
assert.match(dispatchPanel, /<th[^>]*>当前派送单<\/th>/, '外派车辆表格缺少当前派送单列');
|
|
assert.match(dispatchPanel, /<th[^>]*>最近更换原因<\/th>/, '外派车辆表格缺少最近更换原因列');
|
|
assert.match(general, /id="page-vehicle-detail"/, '车辆详情缺少独立页面');
|
|
assert.match(general, /id="vehicle-detail-history-body"/, '车辆详情缺少派送更换记录表体');
|
|
|
|
const ensureSource = extractFunction(app, 'ensureFleetSyncState');
|
|
const labelSource = extractFunction(app, 'getFleetVehicleLabel');
|
|
const applySource = extractFunction(app, 'applyDispatchFleetChange');
|
|
const runtime = {Array, Object};
|
|
vm.createContext(runtime);
|
|
vm.runInContext(
|
|
`${ensureSource};${labelSource};${applySource};` +
|
|
'this.applyDispatchFleetChange=applyDispatchFleetChange;',
|
|
runtime
|
|
);
|
|
|
|
const dispatch = {dispatchNo: 'DSP001', bolNo: 'BOL001'};
|
|
const vehicleA = {id: 'VD1', plate: '沪A10001', driver: '顺通物流', phone: '021-10001'};
|
|
const vehicleB = {id: 'VD2', plate: '浙B20002', driver: '安捷运输', phone: '0571-20002'};
|
|
|
|
const first = runtime.applyDispatchFleetChange(dispatch, null, vehicleA, '', '2026-08-14 10:00', '张经理');
|
|
assert.equal(first.type, 'initial', '首次分配未返回 initial');
|
|
assert.equal(dispatch.fleetVehicleId, 'VD1', '首次分配未写入派送单当前车辆');
|
|
assert.equal(dispatch.fleetVehiclePlate, '沪A10001', '首次分配未写入车牌快照');
|
|
assert.equal(vehicleA.currentDispatchNo, 'DSP001', '首次分配未同步车辆当前派送单');
|
|
assert.equal(vehicleA.lastFleetChangeReason, '首次分配', '首次分配未写入车辆摘要原因');
|
|
assert.equal(dispatch.fleetChangeHistory.length, 1, '首次分配未写入派送历史');
|
|
assert.equal(vehicleA.fleetChangeHistory[0].action, '首次分配', '首次分配未写入车辆历史');
|
|
|
|
const changed = runtime.applyDispatchFleetChange(dispatch, vehicleA, vehicleB, '原车辆临时维修', '2026-08-14 11:00', '张经理');
|
|
assert.equal(changed.type, 'changed', '车辆更换未返回 changed');
|
|
assert.equal(dispatch.fleetVehicleId, 'VD2', '车辆更换未更新派送单当前车辆');
|
|
assert.equal(dispatch.fleetChangeReason, '原车辆临时维修', '车辆更换未写入最近原因');
|
|
assert.equal(vehicleA.currentDispatchNo, '', '车辆更换未释放原车辆');
|
|
assert.equal(vehicleB.currentDispatchNo, 'DSP001', '车辆更换未关联新车辆');
|
|
assert.equal(dispatch.fleetChangeHistory.length, 2, '车辆更换未追加派送历史');
|
|
assert.equal(vehicleA.fleetChangeHistory[0].action, '更换离开', '原车辆历史动作错误');
|
|
assert.equal(vehicleB.fleetChangeHistory[0].action, '更换接入', '新车辆历史动作错误');
|
|
assert.equal(vehicleB.fleetChangeHistory[0].reason, '原车辆临时维修', '新车辆历史缺少更换原因');
|
|
|
|
const unchanged = runtime.applyDispatchFleetChange(dispatch, vehicleB, vehicleB, '不会写入', '2026-08-14 12:00', '张经理');
|
|
assert.equal(unchanged.type, 'unchanged', '重复保存同一车辆未返回 unchanged');
|
|
assert.equal(dispatch.fleetChangeHistory.length, 2, '重复保存同一车辆产生了重复派送历史');
|
|
assert.equal(vehicleB.fleetChangeHistory.length, 1, '重复保存同一车辆产生了重复车辆历史');
|
|
|
|
assert.match(app, /function\s+getDispatchRecordByBolNo\s*\(/, '缺少通过 BOL 查找派送单的函数');
|
|
assert.match(app, /function\s+updateEditFleetReasonState\s*\(/, '缺少更换原因状态更新函数');
|
|
assert.match(app, /function\s+renderDispatchFleetCells\s*\(/, '缺少派送列表车辆回显函数');
|
|
assert.match(app, /row\.cells\[11\]/, '派送列表未更新车牌单元格');
|
|
assert.match(app, /row\.cells\[12\]/, '派送列表未更新司机或外派单位单元格');
|
|
assert.match(app, /请填写更换原因/, '车辆更换缺少原因必填校验');
|
|
assert.match(app, /车队信息已保存并同步到外派车辆/, '缺少同步成功提示');
|
|
assert.match(app, /currentDispatchNo/, '外派车辆未保存当前派送单');
|
|
assert.match(app, /lastFleetChangeReason/, '外派车辆未保存最近更换原因');
|
|
assert.match(app, /function\s+renderVehicleFleetHistory\s*\(/, '缺少车辆更换历史渲染函数');
|
|
assert.match(app, /暂无派送更换记录/, '车辆详情缺少历史空态');
|
|
assert.match(app, /vehicle-fleet-reason/, '外派车辆列表未渲染原因省略样式');
|
|
assert.match(shell, /\.edit-fleet-current/, '缺少当前车辆摘要样式');
|
|
assert.match(shell, /\.vehicle-detail-table/, '缺少车辆详情表格样式');
|
|
|
|
console.log('DISPATCH_FLEET_CHANGE_SYNC_CHECK_PASS');
|