Testing
@whisq/testing provides utilities for mounting components in a test environment, querying the DOM, and simulating user interactions. It works with any test runner — Vitest, Jest, or Node’s built-in test runner.
Install the testing package:
npm install -D @whisq/testingYour First Test
Section titled “Your First Test”import { describe, it, expect, afterEach } from "vitest";import { render, cleanup, screen } from "@whisq/testing";import { signal, component, div, button, span } from "@whisq/core";
afterEach(cleanup);
const Counter = component((props: { initial?: number }) => { const count = signal(props.initial ?? 0); return div( button({ onclick: () => count.value-- }, "-"), span(() => `${count.value}`), button({ onclick: () => count.value++ }, "+"), );});
describe("Counter", () => { it("renders initial value", () => { render(Counter({ initial: 5 })); expect(screen.getByText("5")).toBeTruthy(); });});render() mounts the component into the document. cleanup() removes it after each test. screen provides query methods to find elements.
Querying Elements
Section titled “Querying Elements”By Text
Section titled “By Text”screen.getByText("Submit"); // throws if not foundscreen.queryByText("Submit"); // returns null if not foundBy Role
Section titled “By Role”Role queries use implicit ARIA roles — a <button> has role "button", an <input> has role "textbox":
screen.getByRole("button"); // finds <button>screen.getByRole("textbox"); // finds <input>Simulating Events
Section titled “Simulating Events”Use fireEvent to simulate user interactions:
import { render, cleanup, screen, fireEvent } from "@whisq/testing";
it("increments on click", () => { render(Counter({ initial: 0 }));
const plus = screen.getByText("+"); fireEvent.click(plus);
expect(screen.getByText("1")).toBeTruthy();});Available Events
Section titled “Available Events”fireEvent.click(element);fireEvent.input(element, { target: { value: "hello" } });fireEvent.change(element, { target: { value: "new value" } });fireEvent.submit(element);fireEvent.keydown(element, { key: "Enter" });fireEvent.keyup(element, { key: "Escape" });fireEvent.focus(element);fireEvent.blur(element);Testing Forms
Section titled “Testing Forms”import { signal, computed, component, form, input, button } from "@whisq/core";import { render, cleanup, screen, fireEvent } from "@whisq/testing";
const LoginForm = component(() => { const email = signal(""); const valid = computed(() => email.value.includes("@"));
return form( input({ type: "email", placeholder: "Email", value: () => email.value, oninput: (e) => email.value = e.target.value, }), button({ disabled: () => !valid.value }, "Sign In"), );});
describe("LoginForm", () => { afterEach(cleanup);
it("disables submit when email is invalid", () => { render(LoginForm({})); const btn = screen.getByText("Sign In"); expect(btn.disabled).toBe(true); });
it("enables submit when email is valid", () => { render(LoginForm({})); const input = screen.getByRole("textbox"); fireEvent.input(input, { target: { value: "user@example.com" } }); const btn = screen.getByText("Sign In"); expect(btn.disabled).toBe(false); });});Testing Conditional Rendering
Section titled “Testing Conditional Rendering”import { signal, component, div, p, when } from "@whisq/core";
const Alert = component((props: { show: boolean }) => { const visible = signal(props.show); return div( when(() => visible.value, () => p("Alert is visible")), );});
it("shows alert when visible", () => { render(Alert({ show: true })); expect(screen.queryByText("Alert is visible")).toBeTruthy();});
it("hides alert when not visible", () => { render(Alert({ show: false })); expect(screen.queryByText("Alert is visible")).toBeNull();});Test Structure
Section titled “Test Structure”A recommended file structure for tests:
src/ components/ Counter.ts Counter.test.ts # test file next to component stores/ todos.ts todos.test.ts # test store logic directlyNext Steps
Section titled “Next Steps”- Components — Building the components you’ll test
- Forms — Form patterns that need testing
- State Management — Testing shared stores