- 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
62 lines
2.4 KiB
TypeScript
62 lines
2.4 KiB
TypeScript
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { PettyForm } from "../src/components/form/index";
|
|
import { h } from "./helpers";
|
|
|
|
describe("petty-form", () => {
|
|
let el: PettyForm;
|
|
|
|
beforeEach(() => {
|
|
document.body.textContent = "";
|
|
el = document.createElement("petty-form") as PettyForm;
|
|
const label = h("label", { "data-part": "label" }, "Email");
|
|
const input = h("input", { "data-part": "control", type: "email", name: "email" });
|
|
const error = h("span", { "data-part": "error" });
|
|
const field = h("petty-form-field", { name: "email" }, label, input, error);
|
|
const submit = h("button", { type: "submit" }, "Submit");
|
|
const form = h("form", {}, field, submit);
|
|
el.appendChild(form);
|
|
document.body.appendChild(el);
|
|
});
|
|
|
|
it("registers the custom elements", () => {
|
|
expect(customElements.get("petty-form")).toBe(PettyForm);
|
|
expect(customElements.get("petty-form-field")).toBeDefined();
|
|
});
|
|
|
|
it("sets novalidate on the form", () => {
|
|
const form = el.querySelector("form")!;
|
|
expect(form.hasAttribute("novalidate")).toBe(true);
|
|
});
|
|
|
|
it("dispatches petty-submit with form data when no schema set", () => {
|
|
let detail: { data: Record<string, unknown> } | null = null;
|
|
el.addEventListener("petty-submit", ((e: CustomEvent) => {
|
|
detail = e.detail;
|
|
}) as EventListener);
|
|
const input = el.querySelector("input") as HTMLInputElement;
|
|
input.value = "test@example.com";
|
|
const form = el.querySelector("form")!;
|
|
form.dispatchEvent(new Event("submit", { cancelable: true }));
|
|
expect(detail!.data.email).toBe("test@example.com");
|
|
});
|
|
|
|
it("dispatches petty-invalid on schema failure and shows error", () => {
|
|
const mockSchema = {
|
|
safeParse: () => ({
|
|
success: false,
|
|
error: { issues: [{ path: ["email"], message: "Invalid email" }] },
|
|
}),
|
|
};
|
|
el.setSchema(mockSchema);
|
|
let detail: { errors: Array<{ path: Array<string | number>; message: string }> } | null = null;
|
|
el.addEventListener("petty-invalid", ((e: CustomEvent) => {
|
|
detail = e.detail;
|
|
}) as EventListener);
|
|
const form = el.querySelector("form")!;
|
|
form.dispatchEvent(new Event("submit", { cancelable: true }));
|
|
expect(detail!.errors[0].message).toBe("Invalid email");
|
|
expect(el.querySelector("[data-part=error]")!.textContent).toBe("Invalid email");
|
|
expect(el.querySelector("[data-part=control]")!.getAttribute("aria-invalid")).toBe("true");
|
|
});
|
|
});
|