104 lines
4.3 KiB
TypeScript
104 lines
4.3 KiB
TypeScript
import { describe, expect, test } from "vitest";
|
|
import { act, renderHook, waitFor } from "@testing-library/react";
|
|
import type { ReactNode } from "react";
|
|
import { ThemeProvider } from "../../../contexts/theme/ThemeProvider.tsx";
|
|
import { AuthProvider } from "../../../contexts/auth/AuthProvider.tsx";
|
|
import { useTheme } from "../../../hooks/theme/useTheme.ts";
|
|
import { DEFAULT_SETTINGS } from "../../../../shared/settings.ts";
|
|
import { calls, route } from "../../fetch.ts";
|
|
import { makeSession, storeSession, wire } from "../../fixtures.ts";
|
|
|
|
describe("ThemeProvider", () => {
|
|
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
<ThemeProvider>{children}</ThemeProvider>
|
|
);
|
|
const root = document.documentElement;
|
|
|
|
test("smooth and auto by default, with no attributes", () => {
|
|
const { result } = renderHook(useTheme, { wrapper });
|
|
expect(result.current.style).toBe("smooth");
|
|
expect(result.current.colorScheme).toBe("auto");
|
|
expect(root.hasAttribute("data-style")).toBe(false);
|
|
expect(root.hasAttribute("data-color-scheme")).toBe(false);
|
|
});
|
|
|
|
test("applies and persists a choice", () => {
|
|
const { result } = renderHook(useTheme, { wrapper });
|
|
act(() => result.current.setStyle("brutalist"));
|
|
act(() => result.current.setColorScheme("dark"));
|
|
expect(root.getAttribute("data-style")).toBe("brutalist");
|
|
expect(root.getAttribute("data-color-scheme")).toBe("dark");
|
|
expect(localStorage.getItem("style")).toBe("brutalist");
|
|
expect(localStorage.getItem("color-scheme")).toBe("dark");
|
|
act(() => result.current.setStyle("smooth"));
|
|
act(() => result.current.setColorScheme("auto"));
|
|
expect(root.hasAttribute("data-style")).toBe(false);
|
|
expect(root.hasAttribute("data-color-scheme")).toBe(false);
|
|
});
|
|
|
|
test("restores the stored theme, migrating the legacy name", () => {
|
|
localStorage.setItem("style", "nyt");
|
|
localStorage.setItem("color-scheme", "light");
|
|
const { result } = renderHook(useTheme, { wrapper });
|
|
expect(result.current.style).toBe("nyt");
|
|
expect(result.current.colorScheme).toBe("light");
|
|
localStorage.setItem("style", "default");
|
|
expect(renderHook(useTheme, { wrapper }).result.current.style).toBe(
|
|
"smooth",
|
|
);
|
|
});
|
|
|
|
describe("signed in", () => {
|
|
const authWrapper = ({ children }: { children: ReactNode }) => (
|
|
<AuthProvider>
|
|
<ThemeProvider>{children}</ThemeProvider>
|
|
</AuthProvider>
|
|
);
|
|
const signIn = (settings: typeof DEFAULT_SETTINGS) => {
|
|
const session = makeSession();
|
|
storeSession(session);
|
|
route("GET /api/users/me", () => wire(session.user));
|
|
route("GET /api/users/me/settings", () => settings);
|
|
route(
|
|
"PATCH /api/users/me/settings",
|
|
(req) => ({ ...settings, ...(req.body as object) }),
|
|
);
|
|
};
|
|
|
|
test("the account's choice wins over this browser's, and is kept here", async () => {
|
|
localStorage.setItem("style", "nyt");
|
|
signIn({ ...DEFAULT_SETTINGS, style: "kanagawa", colorScheme: "dark" });
|
|
const { result } = renderHook(useTheme, { wrapper: authWrapper });
|
|
await waitFor(() => expect(result.current.style).toBe("kanagawa"));
|
|
expect(result.current.colorScheme).toBe("dark");
|
|
expect(root.getAttribute("data-style")).toBe("kanagawa");
|
|
expect(localStorage.getItem("style")).toBe("kanagawa");
|
|
expect(calls("PATCH /api/users/me/settings")).toHaveLength(0);
|
|
|
|
act(() => result.current.setStyle("melange"));
|
|
await waitFor(() =>
|
|
expect(calls("PATCH /api/users/me/settings")[0]?.body).toEqual({
|
|
style: "melange",
|
|
})
|
|
);
|
|
expect(result.current.style).toBe("melange");
|
|
act(() => result.current.setStyle("smooth"));
|
|
act(() => result.current.setColorScheme("auto"));
|
|
});
|
|
|
|
test("an account without a choice takes this browser's, once", async () => {
|
|
localStorage.setItem("style", "brutalist");
|
|
signIn({ ...DEFAULT_SETTINGS });
|
|
const { result } = renderHook(useTheme, { wrapper: authWrapper });
|
|
await waitFor(() =>
|
|
expect(calls("PATCH /api/users/me/settings")).toHaveLength(1)
|
|
);
|
|
expect(calls("PATCH /api/users/me/settings")[0].body).toEqual({
|
|
style: "brutalist",
|
|
});
|
|
expect(result.current.style).toBe("brutalist");
|
|
act(() => result.current.setStyle("smooth"));
|
|
});
|
|
});
|
|
});
|