Sergei 1628501e75 Add Layout components for web application
Implemented responsive layout system with:
- Header: Top navigation with profile menu and mobile hamburger
- Sidebar: Desktop-only navigation sidebar (lg and above)
- Breadcrumbs: Auto-generated navigation breadcrumbs
- Layout: Main wrapper component with configurable options

Features:
- Responsive design (mobile, tablet, desktop)
- Active route highlighting
- User profile integration via auth store
- Click-outside dropdown closing
- Comprehensive test coverage (49 tests passing)

Updated (main) layout to use new Layout component system.
Updated dashboard page to work with new layout structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 18:20:13 -08:00

97 lines
2.8 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import { Sidebar } from '../Sidebar';
import { useAuthStore } from '@/stores/authStore';
import { usePathname } from 'next/navigation';
// Mock Next.js navigation
jest.mock('next/navigation', () => ({
usePathname: jest.fn(),
}));
// Mock auth store
jest.mock('@/stores/authStore', () => ({
useAuthStore: jest.fn(),
}));
describe('Sidebar Component', () => {
const mockLogout = jest.fn();
const mockUser = {
user_id: 1,
email: 'test@example.com',
firstName: 'Jane',
lastName: 'Smith',
};
beforeEach(() => {
jest.clearAllMocks();
(usePathname as jest.Mock).mockReturnValue('/dashboard');
(useAuthStore as unknown as jest.Mock).mockReturnValue({
user: mockUser,
logout: mockLogout,
});
});
it('renders the WellNuo logo', () => {
render(<Sidebar />);
expect(screen.getByText('WellNuo')).toBeInTheDocument();
});
it('renders all navigation items', () => {
render(<Sidebar />);
expect(screen.getByText('Dashboard')).toBeInTheDocument();
expect(screen.getByText('Loved Ones')).toBeInTheDocument();
expect(screen.getByText('Sensors')).toBeInTheDocument();
expect(screen.getByText('Settings')).toBeInTheDocument();
});
it('highlights active navigation item', () => {
render(<Sidebar />);
const dashboardLink = screen.getByText('Dashboard').closest('a');
expect(dashboardLink).toHaveClass('bg-blue-50', 'text-blue-700');
});
it('highlights active item for nested routes', () => {
(usePathname as jest.Mock).mockReturnValue('/beneficiaries/123');
render(<Sidebar />);
const beneficiariesLink = screen.getByText('Loved Ones').closest('a');
expect(beneficiariesLink).toHaveClass('bg-blue-50', 'text-blue-700');
});
it('displays user profile with initials', () => {
render(<Sidebar />);
expect(screen.getByText('JS')).toBeInTheDocument();
});
it('displays user full name', () => {
render(<Sidebar />);
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
it('displays user email', () => {
render(<Sidebar />);
expect(screen.getByText('test@example.com')).toBeInTheDocument();
});
it('has a link to profile page', () => {
render(<Sidebar />);
const profileLink = screen.getByText('Jane Smith').closest('a');
expect(profileLink).toHaveAttribute('href', '/profile');
});
it('renders sign out button', () => {
render(<Sidebar />);
expect(screen.getByText('Sign Out')).toBeInTheDocument();
});
it('displays email when no first/last name', () => {
(useAuthStore as unknown as jest.Mock).mockReturnValue({
user: { user_id: 1, email: 'user@example.com' },
logout: mockLogout,
});
render(<Sidebar />);
expect(screen.getAllByText('user@example.com')).toHaveLength(2);
});
});