import { render, screen } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import { AppLayout } from './AppLayout'
import type { AuthContext } from '@/hooks/useAuth'
// Mock ResizeObserver for sidebar tests
globalThis.ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
} as any
// Mock matchMedia for use-mobile hook used by SidebarProvider
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
})
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key }),
}))
vi.mock('react-router-dom', () => ({
Link: ({ children, to, ...props }: any) => (
{children}
),
useLocation: () => ({ pathname: '/' }),
}))
const mockAuth: AuthContext = {
user: { display_name: 'Test' } as any,
loading: false,
login: vi.fn(),
register: vi.fn(),
logout: vi.fn(),
token: 'test',
refetch: vi.fn(),
} as unknown as AuthContext
describe('AppLayout', () => {
it('NAV-01: sidebar element renders with distinct background', () => {
render(
content
)
// The sidebar renders with data-sidebar="sidebar" -- bg-sidebar token provides pastel background
const sidebar = document.querySelector('[data-sidebar="sidebar"]')
expect(sidebar).toBeInTheDocument()
})
it('NAV-02: gradient wordmark renders in sidebar header', () => {
render(
content
)
const wordmark = screen.getByTestId('sidebar-wordmark')
expect(wordmark).toBeInTheDocument()
})
it('NAV-03: active nav item (dashboard at /) has data-active="true"', () => {
render(
content
)
const dashboardLink = screen.getByRole('link', { name: /nav\.dashboard/i })
// SidebarMenuButton sets data-active on itself; it wraps the Link via asChild
const menuButton = dashboardLink.closest('[data-active]')
expect(menuButton).toHaveAttribute('data-active', 'true')
})
it('NAV-04: SidebarTrigger collapse button is rendered', () => {
render(
content
)
// SidebarTrigger renders with sr-only text "Toggle Sidebar"
const trigger = screen.getByRole('button', { name: /toggle sidebar/i })
expect(trigger).toBeInTheDocument()
})
})