mirror of
https://github.com/gethomepage/homepage.git
synced 2026-04-05 09:41:21 -07:00
Chore: homepage tests (#6278)
This commit is contained in:
@@ -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);
|
||||
|
||||
53
src/utils/contexts/color.test.jsx
Normal file
53
src/utils/contexts/color.test.jsx
Normal 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));
|
||||
});
|
||||
});
|
||||
@@ -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]);
|
||||
|
||||
|
||||
33
src/utils/contexts/settings.test.jsx
Normal file
33
src/utils/contexts/settings.test.jsx
Normal 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}');
|
||||
});
|
||||
});
|
||||
@@ -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]);
|
||||
|
||||
|
||||
33
src/utils/contexts/tab.test.jsx
Normal file
33
src/utils/contexts/tab.test.jsx
Normal 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");
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
64
src/utils/contexts/theme.test.jsx
Normal file
64
src/utils/contexts/theme.test.jsx
Normal 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"));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user