57 lines
3.6 KiB
TypeScript
57 lines
3.6 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import test from 'node:test'
|
|
import { parse } from '@vue/compiler-sfc'
|
|
import ts from 'typescript'
|
|
|
|
function harness() {
|
|
const script = parse(readFileSync(new URL('../src/components/business/pl-plan-archive-dialog/index.vue', import.meta.url), 'utf8')).descriptor.scriptSetup!.content
|
|
const ast = ts.createSourceFile('archive.ts', script, ts.ScriptTarget.Latest, true)
|
|
const methods = ast.statements.filter(n => ts.isFunctionDeclaration(n) && ['handleSubmit', 'submitArchiveWithOverageConfirm'].includes(n.name?.text || '')).map(n => n.getText(ast)).join('\n')
|
|
const calls: any[] = []
|
|
let resolveQuota: (value: any) => void = () => {}
|
|
let outcome: any = {}
|
|
const body = { products: [{ id: 'source-a', actualQty: 18 }] }
|
|
const state: any = {
|
|
props: { planId: 'a' }, innerVisible: { value: true }, submitLoading: { value: false },
|
|
canSubmitStopArchive: { value: true }, loadProductRows: { value: [] }, deliveryBlocks: { value: [] },
|
|
productRows: { value: [{ isToxic: 1 }] }, formData: { hasFreight: 0 }, plan: { value: {} },
|
|
isAdjustMode: { value: false }, quotaSettlementDialog: { value: { open: () => new Promise(resolve => { resolveQuota = resolve }) } },
|
|
syncSegmentBrandTotals() {}, syncMultiStopBrandActualQty() {}, validateStopArchiveRows() {}, validateSegmentBrandRows() {}, validatePlanLevelBrandTotals() {},
|
|
hasFeedbackAttachmentUploading: () => false, buildArchiveBody: () => body,
|
|
ElMessage: { warning() {}, success() {}, error() {} }, ElMessageBox: { alert: async () => {} },
|
|
tabsRef: { value: null }, emit() {}, pickBxData: (v: any) => v,
|
|
extractArchiveLineOverageDetail: () => null, applyArchiveOverageHighlight() {}, showArchiveLineOverageConfirm: async () => false,
|
|
planApi: { archive: async (id: string, data: any) => { calls.push({ id, data }); if(outcome instanceof Error) throw outcome; return outcome } }
|
|
}
|
|
const js = ts.transpileModule(`let archiveSession=0; let quotaArchiveAttempt={signature:'',id:''};${methods}; return {run:handleSubmit,submit:submitArchiveWithOverageConfirm,invalidate:()=>archiveSession++}`, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.None } }).outputText
|
|
const actions = new Function(...Object.keys(state), js)(...Object.values(state))
|
|
return { state, calls, body, ...actions, resolveQuota: (v: any) => resolveQuota(v), outcome: (v: any) => { outcome=v } }
|
|
}
|
|
|
|
test('waiting quota response cannot submit a subsequently opened plan', async () => {
|
|
const h = harness(); const task = h.run()
|
|
assert.equal(h.state.submitLoading.value, true)
|
|
h.state.props.planId = 'b'; h.invalidate(); h.resolveQuota({ quotaManualActuals: [] })
|
|
await task
|
|
assert.deepEqual(h.calls, [])
|
|
})
|
|
|
|
test('quota wait preserves the clicked plan and frozen actual quantities', async () => {
|
|
const h = harness(); const task = h.run()
|
|
h.body.products[0].actualQty = 999
|
|
h.resolveQuota({ quotaManualActuals: [] }); await task
|
|
assert.equal(h.calls[0].id, 'a')
|
|
assert.equal(h.calls[0].data.products[0].actualQty, 18)
|
|
})
|
|
|
|
test('committed quota exception starts a new operation; unknown transport failure reuses operation', async () => {
|
|
const h = harness(); h.outcome({ quotaSettlementError: '余额不足' })
|
|
await assert.rejects(h.submit('a', h.body, () => true))
|
|
h.outcome({}); await h.submit('a', h.body, () => true)
|
|
assert.notEqual(h.calls[0].data.operationId, h.calls[1].data.operationId)
|
|
h.outcome(new Error('network')); await assert.rejects(h.submit('a', h.body, () => true))
|
|
h.outcome({}); await h.submit('a', h.body, () => true)
|
|
assert.equal(h.calls[2].data.operationId, h.calls[3].data.operationId)
|
|
})
|