// REQ-USR-003: AppLayout 外壳装配 + 标签↔路由同步(ready / navOverlayOpen / tabOpen 态) import { describe, it, expect } from 'vitest'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { Routes, Route, useLocation } from 'react-router-dom'; import { renderShell } from './renderShell'; import AppLayout from '../../src/layouts/AppLayout/AppLayout'; import type { AuthUser } from '../../src/api/types'; const ADMIN: AuthUser = { id: 1, sUserName: '朱子纯', sUserType: '超级管理员', sLanguage: '中文' }; function LocProbe() { const loc = useLocation(); return
{loc.pathname}
; } function renderLayout(initialEntries: string[]) { return renderShell( }>
home-outlet
} />
users-outlet
} />
, { initialEntries, preloadedAuth: { token: 't', user: ADMIN } }, ); } describe('AppLayout shell', () => { it('renders TopBar + Outlet when ready', () => { renderLayout(['/']); // 顶栏(全部导航 + 当前用户) expect(screen.getByRole('button', { name: '全部导航' })).toBeInTheDocument(); expect(screen.getByText('朱子纯(超级管理员)')).toBeInTheDocument(); // Outlet 子内容 expect(screen.getByTestId('home-outlet')).toBeInTheDocument(); }); it('toggle 全部导航 opens/closes overlay', async () => { renderLayout(['/']); expect(screen.queryByTestId('nav-overlay')).not.toBeInTheDocument(); await userEvent.click(screen.getByRole('button', { name: '全部导航' })); expect(screen.getByTestId('nav-overlay')).toBeInTheDocument(); // 点遮罩关闭 await userEvent.click(screen.getByTestId('nav-overlay-mask')); expect(screen.queryByTestId('nav-overlay')).not.toBeInTheDocument(); }); it('nav overlay 用户列表 navigates and opens tab', async () => { renderLayout(['/']); await userEvent.click(screen.getByRole('button', { name: '全部导航' })); await userEvent.click(screen.getByRole('button', { name: /用户列表/ })); // overlay 关闭 expect(screen.queryByTestId('nav-overlay')).not.toBeInTheDocument(); // URL 到 /usr/users expect(screen.getByTestId('loc').textContent).toBe('/usr/users'); // 顶栏出现「用户列表」标签并激活 const tab = screen.getByTestId('tab-userlist'); expect(tab).toHaveTextContent('用户列表'); expect(tab.getAttribute('aria-pressed')).toBe('true'); }); it('clicking home tab navigates back to /', async () => { renderLayout(['/']); // 先打开用户列表标签 await userEvent.click(screen.getByRole('button', { name: '全部导航' })); await userEvent.click(screen.getByRole('button', { name: /用户列表/ })); expect(screen.getByTestId('loc').textContent).toBe('/usr/users'); // 点主页标签回 / await userEvent.click(screen.getByTestId('tab-home')); expect(screen.getByTestId('loc').textContent).toBe('/'); expect(screen.getByTestId('tab-home').getAttribute('aria-pressed')).toBe('true'); }); it('active tab syncs with current route', () => { renderLayout(['/usr/users']); // 直接进 /usr/users,用户列表标签应存在且激活 const tab = screen.getByTestId('tab-userlist'); expect(tab.getAttribute('aria-pressed')).toBe('true'); }); });