import { beforeEach, describe, expect, it, vi } from 'vitest'; import { Alert } from './alert'; import { ALERT_DISMISS_EVENT, type AlertDismissEvent } from './alert.types'; const MARKUP = `
Heads up

Your trial ends soon.

`; function setup(markup = MARKUP): HTMLElement { document.body.innerHTML = markup; return document.body.firstElementChild as HTMLElement; } function dismissBtn(root: HTMLElement): HTMLButtonElement { return root.querySelector('[data-c42-alert-dismiss]')!; } describe('Alert', () => { beforeEach(() => { document.body.innerHTML = ''; }); it('reads the variant from data-variant and opens', () => { const root = setup(); const alert = new Alert(root); expect(alert.currentVariant).toBe('warning'); expect(root.dataset.state).toBe('open'); }); it('uses role=alert/assertive for warning and error', () => { const root = setup(); new Alert(root); expect(root.getAttribute('role')).toBe('alert'); expect(root.getAttribute('aria-live')).toBe('assertive'); }); it('uses role=status/polite for info and success', () => { const root = setup('
'); new Alert(root); expect(root.getAttribute('role')).toBe('status'); expect(root.getAttribute('aria-live')).toBe('polite'); }); it('falls back to info for an unknown variant', () => { const root = setup('
'); const alert = new Alert(root); expect(alert.currentVariant).toBe('info'); expect(root.dataset.variant).toBe('info'); }); it('option variant overrides the attribute', () => { const root = setup(); const alert = new Alert(root, { variant: 'error' }); expect(alert.currentVariant).toBe('error'); expect(root.dataset.variant).toBe('error'); }); it('respects a role override', () => { const root = setup('
'); new Alert(root, { role: 'status' }); expect(root.getAttribute('role')).toBe('status'); expect(root.getAttribute('aria-live')).toBe('polite'); }); it('setVariant updates the variant and ARIA semantics', () => { const root = setup('
'); const alert = new Alert(root); expect(root.getAttribute('role')).toBe('status'); alert.setVariant('error'); expect(root.dataset.variant).toBe('error'); expect(root.getAttribute('role')).toBe('alert'); }); it('dismisses on dismiss button click and emits the event', () => { const root = setup(); const alert = new Alert(root); const spy = vi.fn(); alert.on(ALERT_DISMISS_EVENT, spy); dismissBtn(root).click(); expect(root.dataset.state).toBe('dismissed'); expect(root.hasAttribute('hidden')).toBe(true); expect(spy).toHaveBeenCalledOnce(); expect(spy.mock.calls[0][0].detail).toEqual({ variant: 'warning' }); }); it('dismiss is idempotent', () => { const root = setup(); const alert = new Alert(root); const spy = vi.fn(); alert.on(ALERT_DISMISS_EVENT, spy); alert.dismiss(); alert.dismiss(); expect(spy).toHaveBeenCalledOnce(); }); it('show re-opens a dismissed alert', () => { const root = setup(); const alert = new Alert(root); alert.dismiss(); alert.show(); expect(root.dataset.state).toBe('open'); expect(root.hasAttribute('hidden')).toBe(false); }); it('works without a dismiss button', () => { const root = setup('
'); expect(() => new Alert(root)).not.toThrow(); }); it('sets type=button on a bare dismiss button', () => { const root = setup(); new Alert(root); expect(dismissBtn(root).type).toBe('button'); }); it('removes listeners on destroy', () => { const root = setup(); const alert = new Alert(root); const spy = vi.fn(); alert.on(ALERT_DISMISS_EVENT, spy); alert.destroy(); dismissBtn(root).click(); expect(spy).not.toHaveBeenCalled(); }); });