1
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const tail = fs.readFileSync(path.join(root, 'prototype-src', 'tail.html'), 'utf8');
|
||||
const moduleHtml = fs.readFileSync(
|
||||
path.join(root, 'prototype-src', 'modules', '04-overseas-delivery.html'),
|
||||
'utf8',
|
||||
);
|
||||
const app = fs.readFileSync(path.join(root, 'prototype-src', 'app.js'), 'utf8');
|
||||
|
||||
function extractFunction(name, source) {
|
||||
const declaration = new RegExp(`function\\s+${name}\\s*\\(`).exec(source);
|
||||
if (!declaration) return '';
|
||||
const bodyStart = source.indexOf('{', declaration.index + declaration[0].length);
|
||||
let depth = 0;
|
||||
let state = 'code';
|
||||
let escaped = false;
|
||||
for (let index = bodyStart; index < source.length; index += 1) {
|
||||
const char = source[index];
|
||||
const next = source[index + 1];
|
||||
if (state === 'line-comment') {
|
||||
if (char === '\n') state = 'code';
|
||||
continue;
|
||||
}
|
||||
if (state === 'block-comment') {
|
||||
if (char === '*' && next === '/') {
|
||||
state = 'code';
|
||||
index += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (state !== 'code') {
|
||||
if (escaped) escaped = false;
|
||||
else if (char === '\\') escaped = true;
|
||||
else if (
|
||||
(state === 'single' && char === "'")
|
||||
|| (state === 'double' && char === '"')
|
||||
|| (state === 'template' && char === '`')
|
||||
) state = 'code';
|
||||
continue;
|
||||
}
|
||||
if (char === '/' && next === '/') {
|
||||
state = 'line-comment';
|
||||
index += 1;
|
||||
} else if (char === '/' && next === '*') {
|
||||
state = 'block-comment';
|
||||
index += 1;
|
||||
} else if (char === "'") state = 'single';
|
||||
else if (char === '"') state = 'double';
|
||||
else if (char === '`') state = 'template';
|
||||
else if (char === '{') depth += 1;
|
||||
else if (char === '}') {
|
||||
depth -= 1;
|
||||
if (depth === 0) return source.slice(declaration.index, index + 1);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function loadPureFunction(name) {
|
||||
const source = extractFunction(name, app);
|
||||
assert.ok(source, `缺少 ${name} 函数`);
|
||||
return new Function(`${source}\nreturn ${name};`)();
|
||||
}
|
||||
|
||||
assert.match(
|
||||
tail,
|
||||
/id="bol-deliveryMethod"[^>]*onchange="handleOutboundVehicleDeliveryMethodChange\(\)"/,
|
||||
'派送方式变化时应更新快递字段',
|
||||
);
|
||||
assert.match(tail, /id="bol-courier-company-field"[^>]*hidden/, '缺少默认隐藏的快递公司字段');
|
||||
assert.match(tail, /id="bol-courierCompanyId"/, '缺少快递公司选择框');
|
||||
assert.match(
|
||||
tail,
|
||||
/<label>派送费用(USD) \*<\/label><input id="bol-dispatchFee"[^>]*type="number"[^>]*min="0"[^>]*step="0\.01"[^>]*required/,
|
||||
'转派送弹框应只保留必填的派送费用',
|
||||
);
|
||||
assert.doesNotMatch(tail, /id="bol-estimatedDispatchFee"/, '不应保留预估派送费用输入');
|
||||
assert.doesNotMatch(tail, /id="bol-courierFee"/, '不应保留独立快递费用输入');
|
||||
assert.doesNotMatch(tail, /id="bol-courier-fee-field"/, '不应保留独立快递费用字段容器');
|
||||
assert.doesNotMatch(
|
||||
tail,
|
||||
/<\/template>\s*<\/div>\s*<\/div>\s*<div class="modal-footer"/,
|
||||
'转派送按钮栏必须位于弹框主体内,避免移动端 flex 挤压',
|
||||
);
|
||||
const feePanel = /<div id="dispatch-fee-panel"[\s\S]*?<\/table>[\s\S]*?<\/div>/.exec(moduleHtml)?.[0] || '';
|
||||
assert.match(feePanel, /<tbody id="tbody-dispatch-fee">/, '费用明细表缺少稳定表体 ID');
|
||||
assert.match(moduleHtml, /id="dispatch-fee-total"/, '费用明细缺少动态总数 ID');
|
||||
|
||||
const validate = loadPureFunction('validateOutboundVehicleDeliveryDraft');
|
||||
const companies = [
|
||||
{id: 'E001', name: 'DHL', status: '合作中'},
|
||||
{id: 'E002', name: '停用快递', status: '已终止'},
|
||||
];
|
||||
const base = {
|
||||
deliveryDate: '2026-08-14',
|
||||
pickupMethod: '一提一卸',
|
||||
loadType: '托盘',
|
||||
dispatchFee: 200,
|
||||
loadingFee: 0,
|
||||
};
|
||||
assert.equal(validate({...base, deliveryMethod: '客户自提'}, companies), '');
|
||||
assert.match(
|
||||
validate({...base, deliveryMethod: '客户自提', dispatchFee: ''}, companies),
|
||||
/派送费用/,
|
||||
);
|
||||
assert.match(
|
||||
validate({...base, deliveryMethod: '客户自提', dispatchFee: '-1'}, companies),
|
||||
/派送费用/,
|
||||
);
|
||||
assert.match(
|
||||
validate({...base, deliveryMethod: '客户自提', dispatchFee: 'not-a-number'}, companies),
|
||||
/派送费用/,
|
||||
);
|
||||
assert.match(
|
||||
validate({...base, deliveryMethod: '快递', courierCompanyId: ''}, companies),
|
||||
/快递公司/,
|
||||
);
|
||||
assert.match(
|
||||
validate({...base, deliveryMethod: '快递', courierCompanyId: 'E002'}, companies),
|
||||
/快递公司/,
|
||||
);
|
||||
assert.equal(
|
||||
validate({...base, deliveryMethod: '快递', courierCompanyId: 'E001'}, companies),
|
||||
'',
|
||||
);
|
||||
|
||||
const getDetails = loadPureFunction('getOutboundVehicleCourierFeeDetails');
|
||||
assert.deepEqual(
|
||||
getDetails([
|
||||
{
|
||||
dispatchNo: 'PSD001',
|
||||
source: 'outboundVehicle',
|
||||
deliveryMethod: '快递',
|
||||
courierCompanyName: 'DHL',
|
||||
dispatchFee: 12.5,
|
||||
appointmentDate: '2026-08-14',
|
||||
},
|
||||
{dispatchNo: 'PSD002', source: 'outboundVehicle', deliveryMethod: '客户自提', dispatchFee: 99},
|
||||
{dispatchNo: 'PSD003', source: 'outboundVehicle', deliveryMethod: '快递', dispatchFee: -1},
|
||||
]),
|
||||
[{dispatchNo: 'PSD001', company: 'DHL', businessDate: '2026-08-14', currency: 'USD', amount: 12.5}],
|
||||
);
|
||||
|
||||
const deliveryMethodChange = extractFunction('handleOutboundVehicleDeliveryMethodChange', app);
|
||||
assert.match(deliveryMethodChange, /status\s*===\s*'合作中'/, '快递公司下拉应只显示合作中公司');
|
||||
assert.doesNotMatch(deliveryMethodChange, /bol-courierFee|bol-courier-fee-field/, '派送方式联动不应再控制独立快递费用');
|
||||
|
||||
const saveBol = extractFunction('saveBol', app);
|
||||
assert.match(saveBol, /validateOutboundVehicleDeliveryDraft\(draft\s*,\s*genExpressData\)/, '保存时应使用快递公司主数据校验');
|
||||
assert.match(saveBol, /courierCompanyId/, '派送单应保存快递公司 ID 快照');
|
||||
assert.match(saveBol, /courierCompanyName/, '派送单应保存快递公司名称快照');
|
||||
assert.doesNotMatch(saveBol, /value\('bol-estimatedDispatchFee'\)/, '保存时不应读取预估派送费用输入');
|
||||
assert.doesNotMatch(saveBol, /value\('bol-courierFee'\)/, '保存时不应读取独立快递费用输入');
|
||||
assert.match(saveBol, /estimatedDispatchFee\s*:\s*deliveryFee/, '现有预估费用展示应复用唯一派送费用');
|
||||
|
||||
const renderFeeRows = extractFunction('renderOutboundVehicleCourierFeeRows', app);
|
||||
assert.match(renderFeeRows, /data-generated-courier-fee/, '动态费用行应支持幂等清理');
|
||||
assert.match(renderFeeRows, /待审核/, '自动生成快递费用应为待审核');
|
||||
assert.match(renderFeeRows, /应付/, '自动生成快递费用应为应付');
|
||||
assert.match(renderFeeRows, /快递费/, '自动生成费用类型应为快递费');
|
||||
assert.match(renderFeeRows, /排车转派送自动生成/, '自动生成快递费用应记录来源备注');
|
||||
|
||||
assert.match(
|
||||
extractFunction('renderLastMileDispatchRows', app),
|
||||
/renderOutboundVehicleCourierFeeRows\(\)/,
|
||||
'派送单列表刷新时应同步刷新快递费用明细',
|
||||
);
|
||||
assert.match(
|
||||
extractFunction('switchDispatchTab', app),
|
||||
/tab\s*===\s*'fee'[^;]*renderOutboundVehicleCourierFeeRows\(\)/,
|
||||
'切换到费用明细标签时应刷新快递费用明细',
|
||||
);
|
||||
|
||||
console.log('OUTBOUND_VEHICLE_COURIER_FEE_CHECK_PASS');
|
||||
Reference in New Issue
Block a user