import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ThemeToggle } from "./ThemeToggle"; const root = document.documentElement; const stored = new Map(); beforeEach(() => { // Vitest's jsdom on Node 24 exposes no localStorage, so give it a minimal one. vi.stubGlobal("localStorage", { getItem: (key: string) => stored.get(key) ?? null, setItem: (key: string, value: string) => stored.set(key, value), removeItem: (key: string) => stored.delete(key), }); }); afterEach(() => { delete root.dataset.theme; stored.clear(); }); describe("ThemeToggle", () => { it("cycles system, light, dark and back to following the system", async () => { render(); const button = screen.getByRole("button", { name: /^Theme: System/ }); expect(root.dataset.theme).toBeUndefined(); await userEvent.click(button); expect(button).toHaveAccessibleName("Theme: Light. Switch to Dark"); expect(root.dataset.theme).toBe("light"); expect(localStorage.getItem("theme")).toBe("light"); await userEvent.click(button); expect(root.dataset.theme).toBe("dark"); expect(localStorage.getItem("theme")).toBe("dark"); await userEvent.click(button); expect(root.dataset.theme).toBeUndefined(); expect(localStorage.getItem("theme")).toBeNull(); }); it("starts from the theme index.html applied before first paint", () => { root.dataset.theme = "dark"; render(); expect(screen.getByRole("button", { name: /^Theme: Dark/ })).toBeVisible(); }); });