blob: 8ec34c2ea4e943514193c53bdcc286792e2cf8eb [file] [log] [blame]
DREWae420b22025-06-02 14:07:20 +08001import React from 'react';
2import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3import { MemoryRouter, Route, Routes } from 'react-router-dom';
4import RequestDetail from './RequestDetail';
5import { isFormData } from 'axios';
6
7const mockNavigate = jest.fn();
8
9
10// 明确 mock API 模块
11jest.mock('../api/requestPost', () => ({
12 getRequestPostDetail: jest.fn(),
13 addRequestPostComment: jest.fn(),
14 likeRequestPost: jest.fn(),
15 deleteRequestPost: jest.fn()
16}));
17
18jest.mock('../api/requestComment', () => ({
19 likeRequestPostComment: jest.fn(),
20 getRequestCommentReplies: jest.fn(),
21 addRequestCommentReply: jest.fn(),
22 deleteRequestComment: jest.fn()
23}));
24
25jest.mock('react-router-dom', () => ({
26 ...jest.requireActual('react-router-dom'),
27 useNavigate: () => mockNavigate,
28 useLocation: jest.fn()
29}));
30
31// 导入 mock 后的 API
32import * as requestPostApi from '../api/requestPost';
33import * as requestCommentApi from '../api/requestComment';
34
35describe('RequestDetail 组件', () => {
36 const mockPost = {
37 id: '1',
38 title: '测试求种帖',
39 content: '这是一个测试求种内容',
40 authorId: 'user1',
41 createTime: '2023-01-01T00:00:00Z',
42 likeCount: 5,
43 replyCount: 3,
44 isSolved: false,
45 };
46
47 const mockComments = [
48 {
49 id: 'c1',
50 content: '测试评论1',
51 authorId: 'user2',
52 createTime: '2023-01-01T01:00:00Z',
53 likeCount: 2,
54 replies: [
55 {
56 id: 'c1r1',
57 content: '测试回复1',
58 authorId: 'user3',
59 createTime: '2023-01-01T02:00:00Z',
60 likeCount: 1,
61 }
62 ]
63 }
64 ];
65
66 beforeEach(() => {
67 jest.clearAllMocks();
68
69 requestPostApi.getRequestPostDetail.mockResolvedValue({
70 data: {
71 code: 200,
72 data: {
73 post: mockPost,
74 comments: mockComments,
75 }
76 }
77 });
78
79 // 模拟 useLocation
80 require('react-router-dom').useLocation.mockReturnValue({
81 state: { fromTab: 'request' }
82 });
83
84 // 模拟 localStorage
85 Storage.prototype.getItem = jest.fn((key) => {
86 if (key === 'username') return 'testuser';
87 return null;
88 });
89 });
90
91 const renderComponent = () => {
92 return render(
93 <MemoryRouter initialEntries={['/request/1']}>
94 <Routes>
95 <Route path="/request/:id" element={<RequestDetail />} />
96 {/* 添加一个模拟的求助列表路由用于导航测试 */}
97 <Route path="/dashboard/request" element={<div>求助列表</div>} />
98 </Routes>
99 </MemoryRouter>
100 );
101 };
102
103 it('应该正确加载和显示求助帖详情', async () => {
104 renderComponent();
105
106 // 检查加载状态
107 expect(screen.getByText('加载中...')).toBeInTheDocument();
108
109 // 增加超时时间
110 await waitFor(() => {
111 expect(screen.getByText(mockPost.title)).toBeInTheDocument();
112 }, { timeout: 3000 });
113
114 // 等待数据加载完成
115 await waitFor(() => {
116 expect(screen.getByText(mockPost.title)).toBeInTheDocument();
117 expect(screen.getByText(mockPost.content)).toBeInTheDocument();
118 expect(screen.getByText(mockPost.authorId)).toBeInTheDocument();
119 });
120 });
121
122 it('应该能够点赞帖子', async () => {
123 renderComponent();
124
125 await waitFor(() => {
126 expect(screen.getByText(mockPost.title)).toBeInTheDocument();
127 });
128
129 const likeButton = screen.getByRole('button', { name: /点赞 \(\d+\)/ });
130 fireEvent.click(likeButton);
131
132 await waitFor(() => {
133 expect(requestPostApi.likeRequestPost).toHaveBeenCalledWith('1');
134 });
135 });
136
137 it('应该能够提交评论', async () => {
138 renderComponent();
139
140 await waitFor(() => {
141 expect(screen.getByText(mockPost.title)).toBeInTheDocument();
142 });
143
144 const commentInput = screen.getByPlaceholderText('写下你的评论...');
145 fireEvent.change(commentInput, { target: { value: '新评论' } });
146
147 const submitButton = screen.getByRole('button', { name: '发表评论' });
148 fireEvent.click(submitButton);
149
150 await waitFor(() => {
151 expect(requestPostApi.addRequestPostComment).toHaveBeenCalled();
152 });
153 });
154
155 it('应该能够点赞评论', async () => {
156 renderComponent();
157
158 await waitFor(() => {
159 expect(screen.getByText(mockComments[0].content)).toBeInTheDocument();
160 });
161
162 const likeButtons = screen.getAllByRole('button', { name: /👍 \(\d+\)/ });
163 fireEvent.click(likeButtons[0]);
164
165 await waitFor(() => {
166 expect(requestCommentApi.likeRequestPostComment).toHaveBeenCalledWith('c1');
167 });
168 });
169
170 it('应该能够打开和关闭回复模态框', async () => {
171 renderComponent();
172
173 await waitFor(() => {
174 expect(screen.getByText(mockComments[0].content)).toBeInTheDocument();
175 });
176
177 // 点击回复按钮
178 const replyButtons = screen.getAllByRole('button', { name: '回复' });
179 fireEvent.click(replyButtons[0]);
180
181 // 检查模态框是否打开
182 await waitFor(() => {
183 expect(screen.getByText(`回复 @${mockComments[0].authorId}`)).toBeInTheDocument();
184 });
185
186 // 点击关闭按钮
187 const closeButton = screen.getByRole('button', { name: '×' });
188 fireEvent.click(closeButton);
189
190 // 检查模态框是否关闭
191 await waitFor(() => {
192 expect(screen.queryByText(`回复 @${mockComments[0].authorId}`)).not.toBeInTheDocument();
193 });
194 });
195
196 it('应该能够返回求助区', async () => {
197 renderComponent();
198
199 await waitFor(() => {
200 expect(screen.getByText(mockPost.title)).toBeInTheDocument();
201 });
202
203 const backButton = screen.getByRole('button', { name: /返回/i });
204 fireEvent.click(backButton);
205
206 expect(mockNavigate).toHaveBeenCalledWith('/dashboard/request');
207
208 });
209});