import { describe, it, expect, afterEach } from 'vitest' import { mount, VueWrapper } from '@vue/test-utils' import ConfirmDialog from '../ConfirmDialog.vue' let wrapper: VueWrapper function mountDialog(props: Record = {}) { wrapper = mount(ConfirmDialog, { props: { open: true, ...props, }, attachTo: document.body, }) return wrapper } function dialog() { return document.body.querySelector('.confirm-dialog') } function overlay() { return document.body.querySelector('.confirm-dialog__overlay') } afterEach(() => { wrapper?.unmount() }) describe('ConfirmDialog', () => { it('renders when open is true', () => { mountDialog() expect(dialog()).not.toBeNull() }) it('does not render when open is false', () => { mountDialog({ open: false }) expect(dialog()).toBeNull() }) it('displays default title', () => { mountDialog() expect(dialog()!.querySelector('.confirm-dialog__title')!.textContent).toBe('Are you sure?') }) it('displays custom title and message', () => { mountDialog({ title: 'Remove event?', message: 'This cannot be undone.', }) expect(dialog()!.querySelector('.confirm-dialog__title')!.textContent).toBe('Remove event?') expect(dialog()!.querySelector('.confirm-dialog__message')!.textContent).toBe('This cannot be undone.') }) it('displays custom button labels', () => { mountDialog({ confirmLabel: 'Delete', cancelLabel: 'Keep', }) const buttons = dialog()!.querySelectorAll('.confirm-dialog__btn') expect(buttons[0]!.textContent!.trim()).toBe('Keep') expect(buttons[1]!.textContent!.trim()).toBe('Delete') }) it('emits confirm when confirm button is clicked', async () => { mountDialog() const btn = dialog()!.querySelector('.confirm-dialog__btn--confirm') as HTMLElement btn.click() await wrapper.vm.$nextTick() expect(wrapper.emitted('confirm')).toHaveLength(1) }) it('emits cancel when cancel button is clicked', async () => { mountDialog() const btn = dialog()!.querySelector('.confirm-dialog__btn--cancel') as HTMLElement btn.click() await wrapper.vm.$nextTick() expect(wrapper.emitted('cancel')).toHaveLength(1) }) it('emits cancel when overlay is clicked', async () => { mountDialog() const el = overlay() as HTMLElement el.click() await wrapper.vm.$nextTick() expect(wrapper.emitted('cancel')).toHaveLength(1) }) it('emits cancel when Escape key is pressed', async () => { mountDialog() const el = dialog() as HTMLElement el.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })) await wrapper.vm.$nextTick() expect(wrapper.emitted('cancel')).toHaveLength(1) }) it('focuses cancel button when opened', async () => { mountDialog({ open: false }) await wrapper.setProps({ open: true }) await wrapper.vm.$nextTick() const cancelBtn = dialog()!.querySelector('.confirm-dialog__btn--cancel') expect(document.activeElement).toBe(cancelBtn) }) it('has alertdialog role and aria-modal', () => { mountDialog() const el = dialog() as HTMLElement expect(el.getAttribute('role')).toBe('alertdialog') expect(el.getAttribute('aria-modal')).toBe('true') }) })