import { afterEach, describe, expect, it } from 'vitest';
import { mount } from './index';
const ACCORDION = `
`;
function trigger(): HTMLButtonElement {
return document.querySelector('[data-c42-accordion-trigger]')!;
}
function accordionRoot(): HTMLElement {
return document.querySelector('[data-c42-accordion]')!;
}
/** Flush the microtask queue the MutationObserver drains on. */
function flush(): Promise {
return new Promise((resolve) => setTimeout(resolve, 0));
}
describe('mount', () => {
afterEach(() => {
document.body.innerHTML = '';
});
it('mounts an accordion from markup and it responds to clicks', () => {
document.body.innerHTML = ACCORDION;
const app = mount();
expect(accordionRoot().hasAttribute('data-c42-mounted')).toBe(true);
expect(trigger().getAttribute('aria-expanded')).toBe('false');
trigger().click();
expect(trigger().getAttribute('aria-expanded')).toBe('true');
app.unmount();
});
it('unmount() destroys controllers and clears the mounted marker', () => {
document.body.innerHTML = ACCORDION;
const app = mount();
app.unmount();
expect(accordionRoot().hasAttribute('data-c42-mounted')).toBe(false);
// Listeners are gone, so the trigger no longer toggles anything.
trigger().click();
expect(trigger().getAttribute('aria-expanded')).toBe('false');
});
it('does not mount the same element twice', () => {
document.body.innerHTML = ACCORDION;
const first = mount();
// A second mount() over the same DOM must be a no-op (idempotent marker).
const second = mount();
trigger().click();
expect(trigger().getAttribute('aria-expanded')).toBe('true');
// The second app tracked nothing, so its unmount leaves the controller live.
second.unmount();
expect(accordionRoot().hasAttribute('data-c42-mounted')).toBe(true);
first.unmount();
expect(accordionRoot().hasAttribute('data-c42-mounted')).toBe(false);
});
it('mounts markup added after mount() via the MutationObserver', async () => {
document.body.innerHTML = '';
const app = mount();
const host = document.getElementById('host')!;
host.innerHTML = ACCORDION;
await flush();
expect(accordionRoot().hasAttribute('data-c42-mounted')).toBe(true);
trigger().click();
expect(trigger().getAttribute('aria-expanded')).toBe('true');
app.unmount();
});
it('destroys controllers when their markup is removed', async () => {
document.body.innerHTML = `${ACCORDION}
`;
const app = mount();
const host = document.getElementById('host')!;
expect(accordionRoot().hasAttribute('data-c42-mounted')).toBe(true);
host.remove();
await flush();
// The detached node's controller was torn down and untracked.
expect(host.querySelector('[data-c42-accordion]')!.hasAttribute('data-c42-mounted')).toBe(
false,
);
app.unmount();
});
});