MeshiTrack/packages/web/tests/components/Sidebar.test.tsx

76 lines
2.3 KiB
TypeScript
Raw Normal View History

2026-05-14 19:59:42 +09:00
import { describe, it, expect, vi, beforeEach } from 'vitest';
2026-05-14 14:47:23 +09:00
import { render, screen } from '@testing-library/react';
const { mockUsePathname } = vi.hoisted(() => ({
mockUsePathname: vi.fn(),
}));
const { mockUseApi } = vi.hoisted(() => ({
mockUseApi: vi.fn(),
}));
vi.mock('next/navigation', () => ({
usePathname: mockUsePathname,
}));
vi.mock('next/link', () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
default: (props: any) => <a href={props.href}>{props.children}</a>,
}));
vi.mock('@/lib/useApi', () => ({ useApi: mockUseApi }));
2026-05-19 14:09:03 +09:00
import { Sidebar, NAV } from '../../src/components/layout/Sidebar';
2026-05-14 14:47:23 +09:00
describe('Sidebar', () => {
beforeEach(() => {
vi.clearAllMocks();
2026-05-19 14:09:03 +09:00
mockUsePathname.mockReturnValue('/shopping-lists');
2026-05-14 14:47:23 +09:00
mockUseApi.mockReturnValue({ profile: { displayName: 'Alice' } });
});
it('renders brand name', () => {
render(<Sidebar />);
expect(screen.getByText('MeshiTrack')).toBeInTheDocument();
});
2026-05-19 14:09:03 +09:00
it('renders active nav items', () => {
2026-05-14 14:47:23 +09:00
render(<Sidebar />);
2026-05-19 14:09:03 +09:00
expect(screen.getByText('Shopping Lists')).toBeInTheDocument();
expect(screen.getByText('Stores')).toBeInTheDocument();
2026-05-14 14:47:23 +09:00
});
it('highlights active route', () => {
2026-05-19 14:09:03 +09:00
mockUsePathname.mockReturnValue('/shopping-lists');
2026-05-14 14:47:23 +09:00
render(<Sidebar />);
2026-05-19 14:09:03 +09:00
const link = screen.getByText('Shopping Lists').closest('a');
expect(link).toHaveAttribute('href', '/shopping-lists');
2026-05-14 14:47:23 +09:00
});
it('shows user avatar with display name', () => {
render(<Sidebar />);
expect(screen.getByLabelText('Alice')).toBeInTheDocument();
});
it('shows fallback name when no profile', () => {
mockUseApi.mockReturnValue({ profile: null });
render(<Sidebar />);
expect(screen.getByLabelText('User')).toBeInTheDocument();
});
it('highlights nested route', () => {
2026-05-19 14:09:03 +09:00
mockUsePathname.mockReturnValue('/shopping-lists/some-id');
render(<Sidebar />);
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' });
2026-05-14 14:47:23 +09:00
render(<Sidebar />);
2026-05-19 14:09:03 +09:00
expect(screen.getByText('5')).toBeInTheDocument();
expect(screen.getByText('Test Section')).toBeInTheDocument();
NAV.pop(); // Clean up NAV list mutation
2026-05-14 14:47:23 +09:00
});
});