/**
* Fase 17.4.1 — ChangelogModal unit tests.
*
* Mirrors the Spinner.test.ts pattern (Svelte 5 `mount`/`unmount`, no
* @testing-library dependency). The modal imports CHANGELOG.md via Vite's
* `?raw` query — vitest runs against the same Vite config (see
* apps/web/vitest.config.ts) so the import resolves to the file contents at
* test time.
*
* Specs:
* CHM-U-01 — `open={false}` renders nothing.
* CHM-U-02 — `open={true}` renders the CHANGELOG `
`.
* CHM-U-03 — click on the overlay fires `onClose`.
* CHM-U-04 — pressing Escape fires `onClose`.
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { flushSync, mount, unmount } from 'svelte';
import ChangelogModal from './ChangelogModal.svelte';
let host: HTMLElement;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let instance: any;
function render(props: Record) {
host = document.createElement('div');
document.body.appendChild(host);
instance = mount(ChangelogModal, { target: host, props });
return host;
}
beforeEach(() => {
host = document.createElement('div');
});
afterEach(() => {
if (instance) unmount(instance);
if (host?.parentNode) host.parentNode.removeChild(host);
instance = null;
});
describe('ChangelogModal component', () => {
it('CHM-U-01: open=false renders nothing visible', () => {
const root = render({ open: false, onClose: () => {} });
expect(root.querySelector('[data-testid="changelog-modal"]')).toBeNull();
});
it('CHM-U-02: open=true renders the CHANGELOG h1 heading', () => {
const root = render({ open: true, onClose: () => {} });
const modal = root.querySelector('[data-testid="changelog-modal"]');
expect(modal).not.toBeNull();
const h1 = modal?.querySelector('h1');
expect(h1).not.toBeNull();
expect(h1?.textContent?.trim()).toBe('Changelog');
});
it('CHM-U-03: click on overlay fires onClose', () => {
const onClose = vi.fn();
const root = render({ open: true, onClose });
const overlay = root.querySelector('[data-testid="changelog-modal-overlay"]') as
| HTMLElement
| null;
expect(overlay).not.toBeNull();
overlay?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
expect(onClose).toHaveBeenCalledTimes(1);
});
it('CHM-U-04: Escape key fires onClose', () => {
const onClose = vi.fn();
render({ open: true, onClose });
// $effect runs in a microtask after mount. flushSync drains pending
// reactive effects so the keydown listener is attached before we
// dispatch the event.
flushSync();
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
expect(onClose).toHaveBeenCalledTimes(1);
});
});