77 lines
5.2 KiB
JavaScript
77 lines
5.2 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')
|
|
const [page, tasks, api, http, completion, scan] = await Promise.all([
|
|
read('../pages/picking/picking.vue'),
|
|
read('../pages/tasks/tasks.vue'),
|
|
read('../api/pda-loading.js'),
|
|
read('../api/http.js'),
|
|
read('../utils/loading-completion-state.js'),
|
|
read('../utils/scan-broadcast.js')
|
|
])
|
|
|
|
test('demo claim supports the PDA broadcast and normalized manual input without duplicate picks', () => {
|
|
assert.match(page, /remoteTask\.workflowStatus === 'UNASSIGNED'[\s\S]*?claimContainerPickingTask\(this\.remoteTaskId, this\.assignmentVersion, operationId\)/)
|
|
assert.match(page, /this\.remotePick\.workflowStatus !== 'IN_PROGRESS'[\s\S]*?任务领取尚未完成/)
|
|
assert.match(scan, /android\.intent\.action\.SCANRESULT/)
|
|
assert.match(scan, /SCAN_BROADCAST_EXTRA = 'value'/)
|
|
assert.match(scan, /getStringExtra\(SCAN_BROADCAST_EXTRA\)/)
|
|
assert.match(page, /handleHardwareScan\(value\)[\s\S]*?processScannedValue\(value, 'broadcast'\)/)
|
|
assert.match(page, /handleManualSubmit\(value\)[\s\S]*?trim\(\)\.toUpperCase\(\)[\s\S]*?processScannedValue\(normalizedValue, 'manual'\)/)
|
|
assert.match(page, /result\.type === 'processed'[\s\S]*?重复扫描[\s\S]*?return/)
|
|
})
|
|
|
|
test('demo batch freezes inventory and submits carton or whole-pallet actions under one stable key', () => {
|
|
const submit = page.match(/async performSubmit\(incompleteReason = ''\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
|
for (const action of ['PICK', 'SKIP', 'PARTIAL', 'PICK_PALLET']) assert.match(submit, new RegExp(`type: '${action}'`))
|
|
assert.match(submit, /palletNo: item\.palletNo/)
|
|
assert.match(submit, /const submitId = this\.submitId \|\| stableOperationId\('submit'/)
|
|
assert.match(submit, /this\.submitId = submitId/)
|
|
assert.match(submit, /const inventorySnapshot = await this\.refreshInventorySnapshot\(\)[\s\S]*?submitContainerPickingBatch/)
|
|
assert.match(api, /'Idempotency-Key': stableIdempotencyKey\(submitOperationId\)/)
|
|
assert.match(api, /inventorySnapshotToken, items/)
|
|
assert.match(page, /value >= this\.partialTargetItem\.qty[\s\S]*?部分拣货必须少于/)
|
|
assert.match(page, /value > available[\s\S]*?超过库存\/库位可用量/)
|
|
assert.match(page, /available < Number\(item\.qty \|\| 0\)[\s\S]*?库存\/库位可用量不足/)
|
|
})
|
|
|
|
test('demo session and network failures never turn into local success', () => {
|
|
assert.match(http, /requireOnline\(\)/)
|
|
assert.match(http, /let refreshPromise = null/)
|
|
assert.match(http, /session\.token && session\.token !== accessToken \? session\.token : await refreshAccessToken\(\)/)
|
|
assert.match(http, /if \(code !== '200'\)[\s\S]*?error\.requestId[\s\S]*?error\.traceId[\s\S]*?throw error/)
|
|
const submit = page.match(/async performSubmit\(incompleteReason = ''\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
|
const failedReturn = submit.indexOf("this.showToast('拣货完成未提交,请查看错误提示', 'error')")
|
|
const localFinalize = submit.indexOf('const result = submitStates')
|
|
assert.ok(failedReturn >= 0 && localFinalize > failedReturn, 'backend failure must return before local completion')
|
|
assert.match(submit, /网络失败,拣货完成未提交,请重试/)
|
|
})
|
|
|
|
test('demo evidence requires all five server-backed types and keeps seal upload explicit', () => {
|
|
for (const type of ['PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL']) {
|
|
assert.match(api, new RegExp(`'${type}'`))
|
|
}
|
|
assert.match(page, /this\.evidenceSlots\.every\(\(slot\) => this\.validEvidenceEntries\(slot\.type\)\.length > 0\)/)
|
|
assert.match(page, /请填写封签号,保存后上传/)
|
|
assert.match(page, /sealSaving \? '上传中…' : '保存并上传'/)
|
|
assert.match(page, /await this\.queueEvidenceUpload\('SEAL', current\.localPath, String\(this\.sealNo\)\.trim\(\)\)/)
|
|
assert.match(api, /if \(code !== '200'\)[\s\S]*?throw error/)
|
|
})
|
|
|
|
test('demo completion reuses its command, restores processing, and refreshes the list on return', () => {
|
|
assert.match(page, /restoreLoadingCompletionAttempt\(this\.draftIdentity\(\)\)/)
|
|
assert.match(page, /const operationId = savedAttempt\?\.operationId \|\| stableOperationId\('complete'/)
|
|
assert.match(page, /saveLoadingCompletionAttempt\(this\.draftIdentity\(\), completionAttempt\)[\s\S]*?completeContainerPickingTask/)
|
|
assert.match(completion, /PROCESSING_STATUSES = new Set\(\['COMPLETING', 'PROCESSING'\]\)/)
|
|
assert.match(completion, /statuses\.some\(\(status\) => PROCESSING_STATUSES\.has\(status\)\)/)
|
|
assert.match(page, /phase === COMPLETION_PHASE\.PROCESSING[\s\S]*?后台处理中[\s\S]*?navigateBackOr\('\/pages\/tasks\/tasks'\)/)
|
|
assert.match(page, /shouldReconcileCompletion\(error\)[\s\S]*?提交结果待确认[\s\S]*?navigateBackOr\('\/pages\/tasks\/tasks'\)/)
|
|
assert.match(tasks, /onShow\(\)[\s\S]*?this\.loadTasks\(\)/)
|
|
assert.match(tasks, /status === 'PROCESSING'[\s\S]*?完成处理中/)
|
|
assert.match(tasks, /isTaskReady\(task\)[\s\S]*?\['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW', 'COMPLETION_FAILED'\]/)
|
|
assert.match(tasks, /isTaskVisible\(task\)[\s\S]*?\['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW', 'PROCESSING', 'COMPLETION_FAILED'\]/)
|
|
assert.doesNotMatch(tasks, /isTaskVisible\(task\)[\s\S]*?'COMPLETED'/)
|
|
})
|