优化
This commit is contained in:
@@ -35,7 +35,8 @@ test('profile exposes manual App update checking and the runtime version', async
|
||||
const profile = await read('../pages/profile/profile.vue')
|
||||
|
||||
assert.match(profile, /应用版本[\s\S]*?\{\{ appVersion \}\}/)
|
||||
assert.match(profile, /@tap="checkUpdate">检查更新/)
|
||||
assert.match(profile, /profile-list[\s\S]*?应用版本[\s\S]*?profile-row-action" @tap="checkUpdate"[\s\S]*?检查更新[\s\S]*?profile-row-action" @tap="showSettings"[\s\S]*?系统设置/)
|
||||
assert.doesNotMatch(profile, /class="profile-action"/)
|
||||
assert.match(profile, /plus\.runtime\.version/)
|
||||
assert.match(profile, /checkForAppUpdate\(\{ manual: true \}\)/)
|
||||
assert.match(profile, /#ifndef APP-PLUS[\s\S]*?请在 PDA App 中检查更新/)
|
||||
|
||||
@@ -48,9 +48,10 @@ test('manual logout revokes the refresh token without delaying local cleanup', a
|
||||
assert.match(profile, /logoutRequest\(refreshToken, accessToken\)\.catch/)
|
||||
})
|
||||
|
||||
test('development login remembers only the account preference, never the entered password', async () => {
|
||||
test('production login starts empty and remembers only the account preference', async () => {
|
||||
const [login, session] = await Promise.all([read('../pages/login/login.vue'), read('../stores/session.js')])
|
||||
assert.match(login, /form: \{ userId: 'platform_admin', password: 'ChangeMe123!' \}/)
|
||||
assert.match(login, /form: \{ userId: '', password: '' \}/)
|
||||
assert.doesNotMatch(login, /platform_admin|ChangeMe123!/)
|
||||
assert.match(session, /export function saveLoginPreference\(rememberAccount, account = ''\)/)
|
||||
const savePreference = session.match(/export function saveLoginPreference[\s\S]*?\n\}/)?.[0] || ''
|
||||
assert.match(savePreference, /account: rememberAccount/)
|
||||
|
||||
@@ -31,7 +31,8 @@ test('login uses the real auth endpoint and relaunches to the modular workbench'
|
||||
assert.match(auth, /\/auth\/login/)
|
||||
assert.match(login, /loginRequest\(userId, this\.form\.password\)/)
|
||||
assert.match(login, /signInWithTokens\(tokens\)/)
|
||||
assert.match(login, /userId: 'platform_admin', password: 'ChangeMe123!'/)
|
||||
assert.match(login, /form: \{ userId: '', password: '' \}/)
|
||||
assert.doesNotMatch(login, /platform_admin|ChangeMe123!/)
|
||||
assert.match(login, /记住账号/)
|
||||
assert.match(login, /loadLoginPreference\(\)/)
|
||||
assert.match(login, /saveLoginPreference\(this\.rememberAccount, userId\)/)
|
||||
@@ -60,7 +61,7 @@ test('picking task list uses remote loading orders and never falls back to demo
|
||||
assert.match(tasks, /task\.workflowStatus === 'AWAITING_REVIEW' \? '继续装柜取证'/)
|
||||
assert.match(tasks, /\['UNASSIGNED', 'IN_PROGRESS', 'AWAITING_REVIEW'\]\.includes\(task\.workflowStatus\)/)
|
||||
assert.match(tasks, /task\.workflowStatus === 'AWAITING_REVIEW' \|\| \(task\.assignmentVersion !== null/)
|
||||
assert.match(tasks, /暂无可领取的拣货任务/)
|
||||
assert.match(tasks, /暂无待处理的拣货任务/)
|
||||
assert.match(tasks, /class="task-label">集装箱号<\/text><text class="task-code">\{\{ task\.currentContainerNo \}\}/)
|
||||
assert.match(tasks, /柜次号 \{\{ task\.cabinetNo \}\}/)
|
||||
assert.match(tasks, /button v-for="task in tasks"[\s\S]*?class="task-card"[\s\S]*?@tap="enterTask\(task\)"/)
|
||||
@@ -79,11 +80,14 @@ 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')
|
||||
])
|
||||
for (const text of ['首页', '库内作业', 'home-operation-grid', 'home-operation-primary', 'getPickingOverview', 'uni-icons']) assert.match(home, new RegExp(text))
|
||||
for (const text of ['首页', '库内作业', 'home-operation-grid', 'home-operation-primary', '开始作业', 'uni-icons']) assert.match(home, new RegExp(text))
|
||||
assert.doesNotMatch(home, /getPickingOverview|pendingBoxCount|箱待拣/)
|
||||
assert.doesNotMatch(home, /BottomNav|status-strip|task-card/)
|
||||
for (const text of ['业务模块', '业务功能', '第一版本仅开放']) assert.match(module, new RegExp(text))
|
||||
assert.match(home, /:disabled="!operation\.available"/)
|
||||
assert.match(home, /home-operation-disabled/)
|
||||
assert.match(home, /home-operation-meta home-operation-pending/)
|
||||
assert.match(home, /home-operation-meta home-operation-unavailable/)
|
||||
assert.match(home, /openOperation\(operation\) \{ if \(operation\.available\) uni\.navigateTo/)
|
||||
assert.doesNotMatch(home, /pages\/operation\/operation\?module=/)
|
||||
assert.match(module, /:disabled="!operation\.available"/)
|
||||
@@ -280,9 +284,17 @@ test('shared visual contracts preserve accessible PDA targets', async () => {
|
||||
assert.match(tasks, /\.task-card\s*\{[^}]*margin:\s*0 0 12px;/)
|
||||
assert.match(tasks, /\.task-card:last-child\s*\{[^}]*margin-bottom:\s*0;/)
|
||||
assert.match(styles, /\.work-content\s*\{[^}]*padding:\s*1px 0 104px;/)
|
||||
assert.match(picking, /class="box-select-summary"/)
|
||||
assert.match(styles, /\.box-select-summary\s*\{[^}]*min-width:\s*0;[^}]*overflow:\s*hidden;[^}]*text-overflow:\s*ellipsis;[^}]*white-space:\s*nowrap;/)
|
||||
assert.match(styles, /\.submit-section\s*\{[\s\S]*?bottom:\s*8px;[\s\S]*?width:\s*calc\(100% - 20px\);/)
|
||||
assert.match(styles, /\.submit-btn\s*\{[\s\S]*?flex:\s*1;[\s\S]*?height:\s*56px;/)
|
||||
assert.match(styles, /\.home-group-mark\s*\{[^}]*margin-right:\s*10px;/)
|
||||
assert.match(styles, /\.home-group-mark\s*\{[^}]*margin-right:\s*7px;/)
|
||||
assert.match(styles, /\.home-operation-grid\s*\{[^}]*grid-template-columns:\s*repeat\(3, minmax\(0, 1fr\)\);/)
|
||||
assert.match(styles, /\.home-operation\s*\{[^}]*width:\s*100%;[^}]*min-height:\s*78px;[^}]*align-self:\s*stretch;/)
|
||||
assert.match(styles, /\.home-operation-name\s*\{[^}]*min-height:\s*30px;[^}]*white-space:\s*normal;/)
|
||||
assert.doesNotMatch(styles, /\.home-operation-name\s*\{[^}]*(text-overflow:\s*ellipsis|-webkit-line-clamp|white-space:\s*nowrap)/)
|
||||
assert.match(styles, /\.home-operation-meta\s*\{[^}]*border-radius:\s*7px;[^}]*white-space:\s*nowrap;/)
|
||||
assert.match(styles, /@media \(max-width:\s*340px\)\s*\{[\s\S]*?\.home-operation-grid\s*\{[^}]*grid-template-columns:\s*repeat\(2, minmax\(0, 1fr\)\);/)
|
||||
assert.match(styles, /\.header-menu-item\s*\{[^}]*width:\s*calc\(100% - 32px\);[^}]*margin:\s*0 16px;/)
|
||||
assert.match(styles, /\.header-menu-close\s*\{[^}]*width:\s*calc\(100% - 32px\);/)
|
||||
assert.match(picking, /aria-modal="true"/)
|
||||
|
||||
@@ -5,10 +5,12 @@ import { readFile } from 'node:fs/promises'
|
||||
const page = await readFile(new URL('../pages/picking/picking.vue', import.meta.url), 'utf8')
|
||||
|
||||
test('picking and photo workspaces are independently selectable', () => {
|
||||
assert.match(page, /v-if="activeStage === 'evidence' && loadingStageReady" class="loading-evidence-section"/)
|
||||
assert.match(page, /v-if="activeStage === 'evidence'" class="loading-evidence-section"/)
|
||||
assert.match(page, /selectStage\(stage\) \{[\s\S]*?this\.activeStage = stage/)
|
||||
assert.doesNotMatch(page, /:disabled="!loadingStageReady"/)
|
||||
assert.match(page, /isPickingCompleteTask\(task\)[\s\S]*?AWAITING_REVIEW[\s\S]*?COMPLETED/)
|
||||
assert.match(page, /pickingCompleted\(\)[\s\S]*?this\.remoteTaskId \|\| this\.currentBox\.taskId[\s\S]*?if \(!this\.remotePick\) return false[\s\S]*?hasServerPending[\s\S]*?isPickingCompleteTask\(this\.remotePick\) && !hasServerPending/)
|
||||
assert.match(page, /请先处理并提交全部待拣明细/)
|
||||
assert.doesNotMatch(page, /任务增量更新后|evidence-stage-hint/)
|
||||
})
|
||||
|
||||
test('a server task never inherits a completed marker from the previously opened local cabinet', () => {
|
||||
@@ -46,12 +48,12 @@ test('AWAITING_REVIEW alone cannot request or mutate server evidence without a s
|
||||
assert.doesNotMatch(page, /if \(this\.pickingCompleted\) await this\.loadLoadingEvidence\(\)/)
|
||||
})
|
||||
|
||||
test('dynamic pending items withdraw evidence until the server task is picking-complete again', () => {
|
||||
test('dynamic pending items do not block switching between picking and evidence', () => {
|
||||
assert.match(page, /class="stage-switch" role="tablist"/)
|
||||
assert.match(page, /@tap="selectStage\('picking'\)"/)
|
||||
assert.match(page, /@tap="selectStage\('evidence'\)"/)
|
||||
assert.match(page, /selectStage\(stage\) \{[\s\S]*?stage === 'evidence' && !this\.loadingStageReady[\s\S]*?请先完成新增待拣明细[\s\S]*?return[\s\S]*?this\.activeStage = stage/)
|
||||
assert.match(page, /class="stage-tab"[\s\S]*?:disabled="!loadingStageReady"[\s\S]*?装柜取证/)
|
||||
assert.doesNotMatch(page, /stage === 'evidence' && !this\.loadingStageReady/)
|
||||
assert.doesNotMatch(page, /请先完成新增待拣明细/)
|
||||
assert.match(page, /activeStage === 'picking'/)
|
||||
assert.match(page, /:disabled="submitting \|\| !loadingStageReady \|\| pickingVersion === null/)
|
||||
})
|
||||
@@ -65,7 +67,8 @@ test('incremental refresh merges new pending items without overwriting submitted
|
||||
assert.match(page, /const previousTaskVersion = this\.pickingVersion/)
|
||||
assert.match(page, /const previousItemIds = new Set/)
|
||||
assert.match(page, /previousEvidenceState[\s\S]*?isSameEvidenceIdentity\(previousEvidenceState\.identity, this\.evidenceIdentity\)[\s\S]*?this\.evidenceFiles = \{ \.\.\.this\.evidenceFiles, \.\.\.previousEvidenceState\.files \}/)
|
||||
assert.match(page, /if \(!this\.pickingCompleted\)[\s\S]*?delete completedBoxes\[this\.currentBoxIndex\][\s\S]*?this\.activeStage = 'picking'/)
|
||||
assert.match(page, /if \(!this\.pickingCompleted\)[\s\S]*?delete completedBoxes\[this\.currentBoxIndex\]/)
|
||||
assert.doesNotMatch(page, /delete completedBoxes\[this\.currentBoxIndex\][\s\S]{0,160}?this\.activeStage = 'picking'/)
|
||||
assert.match(page, /refresh\.incremental[\s\S]*?addedItemIds/)
|
||||
})
|
||||
|
||||
@@ -139,11 +142,16 @@ test('merged or non-active cabinet details are rejected before claim and clear s
|
||||
for (const reset of ['remoteLoading = false', 'submitting = false', 'remoteTaskId = null', 'loadingVersion = null', 'scanHistory = []', 'currentBox.containerBookingId = null', 'currentBox.currentContainerNo = null', 'resetEvidenceState()']) assert.match(clear, new RegExp(reset.replace(/[.()[\]]/g, '\\$&')))
|
||||
})
|
||||
|
||||
test('server evidence mutations remain closed before the loading stage is ready', () => {
|
||||
test('photos upload before picking completion while evidence binding remains gated', () => {
|
||||
assert.match(page, /requireEvidenceStage\(\)[\s\S]*?if \(!this\.pickingCompleted\)[\s\S]*?请先提交拣货完成[\s\S]*?if \(!this\.loadingStageReady\)/)
|
||||
const queue = page.match(/async queueEvidenceUpload\([^)]*\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||
assert.match(queue, /if \(!this\.loadingStageReady\) return/)
|
||||
for (const method of ['retryEvidence', 'saveEvidence', 'voidEvidencePhoto', 'completeLoadingTask']) {
|
||||
assert.ok(queue.indexOf('await queue.flush()') < queue.indexOf('if (!this.loadingStageReady)'))
|
||||
assert.ok(queue.indexOf('if (!this.loadingStageReady)') < queue.indexOf('await this.saveEvidence(type, null, path)'))
|
||||
assert.match(page, /chooseEvidence\(slot\)[\s\S]*?this\.queueEvidenceUpload\(slot\.type, path\)/)
|
||||
const retry = page.match(/async retryEvidence\([^)]*\) \{[\s\S]*?\n\t\t\t\},/)?.[0] || ''
|
||||
assert.match(retry, /if \(!this\.requireEvidenceCapture\(\)\) return/)
|
||||
assert.ok(retry.indexOf('await queue.flush()') < retry.indexOf('if (!this.loadingStageReady)'))
|
||||
for (const method of ['saveEvidence', 'voidEvidencePhoto', 'completeLoadingTask']) {
|
||||
const body = page.match(new RegExp(`(?:async )?${method}\\([^)]*\\) \\{[\\s\\S]*?\\n\\t\\t\\t\\},`))?.[0] || ''
|
||||
assert.match(body, /if \(!this\.requireEvidenceStage\(\)\)(?: return| \{)/, `${method} should guard the evidence stage`)
|
||||
}
|
||||
@@ -158,7 +166,7 @@ test('refresh preserves local photos for the same evidence identity and flushes
|
||||
|
||||
test('evidence photos render thumbnails and support full-screen preview', () => {
|
||||
assert.match(page, /class="evidence-thumbnail" :src="evidencePreviewSource\(photo\)"/)
|
||||
assert.match(page, /class="work-content" :class="\{ 'evidence-active': activeStage === 'evidence' && loadingStageReady \}"/)
|
||||
assert.match(page, /class="work-content" :class="\{ 'evidence-active': activeStage === 'evidence' \}"/)
|
||||
assert.match(page, /class="evidence-photo-grid"/)
|
||||
assert.match(page, /class="evidence-count"/)
|
||||
assert.match(page, /@tap="previewEvidencePhoto\(photo, slot\.type\)"/)
|
||||
|
||||
@@ -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('picking task list exposes only operator-facing production information', async () => {
|
||||
const [tasks, adapter] = await Promise.all([
|
||||
read('../pages/tasks/tasks.vue'),
|
||||
read('../api/pda-loading.js')
|
||||
])
|
||||
|
||||
assert.match(tasks, /共 \$\{tasks\.length\} 个待处理任务/)
|
||||
assert.match(tasks, /v-if="task\.displayNo"/)
|
||||
assert.match(tasks, /v-if="task\.warehouseName"/)
|
||||
assert.doesNotMatch(tasks, /接口返回|当前接口|apiEnvironment|rawTaskCount|账号:|未命名订单|缺少仓库信息|服务端就绪校验|console\.info/)
|
||||
assert.doesNotMatch(adapter, /业务单号待同步/)
|
||||
})
|
||||
|
||||
test('production login contains no built-in account or password', async () => {
|
||||
const login = await read('../pages/login/login.vue')
|
||||
|
||||
assert.match(login, /form: \{ userId: '', password: '' \}/)
|
||||
assert.doesNotMatch(login, /platform_admin|ChangeMe123!/)
|
||||
})
|
||||
Reference in New Issue
Block a user