|
@@ -0,0 +1,169 @@
|
|
|
|
|
+import { describe, it, expect, vi } from 'vitest';
|
|
|
|
|
+import { render, screen, fireEvent } from '@testing-library/react';
|
|
|
|
|
+import { BrowserRouter } from 'react-router-dom';
|
|
|
|
|
+import { Navigation } from '../Navigation';
|
|
|
|
|
+
|
|
|
|
|
+// Mock window.matchMedia
|
|
|
|
|
+Object.defineProperty(window, 'matchMedia', {
|
|
|
|
|
+ writable: true,
|
|
|
|
|
+ value: vi.fn().mockImplementation((query) => ({
|
|
|
|
|
+ matches: false,
|
|
|
|
|
+ media: query,
|
|
|
|
|
+ onchange: null,
|
|
|
|
|
+ addListener: vi.fn(),
|
|
|
|
|
+ removeListener: vi.fn(),
|
|
|
|
|
+ addEventListener: vi.fn(),
|
|
|
|
|
+ removeEventListener: vi.fn(),
|
|
|
|
|
+ dispatchEvent: vi.fn(),
|
|
|
|
|
+ })),
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const renderWithRouter = (component: React.ReactElement) => {
|
|
|
|
|
+ return render(<BrowserRouter>{component}</BrowserRouter>);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+describe('Navigation', () => {
|
|
|
|
|
+ it('应该渲染所有导航菜单项', () => {
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const navigationItems = [
|
|
|
|
|
+ '首页',
|
|
|
|
|
+ '手机改运',
|
|
|
|
|
+ '八字详批',
|
|
|
|
|
+ '取名及改名',
|
|
|
|
|
+ '风水调整',
|
|
|
|
|
+ '职业规划',
|
|
|
|
|
+ '案例分享',
|
|
|
|
|
+ '行业资讯',
|
|
|
|
|
+ '道德经悟道',
|
|
|
|
|
+ '联系我们',
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ navigationItems.forEach((item) => {
|
|
|
|
|
+ expect(screen.getByText(item)).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该在桌面端显示完整水平导航', () => {
|
|
|
|
|
+ // 模拟桌面端屏幕
|
|
|
|
|
+ window.matchMedia = vi.fn().mockImplementation((query) => ({
|
|
|
|
|
+ matches: query === '(min-width: 1024px)',
|
|
|
|
|
+ media: query,
|
|
|
|
|
+ onchange: null,
|
|
|
|
|
+ addListener: vi.fn(),
|
|
|
|
|
+ removeListener: vi.fn(),
|
|
|
|
|
+ addEventListener: vi.fn(),
|
|
|
|
|
+ removeEventListener: vi.fn(),
|
|
|
|
|
+ dispatchEvent: vi.fn(),
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ // 桌面端应该显示水平导航
|
|
|
|
|
+ const desktopNav = screen.getByRole('navigation');
|
|
|
|
|
+ expect(desktopNav).toBeInTheDocument();
|
|
|
|
|
+
|
|
|
|
|
+ // 汉堡菜单按钮应该隐藏
|
|
|
|
|
+ const hamburgerButton = screen.getByRole('button');
|
|
|
|
|
+ expect(hamburgerButton.parentElement).toHaveClass('lg:hidden');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该在移动端显示汉堡菜单按钮', () => {
|
|
|
|
|
+ // 模拟移动端屏幕
|
|
|
|
|
+ window.matchMedia = vi.fn().mockImplementation((query) => ({
|
|
|
|
|
+ matches: false, // 小于 1024px
|
|
|
|
|
+ media: query,
|
|
|
|
|
+ onchange: null,
|
|
|
|
|
+ addListener: vi.fn(),
|
|
|
|
|
+ removeListener: vi.fn(),
|
|
|
|
|
+ addEventListener: vi.fn(),
|
|
|
|
|
+ removeEventListener: vi.fn(),
|
|
|
|
|
+ dispatchEvent: vi.fn(),
|
|
|
|
|
+ }));
|
|
|
|
|
+
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ // 汉堡菜单按钮应该可见
|
|
|
|
|
+ const hamburgerButton = screen.getByRole('button');
|
|
|
|
|
+ expect(hamburgerButton).toBeInTheDocument();
|
|
|
|
|
+ expect(hamburgerButton.parentElement).toHaveClass('lg:hidden');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该高亮显示当前页面对应的导航项', () => {
|
|
|
|
|
+ // 模拟当前在首页
|
|
|
|
|
+ Object.defineProperty(window, 'location', {
|
|
|
|
|
+ writable: true,
|
|
|
|
|
+ value: { pathname: '/' },
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const homeLink = screen.getByText('首页');
|
|
|
|
|
+ expect(homeLink).toHaveClass('text-blue-600');
|
|
|
|
|
+ expect(homeLink).toHaveClass('border-b-2');
|
|
|
|
|
+ expect(homeLink).toHaveClass('border-blue-600');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该正确渲染Logo链接', () => {
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const logoLink = screen.getByText('多八多');
|
|
|
|
|
+ expect(logoLink).toBeInTheDocument();
|
|
|
|
|
+ expect(logoLink.closest('a')).toHaveAttribute('href', '/');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该为导航项设置正确的路由路径', () => {
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const navigationLinks = [
|
|
|
|
|
+ { text: '首页', path: '/' },
|
|
|
|
|
+ { text: '手机改运', path: '/mobile-fortune' },
|
|
|
|
|
+ { text: '八字详批', path: '/bazi-analysis' },
|
|
|
|
|
+ { text: '取名及改名', path: '/naming-service' },
|
|
|
|
|
+ { text: '风水调整', path: '/fengshui-adjustment' },
|
|
|
|
|
+ { text: '职业规划', path: '/career-planning' },
|
|
|
|
|
+ { text: '案例分享', path: '/case-studies' },
|
|
|
|
|
+ { text: '行业资讯', path: '/industry-news' },
|
|
|
|
|
+ { text: '道德经悟道', path: '/taoism-wisdom' },
|
|
|
|
|
+ { text: '联系我们', path: '/contact-us' },
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ navigationLinks.forEach(({ text, path }) => {
|
|
|
|
|
+ const link = screen.getByText(text);
|
|
|
|
|
+ expect(link.closest('a')).toHaveAttribute('href', path);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该具有固定的导航栏样式', () => {
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const nav = screen.getByRole('navigation');
|
|
|
|
|
+ expect(nav).toHaveClass('sticky');
|
|
|
|
|
+ expect(nav).toHaveClass('top-0');
|
|
|
|
|
+ expect(nav).toHaveClass('z-50');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该在滚动时添加背景模糊效果', () => {
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const nav = screen.getByRole('navigation');
|
|
|
|
|
+ // 初始状态应该有透明背景
|
|
|
|
|
+ expect(nav).toHaveClass('bg-white/80');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该能够打开和关闭移动端菜单', () => {
|
|
|
|
|
+ renderWithRouter(<Navigation />);
|
|
|
|
|
+
|
|
|
|
|
+ const hamburgerButton = screen.getByRole('button');
|
|
|
|
|
+
|
|
|
|
|
+ // 初始状态菜单应该关闭
|
|
|
|
|
+ expect(screen.queryByText('首页')).toBeInTheDocument(); // 桌面端菜单项
|
|
|
|
|
+
|
|
|
|
|
+ // 点击汉堡菜单按钮
|
|
|
|
|
+ fireEvent.click(hamburgerButton);
|
|
|
|
|
+
|
|
|
|
|
+ // 菜单应该打开 - 检查遮罩层是否存在
|
|
|
|
|
+ const overlay = screen.getByTestId('mobile-menu-overlay');
|
|
|
|
|
+ expect(overlay).toBeInTheDocument();
|
|
|
|
|
+ });
|
|
|
|
|
+});
|