Files
client-web/src/modules/cross-border/transportPhotos.test.mjs
T

61 lines
4.3 KiB
JavaScript
Raw Normal View History

2026-11-03 16:52:02 +08:00
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { runInNewContext } from 'node:vm'
import ts from 'typescript'
import * as vue from 'vue'
import { parse, compileScript } from 'vue/compiler-sfc'
const source = readFileSync(new URL('./components/TransportPhotoGallery.vue', import.meta.url), 'utf8')
const { descriptor } = parse(source)
const code = ts.transpileModule(compileScript(descriptor, { id: 'transport-photos' }).content, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS } }).outputText
const flush = async () => { await vue.nextTick(); await new Promise(resolve => setImmediate(resolve)); await vue.nextTick() }
function harness(kind, overrides = {}) {
const props = vue.reactive({ kind, orderNo: 'ORDER/1', refreshKey: 0 }), calls = [], disposers = [], created = [], revoked = []
const photos = { pickupPhotos: [{ fileId: '71', fileName: 'pickup.png', contentType: 'image/png', sizeBytes: 5 }], arrivalPhotos: [{ fileId: '72', fileName: 'arrival.png', contentType: 'image/png', sizeBytes: 6 }] }
const api = { getTransportPhotos: async (...args) => { calls.push(['list', ...args]); return photos }, readTransportPhoto: async (...args) => { calls.push(['file', ...args]); return {} }, ...overrides }
const context = { exports: {}, Error, URL: { createObjectURL: () => { const url = `blob:${created.length}`; created.push(url); return url }, revokeObjectURL: url => revoked.push(url) }, require(name) {
if (name === 'vue') return { ...vue, onBeforeUnmount: callback => disposers.push(callback) }
if (name === '../api/businessOrders') return api
throw Error(name)
} }
runInNewContext(code, context)
const state = context.exports.default.setup(props, { expose() {} })
return { state, props, calls, created, revoked, photos, dispose: () => disposers.forEach(fn => fn()) }
}
for (const kind of ['looseboard', 'trucking']) test(`${kind} loads both photo nodes and refreshes with URL cleanup`, async () => {
const h = harness(kind); await flush()
assert.deepEqual(h.calls, [['list', kind, 'ORDER/1'], ['file', kind, 'ORDER/1', 'PICKUP', '71'], ['file', kind, 'ORDER/1', 'ARRIVAL', '72']])
assert.equal(h.state.previewUrls.value.length, 2)
h.props.refreshKey++; await flush()
assert.equal(h.revoked.length, 2)
assert.equal(h.state.previewUrls.value.length, 2)
h.dispose(); assert.equal(h.revoked.length, 4)
})
test('failed photo reads preserve the saved lists and allow retry', async () => {
let failed = true
const h = harness('looseboard', { readTransportPhoto: async () => { if (failed) throw Error('offline'); return {} } }); await flush()
assert.match(h.state.error.value, /加载失败/); assert.equal(h.state.photos.value.pickupPhotos.length, 1)
failed = false; await h.state.load(); await flush()
assert.equal(h.state.error.value, ''); assert.equal(h.state.previewUrls.value.length, 2); h.dispose()
})
test('responses after unmount do not allocate preview URLs', async () => {
let finish
const response = new Promise(resolve => { finish = resolve })
const h = harness('trucking', { readTransportPhoto: () => response }); await flush()
h.dispose(); finish({}); await flush(); assert.equal(h.created.length, 0)
})
test('merchant photo API uses order and phase routes with authenticated blob requests', async () => {
const api = readFileSync(new URL('./api/businessOrders.ts', import.meta.url), 'utf8')
const compiled = ts.transpileModule(api, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS } }).outputText
const calls = [], context = { exports: {}, require: () => ({ apiRequest: request => calls.push(request), apiBlobRequest: request => calls.push(request) }) }
runInNewContext(compiled, context)
for (const [kind, route] of [['looseboard', 'local-looseboard-orders'], ['trucking', 'us-truck-orders']]) {
context.exports.getTransportPhotos(kind, 'ORDER/1')
assert.equal(calls.at(-1).url, `/merchant/v1/last-leg/${route}/ORDER%2F1/transport-photos`)
context.exports.readTransportPhoto(kind, 'ORDER/1', 'ARRIVAL', '72')
assert.equal(calls.at(-1).url, `/merchant/v1/last-leg/${route}/ORDER%2F1/transport-photos/ARRIVAL/72/content`)
}
assert.match(source, /preview-teleported/)
assert.match(source, /flex-wrap: wrap/)
})