import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { MemoryRouter } from 'react-router-dom' import { Layout } from './Layout' import { useAuth } from '@/context/AuthContext' import { useWorkspace } from '@/context/WorkspaceContext' // Layout imports useAuth/useWorkspace directly from their modules (not the // @/context barrel), so those exact modules must be mocked here. vi.mock('@/context/AuthContext') vi.mock('@/context/WorkspaceContext') const mockNavigate = vi.fn() vi.mock('react-router-dom', async () => { const actual = await vi.importActual('react-router-dom') return { ...actual, useNavigate: () => mockNavigate, } }) const mockUseAuth = vi.mocked(useAuth) const mockUseWorkspace = vi.mocked(useWorkspace) function renderLayout(children =
page content
) { return render( {children} , ) } describe('Layout', () => { beforeEach(() => { vi.clearAllMocks() mockUseAuth.mockReturnValue({ accessToken: 'token', isAuthenticated: true, isLoading: false, login: vi.fn(), register: vi.fn(), logout: vi.fn().mockResolvedValue(undefined), }) mockUseWorkspace.mockReturnValue({ workspace: null, isLoading: false, error: null, workspaceId: 'ws1', setWorkspaceId: vi.fn(), refetch: vi.fn(), children: [], selectedChildId: null, selectChild: vi.fn(), personalDataUuid: null, parentName: null, refreshContext: vi.fn(), planVersion: 0, bumpPlanVersion: vi.fn(), }) }) it('renders children inside the main content area', () => { renderLayout(
hello
) expect(screen.getByTestId('layout-main')).toContainElement(screen.getByTestId('page-content')) }) it('renders Header and Footer', () => { renderLayout() expect(screen.getByTestId('header')).toBeInTheDocument() expect(screen.getByTestId('footer')).toBeInTheDocument() }) it('passes parent name and children list from workspace context to Header', () => { mockUseWorkspace.mockReturnValue({ workspace: null, isLoading: false, error: null, workspaceId: 'ws1', setWorkspaceId: vi.fn(), refetch: vi.fn(), children: [ { id: 'c1', name: 'Emma', birth_date: '2024-01-01' }, { id: 'c2', name: 'Oscar', birth_date: '2025-01-01' }, ], selectedChildId: 'c1', selectChild: vi.fn(), personalDataUuid: 'pd1', parentName: 'Elin', refreshContext: vi.fn(), planVersion: 0, bumpPlanVersion: vi.fn(), }) renderLayout() expect(screen.getByTestId('parent-name')).toHaveTextContent('Elin') expect(screen.getByTestId('child-select')).toBeInTheDocument() expect(screen.getByDisplayValue('Emma')).toBeInTheDocument() }) it('selects the correct child when selectedChildId matches', () => { const selectChild = vi.fn() mockUseWorkspace.mockReturnValue({ workspace: null, isLoading: false, error: null, workspaceId: 'ws1', setWorkspaceId: vi.fn(), refetch: vi.fn(), children: [{ id: 'c1', name: 'Emma', birth_date: '2024-01-01' }], selectedChildId: 'c1', selectChild, personalDataUuid: 'pd1', parentName: null, refreshContext: vi.fn(), planVersion: 0, bumpPlanVersion: vi.fn(), }) renderLayout() expect(screen.getByDisplayValue('Emma')).toBeInTheDocument() }) it('logs out and navigates to /auth when the logout button is clicked', async () => { const logout = vi.fn().mockResolvedValue(undefined) mockUseAuth.mockReturnValue({ accessToken: 'token', isAuthenticated: true, isLoading: false, login: vi.fn(), register: vi.fn(), logout, }) renderLayout() await userEvent.click(screen.getByTestId('logout-button')) expect(logout).toHaveBeenCalled() expect(mockNavigate).toHaveBeenCalledWith('/auth', { replace: true }) }) it('still navigates to /auth when logout rejects (S3 — best-effort server call)', async () => { const logout = vi.fn().mockRejectedValue(new Error('network down')) mockUseAuth.mockReturnValue({ accessToken: 'token', isAuthenticated: true, isLoading: false, login: vi.fn(), register: vi.fn(), logout, }) renderLayout() await userEvent.click(screen.getByTestId('logout-button')) expect(logout).toHaveBeenCalled() expect(mockNavigate).toHaveBeenCalledWith('/auth', { replace: true }) }) })