import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest' import { App, ComponentPublicInstance, createApp, defineComponent, h, nextTick } from 'vue' import { FormConfig } from '../../../../components/form/formConfig' import { InfoCronConfig } from '../../../../components/form/infoCron' const ElFormStub = defineComponent({ name: 'ElFormStub', template: '
' }) const ElCollapseStub = defineComponent({ name: 'ElCollapseStub', template: '
' }) const ElCollapseItemStub = defineComponent({ name: 'ElCollapseItemStub', props: { title: { type: String, default: '' } }, template: '

{{ title }}

' }) const InfoFormItemStub = defineComponent({ name: 'InfoFormItemStub', props: { item: { type: Object, required: true } }, template: '
' }) const InfoInputStub = defineComponent({ name: 'InfoInputStub', props: { row: { type: Object, required: true }, modelData: { type: Object, required: true } }, template: '
{{ row.model }}={{ modelData[row.model] }}
' }) vi.mock('../../../../script/useComponent', () => ({ default: () => ({}) })) vi.mock('../../../../components/form/infoCron/vue-js-cron/light/src/components/cron-light.vue', () => ({ default: defineComponent({ name: 'CronLightStub', props: { modelValue: { type: String, default: '' }, format: { type: String, default: '' }, disabled: { type: Boolean, default: false }, initialPeriod: { type: String, default: '' }, locale: { type: String, default: '' } }, emits: ['update:modelValue', 'error'], template: '
' }) })) let InfoForm: any let InfoCron: any interface RenderResult { app: App el: HTMLDivElement vm: ComponentPublicInstance } const renderedApps: RenderResult[] = [] afterEach(() => { renderedApps.splice(0).forEach(({ app, el }) => { app.unmount() el.remove() }) document.body.innerHTML = '' }) beforeAll(async () => { InfoForm = (await import('../../../../components/infoForm/infoForm.vue')).default InfoCron = (await import('../../../../components/form/infoCron/index.vue')).default }) const renderInfoForm = async (config: FormConfig, modelData: Record, slots?: Record any>) => { const el = document.createElement('div') document.body.appendChild(el) const app = createApp({ render () { return h(InfoForm, { config, modelData }, slots || {}) } }) app.config.globalProperties.$t = (key: string) => key app.component('el-form', ElFormStub) app.component('el-collapse', ElCollapseStub) app.component('el-collapse-item', ElCollapseItemStub) app.component('info-form-item', InfoFormItemStub) app.component('InfoFormItem', InfoFormItemStub) app.component('infoInput', InfoInputStub) app.component('infoCron', InfoCron) app.component('infoAddForm', defineComponent({ template: '
' })) app.component('infoDynamic', defineComponent({ template: '
' })) const vm = app.mount(el) await nextTick() const result = { app, el, vm } renderedApps.push(result) return result } describe('components/infoForm/infoForm.vue', () => { it('透传并渲染 infoSlot,同时分发普通表单组件', async () => { const config = new FormConfig({ labelWidth: '80px' }) config.setRule({ type: 'infoSlot', model: 'customSlotField', label: '插槽项', slotName: 'customSlot' }) config.setRule({ type: 'infoInput', model: 'name', label: '名称' }) await renderInfoForm( config, { name: 'Alice' }, { customSlot: () => h('div', { class: 'slot-content' }, '透传插槽内容') } ) const root = document.body expect(root.querySelector('.slot-content')).not.toBeNull() expect(root.querySelector('.slot-content')?.textContent).toBe('透传插槽内容') expect(root.querySelector('.info-input-stub')).not.toBeNull() expect(root.querySelector('.info-input-stub')?.textContent).toContain('name=Alice') expect(root.querySelectorAll('.info-form-item-stub, .el-form-item')).toHaveLength(2) }) it('将 infoCron 规则实例分发到真实组件并完成最小渲染', async () => { const config = new FormConfig({ labelWidth: '80px' }) config.setRule({ type: 'infoCron', model: 'cron', label: 'Cron', placement: 'top' }) expect(config.rule).toHaveLength(1) expect(config.rule[0]).toBeInstanceOf(InfoCronConfig) expect(config.rule[0].type).toBe('infoCron') expect(config.rule[0].placement).toBe('top') const modelData = { cron: '' } await renderInfoForm(config, modelData) const formItem = document.body.querySelector('.info-form-item-stub[data-model="cron"], .form-item--cron, .el-form-item') const cronLight = document.body.querySelector('.cron-light-stub') as HTMLElement | null const cronInput = document.body.querySelector('input') as HTMLInputElement | null expect(formItem).not.toBeNull() expect(cronLight).not.toBeNull() expect(cronLight?.getAttribute('data-format')).toBe('quartz') expect(cronLight?.getAttribute('data-locale')).toBe('en') expect(cronLight?.getAttribute('data-disabled')).toBe('false') expect(cronInput).not.toBeNull() expect(cronInput?.disabled).toBe(false) expect(modelData.cron).toBe('0 0 12 * * ?') }) })