190 lines
6.5 KiB
TypeScript
190 lines
6.5 KiB
TypeScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import { readFileSync } from 'node:fs'
|
|
import ts from 'typescript'
|
|
|
|
function fn(path: string, name: string, context: Record<string, any> = {}) {
|
|
const source = readFileSync(new URL(path, import.meta.url), 'utf8')
|
|
const script = source.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1] || source
|
|
const ast = ts.createSourceFile('page.ts', script, ts.ScriptTarget.Latest, true)
|
|
const node = ast.statements.find((s) => ts.isFunctionDeclaration(s) && s.name?.text === name)
|
|
assert.ok(node, name)
|
|
const js = ts.transpileModule(node.getText(ast), {
|
|
compilerOptions: { target: ts.ScriptTarget.ES2022 }
|
|
}).outputText
|
|
return new Function(...Object.keys(context), `${js}; return ${name}`)(...Object.values(context))
|
|
}
|
|
const detail = {
|
|
id: 'detail-a',
|
|
expenseItemId: 'expense',
|
|
amount: 10,
|
|
remark: 'Reason',
|
|
occurDate: '2026-01-01'
|
|
}
|
|
const webPage = '../src/views/bx/reimburse/apply/index.vue'
|
|
const nativePage = '../../miniprogram/pages/bx/reimburse/apply.vue'
|
|
|
|
for (const [editing, occurDate] of [[true, detail.occurDate], [false, detail.occurDate], [true, ''], [false, '']] as const) {
|
|
test(`Web/H5 expense payload ${editing ? 'preserves edit ID' : 'clears source ID on create'} with ${occurDate || 'cleared date'}`, () => {
|
|
const build = fn(webPage, 'buildAddPayload', {
|
|
addForm: { details: [{ ...detail, occurDate }], remark: '', reimburseDate: '2026-01-01' },
|
|
selectedInvoicePicks: { value: [] },
|
|
editingId: { value: editing ? 'apply' : null },
|
|
ElMessage: { warning() {} }
|
|
})
|
|
const row = build().details[0]
|
|
assert.equal(row.id, editing ? 'detail-a' : undefined)
|
|
assert.equal(row.remark, 'Reason')
|
|
assert.equal(row.occurDate, occurDate || null)
|
|
})
|
|
test(`Native expense payload ${editing ? 'preserves edit ID' : 'clears source ID on create'} with ${occurDate || 'cleared date'}`, async () => {
|
|
let payload: any
|
|
const save = async (...args: any[]) => {
|
|
payload = args.at(-1)
|
|
return {
|
|
data: {
|
|
id: 'apply',
|
|
details: [
|
|
{
|
|
id: 'server-detail',
|
|
expenseId: 'expense',
|
|
amount: 10,
|
|
remark: 'Reason',
|
|
occurDate: '2026-01-01'
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
const context = {
|
|
validateReimburseApplyForm: () => [{ ...detail, occurDate }],
|
|
uploading: { value: false },
|
|
submitting: { value: false },
|
|
editId: { value: editing ? 'apply' : '' },
|
|
savedId: { value: '' },
|
|
buildInvoicePayload: () => ({}),
|
|
form: { details: [{ ...detail }], title: '', remark: '', applicantId: '', reimburseDate: '' },
|
|
reimburseApi: { update: save, create: save },
|
|
bindNewAttachments: async () => {},
|
|
uni: { showModal() {}, showToast() {} }
|
|
}
|
|
await fn(nativePage, 'onSubmit', context)()
|
|
assert.ok(payload)
|
|
assert.equal(payload.details[0].id, editing ? 'detail-a' : undefined)
|
|
assert.equal(payload.details[0].occurDate, occurDate || null)
|
|
assert.equal(
|
|
context.form.details[0].id,
|
|
'server-detail',
|
|
'continued editing uses returned child ID'
|
|
)
|
|
})
|
|
}
|
|
|
|
for (const page of ['reimburse', 'advance']) {
|
|
test(`${page} workflow payload retains node ID and action`, () => {
|
|
const build = fn(`../src/views/bx/workflow/${page}/index.vue`, 'buildNodePayload')
|
|
const [row] = build([{ id: 'node-a', nodeName: 'Review', actionType: 'disburse' }])
|
|
assert.equal(row.id, 'node-a')
|
|
assert.equal(row.actionType, 'disburse')
|
|
})
|
|
}
|
|
test('Native workflow payload retains node ID and action', () => {
|
|
const build = fn('../../miniprogram/components/bx-ui/bx-wf-config-page.vue', 'buildNodePayload')
|
|
const [row] = build([{ id: 'node-a', nodeName: 'Review', actionType: 'disburse' }])
|
|
assert.equal(row.id, 'node-a')
|
|
assert.equal(row.actionType, 'disburse')
|
|
})
|
|
|
|
test('Web/H5 save response refreshes IDs before submit retry', async () => {
|
|
const addForm: any = { details: [{ ...detail }] }
|
|
const editingId = { value: null }
|
|
const save = fn(webPage, 'saveAddForm', {
|
|
addForm,
|
|
editingId,
|
|
pickBxData: (res: any) => res.data,
|
|
reimburseApi: {
|
|
create: async () => ({
|
|
data: {
|
|
id: 'apply',
|
|
details: [
|
|
{
|
|
id: 'server-detail',
|
|
expenseId: 'expense',
|
|
amount: 10,
|
|
remark: 'Reason',
|
|
occurDate: '2026-01-01'
|
|
}
|
|
]
|
|
}
|
|
})
|
|
}
|
|
})
|
|
assert.equal(await save({}), 'apply')
|
|
assert.equal(editingId.value, 'apply')
|
|
assert.equal(addForm.details[0].id, 'server-detail')
|
|
})
|
|
|
|
test('Native edit loading retains detail identity', async () => {
|
|
const form: any = {}
|
|
const load = fn(nativePage, 'loadEditData', {
|
|
form,
|
|
reimburseApi: {
|
|
getById: async () => ({
|
|
data: { details: [{ id: 'detail-a', expenseId: 'expense', amount: 10 }] }
|
|
})
|
|
},
|
|
userStore: { userInfo: { id: 'user' } },
|
|
todayYmd: () => '2026-01-01',
|
|
refreshWallet: async () => {},
|
|
selectedInvoicePicks: { value: [] },
|
|
kickAttachPreviewLoads() {},
|
|
attachmentApi: { listByBiz: async () => ({ data: [] }) },
|
|
files: { value: [] },
|
|
uni: {
|
|
showToast() {
|
|
assert.fail('edit loading failed')
|
|
}
|
|
}
|
|
})
|
|
await load('apply')
|
|
assert.equal(form.details[0].id, 'detail-a')
|
|
})
|
|
|
|
test('Web/H5 edit loading retains detail identity', async () => {
|
|
const addForm: any = {}
|
|
const load = fn(webPage, 'openEditDialog', {
|
|
addForm,
|
|
reimburseApi: {
|
|
getById: async () => ({
|
|
data: { details: [{ id: 'detail-a', expenseId: 'expense', amount: 10 }] }
|
|
})
|
|
},
|
|
selectedInvoicePicks: { value: [] },
|
|
applyAttachments: { value: [] },
|
|
currentUserId: () => 'user',
|
|
todayYmd: () => '2026-01-01',
|
|
titleAutoLinked: { value: false },
|
|
isAutoReimburseTitle: () => false,
|
|
editingId: { value: null },
|
|
dialogs: {},
|
|
loadApplicantOptions: async () => {},
|
|
refreshWalletBalance: async () => {},
|
|
loadSelectedPicksFromInvoices: async () => {},
|
|
loadBxAttachments: async () => [],
|
|
ElMessage: {
|
|
error() {
|
|
assert.fail('edit loading failed')
|
|
}
|
|
}
|
|
})
|
|
await load({ id: 'apply' })
|
|
assert.equal(addForm.details[0].id, 'detail-a')
|
|
})
|
|
|
|
test('Recharge workflow payload retains node ID for rename', () => {
|
|
const build = fn('../src/views/pl/vehicle-recharge-workflow/index.vue', 'buildNodePayload')
|
|
const [row] = build([{ id: 'node-a', nodeName: 'Renamed', actionType: 'disburse' }])
|
|
assert.equal(row.id, 'node-a')
|
|
assert.equal(row.actionType, 'disburse')
|
|
})
|