Files
pda-overseas-uniapp/tests/workbench-counts.test.mjs
T

123 lines
8.8 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { parse, compileScript, compileTemplate, compileStyle } from 'vue/compiler-sfc'
import { assertReceivingWarehouse } from '../src/api/receiving-adapter.js'
import { MODULES } from '../src/data/overseas-workbench.js'
const apiSource = readFileSync(new URL('../src/api/workbench.js', import.meta.url), 'utf8')
const homeSource = readFileSync(new URL('../src/pages/home/home.vue', import.meta.url), 'utf8')
const profile = { id: 'OP', warehouseId: 'WH', warehouseCode: 'WH-CODE', warehouseName: '测试仓' }
const emptySources = () => ({
assertReceivingWarehouse,
listReceivingCandidates: async () => [], listWarehouseTransferCandidates: async () => [],
listReturnCountTasks: async () => ({ records: [], total: 0 }), listPackingPlans: async () => [],
listDispatchTasks: async () => [], listOutboundTasks: async () => [], listCargoDiscardTasks: async () => [],
listRelabelCandidates: async () => []
})
function api(overrides = {}) {
const dependencies = { ...emptySources(), ...overrides }
return new Function(...Object.keys(dependencies), apiSource.replace(/^import .*$/gm, '').replace(/^export /gm, '') + '\nreturn { loadWorkbenchCounts, workbenchBadge, WORKBENCH_COUNT_KEYS }')(...Object.values(dependencies))
}
function home(loadWorkbenchCounts) {
const session = { currentUser: { ...profile }, apiBase: 'LOCAL' }
const scopes = []
const dependencies = { PortalHeader: {}, MODULES, session, requireSession: () => true, applyWarehouseProfile: value => value, getCurrentWarehouseProfile: async () => session.currentUser, loadWorkbenchCounts, workbenchBadge: api().workbenchBadge, restoreWarehouseWork: scope => scopes.push(scope), warehouseWork: { warehouseTransfer: { currentNo: 'T2' } } }
const page = new Function(...Object.keys(dependencies), homeSource.match(/<script>([\s\S]*?)<\/script>/)[1].replace(/^\s*import .*$/gm, '').replace('export default', 'return'))(...Object.values(dependencies))
const value = page.data()
for (const [key, method] of Object.entries(page.methods)) value[key] = method.bind(value)
return { page, value, session, scopes }
}
test('workbench counts separate queues, current work and paginated totals without duplicate business records', async () => {
const container = { sourceContainerBookingId: 'B1', containerNo: 'C1', overseasWarehouseCode: profile.warehouseCode, overseasWarehouseName: profile.warehouseName }
const source = api({
listReceivingCandidates: async () => [container, container, { ...container, sourceContainerBookingId: 'B2', containerNo: 'C2', operable: false }],
listWarehouseTransferCandidates: async () => [{ transferNo: 'T1', status: 'WAIT_PDA_RECEIVING' }, { transferNo: 'T2', status: 'PARTIALLY_RECEIVED' }, { transferNo: 'T2', status: 'PARTIALLY_RECEIVED' }, { transferNo: 'T3', status: 'COMPLETED' }],
listReturnCountTasks: async options => { assert.deepEqual(options, { page: 1, size: 1 }); return { total: 37, records: [{ receiptNo: 'RR1' }] } },
listPackingPlans: async () => [{ planNo: 'P1', status: 'READY' }, { planNo: 'P1', status: 'READY' }, { planNo: 'P2', status: 'COMPLETED' }],
listDispatchTasks: async () => [{ dispatchNo: 'D1', stage: 'LOADING' }, { dispatchNo: 'D1', stage: 'LOADING' }, { dispatchNo: 'D2', stage: 'BEFORE_LOADING' }, { dispatchNo: 'D3', stage: 'DELIVERING' }, { dispatchNo: 'D4', stage: 'DELIVERED' }, { dispatchNo: 'D5', stage: 'LOADING', status: 'CANCELLED' }],
listOutboundTasks: async status => { assert.equal(status, 'PENDING'); return [{ taskNo: 'O1' }, { taskNo: 'O1' }] },
listCargoDiscardTasks: async () => [{ discardNo: 'CD1', status: 'PENDING' }, { discardNo: 'CD1', status: 'PENDING' }, { discardNo: 'CD2', status: 'COMPLETED' }],
listRelabelCandidates: async () => [{ relabelNo: 'R1', taskStatus: 'AVAILABLE' }, { relabelNo: 'R1', taskStatus: 'AVAILABLE' }, { relabelNo: 'R2', taskStatus: 'PROCESSING', claimedByCurrentOperator: true }, { relabelNo: 'R3', taskStatus: 'PROCESSING', claimedByCurrentOperator: false }, { relabelNo: 'R4', taskStatus: 'WAIT_REVIEW' }, { relabelNo: 'R5', taskStatus: 'COMPLETED' }]
})
const counts = await source.loadWorkbenchCounts({ profile, activeTransferNo: 'T2' })
assert.deepEqual(Object.fromEntries(Object.entries(counts).map(([key, value]) => [key, value.count])), { receiving: 2, warehouseTransferList: 2, warehouseTransferWork: 1, returnCounting: 37, returnPacking: 1, dispatchPicking: 2, outboundPicking: 1, relabelReceive: 1, relabelExecute: 1, relabelReview: 1 })
assert.equal(counts.outboundPicking.discardCount, 1)
assert.equal(source.workbenchBadge('outboundPicking', counts.outboundPicking).label, '1 出库 · 1 废弃')
})
test('a failed source stays unknown while other queue counts remain available', async () => {
const error = Object.assign(new Error('网络异常'), { requestId: 'REQ-COUNT', traceId: 'TRACE-COUNT' })
const source = api({ listWarehouseTransferCandidates: async () => { throw error } })
const counts = await source.loadWorkbenchCounts({ profile })
assert.equal(counts.warehouseTransferList.status, 'error')
assert.equal(counts.warehouseTransferWork.error, error)
assert.equal(counts.warehouseTransferList.count, undefined)
assert.equal(counts.receiving.count, 0)
assert.equal(source.workbenchBadge('warehouseTransferList', counts.warehouseTransferList).label, '获取失败')
assert.equal(source.workbenchBadge('receiving', counts.receiving).label, '0 柜')
})
test('foreign receiving warehouses and absent pagination totals cannot produce a success count', async () => {
const source = api({ listReceivingCandidates: async () => [{ overseasWarehouseCode: 'OTHER', overseasWarehouseName: '其他仓' }], listReturnCountTasks: async () => ({ records: [] }) })
const counts = await source.loadWorkbenchCounts({ profile })
assert.equal(counts.receiving.status, 'error')
assert.equal(counts.receiving.error.code, 'RECEIVING_WAREHOUSE_MISMATCH')
assert.equal(counts.returnCounting.status, 'error')
assert.equal(counts.returnCounting.count, undefined)
})
test('a completed or missing local transfer does not count as current measurement work', async () => {
const source = api({ listWarehouseTransferCandidates: async () => [{ transferNo: 'DONE', status: 'COMPLETED' }, { transferNo: 'OTHER', status: 'WAIT_PDA_RECEIVING' }] })
const counts = await source.loadWorkbenchCounts({ profile, activeTransferNo: 'DONE' })
assert.equal(counts.warehouseTransferWork.count, 0)
assert.equal(counts.warehouseTransferList.count, 1)
assert.equal(source.workbenchBadge('warehouseTransferWork', counts.warehouseTransferWork).label, '未开始')
})
test('scan tools and queries do not pretend to have task counts', () => {
const { workbenchBadge } = api()
assert.equal(workbenchBadge('returnReceipt').label, '扫码签收')
assert.equal(workbenchBadge('move').label, '扫码作业')
assert.equal(workbenchBadge('inventoryQuery').label, '扫码查询')
assert.equal(workbenchBadge('receiving').label, '加载中…')
})
test('returning home refreshes counts and ignores a response from a hidden page', async () => {
let resolveFirst, calls = 0
const { page, value, scopes } = home(async ({ activeTransferNo }) => { assert.equal(activeTransferNo, 'T2'); calls++; return calls === 1 ? new Promise(resolve => { resolveFirst = resolve }) : { receiving: { status: 'ready', count: 4, unit: '柜' } } })
const first = page.onShow.call(value)
assert.equal(value.countsLoading, true)
page.onHide.call(value)
resolveFirst({ receiving: { status: 'ready', count: 99, unit: '柜' } })
await first
assert.deepEqual(value.counts, {})
await page.onShow.call(value)
assert.equal(value.counts.receiving.count, 4)
assert.equal(calls, 2)
assert.deepEqual(scopes, ['OP:WH', 'OP:WH'])
})
test('warehouse or account changes invalidate pending home counts', async () => {
let resolve
const { page, value, session } = home(() => new Promise(done => { resolve = done }))
const pending = page.onShow.call(value)
session.currentUser = { ...profile, id: 'OTHER-OP', warehouseId: 'OTHER-WH' }
resolve({ receiving: { status: 'ready', count: 99, unit: '柜' } })
await pending
assert.deepEqual(value.counts, {})
assert.equal(value.countsLoading, false)
assert.equal(value.user.id, 'OTHER-OP')
})
test('home compiles with per-entry badges and no function-count summary', () => {
const { descriptor, errors } = parse(homeSource, { filename: 'home.vue' })
assert.deepEqual(errors, [])
compileScript(descriptor, { id: 'home-counts' })
assert.deepEqual(compileTemplate({ source: descriptor.template.content, filename: 'home.vue', id: 'home-counts' }).errors, [])
for (const style of descriptor.styles) assert.deepEqual(compileStyle({ source: style.content, filename: 'home.vue', id: 'data-v-home-counts', scoped: style.scoped }).errors, [])
assert.doesNotMatch(descriptor.template.content, /availableCount|项可处理|group\.operations\.length/)
assert.match(descriptor.template.content, /countBadge\(operation\)\.label/)
})