Merge remote-tracking branch 'origin/dev' into release/test

# Conflicts:
#	.env.example
#	config/runtime.js
#	pages/profile/profile.vue
#	stores/session.js
#	tests/page-contract.test.mjs
#	vite.config.js
This commit is contained in:
2026-08-26 20:57:50 +08:00
15 changed files with 473 additions and 104 deletions
+42
View File
@@ -0,0 +1,42 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
const read = (path) => readFile(new URL(path, import.meta.url), 'utf8')
test('App-Plus checks the independent update channel after launch', async () => {
const [app, updater, env] = await Promise.all([
read('../App.vue'),
read('../utils/app-update.js'),
read('../.env.example')
])
assert.match(app, /#ifdef APP-PLUS[\s\S]*?checkForAppUpdate\(\)/)
assert.match(env, /VITE_PDA_UPDATE_URL=https:\/\//)
assert.match(updater, /plus\.runtime\.getProperty/)
assert.match(updater, /appid:[\s\S]*?platform: 'android'[\s\S]*?appVersion:[\s\S]*?appVersionCode:[\s\S]*?wgtVersion:/)
})
test('online update supports mandatory APK and wgt installation flows', async () => {
const updater = await read('../utils/app-update.js')
assert.match(updater, /\['apk', 'wgt'\]\.includes\(packageType\)/)
assert.match(updater, /update\.versionCode > runtime\.appVersionCode/)
assert.match(updater, /compareVersions\(update\.versionName, runtime\.wgtVersion\) > 0/)
assert.match(updater, /更新包必须使用 HTTPS 地址/)
assert.match(updater, /showCancel: !update\.mandatory/)
assert.match(updater, /uni\.downloadFile\([\s\S]*?onProgressUpdate/)
assert.match(updater, /plus\.runtime\.install/)
assert.match(updater, /packageType === 'wgt'[\s\S]*?plus\.runtime\.restart/)
assert.match(updater, /if \(retry\) await runUpdate\(update\)/)
})
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, /plus\.runtime\.version/)
assert.match(profile, /checkForAppUpdate\(\{ manual: true \}\)/)
assert.match(profile, /#ifndef APP-PLUS[\s\S]*?请在 PDA App 中检查更新/)
})
+1 -1
View File
@@ -44,7 +44,7 @@ test('confirmed session expiry clears sensitive state and redirects only once',
test('manual logout revokes the refresh token without delaying local cleanup', async () => {
const [profile, auth] = await Promise.all([read('../pages/profile/profile.vue'), read('../api/auth.js')])
assert.match(auth, /export function logout\(refreshToken, accessToken\)/)
assert.match(profile, /const refreshToken = session\.refreshToken; const accessToken = session\.token; signOut\(\); uni\.reLaunch/)
assert.match(profile, /const refreshToken = session\.refreshToken[\s\S]*?const accessToken = session\.token[\s\S]*?signOut\(\)[\s\S]*?uni\.reLaunch/)
assert.match(profile, /logoutRequest\(refreshToken, accessToken\)\.catch/)
})
+12 -7
View File
@@ -23,8 +23,8 @@ test('application uses the Vue 3 uni-app entry and no unused Android permissions
})
test('login uses the real auth endpoint and relaunches to the modular workbench', async () => {
const [login, auth, session, runtime, vite] = await Promise.all([
read('../pages/login/login.vue'), read('../api/auth.js'), read('../stores/session.js'), read('../config/runtime.js'), read('../vite.config.js')
const [login, auth, session, runtime, vite, manifest] = await Promise.all([
read('../pages/login/login.vue'), read('../api/auth.js'), read('../stores/session.js'), read('../config/runtime.js'), read('../vite.config.js'), read('../manifest.json')
])
for (const text of ['柏悦物流 · PDA作业端', '工号 / 用户名', '登录中']) assert.match(login, new RegExp(text))
assert.doesNotMatch(login, /DEMO_ACCOUNTS|快捷登录|作业仓库/)
@@ -39,11 +39,15 @@ test('login uses the real auth endpoint and relaunches to the modular workbench'
assert.match(session, /pda-login-account-preference/)
assert.match(session, /claims\.app_code !== 'PLATFORM'/)
assert.match(runtime, /import\.meta\.env\.VITE_API_BASE_URL/)
assert.match(runtime, /#ifdef H5[\s\S]*?VITE_H5_API_BASE_URL \|\| '\/api'/)
assert.match(runtime, /#ifdef H5[\s\S]*?VITE_H5_API_BASE_URL \|\| '\/gateway-api'/)
assert.doesNotMatch(runtime, /typeof\s+import\.meta/)
assert.match(vite, /API_PROXY_TARGET/)
assert.match(vite, /'\/api'[\s\S]*?target: proxyTarget[\s\S]*?changeOrigin: true/)
assert.match(vite, /process\.env\.UNI_INPUT_DIR \|\| process\.cwd\(\)/)
assert.match(vite, /'\/gateway-api'[\s\S]*?target: proxyTarget[\s\S]*?changeOrigin: true[\s\S]*?replace\(\/\^\\\/gateway-api\/[\s\S]*?'\/api'/)
assert.match(vite, /removeHeader\('origin'\)/)
assert.match(vite, /removeHeader\('referer'\)/)
assert.match(vite, /proxy\.on\('error'/)
assert.doesNotMatch(manifest, /"proxy"\s*:|pathRewrite|disableHostCheck/)
assert.match(login, /uni\.reLaunch\(\{ url: '\/pages\/home\/home' \}\)/)
})
@@ -135,9 +139,10 @@ test('picking page keeps scan, draft and risky transition contracts', async () =
assert.match(page, /lastScanResult\.boxCode/)
assert.match(page, /boxCode: entry\.boxCode \?\? entry\.sku \?\? ''/)
assert.doesNotMatch(page, /lastScanResult\.sku|entry\.sku\s*}}/)
const processScanMethod = page.match(/processScannedValue\(scanValue, source = 'broadcast'\) \{[\s\S]*?\n\t\t\t\},\n\t\t\tpickItem/)
assert.ok(processScanMethod)
assert.doesNotMatch(processScanMethod[0], /this\.scanValue = ''/)
const processScanStart = page.indexOf("processScannedValue(scanValue, source = 'broadcast') {")
const processScanEnd = page.indexOf('\n\t\t\tevidenceEntries', processScanStart)
assert.ok(processScanStart >= 0 && processScanEnd > processScanStart)
assert.doesNotMatch(page.slice(processScanStart, processScanEnd), /this\.scanValue = ''/)
assert.match(scanner, /android\.intent\.action\.SCANRESULT/)
assert.match(scanner, /SCAN_BROADCAST_EXTRA = 'value'/)
assert.match(scanner, /registerReceiver\(receiver, filter\)/)