保存本地对routes.ts的修改
Change-Id: I4f4dbd8069893d7363e251130791dc0594be44e1
diff --git a/src/test/work/WorkPage.test.tsx b/src/test/work/WorkPage.test.tsx
new file mode 100644
index 0000000..4dc5036
--- /dev/null
+++ b/src/test/work/WorkPage.test.tsx
@@ -0,0 +1,268 @@
+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();
+ });
+});
\ No newline at end of file