/** * Tests for AppLayout and related components * Verifies layout structure, responsive behavior, and component integration */ import { render, screen } from '@testing-library/react'; import { AppLayout, PageContainer, PageHeader } from '@/components/layout/AppLayout'; import { useAuth } from '@/lib/auth/AuthContext'; import { useLogout } from '@/lib/api/hooks/useAuth'; import { mockUsePathname } from 'next-intl/navigation'; 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('AppLayout', () => { beforeEach(() => { jest.clearAllMocks(); mockUsePathname.mockReturnValue('/projects'); (useAuth as unknown as jest.Mock).mockReturnValue({ user: createMockUser(), }); (useLogout as jest.Mock).mockReturnValue({ mutate: jest.fn(), isPending: false, }); }); describe('Rendering', () => { it('renders layout container', () => { render(
Content
); expect(screen.getByTestId('app-layout')).toBeInTheDocument(); }); it('renders children', () => { render(
Test Content
); expect(screen.getByTestId('test-content')).toBeInTheDocument(); }); it('renders header', () => { render(
Content
); expect(screen.getByTestId('app-header')).toBeInTheDocument(); }); it('renders sidebar', () => { render(
Content
); expect(screen.getByTestId('sidebar')).toBeInTheDocument(); }); it('renders breadcrumbs', () => { render(
Content
); expect(screen.getByTestId('breadcrumbs')).toBeInTheDocument(); }); it('renders main content area', () => { render(
Content
); const main = screen.getByRole('main'); expect(main).toBeInTheDocument(); expect(main).toHaveAttribute('id', 'main-content'); }); }); describe('Configuration Options', () => { it('hides sidebar when hideSidebar is true', () => { render(
Content
); expect(screen.queryByTestId('sidebar')).not.toBeInTheDocument(); }); it('hides breadcrumbs when hideBreadcrumbs is true', () => { render(
Content
); expect(screen.queryByTestId('breadcrumbs')).not.toBeInTheDocument(); }); it('passes custom breadcrumbs to AppBreadcrumbs', () => { const customBreadcrumbs = [ { label: 'Custom', href: '/custom', current: true }, ]; render(
Content
); expect(screen.getByTestId('breadcrumb-custom')).toBeInTheDocument(); }); it('passes project slug to sidebar', () => { const currentProject = { id: '1', slug: 'test-project', name: 'Test' }; render(
Content
); // Sidebar should show project-specific navigation expect(screen.getByTestId('nav-dashboard')).toBeInTheDocument(); }); }); describe('Custom ClassNames', () => { it('applies className to main content', () => { render(
Content
); const main = screen.getByRole('main'); expect(main).toHaveClass('custom-main'); }); it('applies wrapperClassName to outer container', () => { render(
Content
); const layout = screen.getByTestId('app-layout'); expect(layout).toHaveClass('custom-wrapper'); }); }); describe('Accessibility', () => { it('main content has tabIndex for skip link support', () => { render(
Content
); const main = screen.getByRole('main'); expect(main).toHaveAttribute('tabIndex', '-1'); }); it('layout has min-h-screen for full viewport', () => { render(
Content
); const layout = screen.getByTestId('app-layout'); expect(layout).toHaveClass('min-h-screen'); }); }); }); describe('PageContainer', () => { describe('Rendering', () => { it('renders container', () => { render(
Content
); expect(screen.getByTestId('page-container')).toBeInTheDocument(); }); it('renders children', () => { render(
Test Content
); expect(screen.getByTestId('test-content')).toBeInTheDocument(); }); }); describe('Max Width', () => { it('defaults to max-w-6xl', () => { render(
Content
); const container = screen.getByTestId('page-container'); expect(container).toHaveClass('max-w-6xl'); }); it('applies custom max width', () => { render(
Content
); const container = screen.getByTestId('page-container'); expect(container).toHaveClass('max-w-md'); }); it('applies full width', () => { render(
Content
); const container = screen.getByTestId('page-container'); expect(container).toHaveClass('max-w-full'); }); }); describe('Styling', () => { it('has container and centering classes', () => { render(
Content
); const container = screen.getByTestId('page-container'); expect(container).toHaveClass('container', 'mx-auto'); }); it('has responsive padding', () => { render(
Content
); const container = screen.getByTestId('page-container'); expect(container).toHaveClass('px-4', 'py-6', 'lg:px-6', 'lg:py-8'); }); it('applies custom className', () => { render(
Content
); const container = screen.getByTestId('page-container'); expect(container).toHaveClass('custom-class'); }); }); }); describe('PageHeader', () => { describe('Rendering', () => { it('renders page header', () => { render(); expect(screen.getByTestId('page-header')).toBeInTheDocument(); }); it('renders title', () => { render(); expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('Test Title'); }); it('renders description when provided', () => { render(); expect(screen.getByText('Test description')).toBeInTheDocument(); }); it('does not render description when not provided', () => { render(); const header = screen.getByTestId('page-header'); expect(header.querySelectorAll('p')).toHaveLength(0); }); it('renders actions when provided', () => { render( Action} /> ); expect(screen.getByTestId('action-button')).toBeInTheDocument(); }); }); describe('Styling', () => { it('has responsive flex layout', () => { render(); const header = screen.getByTestId('page-header'); expect(header).toHaveClass('flex', 'flex-col', 'sm:flex-row'); }); it('title has responsive text size', () => { render(); const title = screen.getByRole('heading', { level: 1 }); expect(title).toHaveClass('text-2xl', 'sm:text-3xl'); }); it('description has muted styling', () => { render(); const description = screen.getByText('Description'); expect(description).toHaveClass('text-muted-foreground'); }); it('applies custom className', () => { render(); const header = screen.getByTestId('page-header'); expect(header).toHaveClass('custom-class'); }); }); });