- Require a session on every route; public routes opt out with @allow_anonymous - Split password hashing and pepper loading into passwords.py - Add a system/light/dark theme toggle backed by light-dark() colors - Ignore stale 401s from an earlier session, PATCH only changed book fields, and block overlapping journal-entry saves - Add bin/start and CoderPad Vite server settings for the pad's start/restart - Rewrite README as a mise onboarding guide; expand .gitignore - Include review-round fixes and tests
49 lines
1.7 KiB
TypeScript
49 lines
1.7 KiB
TypeScript
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<string, string>();
|
|
|
|
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(<ThemeToggle />);
|
|
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(<ThemeToggle />);
|
|
expect(screen.getByRole("button", { name: /^Theme: Dark/ })).toBeVisible();
|
|
});
|
|
});
|