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

140 lines
5.9 KiB
JavaScript

#!/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 moduleHtml = fs.readFileSync(path.join(root, 'prototype-src', 'modules', '03-overseas-warehouse.html'), 'utf8');
const overlaysHtml = fs.readFileSync(path.join(root, 'prototype-src', 'overlays.html'), 'utf8');
const appJs = fs.readFileSync(path.join(root, 'prototype-src', 'app.js'), 'utf8');
const failures = [];
function expectMatch(source, pattern, label) {
if (!pattern.test(source)) failures.push(`missing ${label}`);
}
function functionSource(name, source = appJs) {
const match = new RegExp(`function\\s+${name}\\s*\\([^)]*\\)\\s*\\{`).exec(source);
if (!match) return '';
const start = source.indexOf('{', match.index);
let depth = 0;
let quote = '';
let escaped = false;
for (let index = start; 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 === 0) return source.slice(match.index, index + 1);
}
return '';
}
const statusSelect = moduleHtml.match(/<select\b(?=[^>]*\bid="fboo-status")[^>]*>([\s\S]*?)<\/select>/);
if (!statusSelect) failures.push('missing 出库单状态筛选');
else expectMatch(statusSelect[1], /<option>已取消<\/option>/, '已取消筛选选项');
expectMatch(overlaysHtml, /id="modal-fboo-cancel"/, '取消出库单弹窗');
expectMatch(overlaysHtml, /id="fboo-cancel-summary"/, '取消出库单摘要');
expectMatch(overlaysHtml, /id="fboo-cancel-no"/, '取消出库单号');
expectMatch(overlaysHtml, /id="fboo-cancel-reason"[^>]*maxlength="200"/, '取消原因字段');
expectMatch(overlaysHtml, /onclick="confirmFbaOutboundCancellation\(\)"/, '确认取消按钮');
for (const name of [
'getFbaOutboundStockRows',
'prepareFbaOutboundCancellation',
'openFbaOutboundCancellation',
'confirmFbaOutboundCancellation',
'getFilteredFbaOutboundOrders',
'fbooApplyCurrentSearch',
]) {
if (!functionSource(name)) failures.push(`missing ${name} function`);
}
expectMatch(appJs, /data-fboo-cancel-order/, '列表取消数据入口');
expectMatch(appJs, /openFbaOutboundCancellation\(cancel\.dataset\.fbooCancelOrder\)/, '取消入口事件委托');
expectMatch(appJs, /['"]已取消['"]\s*:\s*['"]tag-gray['"]/, '已取消灰色标签');
expectMatch(appJs, /仅待发货出库单可取消/, '仅待发货取消校验');
expectMatch(appJs, /取消原因/, '详情取消原因');
expectMatch(appJs, /取消时间/, '详情取消时间');
const stockRowsSource = functionSource('getFbaOutboundStockRows');
const prepareSource = functionSource('prepareFbaOutboundCancellation');
if (stockRowsSource && prepareSource) {
try {
const prepareFbaOutboundCancellation = new Function(
`${stockRowsSource}\n${prepareSource}\nreturn prepareFbaOutboundCancellation;`,
)();
const stocks = [
{ sku: 'SKU-A', wh: '洛杉矶仓', availQty: 10 },
{ sku: 'SKU-B', wh: '纽约仓', availQty: 20 },
];
const order = {
outboundNo: 'FBOO-TEST',
status: '待发货',
items: [
{ stockIndex: 0, sku: 'SKU-A', warehouse: '洛杉矶仓', quantity: 3 },
{ stockIndex: 0, sku: 'SKU-A', warehouse: '洛杉矶仓', quantity: 2 },
{ stockIndex: 1, sku: 'SKU-B', warehouse: '纽约仓', quantity: 4 },
],
};
const originalStocks = structuredClone(stocks);
const originalOrder = structuredClone(order);
const plan = prepareFbaOutboundCancellation(order, stocks, ' 客户取消 ', '2026-08-14 20:00');
assert.equal(plan.ok, true);
assert.deepEqual(plan.stockUpdates, { 0: 15, 1: 24 });
assert.deepEqual(plan.orderChanges, {
status: '已取消',
cancelReason: '客户取消',
cancelledAt: '2026-08-14 20:00',
updatedAt: '2026-08-14 20:00',
});
assert.deepEqual(stocks, originalStocks);
assert.deepEqual(order, originalOrder);
const shipped = prepareFbaOutboundCancellation({ ...order, status: '已发货' }, stocks, '客户取消', '2026-08-14 20:00');
assert.deepEqual(shipped, { ok: false, message: '仅待发货出库单可取消' });
const emptyReason = prepareFbaOutboundCancellation(order, stocks, ' ', '2026-08-14 20:00');
assert.deepEqual(emptyReason, { ok: false, message: '请填写取消原因' });
const missingStockOrder = {
outboundNo: 'FBOO-MISSING',
status: '待发货',
items: [{ stockIndex: 9, sku: 'SKU-MISSING', warehouse: '未知仓', quantity: 2 }],
};
const missingOriginal = structuredClone(stocks);
const missing = prepareFbaOutboundCancellation(missingStockOrder, stocks, '客户取消', '2026-08-14 20:00');
assert.deepEqual(missing, { ok: false, message: '库存返还校验失败,请刷新后重试' });
assert.deepEqual(stocks, missingOriginal);
assert.equal(missingStockOrder.status, '待发货');
} catch (error) {
failures.push(`取消计划行为失败:${error.message}`);
}
}
const confirmSource = functionSource('confirmFbaOutboundCancellation');
if (confirmSource) {
expectMatch(confirmSource, /prepareFbaOutboundCancellation/, '提交调用取消计划');
expectMatch(confirmSource, /warehouseStockData\[index\]\.availQty\s*=\s*plan\.stockUpdates\[index\]/, '提交一次性写回库存');
expectMatch(confirmSource, /Object\.assign\(order,plan\.orderChanges\)/, '提交写回取消状态');
expectMatch(confirmSource, /fbooApplyCurrentSearch\(\)/, '取消后保留筛选结果');
}
if (failures.length) {
console.error('FBA_OUTBOUND_CANCEL_CHECK_FAIL');
failures.forEach((failure) => console.error(`- ${failure}`));
process.exitCode = 1;
} else {
console.log('FBA_OUTBOUND_CANCEL_CHECK_PASS');
}