blob: 2e917e7ef41babb56c438b48e4ae4d4729dec356 [file] [log] [blame]
Akane121765b61a72025-05-17 13:52:25 +08001import React from 'react';
2import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3import { MemoryRouter, Route, Routes, useNavigate, useLocation } from 'react-router-dom';
4import HelpDetail from './HelpDetail';
5import * as helpPostApi from '../api/helpPost';
6import * as helpCommentApi from '../api/helpComment';
7
8// Mock API 模块
9jest.mock('../api/helpPost');
10jest.mock('../api/helpComment');
11
12// Mock 路由钩子
13const mockNavigate = jest.fn();
14jest.mock('react-router-dom', () => ({
15 ...jest.requireActual('react-router-dom'),
16 useNavigate: () => mockNavigate,
17 useLocation: jest.fn(),
18}));
19
20describe('HelpDetail 组件', () => {
21 const mockPost = {
22 id: '1',
23 title: '测试求助帖',
24 content: '这是一个测试求助内容',
25 authorId: 'user1',
26 createTime: '2023-01-01T00:00:00Z',
27 likeCount: 5,
28 replyCount: 3,
29 isSolved: false,
30 };
31
32 const mockComments = [
33 {
34 id: 'c1',
35 content: '测试评论1',
36 authorId: 'user2',
37 createTime: '2023-01-01T01:00:00Z',
38 likeCount: 2,
39 replies: [
40 {
41 id: 'c1r1',
42 content: '测试回复1',
43 authorId: 'user3',
44 createTime: '2023-01-01T02:00:00Z',
45 likeCount: 1,
46 }
47 ]
48 }
49 ];
50
51 beforeEach(() => {
52 // 重置 mock 函数
53 mockNavigate.mockClear();
54
55 // 设置模拟的 API 响应
DREWae420b22025-06-02 14:07:20 +080056 helpPostApi.getHelpPostDetail.mockResolvedValue({
Akane121765b61a72025-05-17 13:52:25 +080057 data: {
58 code: 200,
59 data: {
60 post: mockPost,
61 comments: mockComments,
62 }
63 }
64 });
65
66 // 模拟 useLocation
67 useLocation.mockReturnValue({
68 state: { fromTab: 'help' }
69 });
70
71 // 模拟 localStorage
72 Storage.prototype.getItem = jest.fn((key) => {
73 if (key === 'username') return 'testuser';
74 return null;
75 });
76 });
77
78 const renderComponent = () => {
79 return render(
80 <MemoryRouter initialEntries={['/help/1']}>
81 <Routes>
82 <Route path="/help/:id" element={<HelpDetail />} />
83 {/* 添加一个模拟的求助列表路由用于导航测试 */}
84 <Route path="/dashboard/help" element={<div>求助列表</div>} />
85 </Routes>
86 </MemoryRouter>
87 );
88 };
89
90 it('应该正确加载和显示求助帖详情', async () => {
91 renderComponent();
92
93 // 检查加载状态
94 expect(screen.getByText('加载中...')).toBeInTheDocument();
95
96 // 等待数据加载完成
97 await waitFor(() => {
98 expect(screen.getByText('测试求助帖')).toBeInTheDocument();
99 expect(screen.getByText('这是一个测试求助内容')).toBeInTheDocument();
100 expect(screen.getByText('user1')).toBeInTheDocument();
101 });
102 });
103
104 it('应该能够点赞帖子', async () => {
105 renderComponent();
106
107 await waitFor(() => {
108 expect(screen.getByText('测试求助帖')).toBeInTheDocument();
109 });
110
111 fireEvent.click(screen.getByText(/点赞 \(5\)/));
112
113 await waitFor(() => {
DREWae420b22025-06-02 14:07:20 +0800114 expect(helpPostApi.likeHelpPost).toHaveBeenCalledWith('1');
Akane121765b61a72025-05-17 13:52:25 +0800115 });
116 });
117
118 it('应该能够提交评论', async () => {
DREWae420b22025-06-02 14:07:20 +0800119 renderComponent();
Akane121765b61a72025-05-17 13:52:25 +0800120
DREWae420b22025-06-02 14:07:20 +0800121 await waitFor(() => {
122 expect(screen.getByText('测试求助帖')).toBeInTheDocument();
Akane121765b61a72025-05-17 13:52:25 +0800123 });
124
DREWae420b22025-06-02 14:07:20 +0800125 const commentInput = screen.getByPlaceholderText('写下你的评论...');
126 fireEvent.change(commentInput, { target: { value: '新评论' } });
127 fireEvent.click(screen.getByText('发表评论'));
128
129 await waitFor(() => {
130 expect(helpPostApi.addHelpPostComment).toHaveBeenCalled();
131
132 const calledArgs = helpPostApi.addHelpPostComment.mock.calls[0];
133 expect(calledArgs[0]).toBe('1'); // postId
134 const formData = calledArgs[1];
135
136 expect(formData instanceof FormData).toBe(true);
137 expect(formData.get('authorId')).toBe('testuser');
138 expect(formData.get('content')).toBe('新评论');
139 });
140});
Akane121765b61a72025-05-17 13:52:25 +0800141 it('应该能够点赞评论', async () => {
142 renderComponent();
143
144 await waitFor(() => {
145 expect(screen.getByText('测试评论1')).toBeInTheDocument();
146 });
147
148 fireEvent.click(screen.getAllByText(/👍 \(2\)/)[0]);
149
150 await waitFor(() => {
DREWae420b22025-06-02 14:07:20 +0800151 expect(helpCommentApi.likeHelpPostComment).toHaveBeenCalledWith('c1');
Akane121765b61a72025-05-17 13:52:25 +0800152 });
153 });
154
155 it('应该能够打开和关闭回复模态框', async () => {
156 renderComponent();
157
158 await waitFor(() => {
159 expect(screen.getByText('测试评论1')).toBeInTheDocument();
160 });
161
162 // 点击回复按钮
163 fireEvent.click(screen.getAllByText('回复')[0]);
164
165 // 检查模态框是否打开
166 await waitFor(() => {
167 expect(screen.getByText('回复 @user2')).toBeInTheDocument();
168 });
169
170 // 点击关闭按钮
171 fireEvent.click(screen.getByText('×'));
172
173 // 检查模态框是否关闭
174 await waitFor(() => {
175 expect(screen.queryByText('回复 @user2')).not.toBeInTheDocument();
176 });
177 });
178
179 it('应该能够返回求助区', async () => {
180 renderComponent();
181
182 await waitFor(() => {
183 expect(screen.getByText('测试求助帖')).toBeInTheDocument();
184 });
185
186 fireEvent.click(screen.getByRole('button', { name: /返回/i }));
187
188 // 修正后的断言(不再验证 state)
189 expect(mockNavigate).toHaveBeenCalledWith('/dashboard/help');
190 });
191});