| import React from 'react'; |
| import { render, screen, fireEvent, waitFor } from '@testing-library/react'; |
| import { MemoryRouter, Route, Routes } from 'react-router-dom'; |
| import RequestDetail from './RequestDetail'; |
| import { isFormData } from 'axios'; |
| |
| const mockNavigate = jest.fn(); |
| |
| |
| // 明确 mock API 模块 |
| jest.mock('../api/requestPost', () => ({ |
| getRequestPostDetail: jest.fn(), |
| addRequestPostComment: jest.fn(), |
| likeRequestPost: jest.fn(), |
| deleteRequestPost: jest.fn() |
| })); |
| |
| jest.mock('../api/requestComment', () => ({ |
| likeRequestPostComment: jest.fn(), |
| getRequestCommentReplies: jest.fn(), |
| addRequestCommentReply: jest.fn(), |
| deleteRequestComment: jest.fn() |
| })); |
| |
| jest.mock('react-router-dom', () => ({ |
| ...jest.requireActual('react-router-dom'), |
| useNavigate: () => mockNavigate, |
| useLocation: jest.fn() |
| })); |
| |
| // 导入 mock 后的 API |
| import * as requestPostApi from '../api/requestPost'; |
| import * as requestCommentApi from '../api/requestComment'; |
| |
| describe('RequestDetail 组件', () => { |
| const mockPost = { |
| id: '1', |
| title: '测试求种帖', |
| content: '这是一个测试求种内容', |
| authorId: 'user1', |
| createTime: '2023-01-01T00:00:00Z', |
| likeCount: 5, |
| replyCount: 3, |
| isSolved: false, |
| }; |
| |
| const mockComments = [ |
| { |
| id: 'c1', |
| content: '测试评论1', |
| authorId: 'user2', |
| createTime: '2023-01-01T01:00:00Z', |
| likeCount: 2, |
| replies: [ |
| { |
| id: 'c1r1', |
| content: '测试回复1', |
| authorId: 'user3', |
| createTime: '2023-01-01T02:00:00Z', |
| likeCount: 1, |
| } |
| ] |
| } |
| ]; |
| |
| beforeEach(() => { |
| jest.clearAllMocks(); |
| |
| requestPostApi.getRequestPostDetail.mockResolvedValue({ |
| data: { |
| code: 200, |
| data: { |
| post: mockPost, |
| comments: mockComments, |
| } |
| } |
| }); |
| |
| // 模拟 useLocation |
| require('react-router-dom').useLocation.mockReturnValue({ |
| state: { fromTab: 'request' } |
| }); |
| |
| // 模拟 localStorage |
| Storage.prototype.getItem = jest.fn((key) => { |
| if (key === 'username') return 'testuser'; |
| return null; |
| }); |
| }); |
| |
| const renderComponent = () => { |
| return render( |
| <MemoryRouter initialEntries={['/request/1']}> |
| <Routes> |
| <Route path="/request/:id" element={<RequestDetail />} /> |
| {/* 添加一个模拟的求助列表路由用于导航测试 */} |
| <Route path="/dashboard/request" element={<div>求助列表</div>} /> |
| </Routes> |
| </MemoryRouter> |
| ); |
| }; |
| |
| it('应该正确加载和显示求助帖详情', async () => { |
| renderComponent(); |
| |
| // 检查加载状态 |
| expect(screen.getByText('加载中...')).toBeInTheDocument(); |
| |
| // 增加超时时间 |
| await waitFor(() => { |
| expect(screen.getByText(mockPost.title)).toBeInTheDocument(); |
| }, { timeout: 3000 }); |
| |
| // 等待数据加载完成 |
| await waitFor(() => { |
| expect(screen.getByText(mockPost.title)).toBeInTheDocument(); |
| expect(screen.getByText(mockPost.content)).toBeInTheDocument(); |
| expect(screen.getByText(mockPost.authorId)).toBeInTheDocument(); |
| }); |
| }); |
| |
| it('应该能够点赞帖子', async () => { |
| renderComponent(); |
| |
| await waitFor(() => { |
| expect(screen.getByText(mockPost.title)).toBeInTheDocument(); |
| }); |
| |
| const likeButton = screen.getByRole('button', { name: /点赞 \(\d+\)/ }); |
| fireEvent.click(likeButton); |
| |
| await waitFor(() => { |
| expect(requestPostApi.likeRequestPost).toHaveBeenCalledWith('1'); |
| }); |
| }); |
| |
| it('应该能够提交评论', async () => { |
| renderComponent(); |
| |
| await waitFor(() => { |
| expect(screen.getByText(mockPost.title)).toBeInTheDocument(); |
| }); |
| |
| const commentInput = screen.getByPlaceholderText('写下你的评论...'); |
| fireEvent.change(commentInput, { target: { value: '新评论' } }); |
| |
| const submitButton = screen.getByRole('button', { name: '发表评论' }); |
| fireEvent.click(submitButton); |
| |
| await waitFor(() => { |
| expect(requestPostApi.addRequestPostComment).toHaveBeenCalled(); |
| }); |
| }); |
| |
| it('应该能够点赞评论', async () => { |
| renderComponent(); |
| |
| await waitFor(() => { |
| expect(screen.getByText(mockComments[0].content)).toBeInTheDocument(); |
| }); |
| |
| const likeButtons = screen.getAllByRole('button', { name: /👍 \(\d+\)/ }); |
| fireEvent.click(likeButtons[0]); |
| |
| await waitFor(() => { |
| expect(requestCommentApi.likeRequestPostComment).toHaveBeenCalledWith('c1'); |
| }); |
| }); |
| |
| it('应该能够打开和关闭回复模态框', async () => { |
| renderComponent(); |
| |
| await waitFor(() => { |
| expect(screen.getByText(mockComments[0].content)).toBeInTheDocument(); |
| }); |
| |
| // 点击回复按钮 |
| const replyButtons = screen.getAllByRole('button', { name: '回复' }); |
| fireEvent.click(replyButtons[0]); |
| |
| // 检查模态框是否打开 |
| await waitFor(() => { |
| expect(screen.getByText(`回复 @${mockComments[0].authorId}`)).toBeInTheDocument(); |
| }); |
| |
| // 点击关闭按钮 |
| const closeButton = screen.getByRole('button', { name: '×' }); |
| fireEvent.click(closeButton); |
| |
| // 检查模态框是否关闭 |
| await waitFor(() => { |
| expect(screen.queryByText(`回复 @${mockComments[0].authorId}`)).not.toBeInTheDocument(); |
| }); |
| }); |
| |
| it('应该能够返回求助区', async () => { |
| renderComponent(); |
| |
| await waitFor(() => { |
| expect(screen.getByText(mockPost.title)).toBeInTheDocument(); |
| }); |
| |
| const backButton = screen.getByRole('button', { name: /返回/i }); |
| fireEvent.click(backButton); |
| |
| expect(mockNavigate).toHaveBeenCalledWith('/dashboard/request'); |
| |
| }); |
| }); |