114 lines
6.3 KiB
JavaScript
114 lines
6.3 KiB
JavaScript
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'] } }),
|
|
getTransfer: async () => ({ status: 'WAIT_CUSTOMER_CONFIRMATION', measurementPhotos: [{ fileId: '101' }], cartons: [{ cartonMark: 'BOX-1', status: 'COUNTED', weight: 5 }], pallets: [{ palletNo: 'PLT-1', grossWeightKg: 5 }] }),
|
|
downloadTransferMeasurementPhoto: async () => ({}),
|
|
confirmTransferMeasurement: async no => calls.push(['transfer', no]),
|
|
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, measurement };')(...Object.values(env))
|
|
return { ...state, props, calls, events, revoked }
|
|
}
|
|
|
|
test('仓储转运客户确认测量弹窗不展示箱子明细和托盘尺寸', () => {
|
|
assert.doesNotMatch(source, /<el-table :data="measurement\.cartons\?\.filter\(carton => carton\.status === 'COUNTED'\)/)
|
|
assert.doesNotMatch(source, /<el-table-column label="尺寸\(cm\)"/)
|
|
assert.doesNotMatch(source, /箱数、尺寸、重量/)
|
|
assert.match(source, /<el-table-column prop="grossWeightKg" label="毛重\(kg\)"/)
|
|
assert.match(source, /<el-table-column prop="volumeCbm" label="体积\(m³\)"/)
|
|
})
|
|
|
|
for (const kind of ['disposal', 'labeling', 'transfer']) {
|
|
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 === 'transfer' ? 'transfer' : 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('transfer confirmation reads current measurements and blocks missing, failed or outdated evidence', async () => {
|
|
const state = harness('transfer', { downloadTransferMeasurementPhoto: async () => { throw Error('offline') } })
|
|
await state.loadEvidence()
|
|
assert.equal(state.measurement.value.pallets[0].grossWeightKg, 5)
|
|
assert.equal(state.measurement.value.cartons[0].cartonMark, 'BOX-1')
|
|
state.checked.value = true
|
|
await state.submit()
|
|
assert.equal(state.canConfirm.value, false)
|
|
assert.deepEqual(state.calls, [])
|
|
for (const detail of [
|
|
{ status: 'WAIT_CUSTOMER_CONFIRMATION', measurementPhotos: [] },
|
|
{ status: 'COMPLETED', measurementPhotos: [{ fileId: '101' }] },
|
|
{ status: 'WAIT_CUSTOMER_CONFIRMATION', customerConfirmedAt: '2026-10-16', measurementPhotos: [{ fileId: '101' }] },
|
|
]) {
|
|
const invalid = harness('transfer', { getTransfer: async () => detail })
|
|
await invalid.loadEvidence(); invalid.checked.value = true; await invalid.submit()
|
|
assert.equal(invalid.canConfirm.value, false)
|
|
assert.deepEqual(invalid.calls, [])
|
|
}
|
|
})
|
|
|
|
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)
|
|
})
|