Files
shanxia2.0/web-admin/scripts/bx-child-identity.test.ts
T

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')
})