import { App, createApp, defineComponent, h, nextTick } from 'vue' import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest' const ElCollapseStub = defineComponent({ name: 'ElCollapse', props: { modelValue: { type: Array, default: () => [] }, accordion: { type: Boolean, default: false } }, setup (props, { slots }) { return () => h('div', { class: 'el-collapse', 'data-accordion': String(props.accordion), 'data-model-value': JSON.stringify(props.modelValue) }, slots.default?.()) } }) const ElCollapseItemStub = defineComponent({ name: 'ElCollapseItem', inheritAttrs: false, props: { name: { type: [String, Number], default: '' } }, setup (props, { attrs, slots }) { return () => h('div', { ...attrs, class: ['el-collapse-item', attrs.class], style: attrs.style as any, 'data-name': String(props.name) }, [ h('div', { class: 'el-collapse-item__header' }, slots.title?.()), h('div', { class: 'el-collapse-item__wrap' }, slots.default?.()) ]) } }) vi.mock('element-plus', () => ({ ElCollapse: ElCollapseStub, ElCollapseItem: ElCollapseItemStub })) let InfoCollapse: any interface RenderResult { app: App el: HTMLDivElement } const renderedApps: RenderResult[] = [] beforeAll(async () => { InfoCollapse = (await import('../../../components/infoCollapse/infoCollapse.vue')).default }) const renderCollapse = async (data: Array>) => { const el = document.createElement('div') document.body.appendChild(el) const app = createApp({ render () { return h(InfoCollapse, { data }, { firstSlot: () => h('div', { class: 'slot-first' }, '第一个内容'), secondSlot: () => h('div', { class: 'slot-second' }, '第二个内容'), hiddenSlot: () => h('div', { class: 'slot-hidden' }, '隐藏内容') }) } }) app.config.globalProperties.$t = (key: string) => { const map: Record = { 'static-title-key': '静态标题', 'dynamic-title-key': '动态标题', 'default-title-key': '默认显示标题', 'hidden-title-key': '隐藏标题' } return map[key] || key } app.mount(el) await nextTick() const result = { app, el } renderedApps.push(result) return result } const getRoot = () => document.body afterEach(() => { while (renderedApps.length) { const current = renderedApps.pop()! current.app.unmount() current.el.remove() } document.body.innerHTML = '' }) describe('components/infoCollapse', () => { it('空数据时渲染折叠容器且不渲染面板', async () => { await renderCollapse([]) const collapse = getRoot().querySelector('.el-collapse') expect(collapse).not.toBeNull() expect(collapse?.getAttribute('data-accordion')).toBe('false') expect(collapse?.getAttribute('data-model-value')).toBe('[]') expect(getRoot().querySelectorAll('.el-collapse-item')).toHaveLength(0) }) it('支持字符串标题与函数标题渲染,并渲染对应具名插槽和面板索引', async () => { const firstShow = vi.fn(() => true) const secondShow = vi.fn(() => true) await renderCollapse([ { title: 'static-title-key', slotName: 'firstSlot', show: firstShow }, { title: () => 'dynamic-title-key', slotName: 'secondSlot', show: secondShow } ]) const titles = Array.from(getRoot().querySelectorAll('.title')).map((item) => item.textContent) const items = getRoot().querySelectorAll('.el-collapse-item') expect(titles).toEqual(['静态标题', '动态标题']) expect(getRoot().querySelector('.slot-first')?.textContent).toBe('第一个内容') expect(getRoot().querySelector('.slot-second')?.textContent).toBe('第二个内容') expect(items[0]?.getAttribute('data-name')).toBe('0') expect(items[1]?.getAttribute('data-name')).toBe('1') expect(firstShow).toHaveBeenCalledTimes(1) expect(secondShow).toHaveBeenCalledTimes(1) }) it('未传 show 时默认展示,show 返回 false 时隐藏对应面板', async () => { const hiddenShow = vi.fn(() => false) await renderCollapse([ { title: 'default-title-key', slotName: 'firstSlot' }, { title: 'hidden-title-key', slotName: 'hiddenSlot', show: hiddenShow } ]) const items = getRoot().querySelectorAll('.el-collapse-item') expect((items[0] as HTMLElement).style.display).toBe('') expect((items[1] as HTMLElement).style.display).toBe('none') expect(getRoot().querySelector('.slot-first')?.textContent).toBe('第一个内容') expect(hiddenShow).toHaveBeenCalledTimes(1) }) })