129 lines
7.7 KiB
JavaScript
129 lines
7.7 KiB
JavaScript
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('opening another task clears prior scan display without deleting scoped draft or submission identity', async () => {
|
|
const page = await read('../pages/picking/picking.vue')
|
|
const load = page.match(/onLoad\(options = \{\}\) \{[\s\S]*?\r?\n\t\t\},/)?.[0]
|
|
const record = page.match(/recordScanResult\([^\n]+\) \{[\s\S]*?\r?\n\t\t\t\},/)?.[0]
|
|
assert.ok(load && record)
|
|
const hooks = new Function('requireSession', 'session', `return ({${load}${record}})`)(() => true, { currentUser: { id: '7' } })
|
|
const savedDraft = { 0: { scannedBoxCodes: { 'DP-OLD': 'old-item' }, itemStates: { 'old-item': 'picked' } } }
|
|
const context = { scanValue: 'DP-OLD', scanHistory: [{ id: 'old', boxCode: 'DP-OLD', title: '整托已记入草稿' }],
|
|
boxDrafts: savedDraft, submitId: 'existing-operation', itemStates: { 'old-item': 'picked' } }
|
|
hooks.onLoad.call(context, { taskId: 'new-task', warehouseId: 'warehouse' })
|
|
assert.equal(context.scanValue, '')
|
|
assert.deepEqual(context.scanHistory, [])
|
|
assert.equal(context.remoteTaskId, 'new-task')
|
|
assert.equal(context.boxDrafts, savedDraft)
|
|
assert.equal(context.submitId, 'existing-operation')
|
|
assert.deepEqual(context.itemStates, { 'old-item': 'picked' })
|
|
hooks.recordScanResult.call(context, 'success', '整托已记入草稿', 'DP-NEW', '整托 1 箱,提交后保存')
|
|
assert.equal(context.scanHistory.length, 1)
|
|
assert.equal(context.scanHistory[0].boxCode, 'DP-NEW')
|
|
// A same-task route reopen also clears input; canonical scoped restoration still owns draft history.
|
|
hooks.onLoad.call(context, { taskId: 'new-task', warehouseId: 'warehouse' })
|
|
assert.deepEqual(context.scanHistory, [])
|
|
assert.equal(context.boxDrafts, savedDraft)
|
|
const restore = page.match(/restoreRemoteDraft\(\) \{[\s\S]*?\r?\n\t\t\t\},/)?.[0] || ''
|
|
assert.match(restore, /restorePickingSession\(this\.draftIdentity\(\)\)/)
|
|
assert.match(restore, /this\.scanHistory = this\.normalizeScanHistory\(restored\.scanHistory\)/)
|
|
})
|
|
|
|
test('duplicate onShow shares one load chain while background return refreshes again', async () => {
|
|
const page = await read('../pages/picking/picking.vue')
|
|
const show = page.match(/async onShow\(\) \{[\s\S]*?\r?\n\t\t\},/)?.[0]
|
|
const hide = page.match(/onHide\(\) \{[\s\S]*?\r?\n\t\t\},/)?.[0]
|
|
const invalidate = page.match(/invalidateTaskSync\(\) \{[^\r\n]+\},/)?.[0]
|
|
assert.ok(show && hide && invalidate)
|
|
const hooks = new Function('requireSession', 'session', `return ({${show}${hide}${invalidate}})`)(() => true, { currentUser: { id: '7' } })
|
|
const calls = []
|
|
let release
|
|
const pending = new Promise((resolve) => { release = resolve })
|
|
const context = { taskRequestId: 0, invalidateTaskSync: hooks.invalidateTaskSync,
|
|
startHardwareScanner() {}, stopHardwareScanner() {}, async refreshAvailableBoxes() { calls.push('pending'); await pending }, async syncRemoteTask() { calls.push('detail+evidence') } }
|
|
const first = hooks.onShow.call(context)
|
|
const duplicate = hooks.onShow.call(context)
|
|
assert.deepEqual(calls, ['pending'])
|
|
release()
|
|
await Promise.all([first, duplicate])
|
|
assert.deepEqual(calls, ['pending', 'detail+evidence'])
|
|
await hooks.onShow.call(context)
|
|
assert.equal(calls.length, 2)
|
|
hooks.onHide.call(context)
|
|
assert.equal(context.taskRequestId, 1)
|
|
assert.equal(context.remoteLoading, false)
|
|
assert.equal(context._showRefreshPromise, null)
|
|
await hooks.onShow.call(context)
|
|
assert.deepEqual(calls, ['pending', 'detail+evidence', 'pending', 'detail+evidence'])
|
|
const onLoad = page.match(/onLoad\(options = \{\}\) \{[\s\S]*?\r?\n\t\t\},/)?.[0]
|
|
assert.doesNotMatch(onLoad, /refreshAvailableBoxes|syncRemoteTask/)
|
|
})
|
|
|
|
test('camera and evidence preview resume still avoid unnecessary refreshes', async () => {
|
|
const page = await read('../pages/picking/picking.vue')
|
|
const show = page.match(/async onShow\(\) \{[\s\S]*?\r?\n\t\t\},/)?.[0]
|
|
const hooks = new Function('requireSession', 'session', `return ({${show}})`)(() => true, { currentUser: {} })
|
|
for (const flag of ['cameraPicking', 'evidencePreviewReturning']) {
|
|
const context = { [flag]: true, startHardwareScanner() {}, debugEvidence() {}, refreshAvailableBoxes() { throw new Error('unexpected refresh') } }
|
|
await hooks.onShow.call(context)
|
|
assert.equal(context._pageVisible, true)
|
|
}
|
|
})
|
|
|
|
test('direct H5 refresh restores the page user before rendering picking content', async () => {
|
|
const page = await read('../pages/picking/picking.vue')
|
|
|
|
assert.match(page, /<view v-if="currentUser" class="page-shell">/)
|
|
assert.match(page, /onLoad\(options = \{\}\) \{[\s\S]*?if \(!requireSession\(\)\) return[\s\S]*?this\.currentUser = session\.currentUser/)
|
|
assert.match(page, /async onShow\(\) \{[\s\S]*?if \(!requireSession\(\)\) return[\s\S]*?this\.currentUser = session\.currentUser/)
|
|
})
|
|
|
|
test('picking route carries only stable task and warehouse identity', async () => {
|
|
const [tasks, page] = await Promise.all([
|
|
read('../pages/tasks/tasks.vue'),
|
|
read('../pages/picking/picking.vue')
|
|
])
|
|
const navigation = tasks.match(/uni\.navigateTo\(\{ url: `\/pages\/picking\/picking\?[^`]+` \}\)/)?.[0] || ''
|
|
const onLoad = page.match(/onLoad\(options = \{\}\) \{[\s\S]*?\r?\n\t\t\},\r?\n\t\tasync onShow/)?.[0] || ''
|
|
|
|
assert.match(navigation, /\?taskId=\$\{encodeURIComponent\(task\.taskId\)\}&warehouseId=\$\{encodeURIComponent\(task\.warehouseId\)\}/)
|
|
assert.doesNotMatch(navigation, /cabinetNo|currentContainerNo|orderNo|inboundId|tenantId/)
|
|
assert.match(onLoad, /taskId: String\(options\.taskId\)/)
|
|
assert.match(onLoad, /warehouseId: options\.warehouseId \? String\(options\.warehouseId\) : null/)
|
|
assert.doesNotMatch(onLoad, /options\.(cabinetNo|currentContainerNo|orderNo|inboundId|inboundVersion|tenantId)/)
|
|
})
|
|
|
|
test('target task detail keeps 19 digit identifiers as strings from route through request', async () => {
|
|
const [adapter, tasks, page] = await Promise.all([
|
|
read('../api/pda-loading.js'),
|
|
read('../pages/tasks/tasks.vue'),
|
|
read('../pages/picking/picking.vue')
|
|
])
|
|
const taskId = '2092889048731414530'
|
|
const warehouseId = '193000000000200002'
|
|
assert.equal(`/first-leg/pda-loading/tasks/${encodeURIComponent(taskId)}?warehouseId=${encodeURIComponent(warehouseId)}`,
|
|
'/first-leg/pda-loading/tasks/2092889048731414530?warehouseId=193000000000200002')
|
|
assert.match(adapter, /tasks\/\$\{encodeURIComponent\(taskId\)\}\?warehouseId=\$\{encodeURIComponent\(warehouseId\)\}/)
|
|
assert.match(tasks, /taskId=\$\{encodeURIComponent\(task\.taskId\)\}&warehouseId=\$\{encodeURIComponent\(task\.warehouseId\)\}/)
|
|
assert.match(page, /taskId: String\(options\.taskId\)/)
|
|
assert.match(page, /warehouseId: options\.warehouseId \? String\(options\.warehouseId\) : null/)
|
|
assert.doesNotMatch(page.match(/onLoad\(options = \{\}\) \{[\s\S]*?\n\t\t\},/)?.[0] || '', /Number\((?:options\.)?(?:taskId|warehouseId)/)
|
|
})
|
|
|
|
test('task detail restores before and after claim and exposes S0500 metadata', async () => {
|
|
const [http, page] = await Promise.all([read('../api/http.js'), read('../pages/picking/picking.vue')])
|
|
const sync = page.match(/async syncRemoteTask\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
|
assert.match(sync, /remoteTask\.workflowStatus === 'UNASSIGNED'[\s\S]*?claimContainerPickingTask/)
|
|
assert.match(sync, /remoteTask\.workflowStatus === 'IN_PROGRESS'[\s\S]*?assigneeId/)
|
|
assert.match(sync, /normalizePickingTask\(\{ \.\.\.task, \.\.\.\(claimed \|\| \{\}\) \}\)/)
|
|
assert.match(http, /error\.code = body\.code \|\| response\.statusCode/)
|
|
assert.match(http, /error\.requestId = body\.requestId/)
|
|
assert.match(http, /error\.traceId = body\.traceId/)
|
|
assert.match(page, /formatApiError\(error, fallback\)/)
|
|
assert.match(page, /@\/utils\/user-facing\.js/)
|
|
assert.match(sync, /this\.remoteFailureMessage\(error, '任务同步失败'\)/)
|
|
})
|