import { describe, expect, it, vi } from "vitest"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { EntryUnitLabelRow, EntryUnitToggle } from "./entry-unit-toggle"; // The control is presentation over one boolean, so what is worth pinning is the // part that is easy to get wrong and invisible on screen: the accessible name, // which has to carry the visible text before it names the action, and the // explicit `type` that stops it submitting the dive form it renders inside. const toggle = () => screen.getByRole("button"); describe("EntryUnitToggle", () => { it("shows both systems for the dimension", () => { render( , ); expect(toggle()).toHaveTextContent("m | ft"); }); it("shows the pressure dimension's own pair", () => { render( , ); expect(toggle()).toHaveTextContent("bar | psi"); }); // WCAG 2.5.3 (Label in Name): a speech-input user says the words they can see, // so an `aria-label` that overrides visible text has to contain it. Same rule // the export buttons follow. it("names the action after the text a diver can see", () => { render( , ); expect(toggle()).toHaveAccessibleName( "m | ft — switch depth entry to feet", ); }); it("names the way back once the flip has happened", () => { render( , ); expect(toggle()).toHaveAccessibleName( "m | ft — switch depth entry to meters", ); }); // The spoken vocabulary, not the short label: "psi" reads as a word either way, // but "°F" would not, and this is the same `unitWord` the chart summaries use. it("speaks the temperature units rather than spelling them", () => { render( , ); expect(toggle()).toHaveAccessibleName( "°C | °F — switch temperature entry to degrees Fahrenheit", ); }); it("highlights whichever system is on", () => { const { rerender } = render( , ); expect(screen.getByText("m")).toHaveClass("text-foreground"); expect(screen.getByText("ft")).not.toHaveClass("text-foreground"); rerender( , ); expect(screen.getByText("ft")).toHaveClass("text-foreground"); expect(screen.getByText("m")).not.toHaveClass("text-foreground"); }); // It renders inside the dive `
`, where a button's default type submits. it("is not a submit button", () => { render( , ); expect(toggle()).toHaveAttribute("type", "button"); }); it("asks for the flip when pressed", async () => { const onToggle = vi.fn(); render( , ); await userEvent.click(toggle()); expect(onToggle).toHaveBeenCalledTimes(1); }); }); // The label row's alignment cannot be tested here, and pretending otherwise would be // worse than not trying: jsdom does no layout, so every `getBoundingClientRect()` is // zeroes and a geometry assertion passes against any markup at all - the same vacuous // pass the storage helper exists to prevent. The alignment itself was measured in a // real browser. What this pins is the three structural properties it rests on, each of // which was a way of getting it wrong. describe("EntryUnitLabelRow", () => { const row = () => render( , ); // The original defect. `