This commit is contained in:
zlwu committed 2026-10-17 11:46:53 +08:00
1 parent 48dcf991dc
commit be2226e4ca
100 files changed
+61340 -40975

No files matched your search

@@ -0,0 +1,487 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { test } from 'node:test'
import ts from 'typescript'
const base = new URL('../src/views/cell-storage/', import.meta.url)
const presign = 'presign-order/PresignOrderFormDialog.vue'
const panel = 'protocol/SamplePaymentPanel.vue'
const protocol = 'protocol/ProtocolFormDialog.vue'
function source(file: string) {
const text = readFileSync(new URL(file, base), 'utf8').match(
/<script setup lang="ts">([\s\S]*?)<\/script>/
)![1]
return ts.createSourceFile(file + '.ts', text, ts.ScriptTarget.Latest, true)
}
function functions(file: string, names: string[], env: Record<string, any>) {
env = {
isEdit: { value: false },
editDetailLoaded: { value: true },
loading: { value: false },
formReady: { value: true },
...env
}
const ast = source(file)
const text = ast.statements
.filter((n) => ts.isFunctionDeclaration(n) && names.includes(n.name!.text))
.map((n) => n.getText(ast))
.join('\n')
const js = ts.transpile(text, { target: ts.ScriptTarget.ES2022 })
return new Function(
...Object.keys(env),
`${js}; return { ${names.filter((n) => text.includes('function ' + n + '(')).join(',')} }`
)(...Object.values(env))
}
function expression(file: string, target: string, env: Record<string, any>) {
const ast = source(file)
let result = ''
function visit(node: ts.Node) {
if (ts.isBinaryExpression(node) && node.left.getText(ast) === target)
result = node.right.getText(ast)
if (ts.isVariableDeclaration(node) && node.name.getText(ast) === target && node.initializer)
result = node.initializer.getText(ast)
node.forEachChild(visit)
}
visit(ast)
assert.ok(result, `missing ${target}`)
const js = ts.transpile(`const result = ${result}`, { target: ts.ScriptTarget.ES2022 })
return new Function(...Object.keys(env), `${js}; return result`)(...Object.values(env))
}
function presignEnv(transfers: any[] = []) {
const writes: any[] = []
const warnings: string[] = []
const env: Record<string, any> = {
form: {
motherName: '测试',
sampleTypeIds: ['sample'],
transfers,
orderDate: '',
expectedDeliveryDate: '',
regionId: '',
hospitalId: '',
sampleCollectTime: '',
collectorUserId: '',
regionManagerUserId: '',
payMode: 'dj',
contractAmount: null,
orderStatus: 'active',
remark: ''
},
props: { orderId: 'order-1' },
saving: { value: false },
ElMessage: { warning: (v: string) => warnings.push(v), success: () => {} },
fetchUpdatePresignOrder: async (_: string, body: any) =>
writes.push(JSON.parse(JSON.stringify(body))),
fetchUpdatePresignTransfer: async (_: string, body: any) =>
writes.push(JSON.parse(JSON.stringify(body))),
fetchCreatePresignTransfer: async (_: string, body: any) => {
writes.push(body)
return { id: 'new' }
},
fetchSubmitPresignTransferFinance: async () => {},
submitDfjTransfersOnSave: async () => [],
emit: () => {}
}
return { env, writes, warnings }
}
test('预签约清空可选字段后,更新请求仍携带空值', async () => {
const { env, writes } = presignEnv()
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
for (const key of [
'expectedDeliveryDate',
'regionId',
'hospitalId',
'sampleCollectTime',
'collectorUserId',
'regionManagerUserId',
'remark'
])
assert.equal(writes[0][key], '', key)
assert.equal(writes[0].contractAmount, null)
})
test('预签约保存转账清空凭证/日期/备注后请求携带空值', async () => {
const { env, writes } = presignEnv([
{ id: 't', payStatus: 'dfj', attachments: [], transferTime: '', remark: '' }
])
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
assert.equal(writes[1].transferAttachments, '')
assert.equal(writes[1].transferTime, '')
assert.equal(writes[1].remark, '')
})
test('预签约提交财务前同步清空凭证', async () => {
const { env, writes } = presignEnv()
await functions(presign, ['submitTransfer', 'attachmentsToUrls'], env).submitTransfer({
id: 't',
payStatus: 'dfj',
attachments: []
})
assert.equal(writes[0].transferAttachments, '')
})
test('预签约凭证上传时禁止保存订单', async () => {
const { env, writes, warnings } = presignEnv([
{ payStatus: 'dfj', attachments: [{ _uploading: true }] }
])
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
assert.equal(writes.length, 0)
assert.equal(warnings.length, 1)
})
test('预签约凭证上传时禁止提交财务', async () => {
const { env, writes, warnings } = presignEnv()
await functions(presign, ['submitTransfer', 'attachmentsToUrls'], env).submitTransfer({
id: 't',
payStatus: 'dfj',
attachments: [{ _uploading: true }]
})
assert.equal(writes.length, 0)
assert.equal(warnings.length, 1)
})
for (const attachments of [[], [{ fileUrl: 'new.pdf' }], undefined]) {
test(`协议整体保存使用当前附件数组(${JSON.stringify(attachments)})`, () => {
const payments = expression(protocol, 'payload.samplePayments', {
formData: {
samplePayments: [
{ schedules: [{ transfers: [{ transferAttachments: 'old.pdf', attachments }] }] }
]
},
toIntOrUndefined: () => undefined,
toFloatOrUndefined: () => undefined,
parseInstallmentRatios: () => []
})
assert.equal(
payments[0].schedules[0].transfers[0].transferAttachments,
attachments ? attachments.map((f) => f.fileUrl).join(',') : 'old.pdf'
)
})
}
test('样本缴费转账行保存删除全部凭证', async () => {
let written: any
const tr = {
id: 't',
transferType: 'bank',
transferAmount: 1,
attachments: [],
transferAttachments: 'old.pdf'
}
const env = {
props: { protocolId: 'p', modelValue: [{ schedules: [{ id: 's', transfers: [tr] }] }] },
savingKey: { value: '' },
isTransferLocked: () => false,
emitChange: () => {},
ElMessage: { warning: () => {}, success: () => {} },
fetchUpdatePaymentTransfer: async (_: string, body: any) => {
written = JSON.parse(JSON.stringify(body))
return { transferAttachments: '', attachments: [] }
}
}
await functions(
panel,
[
'saveTransferRow',
'getTransfer',
'validateTransferRow',
'transferRowKey',
'attachmentsToUrls',
'ensureTransferAttachments',
'applySavedTransfer'
],
env
).saveTransferRow(0, 0, 0)
assert.equal(written.transferAttachments, '')
assert.equal(tr.transferAttachments, '')
})
for (const file of [presign, panel]) {
for (const fail of [false, true]) {
test(`${file} 上传${fail ? '失败' : '成功'}时删除前一个附件不应错位`, async () => {
let resolve!: (value: any) => void
let reject!: (error: Error) => void
const pending = new Promise((yes, no) => {
resolve = yes
reject = no
})
const tr = { attachments: [{ id: 'old', fileUrl: 'old.pdf' }] }
const env = {
form: { transfers: [tr] },
props: { modelValue: [{ schedules: [{ transfers: [tr] }] }] },
emitChange: () => {},
fetchUploadAttachment: () => pending,
ElMessage: { warning: () => {}, error: () => {} }
}
const fns = functions(
file,
['handleTransferAttachmentChange', 'getTransfer', 'ensureTransferAttachments'],
env
)
const upload = { raw: new File(['x'], 'new.pdf') }
const saving =
file === presign
? fns.handleTransferAttachmentChange(0, upload)
: fns.handleTransferAttachmentChange(0, 0, 0, upload)
tr.attachments.splice(0, 1)
if (fail) reject(new Error('upload failed'))
else resolve({ id: 'new', fileUrl: 'new.pdf' })
await saving
assert.equal(tr.attachments.length, fail ? 0 : 1)
if (!fail) assert.equal(tr.attachments[0].fileUrl, 'new.pdf')
})
}
}
for (const handler of ['uploadProtocolAttachmentFile', 'handleDetailAttachmentChange']) {
for (const fail of [false, true]) {
test(`协议 ${handler} 上传${fail ? '失败' : '成功'}不受前项删除影响`, async () => {
let resolve!: (value: any) => void
let reject!: (error: Error) => void
const pending = new Promise((yes, no) => {
resolve = yes
reject = no
})
const list = [{ id: 'old', fileUrl: 'old.pdf' }]
const env = {
formData: { paymentDetails: [{ attachments: list }] },
fetchUploadAttachment: () => pending,
nextTick: (cb: () => void) => cb(),
ElMessage: { error: () => {} }
}
const fns = functions(protocol, [handler], env)
const upload = { raw: new File(['x'], 'new.pdf') }
const saving =
handler === 'uploadProtocolAttachmentFile'
? fns[handler](upload, list, 'protocol', {})
: fns[handler](0, upload)
list.splice(0, 1)
if (fail) reject(new Error('upload failed'))
else resolve({ id: 'new', fileUrl: 'new.pdf' })
await saving
assert.equal(list.length, fail ? 0 : 1)
if (!fail) assert.equal(list[0].fileUrl, 'new.pdf')
})
}
}
for (const field of [
'attachments',
'consentAttachments',
'healthSurveyAttachments',
'paymentDetails',
'samplePayments'
]) {
test(`协议 ${field} 上传中禁止整体保存`, async () => {
let validated = false
const warnings: string[] = []
const file = { _uploading: true }
const formData: any = {
attachments: [],
consentAttachments: [],
healthSurveyAttachments: [],
paymentDetails: [],
samplePayments: []
}
formData[field] =
field === 'paymentDetails'
? [{ attachments: [file] }]
: field === 'samplePayments'
? [{ schedules: [{ transfers: [{ attachments: [file] }] }] }]
: [file]
const env = {
formData,
formRef: {
value: {
validate: async () => {
validated = true
throw new Error('validation')
}
}
},
submitLoading: { value: false },
ElMessage: { warning: (v: string) => warnings.push(v) }
}
await functions(protocol, ['handleSubmit'], env).handleSubmit()
assert.equal(validated, false)
assert.equal(warnings.length, 1)
})
}
test('正式协议清空主单附件、客户和采集字段后提交空值', () => {
const formData = new Proxy(
{
attachments: [],
consentAttachments: [],
healthSurveyAttachments: [],
samples: [],
protocolAttachments: 'old.pdf',
consentFormUrls: 'old-consent.pdf',
healthSurveyFormUrls: 'old-health.pdf'
},
{ get: (o, key) => (key in o ? o[key as keyof typeof o] : '') }
)
const payload = JSON.parse(
JSON.stringify(
expression(protocol, 'payload', {
formData,
protocolAttachmentUrls: '',
consentAttachmentUrls: '',
healthSurveyAttachmentUrls: ''
})
)
)
for (const key of ['protocolAttachments', 'consentFormUrls', 'healthSurveyFormUrls', 'remark'])
assert.equal(payload[key], '', key)
for (const key of ['motherPhone', 'motherEmail', 'fatherPhone', 'newbornIdCard'])
assert.equal(payload.partyCustomer[key], '', key)
for (const key of ['hospitalId', 'medicalStaff', 'sampleCollectTime'])
assert.equal(payload.collectionInfo[key], '', key)
})
test('协议详情加载失败后禁止保存未加载的表单', async () => {
let validated = false
const warnings: string[] = []
const env = {
isEdit: { value: true },
editDetailLoaded: { value: false },
formData: {
attachments: [],
consentAttachments: [],
healthSurveyAttachments: [],
paymentDetails: [],
samplePayments: []
},
formRef: {
value: {
validate: async () => {
validated = true
throw new Error('validation')
}
}
},
submitLoading: { value: false },
ElMessage: { warning: (v: string) => warnings.push(v) }
}
await functions(protocol, ['handleSubmit'], env).handleSubmit()
assert.equal(validated, false)
assert.equal(warnings.length, 1)
})
test('协议详情读取失败保持未就绪并提示重试', async () => {
const messages: string[] = []
const ready = { value: true }
const env = {
props: { listRow: { id: 'p' }, editId: 'p' },
isEdit: { value: false },
dialogTitle: { value: '' },
editingId: { value: '' },
editDetailLoaded: ready,
formData: {},
resetForm: () => {},
dialogFormLoading: { value: false },
loadEthnicOptions: async () => {},
fetchCollectionProtocolDetail: async () => {
throw new Error('network')
},
ElMessage: { error: (v: string) => messages.push(v) },
emit: () => {}
}
await functions(protocol, ['handleOpenForEdit'], env).handleOpenForEdit()
assert.equal(ready.value, false)
assert.equal(messages.length, 1)
})
test('编辑后新增协议不沿用上一条协议ID', () => {
const editingId = { value: 'previous-protocol' }
const env = {
editingId,
isEdit: { value: true },
dialogTitle: { value: '' },
resetForm: () => {},
formData: {},
DEFAULT_MOTHER_ETHNIC: '',
loadEthnicOptions: () => {},
userStore: { getUserInfo: {} }
}
functions(protocol, ['handleOpenForAdd'], env).handleOpenForAdd()
assert.equal(editingId.value, null)
})
function presignOpenWatcher(env: Record<string, any>) {
const ast = source(presign)
const statement = ast.statements.find(
(n) =>
ts.isExpressionStatement(n) &&
ts.isCallExpression(n.expression) &&
n.expression.expression.getText(ast) === 'watch'
)!
let callback: (open: boolean) => Promise<void>
new Function(
...Object.keys(env),
'watch',
ts.transpile(statement.getText(ast), { target: ts.ScriptTarget.ES2022 })
)(...Object.values(env), (_: unknown, cb: typeof callback) => {
callback = cb
})
return callback!
}
test('预签约从字典加载开始即处于加载中,全部初始化后才就绪', async () => {
let resolve!: () => void
const pending = new Promise<void>((yes) => {
resolve = yes
})
const loading = { value: false }
const formReady = { value: true }
const events: string[] = []
const open = presignOpenWatcher({
props: { orderId: 'p' },
loading,
formReady,
resetForm: () => {},
loadLookups: () => pending,
loadDetail: async () => {
events.push('detail')
},
ElMessage: { error: () => {} },
emit: () => {}
})
const initialization = open(true)
const during = { loading: loading.value, ready: formReady.value }
resolve()
await initialization
assert.deepEqual(during, { loading: true, ready: false })
assert.deepEqual(events, ['detail'])
assert.equal(loading.value, false)
assert.equal(formReady.value, true)
})
for (const stage of ['lookups', 'detail']) {
test(`预签约${stage}加载失败保持未就绪并提示重试`, async () => {
const loading = { value: false }
const formReady = { value: true }
const errors: string[] = []
const open = presignOpenWatcher({
props: { orderId: 'p' },
loading,
formReady,
resetForm: () => {},
loadLookups: async () => {
if (stage === 'lookups') throw new Error('network')
},
loadDetail: async () => {
if (stage === 'detail') throw new Error('network')
},
ElMessage: { error: (v: string) => errors.push(v) },
emit: () => {}
})
await open(true).catch(() => {})
assert.equal(formReady.value, false)
assert.equal(loading.value, false)
assert.equal(errors.length, 1)
})
}
for (const state of [
{ loading: true, ready: false },
{ loading: false, ready: false }
]) {
test(`预签约未初始化成功不得保存:${JSON.stringify(state)}`, async () => {
const { env, writes, warnings } = presignEnv()
env.loading = { value: state.loading }
env.formReady = { value: state.ready }
await functions(presign, ['handleSave', 'attachmentsToUrls'], env).handleSave()
assert.equal(writes.length, 0)
assert.equal(warnings.length, 1)
})
}