import { fireEvent, render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { CompositeLab } from "../../src/components/CompositeLab"; import { ConverterLab } from "../../src/components/ConverterLab"; import { PickerLab } from "../../src/components/PickerLab"; import { Workbench } from "../../src/components/Workbench"; describe("colour workbench", () => { beforeEach(() => { localStorage.clear(); history.replaceState(null, "", "/"); }); it("exposes all seven workspaces as accessible tabs", () => { render(); const tabs = screen.getAllByRole("tab"); expect(tabs).toHaveLength(7); expect(tabs[0]).toHaveAttribute("aria-selected", "true"); expect( screen.getByRole("heading", { name: "Convert colours" }), ).toBeVisible(); }); it("supports keyboard navigation between workspaces", async () => { render(); const convert = screen.getByRole("tab", { name: /Convert/ }); convert.focus(); await userEvent.keyboard("{ArrowRight}"); expect(screen.getByRole("tab", { name: /Composite/ })).toHaveAttribute( "aria-selected", "true", ); expect( screen.getByRole("heading", { name: "Composite translucent colours" }), ).toBeVisible(); }); }); describe("interactive colour labs", () => { it("keeps the last valid conversion visible while an input is incomplete", async () => { render(); const source = screen.getByLabelText("Colour"); await userEvent.clear(source); await userEvent.type(source, "oklch("); expect(source).toHaveAttribute("aria-invalid", "true"); expect(screen.getByText(/Keeping the last valid conversion/)).toBeVisible(); expect(screen.getByText("HEX + alpha")).toBeVisible(); }); it("produces an opaque composite over the enabled matte", () => { render(); const result = screen.getByText("Result").closest("aside"); expect(result).not.toBeNull(); expect(within(result!).getByText(/^#[0-9a-f]{6}$/i)).toBeVisible(); expect(screen.getByText(/Produces an opaque RGB result/)).toBeVisible(); }); it("keeps the exact picker field in sync with visual controls", () => { render(); const hue = screen.getByLabelText(/Hue ยท/); fireEvent.change(hue, { target: { value: "0" } }); expect(screen.getByLabelText("Exact CSS colour")).toHaveValue("#d14f4f"); expect(screen.getByText("#d14f4f")).toBeVisible(); }); });