提交
This commit is contained in:
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)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user