61 lines
4.3 KiB
JavaScript
61 lines
4.3 KiB
JavaScript
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/)
|
||
|
|
})
|