Full tests coverage

This commit is contained in:
Aerilyn Weber 2026-05-19 14:09:03 +09:00
parent 99134d8556
commit 02d782c3da
157 changed files with 1074 additions and 34670 deletions

View file

@ -20,12 +20,12 @@ vi.mock('next/link', () => ({
vi.mock('@/lib/useApi', () => ({ useApi: mockUseApi }));
import { Sidebar } from '../../src/components/layout/Sidebar';
import { Sidebar, NAV } from '../../src/components/layout/Sidebar';
describe('Sidebar', () => {
beforeEach(() => {
vi.clearAllMocks();
mockUsePathname.mockReturnValue('/dashboard');
mockUsePathname.mockReturnValue('/shopping-lists');
mockUseApi.mockReturnValue({ profile: { displayName: 'Alice' } });
});
@ -34,33 +34,17 @@ describe('Sidebar', () => {
expect(screen.getByText('MeshiTrack')).toBeInTheDocument();
});
it('renders navigation sections', () => {
it('renders active nav items', () => {
render(<Sidebar />);
expect(screen.getByText('Dashboard')).toBeInTheDocument();
expect(screen.getByText('Medicines')).toBeInTheDocument();
expect(screen.getByText('Food')).toBeInTheDocument();
expect(screen.getByText('Settings')).toBeInTheDocument();
});
it('renders medicines nav items', () => {
render(<Sidebar />);
expect(screen.getByText('Cabinet')).toBeInTheDocument();
expect(screen.getByText('Schedule & Log')).toBeInTheDocument();
expect(screen.getByText('Regimens')).toBeInTheDocument();
expect(screen.getByText('Library')).toBeInTheDocument();
});
it('renders food nav items', () => {
render(<Sidebar />);
expect(screen.getByText('Recipes')).toBeInTheDocument();
expect(screen.getByText('Pantry')).toBeInTheDocument();
expect(screen.getByText('Shopping Lists')).toBeInTheDocument();
expect(screen.getByText('Stores')).toBeInTheDocument();
});
it('highlights active route', () => {
mockUsePathname.mockReturnValue('/medicines/cabinet');
mockUsePathname.mockReturnValue('/shopping-lists');
render(<Sidebar />);
const cabinetLink = screen.getByText('Cabinet').closest('a');
expect(cabinetLink).toHaveAttribute('href', '/medicines/cabinet');
const link = screen.getByText('Shopping Lists').closest('a');
expect(link).toHaveAttribute('href', '/shopping-lists');
});
it('shows user avatar with display name', () => {
@ -75,10 +59,17 @@ describe('Sidebar', () => {
});
it('highlights nested route', () => {
mockUsePathname.mockReturnValue('/medicines/cabinet/some-id');
mockUsePathname.mockReturnValue('/shopping-lists/some-id');
render(<Sidebar />);
// Cabinet link should still match via startsWith
const cabinetLink = screen.getByText('Cabinet').closest('a');
expect(cabinetLink).toBeInTheDocument();
const link = screen.getByText('Shopping Lists').closest('a');
expect(link).toBeInTheDocument();
});
it('renders item badge when present', () => {
NAV.push({ id: 'test-badge', label: 'Test Badge', href: '/test-badge', icon: 'bell', badge: 5, section: 'Test Section' });
render(<Sidebar />);
expect(screen.getByText('5')).toBeInTheDocument();
expect(screen.getByText('Test Section')).toBeInTheDocument();
NAV.pop(); // Clean up NAV list mutation
});
});

View file

@ -1,8 +1,13 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
const themeMock = vi.hoisted(() => ({
theme: 'light',
toggleTheme: vi.fn(),
}));
vi.mock('@/components/ThemeProvider', () => ({
useTheme: () => ({ theme: 'light', toggleTheme: vi.fn() }),
useTheme: () => themeMock,
}));
vi.mock('@/components/layout/PageHeaderContext', () => ({
@ -39,4 +44,10 @@ describe('TopBar', () => {
render(<TopBar />);
expect(screen.getByLabelText('Toggle theme')).toBeInTheDocument();
});
it('renders sun icon when theme is dark', () => {
themeMock.theme = 'dark';
render(<TopBar />);
expect(screen.getByLabelText('Toggle theme')).toBeInTheDocument();
});
});

View file

@ -6,6 +6,7 @@ import { Ring } from '../../src/components/ui/Ring';
import { SparkBars } from '../../src/components/ui/SparkBars';
import { SupplyBar } from '../../src/components/ui/SupplyBar';
import { Card, CardHeader, CardBody } from '../../src/components/ui/Card';
import { Icon } from '../../src/components/ui/Icon';
describe('Avatar', () => {
it('renders initial from name', () => {
@ -19,6 +20,11 @@ describe('Avatar', () => {
const el = screen.getByLabelText('Bob');
expect(el).toHaveStyle({ width: '48px', height: '48px' });
});
it('falls back to ? for empty name', () => {
render(<Avatar name="" />);
expect(screen.getByText('?')).toBeInTheDocument();
});
});
describe('IconButton', () => {
@ -116,3 +122,17 @@ describe('Card', () => {
expect(screen.getByText('Body Content')).toBeInTheDocument();
});
});
describe('Icon', () => {
it('renders paths for valid icon', () => {
const { container } = render(<Icon name="bell" />);
expect(container.querySelector('svg')).toBeInTheDocument();
});
it('renders null for invalid icon name', () => {
const { container } = render(<Icon name="nonexistent" as any />);
const svg = container.querySelector('svg');
expect(svg).toBeInTheDocument();
expect(svg?.childNodes.length).toBe(0);
});
});