This commit is contained in:
2026-10-22 14:38:15 +08:00
parent b1e8de46e2
commit 161128ff9e
22 changed files with 114 additions and 132 deletions
+1 -2
View File
@@ -8,7 +8,7 @@ const interpolations = (source) => [...template(source).matchAll(/\{\{([\s\S]*?)
test('all registered user-facing pages avoid rendering internal identifiers', async () => {
const paths = [
'../src/pages/login/login.vue', '../src/pages/home/home.vue', '../src/pages/module/module.vue',
'../src/pages/login/login.vue', '../src/pages/home/home.vue',
'../src/pages/operation/operation.vue', '../src/pages/pallet/pallet.vue', '../src/pages/profile/profile.vue',
'../src/pages/dispatch/dispatch.vue', '../src/pages/outbound/outbound.vue',
'../src/pages/stocktake/stocktake.vue', '../src/pages/move/move.vue', '../src/pages/relabel/relabel.vue', '../src/pages/query/query.vue',
@@ -86,7 +86,6 @@ test('missing user-visible relationships use explicit empty labels instead of ID
read('../src/components/ReceivingSessionSummary.vue'),
read('../src/pages/pallet/pallet.vue')
])
assert.match(summary, /overseasWarehouseCode[\s\S]*overseasWarehouseName/)
assert.match(summary, /unique\.length \? unique : \['—'\]/)
assert.match(pallet, /inboundCode \|\| '无入仓码'/)
assert.match(pallet, /locationCode \|\| '未绑定库位'/)
+1 -2
View File
@@ -108,7 +108,7 @@ test('container UI locks and signs once, then renders the resolved destination g
for (const fact of ['已分配', '未分配', '可合托·非强制', '不合托', '加急', '普通']) assert.match(summary, new RegExp(fact))
assert.doesNotMatch(operation, /v-for="[^"]*inbound[^"]*"/)
assert.match(operation, /v-if="activeInbound"[\s\S]*activeInbound\.inboundCodes[\s\S]*activeInbound\.pendingPackages/)
assert.match(operation, /activeInbound\.operationMode === 'FBA_GROUP'[\s\S]*按 FBA 仓成组收货/)
assert.match(operation, /class="mode-banner"[^\n]*activeInbound\.destinationType === 'FBA'[^\n]*FBA 按箱数收货[^\n]*海外仓逐箱收货/)
assert.doesNotMatch(operation, /activeInbound\.customer|客户/)
assert.match(operation, /resolveCurrentCarton\(cartonMark\)/)
assert.match(operation, /operationMode !== 'DROPSHIP_PALLET'[\s\S]*receiveCurrentWholePallet/)
@@ -121,7 +121,6 @@ test('container UI locks and signs once, then renders the resolved destination g
assert.match(operation, /applyWarehouseProfile\(await getCurrentWarehouseProfile\(\)\)/)
assert.match(operation, /values\.map\(\(item\) => this\.validateReceivingWarehouse/)
assert.match(operation, /prepareReceivingContainer[\s\S]*validateReceivingWarehouse/)
assert.match(summary, /overseasWarehouseCode[\s\S]*overseasWarehouseName/)
for (const forbidden of ['customerId', 'planNo', 'allocationId', 'warehouseId', 'locationId']) {
assert.doesNotMatch(inboundApi, new RegExp(forbidden))
}
+48
View File
@@ -1,7 +1,12 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { MODULES } from '../src/data/overseas-workbench.js'
import { navigateBackOr, navigateToUnique } from '../src/utils/navigation.js'
const read = (path) => readFile(new URL(path, import.meta.url), 'utf8')
const component = (source, dependencies) => new Function(...Object.keys(dependencies), source.match(/<script>([\s\S]*?)<\/script>/)[1].replace(/^\s*import .*$/gm, '').replace('export default', 'return'))(...Object.values(dependencies))
test('navigateBackOr returns through the stack and falls back when the stack is empty', () => {
const calls = []
globalThis.uni = {
@@ -36,3 +41,46 @@ test('navigateToUnique does not stack the current utility page twice', () => {
navigateToUnique('/pages/history/history')
assert.deepEqual(calls, [{ url: '/pages/history/history' }])
})
test('every registered page header returns home after a reload without page history', async (t) => {
const previousUni = globalThis.uni
const previousPages = globalThis.getCurrentPages
t.after(() => { globalThis.uni = previousUni; globalThis.getCurrentPages = previousPages })
const { pages } = JSON.parse(await read('../src/pages.json'))
const header = component(await read('../src/components/PortalHeader.vue'), { PopupHeader: {}, navigateBackOr, navigateToUnique })
const calls = []
globalThis.uni = { reLaunch: (options) => calls.push(options), navigateBack: () => assert.fail('a reloaded page has no back stack') }
let checked = 0
for (const { path } of pages) {
const source = await read(`../src/${path}.vue`)
const tag = source.match(/<PortalHeader\b[^>]*\/>/)?.[0]
if (!tag) continue
assert.doesNotMatch(tag, /:fallback-url=/, path)
const fallbackUrl = tag.match(/\bfallback-url="([^"]*)"/)?.[1] || header.props.fallbackUrl.default
assert.equal(fallbackUrl, '/pages/home/home', path)
globalThis.getCurrentPages = () => [{ route: path }]
header.methods.goBack.call({ fallbackUrl })
assert.deepEqual(calls.at(-1), { url: '/pages/home/home' }, path)
checked++
}
assert.ok(checked > 0)
assert.equal(calls.length, checked)
})
test('home opens every enabled operation directly on a registered page', async (t) => {
const previousUni = globalThis.uni
t.after(() => { globalThis.uni = previousUni })
const { pages } = JSON.parse(await read('../src/pages.json'))
const registered = new Set(pages.map(({ path }) => `/${path}`))
const home = component(await read('../src/pages/home/home.vue'), { PortalHeader: {}, MODULES })
const calls = []
globalThis.uni = { navigateTo: (options) => calls.push(options) }
for (const group of MODULES) {
for (const operation of group.operations.filter((item) => item.available)) {
const count = calls.length
home.methods.openOperation.call({ groups: MODULES }, operation)
assert.equal(calls.length, count + 1, operation.name)
assert.ok(registered.has(calls.at(-1).url.split('?')[0]), operation.name)
}
}
})
+2 -3
View File
@@ -5,14 +5,13 @@ import { readFile } from 'node:fs/promises'
const read = (path) => readFile(new URL(path, import.meta.url), 'utf8')
test('pallet management is enabled through its dedicated real PDA route', async () => {
const [workbench, pages, home, module] = await Promise.all([
const [workbench, pages, home] = await Promise.all([
read('../src/data/overseas-workbench.js'), read('../src/pages.json'),
read('../src/pages/home/home.vue'), read('../src/pages/module/module.vue')
read('../src/pages/home/home.vue')
])
assert.match(workbench, /key: 'pallet'[\s\S]*available: true[\s\S]*route: '\/pages\/pallet\/pallet'/)
assert.match(pages, /pages\/pallet\/pallet/)
assert.match(home, /operation\.route \|\|/)
assert.match(module, /operation\.route \|\|/)
})
test('pallet API maps only the frozen overseas PDA operation endpoints', async () => {
+4 -4
View File
@@ -90,9 +90,9 @@ test('login has no committed demo credentials and inbound operation implements t
})
test('warehouse operations use dedicated real pages and contain no prototype task data', async () => {
const [operation, home, module, workbench, pages] = await Promise.all([
const [operation, home, workbench, pages] = await Promise.all([
read('../src/pages/operation/operation.vue'), read('../src/pages/home/home.vue'),
read('../src/pages/module/module.vue'), read('../src/data/overseas-workbench.js'), read('../src/pages.json')
read('../src/data/overseas-workbench.js'), read('../src/pages.json')
])
assert.doesNotMatch(operation, /prototypeScan|prototypeInput/)
assert.doesNotMatch(workbench, /PROTOTYPES|getOperationPrototype|模拟成功/)
@@ -101,7 +101,7 @@ test('warehouse operations use dedicated real pages and contain no prototype tas
}
assert.doesNotMatch(workbench, /key: 'stocktake'/)
for (const page of ['pages/stocktake/stocktake', 'pages/move/move', 'pages/relabel/relabel', 'pages/query/query']) assert.match(pages, new RegExp(page))
assert.match(home + module, /operation\.route \|\|/)
assert.match(home, /operation\.route \|\|/)
})
test('receiving phases use a compiled container summary with cabinet and aggregate facts', async () => {
@@ -130,7 +130,7 @@ test('candidate containers use compact card navigation and a small refresh actio
assert.match(operation, /class="candidate-facts"[\s\S]*总订单[\s\S]*入仓码[\s\S]*已生成计划[\s\S]*未操作/)
assert.match(operation, /class="candidate-destinations"/)
assert.match(operation, /计划到仓:\{\{ formatPlannedArrival\(item\.plannedArrivalAt\) \}\}/)
assert.match(operation, /item\.blockReason \|\| '当前柜入库计划尚未齐全'/)
assert.match(operation, /selected\?\.blockReason \|\| '当前柜入库计划尚未齐全'/)
assert.doesNotMatch(operation, /预计到港/)
assert.doesNotMatch(operation, /item\.receivedPackages[\s\S]*item\.totalPackages[\s\S]*progress-fill/)
})
+2 -3
View File
@@ -126,7 +126,6 @@ test('relabel review opens a pending task as customer-confirmation read-only det
assert.equal(visible.call({ ...context, candidates: [] }), false)
assert.equal(visible.call({ ...context, stage: 'review', current: { ...context.current, taskStatus: 'WAIT_REVIEW' } }), true)
assert.equal(visible.call({ ...context, current: { ...context.current, taskStatus: 'WAIT_REVIEW' } }), false)
assert.match(page, /stageSubtitle\(\) \{ return this\.stage === 'review' \? '仅展示当前仓库已完成现场换标并等待客户或平台处理的单据'/)
assert.match(page, /<view v-else class="task-list">[\s\S]*@tap="openReview\(item\)"/)
assert.match(page, /查看并复核/)
assert.match(page, /async openReview\(item\)/)
@@ -145,8 +144,8 @@ test('relabel review opens a pending task as customer-confirmation read-only det
test('returned relabel tasks are highlighted, sorted first, and still obey the single active task lock', async () => {
const page = await read('../src/pages/relabel/relabel.vue')
assert.match(page, /item\.reviewDecision === 'REWORK'[\s\S]*退回重做[\s\S]*优先处理/)
assert.match(page, /退回备注:\{\{ item\.reviewRemark \}\}/)
assert.match(page, /item\.reviewDecision === 'REWORK'[\s\S]*退回重做/)
assert.match(page, /current\.reviewDecision === 'REWORK' && current\.reviewRemark[\s\S]*\{\{ current\.reviewRemark \}\}/)
assert.match(page, /\.sort\(\(left, right\) => Number\(right\.reviewDecision === 'REWORK'\) - Number\(left\.reviewDecision === 'REWORK'\)/)
assert.match(page, /const active = this\.candidates\.find\(\(candidate\) => candidate\.taskStatus === 'PROCESSING' && candidate\.claimedByCurrentOperator === true\)/)
assert.match(page, /if \(active && active\.relabelNo !== item\.relabelNo\) return this\.promptActiveTask\(active\)/)