- Add chart palette, motion, and z-index CSS vars to globals.css - Replace custom theme-provider with next-themes (dark default) - Extend tailwind.config.ts with heading fonts, spacing (row-compact, row-roomy, sidebar), chart colors, elevation shadows, glow shadows, transition timing, pill border-radius, z-index scale - Update tick-flash animations to match design token spec (480ms) - Add prefers-reduced-motion support for all animations - Create base design-system components: Surface, SurfaceElevated, Divider, DensityProvider/useDensity, Numeric (VND/percent/compact formatting), Signal (up/down/neutral pill) - Add dev-only /dev/tokens showcase route (404 in production) - Update theme-provider tests to match next-themes integration Co-Authored-By: Paperclip <noreply@paperclip.ing>
93 lines
2.2 KiB
TypeScript
93 lines
2.2 KiB
TypeScript
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { ThemeProvider, useTheme } from '../theme-provider';
|
|
|
|
// Mock next-themes
|
|
const mockSetTheme = vi.fn();
|
|
let mockTheme = 'dark';
|
|
let mockResolvedTheme = 'dark';
|
|
|
|
vi.mock('next-themes', () => ({
|
|
ThemeProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
useTheme: () => ({
|
|
theme: mockTheme,
|
|
resolvedTheme: mockResolvedTheme,
|
|
setTheme: (t: string) => {
|
|
mockSetTheme(t);
|
|
mockTheme = t;
|
|
mockResolvedTheme = t;
|
|
},
|
|
}),
|
|
}));
|
|
|
|
// Test consumer component
|
|
function ThemeConsumer() {
|
|
const { theme, toggleTheme } = useTheme();
|
|
return (
|
|
<div>
|
|
<span data-testid="theme">{theme}</span>
|
|
<button onClick={toggleTheme}>Toggle</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
describe('ThemeProvider', () => {
|
|
beforeEach(() => {
|
|
mockTheme = 'dark';
|
|
mockResolvedTheme = 'dark';
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('renders children', () => {
|
|
render(
|
|
<ThemeProvider>
|
|
<div>Child content</div>
|
|
</ThemeProvider>,
|
|
);
|
|
expect(screen.getByText('Child content')).toBeInTheDocument();
|
|
});
|
|
|
|
it('defaults to dark theme', () => {
|
|
render(
|
|
<ThemeProvider>
|
|
<ThemeConsumer />
|
|
</ThemeProvider>,
|
|
);
|
|
expect(screen.getByTestId('theme')).toHaveTextContent('dark');
|
|
});
|
|
|
|
it('toggles theme to light', async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<ThemeProvider>
|
|
<ThemeConsumer />
|
|
</ThemeProvider>,
|
|
);
|
|
|
|
await user.click(screen.getByText('Toggle'));
|
|
expect(mockSetTheme).toHaveBeenCalledWith('light');
|
|
});
|
|
|
|
it('toggles theme back to dark', async () => {
|
|
mockTheme = 'light';
|
|
mockResolvedTheme = 'light';
|
|
const user = userEvent.setup();
|
|
render(
|
|
<ThemeProvider>
|
|
<ThemeConsumer />
|
|
</ThemeProvider>,
|
|
);
|
|
|
|
await user.click(screen.getByText('Toggle'));
|
|
expect(mockSetTheme).toHaveBeenCalledWith('dark');
|
|
});
|
|
});
|
|
|
|
describe('useTheme', () => {
|
|
it('returns dark as default outside provider', () => {
|
|
render(<ThemeConsumer />);
|
|
expect(screen.getByTestId('theme')).toHaveTextContent('dark');
|
|
});
|
|
});
|