Skip to content

Testing with Vitest

Testing an Ornata component is usually pretty simple: mount it onto real HTML, interact with it, make assertions, then clean it up.

Most Ornata tests come down to this:

  1. put real fixture markup in document.body
  2. call Component.mount(...)
  3. click, type, or update state
  4. check the DOM or instance.state
  5. call Component.unmountAll() in cleanup

Test Ornata components the same way other code would use them.

  • Component.mount(root) to create an instance
  • instance.state to check or update public state
  • instance.addStateListener() to observe state changes from outside the component
  • instance.dispose() to clean up one mounted instance
  • Component.unmount(root) to remove a specific mounted instance by root
  • Component.unmountAll() to clean up every mounted instance for that component

Ornata is built to enhance existing HTML, so your test fixtures should look like the real markup the component expects.

document.body.innerHTML = `
<section data-disclosure>
<button type="button" data-trigger>Toggle</button>
<div data-panel hidden>Panel</div>
</section>
`;

In most Ornata tests, you will start by putting the expected HTML into the document, then mounting the component onto that markup.

const instance = Disclosure.mount('[data-disclosure]');

This is usually the most useful way to start a test because it matches how Ornata is used in real pages. The component gets real markup, mounts to a real root element, and returns an instance you can use in the rest of the test.

You will usually check one of a few things.

Visible DOM output

Disclosure.mount('[data-disclosure]');
await user.click(screen.getByRole('button', { name: 'Toggle' }));
expect(screen.getByText('Open')).toBeInTheDocument();

Public state

const instance = Counter.mount('[data-counter]');
instance.state.count = 1;
expect(instance.state.count).toBe(1);

Listener or lifecycle calls

const listener = vi.fn();
const instance = Counter.mount(document.createElement('div'));
instance.addStateListener('count', listener);
instance.state.count = 1;
expect(listener).toHaveBeenCalledWith({
property: 'count',
newValue: 1,
oldValue: 0,
target: instance,
});

If a file reuses the same component constructor across multiple tests, unmountAll() is the easiest cleanup.

afterEach(() => {
Disclosure.unmountAll();
document.body.innerHTML = '';
});

This is especially handy when one test mounts more than one root, or when several tests reuse the same component.

If a test only creates one instance and you already have that reference, instance.dispose() is fine too.