feat(pda): connect online picking workflow
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
const { stableOperationId } = await import(new URL('../utils/idempotency.js', import.meta.url).href)
|
||||
|
||||
test('PDA operation id is stable ASCII and within the backend 128 character limit', () => {
|
||||
const payload = { itemStates: Object.fromEntries(Array.from({ length: 500 }, (_, index) => [`明细-${index}`, 'PICKED'])), reason: '中文异常原因'.repeat(100) }
|
||||
const first = stableOperationId('submit', 'operator-1-task-2-container-3', payload)
|
||||
const replay = stableOperationId('submit', 'operator-1-task-2-container-3', payload)
|
||||
assert.equal(first, replay)
|
||||
assert.ok(first.length <= 128)
|
||||
assert.match(first, /^[\x20-\x7E]+$/)
|
||||
})
|
||||
|
||||
test('PDA operation id changes when the draft changes', () => {
|
||||
const first = stableOperationId('submit', 'operator-task-container', { item: 'PICKED' })
|
||||
const second = stableOperationId('submit', 'operator-task-container', { item: 'SKIPPED' })
|
||||
assert.notEqual(first, second)
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
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('remote PDA mutations use the shared online gate', async () => {
|
||||
const [api, network, page] = await Promise.all([
|
||||
read('../api/pda-loading.js'), read('../utils/network.js'), read('../pages/picking/picking.vue')
|
||||
])
|
||||
assert.match(api, /function request\(options\) \{[\s\S]*?requireOnline\(\)/)
|
||||
assert.match(api, /uploadEvidenceFile[\s\S]*?requireOnline\('当前网络不可用,无法上传装柜照片'\)/)
|
||||
assert.match(network, /uni\.onNetworkStatusChange/)
|
||||
assert.match(network, /result\.networkType !== 'none'/)
|
||||
assert.match(page, /当前网络不可用,请联网后再拍照上传/)
|
||||
})
|
||||
|
||||
test('remote refresh keeps draft actions but does not restore stale server item snapshots', async () => {
|
||||
const page = await read('../pages/picking/picking.vue')
|
||||
const restore = page.match(/restoreRemoteDraft\(\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||
assert.match(restore, /itemIds/)
|
||||
assert.match(restore, /restored\.itemStates/)
|
||||
assert.doesNotMatch(restore, /this\.boxes = restored\.boxes/)
|
||||
assert.match(page, /refreshTask\(\)[\s\S]*?persistSession\(\)[\s\S]*?syncRemoteTask\(\)/)
|
||||
})
|
||||
@@ -10,7 +10,7 @@ test('page configuration starts at login and registers independent PDA routes',
|
||||
assert.equal(pages.pages[0].style.navigationStyle, 'custom')
|
||||
assert.deepEqual(pages.pages.map((page) => page.path), [
|
||||
'pages/login/login', 'pages/home/home', 'pages/module/module', 'pages/operation/operation',
|
||||
'pages/history/history', 'pages/profile/profile', 'pages/scanner-test/scanner-test', 'pages/picking/picking'
|
||||
'pages/tasks/tasks', 'pages/history/history', 'pages/profile/profile', 'pages/scanner-test/scanner-test', 'pages/picking/picking'
|
||||
])
|
||||
})
|
||||
|
||||
@@ -22,13 +22,28 @@ test('application uses the Vue 3 uni-app entry and no unused Android permissions
|
||||
assert.doesNotMatch(manifest, /android\.permission\.(VIBRATE|ACCESS_NETWORK_STATE)/)
|
||||
})
|
||||
|
||||
test('login is isolated and relaunches to the modular workbench', async () => {
|
||||
const login = await read('../pages/login/login.vue')
|
||||
for (const text of ['柏悦物流 · PDA作业端', '快捷登录(演示账号)', '工号 / 用户名', '作业仓库']) assert.match(login, new RegExp(text))
|
||||
assert.match(login, /signIn\(account, this\.form\.warehouse\)/)
|
||||
test('login uses the real auth endpoint and relaunches to the modular workbench', async () => {
|
||||
const [login, auth, session] = await Promise.all([read('../pages/login/login.vue'), read('../api/auth.js'), read('../stores/session.js')])
|
||||
for (const text of ['柏悦物流 · PDA作业端', '工号 / 用户名', '登录中']) assert.match(login, new RegExp(text))
|
||||
assert.doesNotMatch(login, /DEMO_ACCOUNTS|快捷登录|作业仓库/)
|
||||
assert.match(auth, /\/auth\/login/)
|
||||
assert.match(login, /loginRequest\(userId, this\.form\.password\)/)
|
||||
assert.match(login, /signInWithTokens\(tokens\)/)
|
||||
assert.match(session, /pda-auth-session/)
|
||||
assert.match(session, /claims\.app_code !== 'PLATFORM'/)
|
||||
assert.match(login, /uni\.reLaunch\(\{ url: '\/pages\/home\/home' \}\)/)
|
||||
})
|
||||
|
||||
test('picking task list uses remote loading orders and never falls back to demo tasks', async () => {
|
||||
const [tasks, home, modulePage] = await Promise.all([read('../pages/tasks/tasks.vue'), read('../pages/home/home.vue'), read('../pages/module/module.vue')])
|
||||
assert.match(tasks, /listLoadingOrders/)
|
||||
assert.match(tasks, /当前网络不可用,请联网后刷新/)
|
||||
assert.match(tasks, /进入并开始拣货/)
|
||||
assert.doesNotMatch(tasks, /demo-data|DEMO_/)
|
||||
assert.match(home, /pages\/tasks\/tasks/)
|
||||
assert.match(modulePage, /pages\/tasks\/tasks/)
|
||||
})
|
||||
|
||||
test('workbench, module, history and profile are independent sources', async () => {
|
||||
const [home, module, history, profile] = await Promise.all([
|
||||
read('../pages/home/home.vue'), read('../pages/module/module.vue'), read('../pages/history/history.vue'), read('../pages/profile/profile.vue')
|
||||
@@ -194,7 +209,7 @@ test('reusable scan target input separates field selection from page-level broad
|
||||
assert.match(picking, /handleHardwareScan\(value\)[\s\S]*?activeScanField !== 'sku'[\s\S]*?processScannedValue\(value, 'broadcast'\)/)
|
||||
})
|
||||
|
||||
test('picking session is memory-only, survives navigation and clears on logout', async () => {
|
||||
test('picking session persists per task identity and clears on logout', async () => {
|
||||
const [store, session, page] = await Promise.all([read('../stores/picking.js'), read('../stores/session.js'), read('../pages/picking/picking.vue')])
|
||||
assert.match(store, /getPickingSession/)
|
||||
assert.match(store, /savePickingSession/)
|
||||
|
||||
@@ -55,18 +55,21 @@ test('picking page exposes five evidence slots, retry and remote completion stat
|
||||
assert.match(page, /evidenceComplete/)
|
||||
})
|
||||
|
||||
test('PDA cabinet picking adapter exposes online claim and item action contracts', () => {
|
||||
assert.match(source, /\/first-leg\/pda-loading\/tasks\?/)
|
||||
assert.match(source, /tasks\/\$\{encodeURIComponent\(taskId\)\}\/claim/)
|
||||
assert.match(source, /items\/\$\{encodeURIComponent\(itemId\)\}\/pick/)
|
||||
assert.match(source, /items\/\$\{encodeURIComponent\(itemId\)\}\/partial/)
|
||||
assert.match(source, /items\/\$\{encodeURIComponent\(itemId\)\}\/skip/)
|
||||
assert.match(source, /submitContainerPickingBatch/)
|
||||
assert.match(source, /stableIdempotencyKey/)
|
||||
assert.match(page, /claimContainerPickingTask\(/)
|
||||
assert.match(page, /submitContainerPickingBatch\(/)
|
||||
assert.match(page, /任务版本已变化,请刷新后重试/)
|
||||
assert.match(source, /locationIds: action\.locationIds \|\| \[\]/)
|
||||
assert.match(page, /availablePickQuantity/)
|
||||
assert.match(page, /库存\/库位可用量不足/)
|
||||
test('PDA cabinet picking adapter claims online and submits one atomic batch', () => {
|
||||
assert.match(source, /\/first-leg\/pda-loading\/tasks\?/)
|
||||
assert.match(source, /tasks\/\$\{encodeURIComponent\(taskId\)\}\/claim/)
|
||||
assert.match(source, /tasks\/\$\{encodeURIComponent\(task\.taskId\)\}\/batch/)
|
||||
assert.match(source, /submitContainerPickingBatch/)
|
||||
assert.match(source, /stableIdempotencyKey/)
|
||||
assert.match(source, /Math\.imul\(hash, 16777619\)/)
|
||||
assert.match(source, /slice\(0, 128\)/)
|
||||
assert.match(page, /claimContainerPickingTask\(/)
|
||||
assert.match(page, /submitContainerPickingBatch\(/)
|
||||
assert.match(page, /任务版本已变化,请刷新后重试/)
|
||||
assert.match(source, /data: \{ warehouseId, containerNo: task\.containerNo, expectedVersion, items \}/)
|
||||
assert.doesNotMatch(source.match(/export async function submitContainerPickingBatch[\s\S]*?\n\}/)?.[0] || '', /for \(/)
|
||||
assert.match(page, /version: Number\(item\.version \|\| 0\)/)
|
||||
assert.match(page, /refreshTask/)
|
||||
assert.match(page, /availablePickQuantity/)
|
||||
assert.match(page, /库存\/库位可用量不足/)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user