import { act, fireEvent, render, screen, within } from "@testing-library/react"; import Checkbox, { CheckboxGroup } from "./Checkbox"; /** * The drawn box, which is the label's one child that does not hold the hidden * input. StyleX compiles to class names and jsdom loads no stylesheet, so the * class list is the only place the composed ring is observable. */ function indicator(input: HTMLElement): HTMLElement { const label = input.closest("label") as HTMLElement; const spans = [...label.querySelectorAll("span")]; const box = spans.find((span) => !span.contains(input)); if (box === undefined) throw new Error("the checkbox drew no indicator"); return box; } test("a standalone checkbox reports its state and reads its label as its name", () => { const onChange = vi.fn(); render( Safe search , ); const box = screen.getByRole("checkbox", { name: "Safe search" }) as HTMLInputElement; expect(box.checked).toBe(true); fireEvent.click(box); // The caller owns the state, so the box reports the value it would move to // rather than moving there itself. expect(onChange).toHaveBeenCalledWith(false); }); test("a disabled checkbox keeps its state on screen but takes no input", () => { render( Safe search , ); const box = screen.getByRole("checkbox", { name: "Safe search" }) as HTMLInputElement; expect(box.checked).toBe(true); // The disabled input is the whole guard: a browser fires no click on one, and // it is out of the tab order. Clicking it here would prove nothing either way, // because `fireEvent` dispatches straight at the node and skips that check. expect(box.disabled).toBe(true); }); test("keyboard focus composes the ring onto the drawn box", () => { render( Ads , ); const input = screen.getByRole("checkbox", { name: "Ads" }); const box = indicator(input); const idle = box.className.split(" "); expect(input.closest("label")?.getAttribute("data-focus-visible")).toBeNull(); // React Aria only calls focus visible after the modality is keyboard, which // is why a bare focus() is not enough to raise the ring. act(() => { fireEvent.keyDown(document.body, { key: "Tab" }); input.focus(); }); expect(input.closest("label")?.getAttribute("data-focus-visible")).toBe("true"); const ringed = box.className.split(" "); // The box gained classes it did not have: the ring is composed onto it, not // merely reported by React Aria on the root. expect(ringed.length).toBeGreaterThan(idle.length); expect(idle.every((name) => ringed.includes(name))).toBe(true); }); test("inside a group the group owns the selection, and the group carries the name", () => { const onChange = vi.fn(); render( Ads Trackers , ); const group = screen.getByRole("group", { name: "Assigned sources" }); expect((within(group).getByRole("checkbox", { name: "Ads" }) as HTMLInputElement).checked).toBe(true); expect((within(group).getByRole("checkbox", { name: "Trackers" }) as HTMLInputElement).checked).toBe(false); // The group reports the whole set, not the box that moved. fireEvent.click(within(group).getByRole("checkbox", { name: "Trackers" })); expect(onChange).toHaveBeenCalledWith(["1", "2"]); });