- 51 headless Web Components (45 core + 6 animation) - Shared helpers: emit(), part(), listen(), wireLabel(), initialValue() - Zero `new CustomEvent` or `static #counter` — all use shared utils - Zod schemas for all 44 core components - MCP package with discover, inspect, compose, validate tools - Showcase with Aperture Science theme, M3 Expressive motion - 81 tests passing, TypeScript strict mode clean - Signals (~500B), SPA router (~400B), zero dependencies
66 lines
2.2 KiB
TypeScript
66 lines
2.2 KiB
TypeScript
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { part, emit, initialValue, listen, wireLabel } from "../src/shared/helpers";
|
|
|
|
describe("shared helpers", () => {
|
|
beforeEach(() => { document.body.replaceChildren(); });
|
|
|
|
it("part() finds child by data-part attribute", () => {
|
|
const host = document.createElement("div");
|
|
const child = document.createElement("input");
|
|
child.dataset.part = "control";
|
|
host.appendChild(child);
|
|
expect(part(host, "control")).toBe(child);
|
|
expect(part(host, "missing")).toBeNull();
|
|
});
|
|
|
|
it("emit() dispatches a bubbling petty-prefixed event", () => {
|
|
const el = document.createElement("div");
|
|
document.body.appendChild(el);
|
|
let received: { value: unknown } | null = null;
|
|
document.body.addEventListener("petty-change", (e) => { received = (e as CustomEvent).detail; });
|
|
emit(el, "change", { value: "test" });
|
|
expect(received).toEqual({ value: "test" });
|
|
});
|
|
|
|
it("initialValue() reads default-value then value then empty", () => {
|
|
const el = document.createElement("div");
|
|
expect(initialValue(el)).toBe("");
|
|
el.setAttribute("value", "v");
|
|
expect(initialValue(el)).toBe("v");
|
|
el.setAttribute("default-value", "dv");
|
|
expect(initialValue(el)).toBe("dv");
|
|
});
|
|
|
|
it("listen() attaches and cleanup removes listeners", () => {
|
|
const el = document.createElement("button");
|
|
let count = 0;
|
|
const handler = () => { count++; };
|
|
const cleanup = listen(el, [["click", handler]]);
|
|
el.click();
|
|
expect(count).toBe(1);
|
|
cleanup();
|
|
el.click();
|
|
expect(count).toBe(1);
|
|
});
|
|
|
|
it("listen() returns noop for null element", () => {
|
|
const cleanup = listen(null, [["click", () => {}]]);
|
|
expect(typeof cleanup).toBe("function");
|
|
cleanup();
|
|
});
|
|
|
|
it("wireLabel() connects label htmlFor to control id", () => {
|
|
const control = document.createElement("input");
|
|
const label = document.createElement("label");
|
|
wireLabel(control, label, "test");
|
|
expect(control.id).toMatch(/^test-/);
|
|
expect(label.htmlFor).toBe(control.id);
|
|
});
|
|
|
|
it("wireLabel() does nothing with null label", () => {
|
|
const control = document.createElement("input");
|
|
wireLabel(control, null, "test");
|
|
expect(control.id).toBe("");
|
|
});
|
|
});
|