feat(#415): theme fix, AuthDivider, SessionExpiryWarning components
- AUTH-014: Fix theme storage key (jarvis-theme -> mosaic-theme) - AUTH-016: Create AuthDivider component with customizable text - AUTH-019: Create SessionExpiryWarning floating banner (PDA-friendly, blue) - Fix lint errors in LoginForm, OAuthButton from parallel agents - Sync pnpm-lock.yaml for recharts dependency Refs #415 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
120
apps/web/src/providers/ThemeProvider.test.tsx
Normal file
120
apps/web/src/providers/ThemeProvider.test.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { render, screen, act } from "@testing-library/react";
|
||||
import { ThemeProvider, useTheme } from "./ThemeProvider";
|
||||
|
||||
function ThemeConsumer(): React.JSX.Element {
|
||||
const { theme, resolvedTheme, setTheme, toggleTheme } = useTheme();
|
||||
return (
|
||||
<div>
|
||||
<span data-testid="theme">{theme}</span>
|
||||
<span data-testid="resolved">{resolvedTheme}</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
setTheme("light");
|
||||
}}
|
||||
>
|
||||
Set Light
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setTheme("dark");
|
||||
}}
|
||||
>
|
||||
Set Dark
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
toggleTheme();
|
||||
}}
|
||||
>
|
||||
Toggle
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
describe("ThemeProvider", (): void => {
|
||||
let mockMatchMedia: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach((): void => {
|
||||
localStorage.clear();
|
||||
document.documentElement.classList.remove("light", "dark");
|
||||
|
||||
mockMatchMedia = vi.fn().mockReturnValue({
|
||||
matches: false,
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
});
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: mockMatchMedia,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach((): void => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("should use 'mosaic-theme' as storage key", (): void => {
|
||||
localStorage.setItem("mosaic-theme", "light");
|
||||
|
||||
render(
|
||||
<ThemeProvider>
|
||||
<ThemeConsumer />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("theme")).toHaveTextContent("light");
|
||||
});
|
||||
|
||||
it("should NOT read from old 'jarvis-theme' storage key", (): void => {
|
||||
localStorage.setItem("jarvis-theme", "light");
|
||||
|
||||
render(
|
||||
<ThemeProvider>
|
||||
<ThemeConsumer />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
// Should default to system, not read from jarvis-theme
|
||||
expect(screen.getByTestId("theme")).toHaveTextContent("system");
|
||||
});
|
||||
|
||||
it("should store theme under 'mosaic-theme' key", (): void => {
|
||||
render(
|
||||
<ThemeProvider>
|
||||
<ThemeConsumer />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
screen.getByText("Set Light").click();
|
||||
});
|
||||
|
||||
expect(localStorage.getItem("mosaic-theme")).toBe("light");
|
||||
expect(localStorage.getItem("jarvis-theme")).toBeNull();
|
||||
});
|
||||
|
||||
it("should render children", (): void => {
|
||||
render(
|
||||
<ThemeProvider>
|
||||
<div data-testid="child">Hello</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("child")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should throw when useTheme is used outside provider", (): void => {
|
||||
// Suppress console.error for expected error
|
||||
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {
|
||||
// Intentionally empty
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
render(<ThemeConsumer />);
|
||||
}).toThrow("useTheme must be used within a ThemeProvider");
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ interface ThemeContextValue {
|
||||
|
||||
const ThemeContext = createContext<ThemeContextValue | null>(null);
|
||||
|
||||
const STORAGE_KEY = "jarvis-theme";
|
||||
const STORAGE_KEY = "mosaic-theme";
|
||||
|
||||
function getSystemTheme(): "light" | "dark" {
|
||||
if (typeof window === "undefined") return "dark";
|
||||
|
||||
Reference in New Issue
Block a user