Chore: homepage tests (#6278)

This commit is contained in:
shamoon
2026-02-04 19:58:39 -08:00
committed by GitHub
parent 7d019185a3
commit 872a3600aa
558 changed files with 32606 additions and 84 deletions

View File

@@ -17,7 +17,7 @@ const getInitialColor = () => {
export const ColorContext = createContext();
export function ColorProvider({ initialTheme, children }) {
const [color, setColor] = useState(getInitialColor);
const [color, setColor] = useState(() => initialTheme ?? getInitialColor());
const rawSetColor = (rawColor) => {
const root = window.document.documentElement;
@@ -30,9 +30,10 @@ export function ColorProvider({ initialTheme, children }) {
lastColor = rawColor;
};
if (initialTheme) {
rawSetColor(initialTheme);
}
useEffect(() => {
if (initialTheme !== undefined) setColor(initialTheme ?? getInitialColor());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialTheme]);
useEffect(() => {
rawSetColor(color);

View File

@@ -0,0 +1,53 @@
// @vitest-environment jsdom
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { useContext } from "react";
import { describe, expect, it } from "vitest";
import { ColorContext, ColorProvider } from "./color";
function Reader() {
const { color, setColor } = useContext(ColorContext);
return (
<div>
<div data-testid="value">{color}</div>
<button type="button" onClick={() => setColor("red")}>
red
</button>
</div>
);
}
describe("utils/contexts/color", () => {
it("initializes from localStorage and writes theme class + storage on updates", async () => {
localStorage.setItem("theme-color", "blue");
document.documentElement.className = "";
render(
<ColorProvider>
<Reader />
</ColorProvider>,
);
expect(screen.getByTestId("value")).toHaveTextContent("blue");
await waitFor(() => expect(document.documentElement.classList.contains("theme-blue")).toBe(true));
fireEvent.click(screen.getByRole("button", { name: "red" }));
await waitFor(() => expect(document.documentElement.classList.contains("theme-red")).toBe(true));
expect(localStorage.getItem("theme-color")).toBe("red");
});
it("defaults to slate when localStorage is empty", async () => {
localStorage.removeItem("theme-color");
document.documentElement.className = "";
render(
<ColorProvider>
<Reader />
</ColorProvider>,
);
expect(screen.getByTestId("value")).toHaveTextContent("slate");
await waitFor(() => expect(document.documentElement.classList.contains("theme-slate")).toBe(true));
});
});

View File

@@ -1,13 +1,13 @@
import { createContext, useMemo, useState } from "react";
import { createContext, useEffect, useMemo, useState } from "react";
export const SettingsContext = createContext();
export function SettingsProvider({ initialSettings, children }) {
const [settings, setSettings] = useState({});
const [settings, setSettings] = useState(() => initialSettings ?? {});
if (initialSettings) {
setSettings(initialSettings);
}
useEffect(() => {
if (initialSettings !== undefined) setSettings(initialSettings ?? {});
}, [initialSettings]);
const value = useMemo(() => ({ settings, setSettings }), [settings]);

View File

@@ -0,0 +1,33 @@
// @vitest-environment jsdom
import { fireEvent, render, screen } from "@testing-library/react";
import { useContext } from "react";
import { describe, expect, it } from "vitest";
import { SettingsContext, SettingsProvider } from "./settings";
function Reader() {
const { settings, setSettings } = useContext(SettingsContext);
return (
<div>
<div data-testid="value">{JSON.stringify(settings)}</div>
<button type="button" onClick={() => setSettings({ updated: true })}>
update
</button>
</div>
);
}
describe("utils/contexts/settings", () => {
it("provides initial settings and allows updates", () => {
render(
<SettingsProvider initialSettings={{ a: 1 }}>
<Reader />
</SettingsProvider>,
);
expect(screen.getByTestId("value")).toHaveTextContent('{"a":1}');
fireEvent.click(screen.getByRole("button", { name: "update" }));
expect(screen.getByTestId("value")).toHaveTextContent('{"updated":true}');
});
});

View File

@@ -1,13 +1,13 @@
import { createContext, useMemo, useState } from "react";
import { createContext, useEffect, useMemo, useState } from "react";
export const TabContext = createContext();
export function TabProvider({ initialTab, children }) {
const [activeTab, setActiveTab] = useState(false);
const [activeTab, setActiveTab] = useState(() => initialTab ?? false);
if (initialTab) {
setActiveTab(initialTab);
}
useEffect(() => {
if (initialTab !== undefined) setActiveTab(initialTab ?? false);
}, [initialTab]);
const value = useMemo(() => ({ activeTab, setActiveTab }), [activeTab]);

View File

@@ -0,0 +1,33 @@
// @vitest-environment jsdom
import { fireEvent, render, screen } from "@testing-library/react";
import { useContext } from "react";
import { describe, expect, it } from "vitest";
import { TabContext, TabProvider } from "./tab";
function Reader() {
const { activeTab, setActiveTab } = useContext(TabContext);
return (
<div>
<div data-testid="value">{String(activeTab)}</div>
<button type="button" onClick={() => setActiveTab("next")}>
next
</button>
</div>
);
}
describe("utils/contexts/tab", () => {
it("provides initial tab and allows updates", () => {
render(
<TabProvider initialTab="first">
<Reader />
</TabProvider>,
);
expect(screen.getByTestId("value")).toHaveTextContent("first");
fireEvent.click(screen.getByRole("button", { name: "next" }));
expect(screen.getByTestId("value")).toHaveTextContent("next");
});
});

View File

@@ -19,7 +19,7 @@ const getInitialTheme = () => {
export const ThemeContext = createContext();
export function ThemeProvider({ initialTheme, children }) {
const [theme, setTheme] = useState(getInitialTheme);
const [theme, setTheme] = useState(() => initialTheme ?? getInitialTheme());
const rawSetTheme = (rawTheme) => {
const root = window.document.documentElement;
@@ -31,9 +31,10 @@ export function ThemeProvider({ initialTheme, children }) {
localStorage.setItem("theme-mode", rawTheme);
};
if (initialTheme) {
rawSetTheme(initialTheme);
}
useEffect(() => {
if (initialTheme !== undefined) setTheme(initialTheme ?? getInitialTheme());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [initialTheme]);
useEffect(() => {
rawSetTheme(theme);

View File

@@ -0,0 +1,64 @@
// @vitest-environment jsdom
import { render, screen, waitFor } from "@testing-library/react";
import { useContext } from "react";
import { describe, expect, it, vi } from "vitest";
import { ThemeContext, ThemeProvider } from "./theme";
function Reader() {
const { theme } = useContext(ThemeContext);
return <div data-testid="value">{theme}</div>;
}
describe("utils/contexts/theme", () => {
it("initializes from localStorage and writes html classes", async () => {
// jsdom doesn't implement matchMedia by default; ensure it exists for getInitialTheme.
window.matchMedia =
window.matchMedia || vi.fn(() => ({ matches: false, addEventListener: vi.fn(), removeEventListener: vi.fn() }));
localStorage.setItem("theme-mode", "light");
document.documentElement.className = "";
render(
<ThemeProvider>
<Reader />
</ThemeProvider>,
);
expect(screen.getByTestId("value")).toHaveTextContent("light");
await waitFor(() => expect(document.documentElement.classList.contains("light")).toBe(true));
expect(localStorage.getItem("theme-mode")).toBe("light");
});
it("falls back to prefers-color-scheme when localStorage is empty", async () => {
const matchMedia = vi.fn(() => ({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn() }));
window.matchMedia = matchMedia;
localStorage.removeItem("theme-mode");
render(
<ThemeProvider>
<Reader />
</ThemeProvider>,
);
expect(matchMedia).toHaveBeenCalledWith("(prefers-color-scheme: dark)");
expect(screen.getByTestId("value")).toHaveTextContent("dark");
});
it("defaults to dark when prefers-color-scheme does not match", async () => {
const matchMedia = vi.fn(() => ({ matches: false, addEventListener: vi.fn(), removeEventListener: vi.fn() }));
window.matchMedia = matchMedia;
localStorage.removeItem("theme-mode");
render(
<ThemeProvider>
<Reader />
</ThemeProvider>,
);
expect(matchMedia).toHaveBeenCalledWith("(prefers-color-scheme: dark)");
expect(screen.getByTestId("value")).toHaveTextContent("dark");
await waitFor(() => expect(localStorage.getItem("theme-mode")).toBe("dark"));
});
});