This commit is contained in:
2026-10-15 15:45:22 +08:00
parent 63e169b051
commit 5b320a3293
7 changed files with 430 additions and 63 deletions
@@ -0,0 +1,81 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import ts from 'typescript'
const source = readFileSync(new URL('./CompletionConfirmationDialog.vue', import.meta.url), 'utf8')
const script = source.match(/<script setup lang="ts">([\s\S]*?)<\/script>/)[1]
const js = ts.transpileModule(script, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext } }).outputText
.replace(/^import .*$/gm, '').replace(/^export \{\};?$/gm, '')
function harness(kind = 'disposal', overrides = {}) {
const props = { modelValue: true, kind, documentNo: 'CD/1' }
const calls = []; const events = []; const revoked = []
const api = {
getDiscard: async () => ({ summary: { status: 'PDA_COMPLETED' }, photos: [{ fileId: '101' }] }),
getRelabel: async () => ({ application: { status: 'CUSTOMER_CONFIRM_PENDING' }, completionEvidence: { photoFileIds: ['101'] } }),
downloadDiscardPhoto: async () => ({}), downloadRelabelCompletionPhoto: async () => ({}),
confirmDiscardCompletion: async no => calls.push(['discard', no]),
confirmRelabelCompletion: async no => calls.push(['relabel', no]), ...overrides,
}
const env = {
computed: fn => ({ get value() { return fn() } }), ref: value => ({ value }), watch: () => {}, onBeforeUnmount: () => {},
defineProps: () => props, defineEmits: () => (...args) => events.push(args),
useAuthStore: () => ({ hasPermission: () => true }), ElMessage: { success() {}, error() {} },
URL: { createObjectURL: () => 'blob:photo', revokeObjectURL: url => revoked.push(url) }, ...api,
}
const state = new Function(...Object.keys(env), js + '\nreturn { loadEvidence, loadPhoto, submit, clear, checked, canConfirm, urls, failed, error, busy };')(...Object.values(env))
return { ...state, props, calls, events, revoked }
}
for (const kind of ['disposal', 'labeling']) {
test(`${kind}: opening loads photos without confirming; only reviewed evidence can be submitted`, async () => {
const state = harness(kind)
await state.loadEvidence()
assert.deepEqual(state.calls, [])
await state.submit(); assert.deepEqual(state.calls, [])
state.checked.value = true
assert.equal(state.canConfirm.value, true)
await state.submit()
assert.deepEqual(state.calls, [[kind === 'disposal' ? 'discard' : 'relabel', 'CD/1']])
assert.deepEqual(state.events, [['confirmed', 'CD/1'], ['update:modelValue', false]])
state.clear(); assert.deepEqual(state.revoked, ['blob:photo'])
})
}
test('photo failure blocks confirmation and supports retry', async () => {
let reject = true
const state = harness('disposal', { downloadDiscardPhoto: async () => { if (reject) throw Error('offline'); return {} } })
await state.loadEvidence(); state.checked.value = true
assert.equal(state.failed.value['101'], true); assert.equal(state.canConfirm.value, false)
reject = false; await state.loadPhoto('101')
assert.equal(state.canConfirm.value, true)
})
test('closing while photos load prevents stale preview URLs', async () => {
let resolve
const state = harness('disposal', { downloadDiscardPhoto: () => new Promise(done => { resolve = done }) })
const pending = state.loadEvidence()
await Promise.resolve()
state.props.modelValue = false; state.clear(); resolve({}); await pending
assert.deepEqual(state.urls.value, {})
})
test('completed orders and missing evidence cannot be confirmed', async () => {
for (const detail of [{ summary: { status: 'COMPLETED' }, photos: [{ fileId: '101' }] }, { summary: { status: 'PDA_COMPLETED' }, photos: [] }]) {
const state = harness('disposal', { getDiscard: async () => detail })
await state.loadEvidence(); state.checked.value = true; await state.submit()
assert.equal(state.canConfirm.value, false); assert.deepEqual(state.calls, [])
}
})
test('failed submission preserves dialog and a pending submission is not sent twice', async () => {
let reject
const state = harness('disposal', { confirmDiscardCompletion: () => new Promise((_, fail) => { reject = fail }) })
await state.loadEvidence(); state.checked.value = true
const pending = state.submit()
assert.equal(state.busy.value, true); await state.submit()
reject(Error('offline')); await pending
assert.deepEqual(state.events, []); assert.equal(state.busy.value, false)
assert.equal(state.canConfirm.value, true)
})