const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const test = require('node:test') const ts = require('typescript') const vue = require('vue') const { parse, compileScript, compileTemplate } = require('@vue/compiler-sfc') const selected = [ 'patchUsageVisible', 'patchUsageSaving', 'patchUsageFormRef', 'patchUsageForm', 'patchUsageRules', 'openPatchUsageDialog', 'resetPatchUsageForm', 'submitPatchUsage' ] for (const page of ['components/PrescriptionOrderDetailDrawer.vue', 'order_list_h5.vue']) { const filename = path.join(__dirname, '../src/views/consumer/prescription', page) const { descriptor, errors } = parse(fs.readFileSync(filename, 'utf8'), { filename }) assert.deepEqual(errors, []) const script = compileScript(descriptor, { id: page }) const template = compileTemplate({ source: descriptor.template.content, filename, id: page, compilerOptions: { bindingMetadata: script.bindings } }) assert.deepEqual(template.errors, []) // Run the real handlers; replace the network and the rest of the order page. const ast = ts.createSourceFile(filename + '.ts', descriptor.scriptSetup.content, ts.ScriptTarget.Latest, true) const declarations = new Map() for (const node of ast.statements) { if (ts.isFunctionDeclaration(node) && node.name) declarations.set(node.name.text, node.getText(ast)) if (ts.isVariableStatement(node)) { for (const declaration of node.declarationList.declarations) { declarations.set(declaration.name.getText(ast), `const ${declaration.getText(ast)}`) } } } const executed = page.startsWith('components/') ? [...selected, 'refresh'] : selected for (const name of executed) assert.ok(declarations.has(name), `${page}: missing ${name}`) const compiled = ts.transpileModule(executed.map(name => declarations.get(name)).join('\n'), { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS } }).outputText function instance({ hasAux = true, prescriptionType = '浓缩水丸', bagCount = 2, mainBagCount = 2, status = 2, fail = false } = {}) { const calls = [] const events = [] const aux = { times_per_day: 3, usage_days: 10, dosage_bag_count: bagCount } const detailPrescription = vue.ref({ prescription_type: prescriptionType, times_per_day: 3, usage_days: 10, dosage_bag_count: mainBagCount }) const detailAuxUsage = vue.ref(hasAux ? aux : null) const detailData = vue.ref({ id: 42, prescription_id: 17, medication_days: 10, fulfillment_status: status, gancao_reciperl_order_no: 'TEST-SUBMITTED', ej_pharmacy_order_no: 'TEST-EJ-SUBMITTED' }) const refresh = async () => { if (calls[0]?.dosage_bag_count) detailPrescription.value.dosage_bag_count = calls[0].dosage_bag_count if (calls[0]?.aux_dosage_bag_count) detailAuxUsage.value.dosage_bag_count = calls[0].aux_dosage_bag_count events.push('refresh') return { ...detailData.value } } const globals = { ref: vue.ref, reactive: vue.reactive, computed: vue.computed, nextTick: vue.nextTick, detailPrescription, detailAuxUsage, detailData, detailHasAuxHerbs: vue.ref(hasAux), feedback: { msgWarning() {}, msgSuccess() {} }, prescriptionOrderPatchPrescriptionUsage: async payload => { calls.push(payload) if (fail) throw new Error('Save failed') }, requestDetail: refresh, refreshCurrentPrescriptionOrderDetail: refresh, props: { loadRelatedData: true }, emit: event => events.push(event), fetchLogs: async () => { events.push('logs:start') await Promise.resolve() events.push('logs:done') }, getLists: () => events.push('lists') } const state = new Function(...Object.keys(globals), `${compiled}\nreturn { ${selected.join(', ')} }`)(...Object.values(globals)) state.patchUsageFormRef.value = { validate: async () => {}, clearValidate() {} } return { state, calls, events, detailAuxUsage, detailPrescription } } test(`${page}: edits auxiliary bags and refreshes the persisted value without changing usage`, async () => { const { state, calls, events, detailAuxUsage } = instance() state.openPatchUsageDialog() assert.equal(state.patchUsageForm.aux_dosage_bag_count, 2) state.patchUsageForm.aux_dosage_bag_count = 4 await state.submitPatchUsage() assert.deepEqual(calls, [{ id: 42, times_per_day: 3, usage_days: 10, medication_days: 10, dosage_bag_count: 2, aux_times_per_day: 3, aux_usage_days: 10, aux_dosage_bag_count: 4 }]) assert.equal(state.patchUsageVisible.value, false) assert.equal(detailAuxUsage.value.dosage_bag_count, 4) assert.ok(events.includes('refresh')) assert.ok(events.indexOf('logs:done') > events.indexOf('refresh')) const completedEvent = page.startsWith('components/') ? 'detail-changed' : 'lists' assert.ok(events.indexOf(completedEvent) > events.indexOf('logs:done')) state.openPatchUsageDialog() assert.equal(state.patchUsageForm.aux_dosage_bag_count, 4) }) test(`${page}: missing historical bag count defaults to one and resets on close`, () => { const { state } = instance({ bagCount: null, mainBagCount: null }) state.openPatchUsageDialog() assert.equal(state.patchUsageForm.aux_dosage_bag_count, 1) assert.equal(state.patchUsageForm.dosage_bag_count, 1) state.patchUsageForm.dosage_bag_count = 4 state.patchUsageForm.aux_dosage_bag_count = 5 state.resetPatchUsageForm() assert.equal(state.patchUsageForm.aux_dosage_bag_count, 1) assert.equal(state.patchUsageForm.dosage_bag_count, 1) }) test(`${page}: only water pills with an auxiliary formula submit or require auxiliary bags`, async () => { for (const options of [{ hasAux: false }, { prescriptionType: '饮片' }, { prescriptionType: '颗粒' }]) { const { state, calls } = instance(options) state.openPatchUsageDialog() state.patchUsageForm.aux_dosage_bag_count = 5 assert.equal(state.patchUsageRules.value.aux_dosage_bag_count, undefined) await state.submitPatchUsage() assert.ok(!Object.hasOwn(calls[0], 'aux_dosage_bag_count')) assert.equal(Object.hasOwn(calls[0], 'dosage_bag_count'), !options.prescriptionType) } const { state } = instance() assert.equal(state.patchUsageRules.value.aux_dosage_bag_count[0].required, true) assert.equal(state.patchUsageRules.value.dosage_bag_count[0].required, true) }) test(`${page}: main and auxiliary bags can be edited independently in every submitted order status`, async () => { for (let status = 1; status <= 12; status++) { const { state, calls, detailPrescription, detailAuxUsage } = instance({ status }) state.openPatchUsageDialog() assert.equal(state.patchUsageForm.dosage_bag_count, 2) state.patchUsageForm.dosage_bag_count = 5 state.patchUsageForm.aux_dosage_bag_count = 1 await state.submitPatchUsage() assert.equal(calls.length, 1, `status ${status}`) assert.equal(calls[0].dosage_bag_count, 5) assert.equal(calls[0].aux_dosage_bag_count, 1) assert.equal(detailPrescription.value.dosage_bag_count, 5) assert.equal(detailAuxUsage.value.dosage_bag_count, 1) } const { state, calls } = instance({ hasAux: false }) state.openPatchUsageDialog() state.patchUsageForm.dosage_bag_count = 4 await state.submitPatchUsage() assert.equal(calls[0].dosage_bag_count, 4) assert.ok(!Object.hasOwn(calls[0], 'aux_dosage_bag_count')) }) test(`${page}: validation blocks requests and a rejected save retains the selected bags`, async () => { const { state, calls, events } = instance({ fail: true }) state.openPatchUsageDialog() state.patchUsageForm.aux_dosage_bag_count = 3 state.patchUsageForm.dosage_bag_count = 4 state.patchUsageFormRef.value.validate = async () => { throw new Error('Required') } await state.submitPatchUsage() assert.equal(calls.length, 0) state.patchUsageFormRef.value.validate = async () => {} await state.submitPatchUsage() assert.equal(state.patchUsageForm.aux_dosage_bag_count, 3) assert.equal(state.patchUsageForm.dosage_bag_count, 4) assert.equal(state.patchUsageVisible.value, true) assert.equal(state.patchUsageSaving.value, false) assert.deepEqual(events, []) }) }