import assert from 'node:assert/strict' import { existsSync, readFileSync } from 'node:fs' import test from 'node:test' import ts from 'typescript' const apiSource = readFileSync(new URL('./api/deliveryOrders.ts', import.meta.url), 'utf8') const routesSource = readFileSync(new URL('./routes.ts', import.meta.url), 'utf8') const listSource = readFileSync(new URL('./views/DeliveryOrdersView.vue', import.meta.url), 'utf8') const detailSource = readFileSync(new URL('./views/DeliveryOrderDetailView.vue', import.meta.url), 'utf8') const deliveryStyles = readFileSync(new URL('./views/deliveryOrders.css', import.meta.url), 'utf8') const permissionSource = readFileSync(new URL('../tenant-management/contracts.ts', import.meta.url), 'utf8') async function importTypeScriptModule(source) { const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 }, }).outputText return import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`) } for (const signedPackages of [null, 0, 3]) test(`preserves actual signed quantity ${signedPackages} in merchant list and detail`, async () => { const api = await importTypeScriptModule(apiSource.replace( "import { apiBlobRequest, apiRequest } from '@/api/http'", `const apiBlobRequest = async () => new Blob([]) const order = { deliveryNo: 'DL-1', packageCount: 9, signedPackages: ${JSON.stringify(signedPackages)} } const apiRequest = async (config) => config.url.endsWith('/DL-1') ? { order, skus: [], fees: [] } : { records: [order], page: 1, size: 20, total: 1, pages: 1 }`, )) assert.equal((await api.listDeliveryOrders({ page: 1, size: 20 })).records[0].signedPackages, signedPackages) assert.equal((await api.getDeliveryOrder('DL-1')).signedPackages, signedPackages) }) test('uses only authenticated merchant delivery endpoints without customer scope or mock fallback', () => { assert.match(apiSource, /method: 'GET'[\s\S]*url: '\/merchant\/v1\/last-leg\/delivery-orders'/) assert.match(apiSource, /delivery-orders\/\$\{encodeURIComponent\(deliveryNo\)\}/) assert.doesNotMatch(apiSource, /method: '(?:POST|PUT|PATCH|DELETE)'|createDeliveryOrder|DeliveryTransferPayload|DeliveryTransferResult/) assert.match(apiSource, /apiRequest/) assert.match(apiSource, /\.then\(\(\{ order, skus, fees \}\) => \(\{[\s\S]*\.\.\.order,[\s\S]*fees,[\s\S]*items: skus/) assert.doesNotMatch(apiSource, /customerId|tenantId|Record|mock|fallback/i) }) test('keeps customer delivery list and read-only detail active without a creation route', () => { const listRoute = routesSource.match(/path: 'orders\/delivery',[\s\S]*?\n\s*},/)?.[0] ?? '' assert.match(listRoute, /permission: 'merchant:warehouse:delivery:list'/) assert.match(listRoute, /navigation: \{ key: 'orders-delivery', icon: Truck \}/) assert.doesNotMatch(routesSource, /orders\/delivery\/create|orders-delivery-create|DeliveryOrderCreateView/) assert.match(routesSource, /path: 'orders\/delivery\/:deliveryNo'[\s\S]*DeliveryOrderDetailView\.vue[\s\S]*props: true[\s\S]*permission: 'merchant:warehouse:delivery:list'/) assert.match(routesSource, /orders-delivery-detail[\s\S]*keepAlive: false/) }) test('renders the operation column first and covers loading, empty, error and pagination states', () => { const operationIndex = listSource.indexOf("label: '操作'") const deliveryNumberIndex = listSource.indexOf("label: '派送单号'") assert.ok(operationIndex >= 0 && operationIndex < deliveryNumberIndex) assert.match(listSource, /:loading="loading"/) assert.match(listSource, /empty-text="暂无派送单"/) assert.match(listSource, /delivery-state--error/) assert.match(listSource, /@click="loadOrders">重试/) assert.match(listSource, /listDeliveryOrders\(buildQuery\(\)\)/) assert.match(listSource, / { assert.doesNotMatch(listSource, /delivery:create|canCreate|openCreate|orders-delivery-create|创建派送单/) assert.equal(existsSync(new URL('./views/DeliveryOrderCreateView.vue', import.meta.url)), false) }) test('loads a typed detail route by delivery number and never exposes an internal id fallback', () => { assert.match(detailSource, /defineProps<\{ deliveryNo: string \}>/) assert.match(detailSource, /getDeliveryOrder\(props\.deliveryNo\)/) assert.match(detailSource, /派送单详情加载失败/) for (const heading of ['订单概览', '运输与预约', '货物明细', '费用明细']) assert.match(detailSource, new RegExp(heading)) assert.doesNotMatch(detailSource, /detail\?\.id|deliveryNo \|\| detail\?\.id|customerId|tenantId/) }) test('receipt list and binary download use the fixed merchant scope without category or revision escape', async () => { const api = await importTypeScriptModule(apiSource.replace( "import { apiBlobRequest, apiRequest } from '@/api/http'", `export const requests = [] const apiRequest = async (config) => { requests.push(config); return { deliveryNo: 'DL/1', files: [] } } const apiBlobRequest = async (config) => { requests.push(config); return new Blob(['receipt-bytes']) }`, )) assert.deepEqual(await api.getDeliveryReceipts('DL/1'), { deliveryNo: 'DL/1', files: [] }) assert.equal(await (await api.downloadDeliveryReceipt('DL/1', '123')).text(), 'receipt-bytes') assert.deepEqual(api.requests, [ { method: 'GET', url: '/merchant/v1/last-leg/delivery-orders/DL%2F1/documents/receipt' }, { method: 'GET', url: '/merchant/v1/last-leg/delivery-orders/DL%2F1/documents/receipt/files/123/download' }, ]) assert.match(detailSource, /empty-text="暂无签收资料"/) assert.match(detailSource, /receiptsError[\s\S]*@click="loadReceipts">重试/) assert.match(detailSource, /:loading="receiptsLoading"/) assert.match(detailSource, /downloadDeliveryReceipt\(props\.deliveryNo, file\.fileId\)/) assert.match(detailSource, /URL\.revokeObjectURL\(url\)/) assert.match(detailSource, /DELIVERING: '派送中'/) for (const status of ['WAIT_DELIVERY', 'DELIVERING', 'DELIVERED', 'REJECTED', 'RETURN_PENDING', 'RETURNED']) { assert.match(listSource, new RegExp(`value: '${status}'`)) } assert.doesNotMatch(listSource, /value: '(?:PENDING|IN_TRANSIT|SIGNED)'/) assert.doesNotMatch(detailSource, /el-upload|revisionVersion|documents\/BOL/) }) test('renders a compact customer-readable delivery detail surface', () => { assert.match(detailSource, /
/) assert.match(detailSource, /]*\/>返回列表<\/el-button>/) assert.match(detailSource, /class="client-status" :data-tone="statusTone\(detail\.status\)"/) assert.match(detailSource, /TRUCK_PALLET: '卡派打托'/) assert.match(detailSource, /WAREHOUSE_PICKUP: '仓库自提'/) assert.match(detailSource, /PALLET: '托盘'/) assert.match(detailSource, /TRUCK: '卡车'/) assert.match(detailSource, /SELF: '自有账号'/) assert.match(detailSource, /formatter: tableValueFormatter/) assert.match(detailSource, /]*>派送单详情<\/h2>|display\(detail\.status\)/) assert.match(deliveryStyles, /delivery-detail-surface \.delivery-detail-grid div\{display:grid;min-height:42px;/) }) test('keeps merchant delivery reads and removes creation from the advertised permission map', async () => { assert.match(permissionSource, /'merchant:warehouse:delivery:list': \['merchant:warehouse:delivery:list'\]/) assert.doesNotMatch(permissionSource, /merchant:warehouse:delivery:create/) const contracts = await importTypeScriptModule(permissionSource) assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:list', new Set(['merchant:warehouse:delivery:list'])), true) assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:list', new Set(['merchant:warehouse:delivery:create'])), false) assert.equal(Object.hasOwn(contracts.FRONTEND_TO_MERCHANT_PERMISSION_CODES, 'merchant:warehouse:delivery:create'), false) assert.equal(contracts.hasMappedMerchantPermission('merchant:warehouse:delivery:create', new Set(['merchant:warehouse:delivery:list'])), false) })