/** * Tests for AppHeader Component * Verifies header rendering, project switcher integration, and responsive behavior */ import { render, screen } from '@testing-library/react'; import { AppHeader } from '@/components/layout/AppHeader'; import { useAuth } from '@/lib/auth/AuthContext'; import { useLogout } from '@/lib/api/hooks/useAuth'; import type { User } from '@/lib/stores/authStore'; // Mock dependencies jest.mock('@/lib/auth/AuthContext', () => ({ useAuth: jest.fn(), AuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}, })); jest.mock('@/lib/api/hooks/useAuth', () => ({ useLogout: jest.fn(), })); jest.mock('@/components/theme', () => ({ ThemeToggle: () =>
Theme Toggle
, })); jest.mock('@/components/i18n', () => ({ LocaleSwitcher: () =>
Locale Switcher
, })); // Helper to create mock user function createMockUser(overrides: Partial = {}): User { return { id: 'user-123', email: 'test@example.com', first_name: 'Test', last_name: 'User', phone_number: null, is_active: true, is_superuser: false, created_at: new Date().toISOString(), updated_at: null, ...overrides, }; } describe('AppHeader', () => { const mockProjects = [ { id: '1', slug: 'project-one', name: 'Project One' }, { id: '2', slug: 'project-two', name: 'Project Two' }, ]; beforeEach(() => { jest.clearAllMocks(); (useAuth as unknown as jest.Mock).mockReturnValue({ user: createMockUser(), }); (useLogout as jest.Mock).mockReturnValue({ mutate: jest.fn(), isPending: false, }); }); describe('Rendering', () => { it('renders header element', () => { render(); expect(screen.getByTestId('app-header')).toBeInTheDocument(); }); it('renders with sticky positioning', () => { render(); const header = screen.getByTestId('app-header'); expect(header).toHaveClass('sticky', 'top-0'); }); it('renders theme toggle', () => { render(); expect(screen.getByTestId('theme-toggle')).toBeInTheDocument(); }); it('renders locale switcher', () => { render(); // LocaleSwitcher renders a button with aria-label="switchLanguage" expect(screen.getByRole('button', { name: /switchLanguage/i })).toBeInTheDocument(); }); it('renders user menu', () => { render(); expect(screen.getByTestId('user-menu-trigger')).toBeInTheDocument(); }); }); describe('Logo', () => { it('renders logo on mobile', () => { render(); const logoLink = screen.getByRole('link', { name: /syndarix home/i }); expect(logoLink).toBeInTheDocument(); expect(logoLink).toHaveAttribute('href', '/'); }); it('contains syndarix text', () => { render(); expect(screen.getByText('Syndarix')).toBeInTheDocument(); }); }); describe('Project Switcher', () => { it('renders project switcher when projects are provided', () => { render(); // Multiple switchers may render for desktop/mobile - just check at least one exists expect(screen.getAllByTestId('project-switcher-trigger').length).toBeGreaterThan(0); }); it('does not render project switcher when no projects', () => { render(); expect(screen.queryByTestId('project-switcher-trigger')).not.toBeInTheDocument(); }); it('displays current project name', () => { render( ); // Multiple instances may show the project name expect(screen.getAllByText('Project One').length).toBeGreaterThan(0); }); it('calls onProjectChange when project is changed', async () => { const mockOnChange = jest.fn(); render( ); // The actual test of project switching is in ProjectSwitcher.test.tsx // Here we just verify the prop is passed by checking switcher exists expect(screen.getAllByTestId('project-switcher-trigger').length).toBeGreaterThan(0); }); }); describe('Accessibility', () => { it('header has proper element type', () => { render(); const header = screen.getByTestId('app-header'); expect(header.tagName).toBe('HEADER'); }); it('logo link is accessible', () => { render(); const logoLink = screen.getByRole('link', { name: /syndarix home/i }); expect(logoLink).toBeInTheDocument(); }); }); describe('Custom className', () => { it('applies custom className', () => { render(); const header = screen.getByTestId('app-header'); expect(header).toHaveClass('custom-class'); }); }); });