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

180 lines
7.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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');