Files
shanxia2.0/web-admin/scripts/mobile-pl-paste.test.ts
2026-10-23 23:07:00 +08:00

173 lines
7.5 KiB
TypeScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
import { parse } from '@vue/compiler-sfc'
import ts from 'typescript'
import { ref } from 'vue'
function evaluate(file: string, names: string[], b: Record<string, any>) {
const ast = ts.createSourceFile(
'x.ts',
parse(readFileSync(new URL(file, import.meta.url), 'utf8')).descriptor.scriptSetup!.content,
ts.ScriptTarget.Latest,
true
)
const text = names
.map((name) => {
const n = ast.statements.find((n) =>
ts.isFunctionDeclaration(n)
? n.name?.text === name
: ts.isVariableStatement(n) &&
n.declarationList.declarations.some((d) => d.name.getText(ast) === name)
)
assert.ok(n, name)
return n.getText(ast)
})
.join('\n')
return new Function(
...Object.keys(b),
ts.transpile(text, { target: ts.ScriptTarget.ESNext }) + ';return {' + names.join(',') + '}'
)(...Object.values(b))
}
const file = '../src/components/business/pl-paste-recognize-dialog/index.vue'
function harness(overrides: any = {}) {
const calls: any[] = [],
events: any[] = []
const b: any = {
ref,
pasteSession: ref(0),
mobileCarriers: ref([]),
mobileCarrierLoading: ref(false),
pasteDisposed: ref(false),
props: { mode: 'vehicle-bundle', visible: true },
isPageVariant: ref(true),
isMobileVariant: ref(true),
parsed: ref({}),
rawText: ref('车牌:皖QA1023 皖QA102挂'),
vehicleForm: { licensePlate: '皖QA1023', trailerNo: '皖QA102挂', loadCapacity: 3 },
driverForm: { personnelName: '', phoneNo: '', idCardNo: '' },
escortForm: { personnelName: '', phoneNo: '', idCardNo: '' },
carrierOptions: ref([]),
carrierId: ref(''),
conflictRows: ref([]),
showVehicleFields: ref(true),
submitLoading: ref(false),
parseLoading: ref(false),
conflictFingerprint: ref(''),
currentConflictFingerprint: () => '',
ensureCarriers: async () => {},
detectConflicts: async () => {},
pasteRecognizeApi: {
commit: async (p: any) => {
calls.push(p)
return { vehicleId: 'v1' }
}
},
pickBxData: (x: any) => x,
ElMessage: { warning: () => {}, info: () => {}, error: () => {}, success: () => {} },
emit: (...a: any[]) => events.push(a),
innerVisible: ref(true),
...overrides
}
Object.assign(b, evaluate(file, ['isPasteSessionCurrent', 'invalidatePasteSession'], b))
return { ...evaluate(file, ['handleSubmit', 'handleCancel'], b), b, calls, events }
}
test('optional empty personnel produces null people and correct vehicle ID', async () => {
const h = harness()
await h.handleSubmit()
assert.equal(h.calls[0].driver, null)
assert.equal(h.calls[0].escort, null)
assert.deepEqual(h.events[0], ['done', { vehicleId: 'v1' }])
})
test('quick create double click commits once', async () => {
const h = harness()
await Promise.all([h.handleSubmit(), h.handleSubmit()])
assert.equal(h.calls.length, 1)
})
test('cancel does not commit; failed generation retains inputs', async () => {
const h = harness({
pasteRecognizeApi: {
commit: async () => {
throw Error('failed')
}
}
})
h.handleCancel()
assert.equal(h.calls.length, 0)
await h.handleSubmit()
assert.equal(h.b.vehicleForm.licensePlate, '皖QA1023')
assert.equal(
h.events.some((e: any) => e[0] === 'done'),
false
)
})
for (const action of ['reuse', 'overwrite', 'cancel'])
test('conflict ' + action + ' keeps payload choice', async () => {
const row = { key: 'vehicle', existingId: 'old', action }
const h = harness({ conflictRows: ref([row]) })
await h.handleSubmit()
if (action === 'cancel') assert.equal(h.calls.length, 0)
else assert.deepEqual(h.calls[0].conflicts, [row])
})
test('edited identity triggers fresh conflict lookup before commit', async () => {
const h = harness({
conflictFingerprint: ref('old'),
currentConflictFingerprint: () => 'new',
conflictRows: ref([{ key: 'vehicle', existingId: 'old', action: 'reuse' }])
})
h.b.detectConflicts = async () => {
h.b.conflictRows.value = [{ key: 'vehicle', existingId: 'new', action: 'reuse' }]
h.b.conflictFingerprint.value = 'new'
return true
}
const run = evaluate(file, ['handleSubmit'], h.b).handleSubmit
await run()
assert.equal(h.calls.length, 0)
await run()
assert.equal(h.calls[0].conflicts[0].existingId, 'new')
})
test('actual lookup ignores stale identity response', async () => {
let release: any
const pending = new Promise((r) => (release = r))
const h = harness({
currentConflictFingerprint: () => h.b.vehicleForm.licensePlate,
vehicleApi: { list: () => pending },
personnelApi: { list: async () => ({ list: [] }) },
pickBxPageResult: (x: any) => x
})
const lookup = evaluate(file, ['detectConflicts'], h.b).detectConflicts
const running = lookup()
h.b.vehicleForm.licensePlate = 'changed'
release({ list: [{ id: 'old', licensePlate: '皖QA1023' }] })
assert.equal(await running, false)
assert.equal(h.b.conflictRows.value.length, 0)
})
test('old same-identity lookup cannot overwrite a reopened session conflict', async () => {
let release:any
const h=harness({ createVehicle:ref(true), PL_PASTE_SAMPLE_TEXT:'', vehicleApi:{list:()=>new Promise(r=>release=r)}, personnelApi:{list:async()=>({list:[]})}, pickBxPageResult:(x:any)=>x })
const f=evaluate(file,['detectConflicts','handleClosed'],h.b)
const old=f.detectConflicts();f.handleClosed()
h.b.vehicleForm.licensePlate='皖QA1023';h.b.conflictRows.value=[{key:'vehicle',existingId:'new-session-id',action:'reuse'}]
release({list:[{id:'old-session-id',licensePlate:'皖QA1023'}]})
await old;assert.equal(h.b.conflictRows.value[0].existingId,'new-session-id')
})
for (const reject of [false,true]) test('closed-session commit '+(reject?'failure':'success')+' neither emits nor resets new loading',async()=>{
let settle:any
const h=harness({createVehicle:ref(true), PL_PASTE_SAMPLE_TEXT:'',pasteRecognizeApi:{commit:()=>new Promise((yes,no)=>settle=reject?no:yes)}})
const f=evaluate(file,['handleSubmit','handleClosed'],h.b)
const running=f.handleSubmit();await new Promise(r=>setTimeout(r,0));f.handleClosed();h.b.submitLoading.value=true
settle(reject?Error('old failure'):{vehicleId:'old-vehicle'});await running
assert.equal(h.events.length,0);assert.equal(h.b.submitLoading.value,true)
})
test('old parse failure does not clear reopened loading or display error',async()=>{
let reject:any;const errors:any[]=[]
const h=harness({createVehicle:ref(true),PL_PASTE_SAMPLE_TEXT:'',parsePlCustomerVehicleText:()=>({vehicle:{licensePlate:'皖QA1023'},warnings:[]}),detectConflicts:()=>new Promise((_,no)=>reject=no),ElMessage:{warning:()=>{},error:(value:any)=>errors.push(value)}})
const f=evaluate(file,['handleParse','handleClosed'],h.b);const old=f.handleParse();f.handleClosed();h.b.parseLoading.value=true;reject(Error('old'));await old
assert.equal(h.b.parseLoading.value,true);assert.deepEqual(errors,[])
})
test('old carrier search cannot replace new session options or clear its loading',async()=>{
let release:any;const h=harness({createVehicle:ref(true),PL_PASTE_SAMPLE_TEXT:'',mobileCarrierRequest:0,carrierApi:{list:()=>new Promise(r=>release=r)},pickBxList:(value:any)=>value})
const f=evaluate(file,['searchMobileCarriers','handleClosed'],h.b);const old=f.searchMobileCarriers('');f.handleClosed();h.b.mobileCarriers.value=[{id:'new'}];h.b.mobileCarrierLoading.value=true;release([{id:'old'}]);await old
assert.equal(h.b.mobileCarriers.value[0].id,'new');assert.equal(h.b.mobileCarrierLoading.value,true)
})