Skip to content

Testing

Rivet itself is tested with bun test and happy-dom. The same setup works for components in your project.

happy-dom provides a DOM in Node/Bun. A setup file registers the global document/window before tests run.

test/setup.ts
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register();
Terminal window
bun test --preload ./test/setup.ts

Build the markup in the DOM, mount the app and assert behaviour.

import { afterEach, expect, test } from "bun:test";
import { createRivet, defineComponent } from "@fullhaus/rivet";
const counter = defineComponent(({ part, signal, on, effect }) => {
const output = part("output");
const count = signal(0);
on(part("inc"), "click", () => count.update((v) => v + 1));
effect(() => { output.textContent = String(count()); });
return { reset: () => count.set(0) };
});
let app: ReturnType<typeof createRivet>;
afterEach(() => app?.unmount());
test("counts up and can be reset", () => {
document.body.innerHTML = `
<div data-rivet="counter" data-rivet-id="c1">
<output data-rivet-part="output">0</output>
<button data-rivet-part="inc">+1</button>
</div>`;
app = createRivet().component("counter", counter).mount();
const btn = document.querySelector<HTMLButtonElement>('[data-rivet-part="inc"]')!;
btn.click();
btn.click();
expect(document.querySelector('[data-rivet-part="output"]')!.textContent).toBe("2");
app.require<{ reset(): void }>("c1").reset();
expect(document.querySelector('[data-rivet-part="output"]')!.textContent).toBe("0");
});

The reactive system works without a DOM:

import { expect, test } from "bun:test";
import { computed, effect, signal } from "@fullhaus/rivet";
test("computed updates", () => {
const a = signal(1);
const b = computed(() => a() * 2);
expect(b()).toBe(2);
a.set(5);
expect(b()).toBe(10);
});
test("effect runs on change", () => {
const s = signal(0);
const seen: number[] = [];
const stop = effect(() => seen.push(s()));
s.set(1);
s.set(2);
expect(seen).toEqual([0, 1, 2]);
stop();
});

memoryTransport() collects entries in memory — perfect for assertions.

import { expect, test } from "bun:test";
import { createLogger, memoryTransport } from "@fullhaus/rivet/logger";
test("logs messages", () => {
const mem = memoryTransport();
const log = createLogger({ transports: [mem.transport] });
log.info("hello");
expect(mem.entries[0].entry.message).toBe("hello");
expect(mem.entries[0].formatted).toMatch(/hello$/);
});

memoryTransport() returns entries (each { formatted, entry }) and a clear() to reset between tests.