import test from 'node:test' import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' const read = (path) => readFile(new URL(path, import.meta.url), 'utf8') test('system pallet action binds through one request and keeps the same operation on retry', async () => { const page = await read('../pages/dropship-palletizing/dropship-palletizing.vue') const method = page.slice(page.indexOf('async createPallet()'), page.indexOf('statusLabel(status)')) const requests = [] const { stableOperationId } = await import('../utils/idempotency.js') const methods = new Function('createAndBindDropshipPallet', 'stableOperationId', 'playSuccessSound', 'playErrorSound', 'formatApiError', `return ({${method}})`)(async (taskId, payload) => { requests.push({ taskId, payload }) if (requests.length === 1) throw new Error('network timeout') return { taskStatus: 'CLAIMED', unbound: [], pallets: [{ palletNo: 'DP-260909-0001', cartons: [{ cartonId: '9007199254740993' }] }] } }, stableOperationId, () => {}, () => {}, (error) => error.message) const context = { busy: false, online: true, task: { taskStatus: 'CLAIMED' }, taskId: '10', warehouseId: '100', user: { id: '7' }, unboundCartons: [{ cartonId: '9007199254740993' }], scanState: {}, persistDraft() {}, get pallets() { return this.task.pallets || [] } } await methods.createPallet.call(context) assert.equal(context.error, 'network timeout') assert.equal(context.busy, false) await methods.createPallet.call(context) assert.deepEqual(requests[0], requests[1]) assert.equal(context.activePalletNo, 'DP-260909-0001') assert.equal(context.scanState.activePalletNo, context.activePalletNo) context.unboundCartons = [] await methods.createPallet.call(context) assert.equal(requests.length, 2) assert.match(page, /新建托盘并绑定/) }) test('dropship palletizing route and workbench entry are registered', async () => { const [pages, data, home, modulePage] = await Promise.all([read('../pages.json'), read('../data/demo-data.js'), read('../pages/home/home.vue'), read('../pages/module/module.vue')]) assert.match(pages, /pages\/dropship-palletizing\/tasks/) assert.match(pages, /pages\/dropship-palletizing\/dropship-palletizing/) assert.match(data, /一件代发清点组托[\s\S]*?route: '\/pages\/dropship-palletizing\/tasks'/) assert.match(home, /operation\.route \|\| '\/pages\/operation\/operation'/) assert.match(modulePage, /operation\.route \|\| '\/pages\/operation\/operation'/) }) test('page exposes both scan orders, receiving confirmation, pending boxes, pallet changes, and sealing guards', async () => { const page = await read('../pages/dropship-palletizing/dropship-palletizing.vue') for (const text of ['先扫托盘', '先扫多箱', '确认本箱实收', '待绑托箱', '移回待绑托', '封托']) assert.match(page, new RegExp(text)) assert.match(page, /startScanBroadcast/) assert.match(page, /watchNetwork/) assert.match(page, /saveDropshipPalletizingDraft/) assert.match(page, /restoreDropshipPalletizingDraft/) assert.match(page, /resolveDropshipCarton/) assert.match(page, /countDropshipCarton/) assert.match(page, /bindPendingDropshipCartons/) assert.match(page, /moveDropshipCarton/) assert.match(page, /sealDropshipPallet/) assert.match(page, /pallet\.cartons \|\| \[\]/) assert.match(page, /carton\.canCount === false[\s\S]*?carton\.blockReason/) assert.doesNotMatch(page, /\(pallet\.items \|\| \[\]\)\.filter/) assert.match(page, /:disabled="busy \|\| !online \|\| !canSeal"/) assert.doesNotMatch(page, /warehouseId:\s*Number\(this\.warehouseId\)/) assert.doesNotMatch(page, /模拟成功|DEMO_|palletStatus/) }) test('390px work layout uses bounded grids and touch-sized primary actions', async () => { const page = await read('../pages/dropship-palletizing/dropship-palletizing.vue') assert.match(page, /\.dropship-page\{min-width:0;overflow:hidden;/) assert.match(page, /\.summary-grid\{[^}]*grid-template-columns:repeat\(3,minmax\(0,1fr\)\);/) assert.match(page, /\.primary-button,\.secondary-button\{[^}]*width:100%;min-height:48px;/) assert.match(page, /\.quantity-control\{[^}]*grid-template-columns:52px minmax\(0,1fr\) 52px/) }) test('dropship API uses only the frozen cartons/count contract', async () => { const api = await read('../api/pda-dropship.js') for (const path of ['tasks/pending', 'cartons/resolve', 'cartons/count', 'pallets/bind-pending', '/move', '/seal']) assert.match(api, new RegExp(path.replace('/', '\\/'))) for (const field of ['actualSkuQuantity', 'discrepancyAction', 'discrepancyRemark', 'palletNo', 'operationId', 'expectedVersion']) assert.match(api, new RegExp(field)) assert.doesNotMatch(api, /pallets\/open|pallets\/\$\{encodeURIComponent\(palletId\)\}\/scan|palletStatus/) })