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