import test from 'node:test' import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import ts from 'typescript' const source = readFileSync(new URL('./WarehouseServiceView.vue', import.meta.url), 'utf8') const csvCell = source.slice(source.indexOf('function csvCell('), source.indexOf('function transferCustomerMarks')) const buildItems = source.slice(source.indexOf('function buildSkuRelabelItems('), source.indexOf('function addRelabelCargoRow(')) const submitStart = source.indexOf('const sourceInboundOrders =', source.indexOf('async function submit(')) const submitBody = source.slice(submitStart, source.indexOf(' try {', submitStart)) const code = ts.transpileModule(csvCell + buildItems + source.slice(source.indexOf('function relabelTemplateTypeForMode'), source.indexOf('function selectDiscardInbound')) + '\nasync function submitImported() { const idempotencyKey = "test-key"; ' + submitBody + '\nreturn created; }', { compilerOptions: { target: ts.ScriptTarget.ES2022 } }).outputText function harness(service = 'labeling') { const state = { props: { service }, cargoRows: { value: [{ id: 1, sku: 'OLD', name: '货物', included: false, cartonMark: 'BOX-1', customerCartonMarks: 'CUSTOMER-1', shipmentNo: '', remark: '' }] }, labelingMode: { value: 'carton' }, importedRelabelBindings: { value: [] }, skuRelabelRows: { value: [] }, transferRelabelRows: { value: [] }, form: { destination: 'ONT8' }, ElMessage: { error: message => errors.push(message), warning: message => errors.push(message), success: () => {} }, createCargoRow: () => ({}), transferCustomerMarks: row => row.customerCartonMarks.split(',') } const requests = [] Object.assign(state, { isReturnRelabelSource: { value: false }, selectedReturnCandidates: { value: [] }, cartonDetailRows: { get value() { return state.cargoRows.value.filter(row => row.included) } }, createRelabel: async request => { requests.push(request); return { application: { relabelNo: 'TEST-RELABEL' } } } }) Object.assign(state.cargoRows.value[0], { availableQuantity: 1, sourceInventoryBalanceId: '101', sourceInventoryPositionId: '102', sourceInboundOrderId: '103' }) const errors = [], downloads = [] const URL = { createObjectURL: blob => { downloads.push(blob); return 'blob:test' }, revokeObjectURL: () => {} } const document = { createElement: () => ({ click() {} }) } const result = new Function(...Object.keys(state), 'URL', 'document', 'let skuRelabelSequence = 0;\n' + code + '\nreturn { downloadRelabelTemplate, importRelabelTemplate, importSelectedBindingsToDetails, buildSkuRelabelItems, submitImported };')(...Object.values(state), URL, document) async function upload(rows) { const csv = [['明细行标识', '贴标类型', '仓库唛头', '旧SKU标签', '新SKU标签', '换标数量(件)', '新FBA箱唛', 'FBA货件编号', '备注'], ...rows].map(row => row.join(',')).join('\r\n') await result.importRelabelTemplate({ target: { files: [new File([csv], '统一模板.csv')], value: 'file' } }) } return { ...state, ...result, upload, errors, downloads, requests } } for (const [type, mode, expected] of [['只换箱唛', 'carton', 'carton'], ['只换 SKU 标签', 'sku', 'sku'], ['SKU 和箱唛都换', 'carton_and_sku', 'mixed']]) { test(`${type}:统一模板导入并生成对应类型明细`, async () => { const state = harness() await state.upload([[1, type, 'BOX-1', 'OLD', mode === 'carton' ? '' : 'NEW', 3, mode === 'sku' ? '' : 'FBA-1', mode === 'sku' ? '' : 'SHIP-1', '']]) assert.deepEqual(state.errors, []) assert.equal(state.importedRelabelBindings.value[0].templateType, expected) state.importSelectedBindingsToDetails() assert.equal(state.labelingMode.value, mode) if (mode === 'carton') { assert.equal(state.cargoRows.value[0].included, true); assert.equal(state.cargoRows.value[0].quantity, 1) } else { assert.equal(state.skuRelabelRows.value[0].newSku, 'NEW') const [item] = state.buildSkuRelabelItems() assert.equal(item.operationType, mode === 'sku' ? 'SKU_LABEL' : 'CARTON_AND_SKU') assert.equal(item.totalPackageCount, 1) assert.equal(item.skuItems[0].quantity, 3) assert.equal(item.destinationFbaWarehouseCode, mode === 'sku' ? undefined : 'ONT8') } state.form.deliveryMethod = 'TRUCK' await state.submitImported() assert.equal(state.requests.length, 1) assert.equal(state.requests[0].items[0].operationType, mode === 'carton' ? 'CARTON_LABEL' : mode === 'sku' ? 'SKU_LABEL' : 'CARTON_AND_SKU') assert.equal(state.requests[0].deliveryMethod, mode === 'sku' ? undefined : 'TRUCK') }) } test('三种类型下载同一列结构,并带出当前换标类型', async () => { const state = harness() const headers = [] for (const mode of ['carton', 'sku', 'carton_and_sku']) { state.labelingMode.value = mode state.downloadRelabelTemplate() const csv = await state.downloads.at(-1).text() const lines = csv.split('\r\n') assert.equal(lines.length, 3) headers.push(lines[0]) assert.match(lines[1], /只换箱唛|只换 SKU 标签|SKU 和箱唛都换/) } assert.equal(new Set(headers).size, 1) }) test('组合换标同箱多 SKU 生成一条箱任务并保留全部 SKU 子项', async () => { const state = harness() await state.upload([[1, 'SKU 和箱唛都换', 'BOX-1', 'OLD', 'NEW', 3, 'FBA-1', 'SHIP-1', ''], [1, 'SKU 和箱唛都换', 'BOX-1', 'OLD2', 'NEW2', 2, 'FBA-1', 'SHIP-1', '']]) assert.deepEqual(state.errors, []) state.importSelectedBindingsToDetails() await state.submitImported() assert.equal(state.requests[0].items.length, 1) assert.equal(state.requests[0].items[0].totalPackageCount, 1) assert.deepEqual(state.requests[0].items[0].skuItems.map(item => item.quantity), [3, 2]) }) test('混合类型、缺字段、无效数量、重复关系和未知库存均不导入', async () => { const valid = [1, 'SKU 和箱唛都换', 'BOX-1', 'OLD', 'NEW', 3, 'FBA-1', 'SHIP-1', ''] for (const rows of [[valid, [1, '只换箱唛', 'BOX-2', '', '', 1, 'FBA-2', 'SHIP-2', '']], [[...valid.slice(0, 4), '', ...valid.slice(5)]], [[...valid.slice(0, 5), 0, ...valid.slice(6)]], [valid, valid], [[999, ...valid.slice(1)]], [valid, [1, 'SKU 和箱唛都换', 'BOX-1', 'OTHER', 'NEW2', 1, 'FBA-OTHER', 'SHIP-1', '']]]) { const state = harness() await state.upload(rows) assert.equal(state.errors.length, 1) assert.equal(state.importedRelabelBindings.value.length, 0) } })