import { App as AntApp, ConfigProvider } from 'antd'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MemoryRouter, Route, Routes, useLocation } from 'react-router-dom'; import { describe, expect, it, vi } from 'vitest'; import { AppContext } from '../../src/frontend/app-context'; import { I18nProvider } from '../../src/frontend/i18n/react'; import { mailhubTheme } from '../../src/frontend/theme'; import { AdminLayout, navigationSelection, visibleNavigation } from '../../src/layouts/AdminLayout'; import type { AppContextValue } from '../../src/frontend/app-context'; describe('AdminLayout navigation', () => { it('hides all system management destinations from a normal user', () => { const paths = visibleNavigation(false).flatMap((group) => group.items.map((item) => item.path)); expect(paths).not.toContain('/admin/users'); expect(paths).not.toContain('/settings'); }); it('maps detail routes back to their owning navigation item', () => { expect(navigationSelection('/activity/42')).toBe('/activity'); expect(navigationSelection('/domains/7/dns')).toBe('/domains'); expect(navigationSelection('/inbox/messages/9')).toBe('/inbox'); }); it('navigates with semantic menu items and exposes a skip link', async () => { const user = userEvent.setup(); renderShell('/overview', { role: 'admin' }); expect(screen.getByRole('link', { name: '跳至主要内容' }).getAttribute('href')).toBe('#main-content'); const activityItems = screen.getAllByText('发送活动'); await user.click(activityItems[0]); expect(screen.getByTestId('location').textContent).toBe('/activity'); }); it('provides an explicit mobile navigation escape action', async () => { const user = userEvent.setup(); renderShell('/overview', { role: 'user' }); await user.click(screen.getByRole('button', { name: '打开主导航' })); const close = await screen.findByRole('button', { name: '关闭主导航' }); expect(close.style.minHeight).toBe('44px'); expect(close.style.minWidth).toBe('44px'); await user.click(close); await waitFor(() => expect(screen.queryByRole('button', { name: '关闭主导航' })).toBeNull()); }); it('keeps mobile navigation available between Ant Design lg and the 1024px product breakpoint', async () => { vi.spyOn(window, 'matchMedia').mockImplementation((query) => mediaQueryList(query === '(min-width: 992px)', query)); const user = userEvent.setup(); renderShell('/overview', { role: 'user' }); await user.click(screen.getByRole('button', { name: '打开主导航' })); expect(await screen.findByRole('button', { name: '关闭主导航' })).toBeTruthy(); }); }); function mediaQueryList(matches: boolean, media: string): MediaQueryList { return { matches, media, onchange: null, addListener: () => undefined, removeListener: () => undefined, addEventListener: () => undefined, removeEventListener: () => undefined, dispatchEvent: () => false }; } function renderShell(path: string, { role }: { role: 'admin' | 'user' }) { const context: AppContextValue = { user: { id: 1, username: 'operator', email: 'operator@example.test', role, status: 'active' }, config: { appBaseUrl: 'https://mail.example.test', mailHostname: 'mail.example.test', sendingIp: '192.0.2.10', defaultSpfMechanisms: '', dmarcPolicy: 'none', dmarcRua: '', sendRequiresVerified: true, engagementTrackingEnabled: true, listUnsubscribeMailto: '', listUnsubscribeUrl: '', listUnsubscribePostEnabled: false, feedbackIdEnabled: false, reportAbuseTo: '', csaComplaintsTo: '', bounceAddress: '', bounceEnvelopeEnabled: false }, refreshBootstrap: vi.fn(async () => undefined), logout: vi.fn(async () => undefined) }; return render( }> } /> ); } function LocationProbe() { const location = useLocation(); return
{location.pathname}
; }