Files
kandev/apps/web/components/theme/app-theme.test.tsx
T
2026-08-26 09:04:23 +00:00

91 lines
2.6 KiB
TypeScript

import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { useEffect } from "react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { AppThemeProvider, useTheme } from "./app-theme";
function ThemeProbe() {
const { theme, savedTheme, previewTheme, commitTheme, restoreTheme } = useTheme();
return (
<div>
<span data-testid="current-theme">{theme}</span>
<span data-testid="saved-theme">{savedTheme}</span>
<button type="button" onClick={() => previewTheme("light")}>
Preview light
</button>
<button type="button" onClick={() => commitTheme("light")}>
Save light
</button>
<button type="button" onClick={restoreTheme}>
Discard preview
</button>
</div>
);
}
describe("AppThemeProvider settings preview", () => {
beforeEach(() => {
window.localStorage.setItem("theme", "dark");
vi.stubGlobal("matchMedia", () => ({
matches: false,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
}));
});
afterEach(() => {
cleanup();
document.documentElement.className = "";
window.localStorage.clear();
vi.unstubAllGlobals();
});
it("applies the initial resolved theme before descendant passive effects", () => {
const observedRootThemes: string[] = [];
function InitialThemeProbe() {
useEffect(() => {
observedRootThemes.push(document.documentElement.className);
}, []);
return null;
}
render(
<AppThemeProvider>
<InitialThemeProbe />
</AppThemeProvider>,
);
expect(observedRootThemes).toEqual(["dark"]);
});
it("previews without writing storage and restores the saved theme", () => {
render(
<AppThemeProvider>
<ThemeProbe />
</AppThemeProvider>,
);
fireEvent.click(screen.getByRole("button", { name: "Preview light" }));
expect(screen.getByTestId("current-theme").textContent).toBe("light");
expect(screen.getByTestId("saved-theme").textContent).toBe("dark");
expect(window.localStorage.getItem("theme")).toBe("dark");
fireEvent.click(screen.getByRole("button", { name: "Discard preview" }));
expect(screen.getByTestId("current-theme").textContent).toBe("dark");
});
it("writes the durable theme only when committed", () => {
render(
<AppThemeProvider>
<ThemeProbe />
</AppThemeProvider>,
);
fireEvent.click(screen.getByRole("button", { name: "Save light" }));
expect(screen.getByTestId("saved-theme").textContent).toBe("light");
expect(window.localStorage.getItem("theme")).toBe("light");
});
});