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'/) })