import type { ReactNode } from 'react' import type { Mock } from 'vite-plus/test' import { fireEvent, screen } from '@testing-library/react' import { useStore as useAppStore } from '@/app/components/app/store' import { isAgentV2Enabled } from '@/features/agent-v2/feature-flag' import { usePathname } from '@/next/navigation' import { render } from '@/test/console/render' import MainNavLayout from '../layout' const mockConsoleState = vi.hoisted(() => ({ current: { isCurrentWorkspaceDatasetOperator: false, }, })) vi.mock('@/app/components/header', () => ({ default: () =>
Header
, })) vi.mock('@/app/components/header/header-wrapper', () => ({ default: ({ children }: { children: ReactNode }) => (
{children}
), })) vi.mock('@/context/workspace-state', async () => { const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture') return createWorkspaceStateModuleMock(() => mockConsoleState.current) }) vi.mock('@/features/agent-v2/feature-flag', () => ({ isAgentV2Enabled: vi.fn(), })) vi.mock('@/next/navigation', async (importOriginal) => { const actual = await importOriginal() return { ...actual, usePathname: vi.fn(), } }) vi.mock('../index', () => ({ MainNav: ({ className }: { className?: string }) => ( ), })) describe('MainNavLayout', () => { beforeEach(() => { vi.clearAllMocks() localStorage.clear() useAppStore.getState().setAppDetail() ;(usePathname as Mock).mockReturnValue('/apps') mockConsoleState.current = { isCurrentWorkspaceDatasetOperator: false, } ;(isAgentV2Enabled as Mock).mockReturnValue(true) }) it('renders desktop main nav instead of the desktop header', () => { render(
content
, ) expect(screen.getByTestId('main-nav')).toBeInTheDocument() expect(screen.queryByTestId('desktop-header')).not.toBeInTheDocument() expect(screen.getByText('content')).toBeInTheDocument() }) it('uses the main nav without the desktop header wrapper', () => { render(
content
, ) expect(screen.getByTestId('main-nav')).toBeInTheDocument() expect(screen.queryByTestId('header-wrapper')).not.toBeInTheDocument() expect(screen.queryByTestId('desktop-header')).not.toBeInTheDocument() }) it('renders one main landmark as the skip navigation target', () => { render(
content
, ) const main = screen.getByRole('main') expect(screen.getAllByRole('main')).toHaveLength(1) expect(main).toHaveAttribute('id', 'main-content') expect(main).toHaveAttribute('tabIndex', '-1') expect(main).toHaveClass( 'outline-hidden', 'focus:outline-hidden', 'focus-visible:outline-hidden', ) expect(main).toHaveTextContent('content') }) it('renders skip navigation before the repeated main navigation', () => { const { container } = render(
content
, ) const skipLink = screen.getByRole('link', { name: /(?:^|\.)navigation\.skipToMain(?=$|:)/ }) expect(skipLink).toHaveAttribute('href', '#main-content') expect(skipLink).toHaveClass( 'outline-hidden', 'focus-visible:ring-2', 'focus-visible:ring-state-accent-solid', ) expect( container.querySelector( 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])', ), ).toBe(skipLink) }) it('moves focus to the main content when skip navigation is activated', () => { render(
content
, ) const skipLink = screen.getByRole('link', { name: /(?:^|\.)navigation\.skipToMain(?=$|:)/ }) const main = screen.getByRole('main') fireEvent.click(skipLink) expect(main).toHaveFocus() }) it.each(['/datasets/dataset-1/documents', '/datasets/dataset-1/documents/document-1/settings'])( 'renders the detail sidebar slot outside the single skip navigation target on route %s', (pathname) => { ;(usePathname as Mock).mockReturnValue(pathname) render( Detail sidebar}>
dataset detail
, ) const main = screen.getByRole('main') const detailSidebar = screen.getByRole('complementary', { name: 'Detail sidebar' }) expect(screen.queryByTestId('main-nav')).not.toBeInTheDocument() expect(screen.getAllByRole('main')).toHaveLength(1) expect(main).toHaveAttribute('id', 'main-content') expect(main).toHaveTextContent('dataset detail') expect(main).not.toContainElement(detailSidebar) }, ) it('ignores a retained legacy detail sidebar on New Knowledge routes', () => { ;(usePathname as Mock).mockReturnValue('/datasets/new/knowledge-1/sources') render( Legacy dataset sidebar} >
new knowledge detail
, ) expect(screen.queryByTestId('main-nav')).not.toBeInTheDocument() expect( screen.queryByRole('complementary', { name: 'Legacy dataset sidebar' }), ).not.toBeInTheDocument() expect(screen.getByRole('main')).toHaveTextContent('new knowledge detail') }) it('hides the global main nav on a skill detail route', () => { ;(usePathname as Mock).mockReturnValue('/skills/skill-1') render(
skill detail
, ) expect(screen.queryByTestId('main-nav')).not.toBeInTheDocument() expect(screen.getByRole('main')).toHaveTextContent('skill detail') }) it('keeps the global main nav on the skills collection route', () => { ;(usePathname as Mock).mockReturnValue('/skills') render(
skills collection
, ) expect(screen.getByTestId('main-nav')).toBeInTheDocument() }) it.each(['/datasets/create', '/datasets/new/create', '/datasets/dataset-1/documents/create'])( 'keeps the global main nav on collection and creation route %s', (pathname) => { ;(usePathname as Mock).mockReturnValue(pathname) render( Detail sidebar}>
content
, ) expect(screen.getByTestId('main-nav')).toBeInTheDocument() expect( screen.queryByRole('complementary', { name: 'Detail sidebar' }), ).not.toBeInTheDocument() }, ) it('keeps the global main nav on agent detail routes for dataset operators', () => { ;(usePathname as Mock).mockReturnValue('/agents/agent-1/configure') mockConsoleState.current = { isCurrentWorkspaceDatasetOperator: true, } render( Detail sidebar}>
detail route content
, ) expect(screen.getByTestId('main-nav')).toBeInTheDocument() expect(screen.queryByRole('complementary', { name: 'Detail sidebar' })).not.toBeInTheDocument() expect(screen.getAllByRole('main')).toHaveLength(1) expect(screen.getByRole('main')).toHaveTextContent('detail route content') }) it('clears app detail state after leaving app routes', () => { useAppStore .getState() .setAppDetail({ id: 'app-1' } as ReturnType['appDetail']) ;(usePathname as Mock).mockReturnValue('/datasets') render(
content
, ) expect(useAppStore.getState().appDetail).toBeUndefined() }) })