// @vitest-environment jsdom // // E28 review D5 + L5 + D6: the Plex player's settings panel. Its section switch was a row of // `aria-pressed` buttons (now an ARIA tab list on `ui/Tabs`), its rows sat under the 44 touch target, // its on/off rows were a hand-laid label (now `CheckRow`), and its sliders and colour wells had no // accessible name — the visible label beside them was not tied to them. import { useRef, useState } from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { cleanup, fireEvent, render, screen } from "@testing-library/react"; vi.mock("react-i18next", async (importOriginal) => ({ ...(await importOriginal()), useTranslation: () => ({ t: (k: string) => k }), })); import { PlexPlayerSettings } from "./PlexPlayerSettings"; import { DEFAULT_PREFS, type PlexPlayerPrefs } from "../lib/plexPlayerPrefs"; afterEach(cleanup); type Tab = "sync" | "playback" | "subtitle" | "clock"; const TABS: { key: Tab; label: string }[] = [ { key: "sync", label: "Sync" }, { key: "playback", label: "Playback" }, { key: "subtitle", label: "Subtitles" }, { key: "clock", label: "Clock" }, ]; function Harness({ start = "sync" as Tab, prefs: initial = {} as Partial }) { const [tab, setTab] = useState(start); const [prefs, setPrefs] = useState({ ...DEFAULT_PREFS, ...initial }); const menuRef = useRef(null); const back = useRef(null); return ( setPrefs((cur) => ({ ...cur, ...p }))} changeAudioOffset={() => {}} now={new Date(2026, 8, 27, 12, 0)} /> ); } const tab = (name: string) => screen.getByRole("tab", { name }); describe("the Plex player's settings panel", () => { it("switches its sections as a tab list, the focus on the selected tab", () => { render(); expect(screen.getByRole("tablist")).toBeTruthy(); expect(document.activeElement).toBe(tab("Subtitles")); fireEvent.keyDown(document.activeElement!, { key: "ArrowRight" }); expect(tab("Clock").getAttribute("aria-selected")).toBe("true"); expect(screen.getByRole("tabpanel", { name: "Clock" })).toBeTruthy(); }); it("gives every slider and colour well the name its row shows", () => { render(); for (const name of ["plex.player.subSize", "plex.player.subPos", "plex.player.subShadow"]) expect(screen.getByRole("slider", { name })).toBeTruthy(); for (const name of ["plex.player.subColor", "plex.player.subBg", "plex.player.subShadowColor"]) expect(screen.getByLabelText(name).getAttribute("type")).toBe("color"); fireEvent.click(tab("Clock")); expect(screen.getByLabelText("plex.player.clockColor").getAttribute("type")).toBe("color"); }); it("lays its on/off rows on CheckRow and its buttons at the touch target", () => { render(); const row = screen.getByRole("checkbox", { name: "plex.player.clockShow" }).closest("label")!; expect(row.className).toContain("pointer-coarse:min-h-(--touch-target)"); for (const b of screen.getAllByRole("button")) expect(b.className).toContain("touch-target"); for (const t of screen.getAllByRole("tab")) expect(t.className).toContain("touch-target"); fireEvent.click(tab("Subtitles")); const none = screen.getByRole("checkbox", { name: "plex.player.subBgNone" }).closest("label")!; expect(none.className).toContain("pointer-coarse:min-h-(--touch-target)"); }); });