elder-yudao-pro/yudao-ui-admin-vue3/tests/collection-draft.test.cjs

243 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 esbuild = createRequire(require.resolve('vite'))('esbuild')
const flowModule = { exports: {} }
vm.runInNewContext(
esbuild.transformSync(
fs.readFileSync(path.join(__dirname, '../src/views/health/identity/collectionFlow.ts'), 'utf8'),
{ loader: 'ts', format: 'cjs' }
).code,
{ module: flowModule, exports: flowModule.exports }
)
// 加载实际 composable,仅替换 HTTP 与组件卸载回调,无浏览器或 DOM 依赖。
function fixture(initialDraft = null) {
const calls = []
const backend = {
getCollectionDraft: async () => initialDraft,
saveCollectionDraft: async (payload) => {
calls.push(JSON.parse(JSON.stringify(payload)))
}
}
const module = { exports: {} }
const source = fs.readFileSync(
path.join(__dirname, '../src/views/health/identity/useCollectionDraft.ts'),
'utf8'
)
const code = esbuild.transformSync(source, { loader: 'ts', format: 'cjs' }).code
vm.runInNewContext(code, {
module,
exports: module.exports,
require: (id) =>
id === 'vue'
? { ...vue, onBeforeUnmount: () => {} }
: id === './collectionFlow'
? flowModule.exports
: backend,
// 测试手动触发保存,避免真实的一秒定时器影响执行和结果。
setTimeout: () => 1,
clearTimeout: () => {}
})
const data = vue.ref({
identityType: 10,
realName: '',
gender: 3,
consentConfirmed: true,
password: 'never-store-this'
})
const accountMode = vue.ref('new')
const activeStep = vue.ref(1)
const visible = vue.ref(true)
const readOnly = vue.ref(false)
const draft = module.exports.useCollectionDraft(data, accountMode, activeStep, visible, readOnly)
return { data, accountMode, activeStep, visible, readOnly, draft, calls, backend }
}
test('restores incomplete fields and step without restoring password', async () => {
const f = fixture({
currentStep: 2,
fields: { realName: '测试老人', educationLevel: null, accountMode: 'existing' }
})
await f.draft.initializeDraft()
assert.equal(f.data.value.realName, '测试老人')
assert.equal(f.activeStep.value, 2)
assert.equal(f.accountMode.value, 'existing')
assert.equal(f.data.value.password, undefined)
await f.draft.pauseDraft()
})
test('normalizes cleared options while preserving zero and false; excludes password', async () => {
const f = fixture()
await f.draft.initializeDraft()
Object.assign(f.data.value, {
maritalStatus: 0,
elderlyUniversity: false,
residenceType: '',
educationLevel: undefined
})
await f.draft.saveDraftNow()
const fields = f.calls[0].fields
assert.equal(fields.maritalStatus, 0)
assert.equal(fields.elderlyUniversity, false)
assert.equal(fields.residenceType, null)
assert.equal(fields.educationLevel, null)
assert.equal('password' in fields, false)
await f.draft.pauseDraft()
})
test('serializes saves so delayed earlier requests cannot overwrite newer input', async () => {
const f = fixture()
await f.draft.initializeDraft()
let release
f.backend.saveCollectionDraft = async (payload) => {
f.calls.push(JSON.parse(JSON.stringify(payload)))
if (f.calls.length === 1)
await new Promise((resolve) => {
release = resolve
})
}
f.data.value.realName = '较早输入'
const first = f.draft.saveDraftNow()
await Promise.resolve()
await Promise.resolve()
f.data.value.realName = '最新输入'
const second = f.draft.saveDraftNow()
assert.equal(f.calls.length, 1)
release()
await first
await second
assert.equal(f.calls[1].fields.realName, '最新输入')
await f.draft.pauseDraft()
})
test('pausing before a formal submission prevents close from recreating the old draft', async () => {
const f = fixture()
await f.draft.initializeDraft()
f.data.value.realName = '正式提交'
await f.draft.saveDraftNow()
await f.draft.pauseDraft()
f.visible.value = false
await vue.nextTick()
await Promise.resolve()
assert.equal(f.calls.length, 1)
})
test('restores seventh-step nested drafts and excludes server metadata from subsequent saves', async () => {
const f = fixture({
currentStep: 7,
fields: {
consentConfirmed: true,
chronicDiseaseStatus: 1,
chronicDiseases: [{ code: 'diabetes', currentDisease: false }],
emergencyContacts: [
{
name: '联系人',
relationship: 'child',
phone: '13900000001',
primary: true,
contactOnly: true
}
]
}
})
await f.draft.initializeDraft()
assert.equal(f.activeStep.value, 7)
assert.equal(f.data.value.chronicDiseases[0].currentDisease, false)
Object.assign(f.data.value, {
collectorId: 999,
collectionCompletedTime: 'forged',
incomeRange: 0,
disabled: false
})
await f.draft.saveDraftNow()
assert.equal(f.calls[0].currentStep, 7)
assert.equal(f.calls[0].fields.incomeRange, 0)
assert.equal(f.calls[0].fields.disabled, false)
assert.equal('collectorId' in f.calls[0].fields, false)
assert.equal('collectionCompletedTime' in f.calls[0].fields, false)
await f.draft.pauseDraft()
})
test('queued snapshots keep the original nested contacts while later edits save separately', async () => {
const f = fixture()
await f.draft.initializeDraft()
let release
f.backend.saveCollectionDraft = async (payload) => {
if (f.calls.length === 0)
await new Promise((resolve) => {
release = resolve
})
f.calls.push(JSON.parse(JSON.stringify(payload)))
}
f.data.value.emergencyContacts = [
{
name: '原联系人',
relationship: 'child',
phone: '13900000001',
primary: true,
contactOnly: true
}
]
const first = f.draft.saveDraftNow()
await Promise.resolve()
await Promise.resolve()
f.data.value.emergencyContacts[0].name = '修改联系人'
const second = f.draft.saveDraftNow()
release()
await first
await second
assert.equal(f.calls[0].fields.emergencyContacts[0].name, '原联系人')
assert.equal(f.calls[1].fields.emergencyContacts[0].name, '修改联系人')
await f.draft.pauseDraft()
})
test('unconfirmed consent limits autosave to basic identity and contact fields', async () => {
const f = fixture()
await f.draft.initializeDraft()
Object.assign(f.data.value, {
consentConfirmed: false,
realName: '基础姓名',
contactPhone: '13800000001',
idCard: 'must-not-upload',
birthDate: '1949-01-01',
chronicDiseases: [{ code: 'diabetes' }],
medicalInsuranceNumber: 'must-not-upload',
emergencyContacts: [{ name: '敏感联系人' }]
})
await f.draft.saveDraftNow()
const fields = f.calls[0].fields
assert.equal(fields.realName, '基础姓名')
assert.equal(fields.contactPhone, '13800000001')
assert.equal(fields.consentConfirmed, false)
for (const key of [
'idCard',
'birthDate',
'medicalInsuranceNumber',
'chronicDiseases',
'emergencyContacts'
])
assert.equal(key in fields, false)
await f.draft.pauseDraft()
})
test('failed draft initialization can be retried without losing current user input', async () => {
const f = fixture()
f.backend.getCollectionDraft = async () => {
throw new Error('offline')
}
await f.draft.initializeDraft()
assert.equal(f.draft.draftFailed.value, true)
f.data.value.realName = '当前输入'
f.backend.getCollectionDraft = async () => ({ currentStep: 1, fields: { realName: '较早草稿' } })
await f.draft.saveDraftNow()
assert.equal(f.calls[0].fields.realName, '当前输入')
assert.equal(f.data.value.realName, '当前输入')
assert.equal(f.draft.draftFailed.value, false)
await f.draft.pauseDraft()
})