blob: 4dc50369b96ff176770e5b29615f5960979921f0 [file] [log] [blame]
223010143d966302025-06-07 22:54:40 +08001import { render, screen } from '@testing-library/react';
2import WorkPage from '../../feature/work/WorkPage';
3import { Provider } from 'react-redux';
4import { store } from '../../store/store';
5import { MemoryRouter, Route, Routes, useNavigate } from 'react-router';
6import { act } from 'react-dom/test-utils';
7import WorkAPI from '../../api/workApi';
8import type { Work } from '../../api/otherType';
9import '@testing-library/jest-dom';
10
11// 模拟整个WorkAPI类
12jest.mock('../../api/workApi', () => {
13 return {
14 __esModule: true,
15 default: {
16 getWorkById: jest.fn(),
17 likeWork: jest.fn(),
18 // 添加其他可能用到的方法
19 getBugReports: jest.fn(),
20 getDiscussions: jest.fn()
21 }
22 }
23});
24
25// 模拟子组件
26jest.mock('../../components/BugReportSection', () => () => (
27 <div data-testid="bug-report-mock">BugReportSection Mock</div>
28));
29
30jest.mock('../../components/DiscussionSection', () => () => (
31 <div data-testid="discussion-mock">DiscussionSection Mock</div>
32));
33
34// 模拟react-router-dom的useNavigate
35jest.mock('react-router-dom', () => ({
36 ...jest.requireActual('react-router-dom'),
37 useNavigate: () => jest.fn()
38}));
39
40describe('WorkPage Component', () => {
41 const mockWork: Work = {
42 id: 1,
43 title: '测试作品',
44 author: '测试作者',
45 categoryName: '测试分类',
46 views: 100,
47 likes: 50,
48 createTime: '2023-01-01T00:00:00Z',
49 description: '测试描述',
50 content: '测试内容',
51 size: '',
52 data: function (): unknown {
53 throw new Error('Function not implemented.');
54 },
55 artist: '',
56 quality: '',
57 genre: [],
58 authorId: 0,
59 categoryId: 0,
60 coverUrl: '',
61 attachments: [],
62 bugCount: 0,
63 discussionCount: 0
64 };
65
66 beforeEach(() => {
67 // 重置所有模拟
68 jest.clearAllMocks();
69
70 // 设置默认模拟实现
71 (WorkAPI.getWorkById as jest.Mock).mockResolvedValue(mockWork);
72 (WorkAPI.likeWork as jest.Mock).mockResolvedValue({});
73 });
74
75 it('renders loading spinner initially', async () => {
76 // 延迟API响应以测试加载状态
77 let resolvePromise: (value: Work) => void;
78 (WorkAPI.getWorkById as jest.Mock).mockImplementation(
79 () => new Promise<Work>((resolve) => {
80 resolvePromise = resolve;
81 })
82 );
83
84 render(
85 <MemoryRouter initialEntries={['/works/1']}>
86 <Provider store={store}>
87 <Routes>
88 <Route path="/works/:id" element={<WorkPage />} />
89 </Routes>
90 </Provider>
91 </MemoryRouter>
92 );
93
94 // 验证加载状态
95 expect(screen.getByRole('status')).toBeInTheDocument();
96 expect(screen.getByText(/加载/)).toBeInTheDocument();
97
98 // 完成API请求
99 await act(async () => {
100 resolvePromise(mockWork);
101 });
102
103 // 验证加载完成后内容
104 expect(await screen.findByText('测试作品')).toBeInTheDocument();
105 });
106
107 it('renders work details after loading', async () => {
108 render(
109 <MemoryRouter initialEntries={['/works/1']}>
110 <Provider store={store}>
111 <Routes>
112 <Route path="/works/:id" element={<WorkPage />} />
113 </Routes>
114 </Provider>
115 </MemoryRouter>
116 );
117
118 // 等待数据加载完成
119 expect(await screen.findByText('测试作品')).toBeInTheDocument();
120
121 // 验证头部信息
122 expect(screen.getByText('作者: 测试作者')).toBeInTheDocument();
123 expect(screen.getByText('测试分类')).toHaveClass('ant-tag');
124 expect(screen.getByText('浏览: 100')).toBeInTheDocument();
125 expect(screen.getByText('点赞: 50')).toBeInTheDocument();
126 expect(screen.getByText(/2023/)).toBeInTheDocument();
127
128 // 验证作品内容
129 expect(screen.getByRole('heading', { level: 4, name: '作品描述' })).toBeInTheDocument();
130 expect(screen.getByText('测试描述')).toBeInTheDocument();
131 expect(screen.getByRole('heading', { level: 4, name: '作品内容' })).toBeInTheDocument();
132 expect(screen.getByText('测试内容')).toBeInTheDocument();
133
134 // 验证标签页
135 expect(screen.getByRole('tab', { name: '作品详情' })).toBeInTheDocument();
136 expect(screen.getByRole('tab', { name: /Bug反馈/ })).toBeInTheDocument();
137 expect(screen.getByRole('tab', { name: /交流区/ })).toBeInTheDocument();
138 });
139
140 it('handles back button click', async () => {
141 const mockNavigate = jest.fn();
142 (useNavigate as jest.Mock).mockReturnValue(mockNavigate);
143
144 render(
145 <MemoryRouter initialEntries={['/works/1']}>
146 <Provider store={store}>
147 <Routes>
148 <Route path="/works/:id" element={<WorkPage />} />
149 </Routes>
150 </Provider>
151 </MemoryRouter>
152 );
153
154 // 等待数据加载完成
155 await screen.findByText('测试作品');
156
157 // 点击返回按钮
158 const backButton = screen.getByRole('button', { name: '返回' });
159 await act(async () => {
160 backButton.click();
161 });
162
163 // 验证导航被调用
164 expect(mockNavigate).toHaveBeenCalledWith(-1);
165 });
166
167 it('handles like button click', async () => {
168 render(
169 <MemoryRouter initialEntries={['/works/1']}>
170 <Provider store={store}>
171 <Routes>
172 <Route path="/works/:id" element={<WorkPage />} />
173 </Routes>
174 </Provider>
175 </MemoryRouter>
176 );
177
178 // 等待数据加载完成
179 await screen.findByText('测试作品');
180
181 // 点击点赞按钮
182 const likeButton = screen.getByRole('button', { name: '点赞' });
183 await act(async () => {
184 likeButton.click();
185 });
186
187 // 验证API调用
188 expect(WorkAPI.likeWork).toHaveBeenCalledTimes(1);
189 expect(WorkAPI.likeWork).toHaveBeenCalledWith(1);
190
191 // 验证UI反馈
192 expect(await screen.findByText('点赞成功')).toBeInTheDocument();
193 });
194
195 it('shows error message when work loading fails', async () => {
196 const errorMessage = '加载失败';
197 (WorkAPI.getWorkById as jest.Mock).mockRejectedValue(new Error(errorMessage));
198
199 const mockNavigate = jest.fn();
200 (useNavigate as jest.Mock).mockReturnValue(mockNavigate);
201
202 render(
203 <MemoryRouter initialEntries={['/works/1']}>
204 <Provider store={store}>
205 <Routes>
206 <Route path="/works/:id" element={<WorkPage />} />
207 </Routes>
208 </Provider>
209 </MemoryRouter>
210 );
211
212 // 验证错误消息
213 expect(await screen.findByText('作品不存在')).toBeInTheDocument();
214
215 // 验证导航到首页
216 expect(mockNavigate).toHaveBeenCalledWith('/');
217 });
218
219 it('switches between tabs correctly', async () => {
220 render(
221 <MemoryRouter initialEntries={['/works/1']}>
222 <Provider store={store}>
223 <Routes>
224 <Route path="/works/:id" element={<WorkPage />} />
225 </Routes>
226 </Provider>
227 </MemoryRouter>
228 );
229
230 // 等待数据加载完成
231 await screen.findByText('测试作品');
232
233 // 初始显示作品详情
234 expect(screen.getByText('测试描述')).toBeInTheDocument();
235 expect(screen.queryByTestId('bug-report-mock')).not.toBeInTheDocument();
236 expect(screen.queryByTestId('discussion-mock')).not.toBeInTheDocument();
237
238 // 点击Bug反馈标签
239 const bugTab = screen.getByRole('tab', { name: /Bug反馈/ });
240 await act(async () => {
241 bugTab.click();
242 });
243
244 // 验证Bug反馈内容显示
245 expect(screen.queryByText('测试描述')).not.toBeInTheDocument();
246 expect(screen.getByTestId('bug-report-mock')).toBeInTheDocument();
247
248 // 点击交流区标签
249 const discussionTab = screen.getByRole('tab', { name: /交流区/ });
250 await act(async () => {
251 discussionTab.click();
252 });
253
254 // 验证交流区内容显示
255 expect(screen.queryByTestId('bug-report-mock')).not.toBeInTheDocument();
256 expect(screen.getByTestId('discussion-mock')).toBeInTheDocument();
257
258 // 切换回作品详情
259 const detailsTab = screen.getByRole('tab', { name: '作品详情' });
260 await act(async () => {
261 detailsTab.click();
262 });
263
264 // 验证作品详情再次显示
265 expect(screen.getByText('测试描述')).toBeInTheDocument();
266 expect(screen.queryByTestId('discussion-mock')).not.toBeInTheDocument();
267 });
268});