| import { render, screen } from '@testing-library/react'; |
| import WorkPage from '../../feature/work/WorkPage'; |
| import { Provider } from 'react-redux'; |
| import { store } from '../../store/store'; |
| import { MemoryRouter, Route, Routes, useNavigate } from 'react-router'; |
| import { act } from 'react-dom/test-utils'; |
| import WorkAPI from '../../api/workApi'; |
| import type { Work } from '../../api/otherType'; |
| import '@testing-library/jest-dom'; |
| |
| // 模拟整个WorkAPI类 |
| jest.mock('../../api/workApi', () => { |
| return { |
| __esModule: true, |
| default: { |
| getWorkById: jest.fn(), |
| likeWork: jest.fn(), |
| // 添加其他可能用到的方法 |
| getBugReports: jest.fn(), |
| getDiscussions: jest.fn() |
| } |
| } |
| }); |
| |
| // 模拟子组件 |
| jest.mock('../../components/BugReportSection', () => () => ( |
| <div data-testid="bug-report-mock">BugReportSection Mock</div> |
| )); |
| |
| jest.mock('../../components/DiscussionSection', () => () => ( |
| <div data-testid="discussion-mock">DiscussionSection Mock</div> |
| )); |
| |
| // 模拟react-router-dom的useNavigate |
| jest.mock('react-router-dom', () => ({ |
| ...jest.requireActual('react-router-dom'), |
| useNavigate: () => jest.fn() |
| })); |
| |
| describe('WorkPage Component', () => { |
| const mockWork: Work = { |
| id: 1, |
| title: '测试作品', |
| author: '测试作者', |
| categoryName: '测试分类', |
| views: 100, |
| likes: 50, |
| createTime: '2023-01-01T00:00:00Z', |
| description: '测试描述', |
| content: '测试内容', |
| size: '', |
| data: function (): unknown { |
| throw new Error('Function not implemented.'); |
| }, |
| artist: '', |
| quality: '', |
| genre: [], |
| authorId: 0, |
| categoryId: 0, |
| coverUrl: '', |
| attachments: [], |
| bugCount: 0, |
| discussionCount: 0 |
| }; |
| |
| beforeEach(() => { |
| // 重置所有模拟 |
| jest.clearAllMocks(); |
| |
| // 设置默认模拟实现 |
| (WorkAPI.getWorkById as jest.Mock).mockResolvedValue(mockWork); |
| (WorkAPI.likeWork as jest.Mock).mockResolvedValue({}); |
| }); |
| |
| it('renders loading spinner initially', async () => { |
| // 延迟API响应以测试加载状态 |
| let resolvePromise: (value: Work) => void; |
| (WorkAPI.getWorkById as jest.Mock).mockImplementation( |
| () => new Promise<Work>((resolve) => { |
| resolvePromise = resolve; |
| }) |
| ); |
| |
| render( |
| <MemoryRouter initialEntries={['/works/1']}> |
| <Provider store={store}> |
| <Routes> |
| <Route path="/works/:id" element={<WorkPage />} /> |
| </Routes> |
| </Provider> |
| </MemoryRouter> |
| ); |
| |
| // 验证加载状态 |
| expect(screen.getByRole('status')).toBeInTheDocument(); |
| expect(screen.getByText(/加载/)).toBeInTheDocument(); |
| |
| // 完成API请求 |
| await act(async () => { |
| resolvePromise(mockWork); |
| }); |
| |
| // 验证加载完成后内容 |
| expect(await screen.findByText('测试作品')).toBeInTheDocument(); |
| }); |
| |
| it('renders work details after loading', async () => { |
| render( |
| <MemoryRouter initialEntries={['/works/1']}> |
| <Provider store={store}> |
| <Routes> |
| <Route path="/works/:id" element={<WorkPage />} /> |
| </Routes> |
| </Provider> |
| </MemoryRouter> |
| ); |
| |
| // 等待数据加载完成 |
| expect(await screen.findByText('测试作品')).toBeInTheDocument(); |
| |
| // 验证头部信息 |
| expect(screen.getByText('作者: 测试作者')).toBeInTheDocument(); |
| expect(screen.getByText('测试分类')).toHaveClass('ant-tag'); |
| expect(screen.getByText('浏览: 100')).toBeInTheDocument(); |
| expect(screen.getByText('点赞: 50')).toBeInTheDocument(); |
| expect(screen.getByText(/2023/)).toBeInTheDocument(); |
| |
| // 验证作品内容 |
| expect(screen.getByRole('heading', { level: 4, name: '作品描述' })).toBeInTheDocument(); |
| expect(screen.getByText('测试描述')).toBeInTheDocument(); |
| expect(screen.getByRole('heading', { level: 4, name: '作品内容' })).toBeInTheDocument(); |
| expect(screen.getByText('测试内容')).toBeInTheDocument(); |
| |
| // 验证标签页 |
| expect(screen.getByRole('tab', { name: '作品详情' })).toBeInTheDocument(); |
| expect(screen.getByRole('tab', { name: /Bug反馈/ })).toBeInTheDocument(); |
| expect(screen.getByRole('tab', { name: /交流区/ })).toBeInTheDocument(); |
| }); |
| |
| it('handles back button click', async () => { |
| const mockNavigate = jest.fn(); |
| (useNavigate as jest.Mock).mockReturnValue(mockNavigate); |
| |
| render( |
| <MemoryRouter initialEntries={['/works/1']}> |
| <Provider store={store}> |
| <Routes> |
| <Route path="/works/:id" element={<WorkPage />} /> |
| </Routes> |
| </Provider> |
| </MemoryRouter> |
| ); |
| |
| // 等待数据加载完成 |
| await screen.findByText('测试作品'); |
| |
| // 点击返回按钮 |
| const backButton = screen.getByRole('button', { name: '返回' }); |
| await act(async () => { |
| backButton.click(); |
| }); |
| |
| // 验证导航被调用 |
| expect(mockNavigate).toHaveBeenCalledWith(-1); |
| }); |
| |
| it('handles like button click', async () => { |
| render( |
| <MemoryRouter initialEntries={['/works/1']}> |
| <Provider store={store}> |
| <Routes> |
| <Route path="/works/:id" element={<WorkPage />} /> |
| </Routes> |
| </Provider> |
| </MemoryRouter> |
| ); |
| |
| // 等待数据加载完成 |
| await screen.findByText('测试作品'); |
| |
| // 点击点赞按钮 |
| const likeButton = screen.getByRole('button', { name: '点赞' }); |
| await act(async () => { |
| likeButton.click(); |
| }); |
| |
| // 验证API调用 |
| expect(WorkAPI.likeWork).toHaveBeenCalledTimes(1); |
| expect(WorkAPI.likeWork).toHaveBeenCalledWith(1); |
| |
| // 验证UI反馈 |
| expect(await screen.findByText('点赞成功')).toBeInTheDocument(); |
| }); |
| |
| it('shows error message when work loading fails', async () => { |
| const errorMessage = '加载失败'; |
| (WorkAPI.getWorkById as jest.Mock).mockRejectedValue(new Error(errorMessage)); |
| |
| const mockNavigate = jest.fn(); |
| (useNavigate as jest.Mock).mockReturnValue(mockNavigate); |
| |
| render( |
| <MemoryRouter initialEntries={['/works/1']}> |
| <Provider store={store}> |
| <Routes> |
| <Route path="/works/:id" element={<WorkPage />} /> |
| </Routes> |
| </Provider> |
| </MemoryRouter> |
| ); |
| |
| // 验证错误消息 |
| expect(await screen.findByText('作品不存在')).toBeInTheDocument(); |
| |
| // 验证导航到首页 |
| expect(mockNavigate).toHaveBeenCalledWith('/'); |
| }); |
| |
| it('switches between tabs correctly', async () => { |
| render( |
| <MemoryRouter initialEntries={['/works/1']}> |
| <Provider store={store}> |
| <Routes> |
| <Route path="/works/:id" element={<WorkPage />} /> |
| </Routes> |
| </Provider> |
| </MemoryRouter> |
| ); |
| |
| // 等待数据加载完成 |
| await screen.findByText('测试作品'); |
| |
| // 初始显示作品详情 |
| expect(screen.getByText('测试描述')).toBeInTheDocument(); |
| expect(screen.queryByTestId('bug-report-mock')).not.toBeInTheDocument(); |
| expect(screen.queryByTestId('discussion-mock')).not.toBeInTheDocument(); |
| |
| // 点击Bug反馈标签 |
| const bugTab = screen.getByRole('tab', { name: /Bug反馈/ }); |
| await act(async () => { |
| bugTab.click(); |
| }); |
| |
| // 验证Bug反馈内容显示 |
| expect(screen.queryByText('测试描述')).not.toBeInTheDocument(); |
| expect(screen.getByTestId('bug-report-mock')).toBeInTheDocument(); |
| |
| // 点击交流区标签 |
| const discussionTab = screen.getByRole('tab', { name: /交流区/ }); |
| await act(async () => { |
| discussionTab.click(); |
| }); |
| |
| // 验证交流区内容显示 |
| expect(screen.queryByTestId('bug-report-mock')).not.toBeInTheDocument(); |
| expect(screen.getByTestId('discussion-mock')).toBeInTheDocument(); |
| |
| // 切换回作品详情 |
| const detailsTab = screen.getByRole('tab', { name: '作品详情' }); |
| await act(async () => { |
| detailsTab.click(); |
| }); |
| |
| // 验证作品详情再次显示 |
| expect(screen.getByText('测试描述')).toBeInTheDocument(); |
| expect(screen.queryByTestId('discussion-mock')).not.toBeInTheDocument(); |
| }); |
| }); |