import { App, ComponentPublicInstance, createApp, defineComponent, h, nextTick, reactive } from 'vue' import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest' const ElButtonStub = defineComponent({ name: 'ElButton', emits: ['click'], setup (_, { slots, emit, attrs }) { return () => h('button', { class: ['el-button', attrs.type === 'primary' ? 'el-button--primary' : ''].filter(Boolean), disabled: attrs.loading === true, onClick: () => emit('click') }, slots.default?.()) } }) const ElDialogStub = defineComponent({ name: 'ElDialog', props: { modelValue: { type: Boolean, default: false }, beforeClose: { type: Function, default: undefined } }, emits: ['open'], mounted () { if (this.modelValue) { this.$emit('open') } }, render () { if (!this.modelValue) { return null } return h('div', { class: 'el-dialog info-dialog' }, [ h('div', { class: 'el-dialog__header' }, [ this.$slots.title?.(), h('button', { class: 'el-dialog__headerbtn', onClick: () => this.beforeClose?.() }, 'x') ]), h('div', { class: 'el-dialog__body' }, this.$slots.default?.()), this.$slots.footer ? h('div', { class: 'el-dialog__footer' }, this.$slots.footer()) : null ]) } }) const ElDrawerStub = defineComponent({ name: 'ElDrawer', props: { modelValue: { type: Boolean, default: false }, title: { type: String, default: '' }, beforeClose: { type: Function, default: undefined }, withHeader: { type: Boolean, default: true } }, emits: ['open', 'close'], mounted () { if (this.modelValue) { this.$emit('open') } }, render () { if (!this.modelValue) { return null } return h('div', { class: 'el-drawer info-dialog', 'aria-label': this.title }, [ this.withHeader ? h('div', { class: 'el-drawer__header' }, [ this.$slots.title?.(), h('button', { class: 'el-drawer__close-btn', onClick: () => { this.beforeClose?.() } }, 'x') ]) : null, h('div', { class: 'el-drawer__body' }, this.$slots.default?.()), this.$slots.footer ? h('div', { class: 'el-drawer__footer' }, this.$slots.footer()) : null ]) } }) vi.mock('element-plus', () => ({ ElDialog: ElDialogStub, ElDrawer: ElDrawerStub, ElButton: ElButtonStub })) let InfoDialog: any interface RenderResult { app: App el: HTMLDivElement vm: ComponentPublicInstance events: { closeDialog: ReturnType confirmDialog: ReturnType open: ReturnType } options: Record } const renderedApps: RenderResult[] = [] beforeAll(async () => { InfoDialog = (await import('../../../components/infoDialog/index.vue')).default }) const renderDialog = async (rawOptions: Record = {}): Promise => { const events = { closeDialog: vi.fn(), confirmDialog: vi.fn(), open: vi.fn() } const el = document.createElement('div') document.body.appendChild(el) const options = reactive({ title: '测试dialog', isShowDialog: true, isShowBtn: true, ...rawOptions }) const app = createApp({ render () { return h(InfoDialog, { options, onCloseDialog: events.closeDialog, onConfirmDialog: events.confirmDialog, onOpen: events.open }, { default: () => h('div', { class: 'slot-content' }, 'slot content') }) } }) app.config.globalProperties.$t = (key: string) => key const vm = app.mount(el) await nextTick() const result = { app, el, vm, events, options } renderedApps.push(result) return result } const getRoot = () => document.body const click = async (selector: string) => { const target = getRoot().querySelector(selector) as HTMLElement | null expect(target).not.toBeNull() target!.click() await nextTick() } afterEach(() => { while (renderedApps.length) { const current = renderedApps.pop()! current.app.unmount() current.el.remove() } document.body.innerHTML = '' }) describe('components/infoDialog', () => { it('根据 options 渲染标题与显示状态', async () => { await renderDialog() expect(getRoot().querySelector('.info-dialog')).not.toBeNull() expect(getRoot().textContent).toContain('测试dialog') }) it('isShowDialog 为 false 时不显示', async () => { await renderDialog({ isShowDialog: false }) expect(getRoot().querySelector('.info-dialog')).toBeNull() }) it('显示默认底部按钮并支持确认与取消事件', async () => { const rendered = await renderDialog() const buttons = Array.from(getRoot().querySelectorAll('.dialog-footer button')).map((button) => button.textContent) expect(buttons).toContain('confirm') expect(buttons).toContain('cancel') await click('.el-dialog__headerbtn') expect(rendered.events.closeDialog).toHaveBeenCalledTimes(1) await click('.dialog-footer .el-button--primary') expect(rendered.events.confirmDialog).toHaveBeenCalledTimes(1) }) it('isShowBtn 为 false 时隐藏默认底部按钮', async () => { await renderDialog({ isShowBtn: false }) expect(getRoot().textContent).not.toContain('confirm') expect(getRoot().textContent).not.toContain('cancel') expect(getRoot().querySelector('.dialog-footer')).toBeNull() }) it('渲染默认插槽内容并在打开时触发 open 事件', async () => { const rendered = await renderDialog() expect(getRoot().querySelector('.slot-content')?.textContent).toBe('slot content') expect(rendered.events.open).toHaveBeenCalledTimes(1) }) it('isCloseLeft 为 true 时优先渲染取消按钮', async () => { await renderDialog({ isCloseLeft: true }) const buttons = Array.from(getRoot().querySelectorAll('.dialog-footer button')).map((button) => button.textContent) expect(buttons[0]).toBe('cancel') expect(buttons[1]).toBe('confirm') }) it('支持自定义操作按钮与显示逻辑', async () => { const customClick = vi.fn() await renderDialog({ isShowBtn: false, dialogOperates: [ { label: () => 'customAction', method: customClick, show: () => true }, { label: () => 'hiddenAction', method: vi.fn(), show: () => false } ] }) const footerButtons = Array.from(getRoot().querySelectorAll('.dialog-footer button')) expect(footerButtons).toHaveLength(2) expect(footerButtons[0].textContent).toBe('customAction') expect((footerButtons[1] as HTMLButtonElement).style.display).toBe('none') await click('.dialog-footer button') expect(customClick).toHaveBeenCalledTimes(1) }) it('支持 drawer 模式的最小渲染与交互', async () => { const rendered = await renderDialog({ title: '测试drawer', isDrawer: true, withHeader: true }) const drawer = getRoot().querySelector('.el-drawer.info-dialog') expect(drawer).not.toBeNull() expect(drawer?.getAttribute('aria-label')).toBe('测试drawer') expect(getRoot().textContent).toContain('测试drawer') expect(rendered.events.open).toHaveBeenCalledTimes(1) await click('.el-drawer__close-btn') expect(rendered.events.closeDialog).toHaveBeenCalledTimes(1) }) })