const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const vm = require('node:vm') const { createRequire } = require('node:module') const vue = require('vue') const { parse, compileScript, compileTemplate } = require('vue/compiler-sfc') const esbuild = createRequire(require.resolve('vite'))('esbuild') const directory = path.join(__dirname, '../src/views/health/identity') const familyModule = { exports: {} } vm.runInNewContext( esbuild.transformSync(fs.readFileSync(path.join(directory, 'collectionFamily.ts'), 'utf8'), { loader: 'ts', format: 'cjs' }).code, { module: familyModule, exports: familyModule.exports } ) const helpers = familyModule.exports const privacyModule = { exports: {} } vm.runInNewContext( esbuild.transformSync( fs.readFileSync(path.join(__dirname, '../src/utils/healthPrivacy.ts'), 'utf8'), { loader: 'ts', format: 'cjs' } ).code, { module: privacyModule, exports: privacyModule.exports } ) const componentPath = path.join(directory, 'CollectionFamilySteps.vue') const { descriptor } = parse(fs.readFileSync(componentPath, 'utf8'), { filename: componentPath }) const script = compileScript(descriptor, { id: 'collection-family-tests' }) const compiled = esbuild.transformSync(script.content, { loader: 'ts', format: 'cjs' }).code function fixture({ activeStep = 1, readOnly = false, data = {} } = {}) { const module = { exports: {} } vm.runInNewContext(compiled, { module, exports: module.exports, require: (id) => { if (id === 'vue') return vue if (id === './collectionFamily') return helpers if (id === '@/utils/healthPrivacy') return privacyModule.exports throw new Error(`Unexpected import: ${id}`) } }) const props = vue.reactive({ activeStep, readOnly, data }) const scope = vue.effectScope() const component = scope.run(() => module.exports.default.setup(props, { expose: () => {} })) return { props, component, stop: () => scope.stop() } } test('both collection step sections and form fields compile without a nested form', () => { const result = compileTemplate({ source: descriptor.template.content, filename: componentPath, id: 'collection-family-tests', compilerOptions: { bindingMetadata: script.bindings } }) assert.deepEqual(result.errors, []) assert.doesNotMatch(descriptor.template.content, / { const f = fixture() t.after(f.stop) assert.equal(f.props.data.emergencyContacts, undefined) f.props.activeStep = 7 await vue.nextTick() assert.equal(f.props.data.emergencyContacts.length, 1) assert.equal(f.props.data.emergencyContacts[0].primary, true) assert.equal(f.props.data.emergencyContacts[0].contactOnly, true) assert.equal(f.props.data.emergencyContacts[0].relationship, '') }) test('selecting a priority contact replaces the previous selection and deleting it chooses the remaining first', (t) => { const f = fixture({ activeStep: 7 }) t.after(f.stop) f.component.addContact() f.component.addContact() f.component.primaryIndex.value = 2 assert.deepEqual( Array.from(f.props.data.emergencyContacts, (contact) => contact.primary), [false, false, true] ) f.component.removeContact(2) assert.deepEqual( Array.from(f.props.data.emergencyContacts, (contact) => contact.primary), [true, false] ) f.component.removeContact(1) f.component.removeContact(0) assert.equal(f.component.contacts.value.length, 0) f.component.addContact() assert.equal(f.component.contacts.value[0].primary, true) }) test('deleting a non-primary contact preserves the existing choice and stable contact key', (t) => { const f = fixture({ activeStep: 7 }) t.after(f.stop) f.component.addContact() const second = f.component.contacts.value[1] const key = f.component.contactKey(second) f.component.primaryIndex.value = 1 f.component.removeContact(0) assert.equal(f.component.contacts.value[0], second) assert.equal(second.primary, true) assert.equal(f.component.contactKey(second), key) }) test('adding stops at ten contacts and normalizes multiple or missing priority flags on step seven', (t) => { const f = fixture({ activeStep: 7, data: { emergencyContacts: [ helpers.createEmergencyContact(true), helpers.createEmergencyContact(true) ] } }) t.after(f.stop) for (let index = 0; index < 15; index++) f.component.addContact() assert.equal(f.component.contacts.value.length, 10) assert.equal(f.component.contacts.value.filter((contact) => contact.primary).length, 1) const contacts = [helpers.createEmergencyContact(), helpers.createEmergencyContact()] helpers.ensureOnePrimaryContact(contacts) assert.equal(contacts[0].primary, true) helpers.selectPrimaryContact(contacts, -1) helpers.removeEmergencyContact(contacts, 20) assert.equal(contacts.length, 2) assert.equal(contacts[0].primary, true) }) test('parent selection clears hidden reasons and detail independently', (t) => { const f = fixture({ data: { fatherAlive: 2, fatherDeathReason: 4, fatherDeathReasonDetail: '父亲原因', motherAlive: 2, motherDeathReason: 4, motherDeathReasonDetail: '母亲原因' } }) t.after(f.stop) f.component.setParentAlive('father', 3) assert.equal(f.props.data.fatherDeathReason, null) assert.equal(f.props.data.fatherDeathReasonDetail, null) assert.equal(f.props.data.motherDeathReasonDetail, '母亲原因') f.component.setParentDeathReason('mother', 2) assert.equal(f.props.data.motherDeathReason, 2) assert.equal(f.props.data.motherDeathReasonDetail, null) f.component.setParentDeathReason('mother', 4) f.component.setParentDetail('mother', '新增其他原因') assert.equal(f.props.data.motherDeathReasonDetail, '新增其他原因') f.component.setParentDeathReason('mother', '') assert.equal(f.props.data.motherDeathReason, null) assert.equal(f.props.data.motherDeathReasonDetail, null) }) test('read-only viewing never initializes or changes any fields', async (t) => { const data = { fatherAlive: 1, fatherDeathReason: 4, fatherDeathReasonDetail: '历史资料', emergencyContacts: [helpers.createEmergencyContact(true), helpers.createEmergencyContact(true)] } const original = JSON.stringify(data) const f = fixture({ activeStep: 7, readOnly: true, data }) t.after(f.stop) f.component.addContact() f.component.removeContact(0) f.component.primaryIndex.value = 1 f.component.setParentAlive('father', 2) f.component.setParentDeathReason('father', 3) f.component.setParentDetail('father', '改动') assert.equal(JSON.stringify(f.props.data), original) const empty = fixture({ activeStep: 7, readOnly: true }) t.after(empty.stop) assert.equal(empty.props.data.emergencyContacts, undefined) f.props.activeStep = 6 await vue.nextTick() assert.equal(JSON.stringify(f.props.data), original) })