import React from 'react' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import ActivitySelectPage from '../../src/pages/select-activity/ActivitySelectPage' // 导入 Taro mock 函数 import taroMock, { mockUseRouter, mockNavigateBack } from '../__mocks__/taroMock' // Mock API 客户端 let mockRouteClient: any jest.mock('../../src/api', () => { mockRouteClient = { search: { $get: jest.fn() } } return { routeClient: mockRouteClient } }) // Mock Navbar 组件 jest.mock('@/components/ui/navbar', () => ({ Navbar: jest.fn(({ title, leftIcon, onClickLeft, ...props }) => (
{title}
)), NavbarPresets: { primary: {} } })) // 创建测试用的 QueryClient const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }) // 包装组件 const Wrapper = ({ children }: { children: React.ReactNode }) => { const queryClient = createTestQueryClient() return ( {children} ) } // 模拟活动数据 const mockActivities = [ { id: 1, name: '音乐节活动', description: '大型音乐节活动', startDate: '2025-11-01T10:00:00Z', endDate: '2025-11-01T22:00:00Z', venueLocationId: 1, venueLocation: { id: 1, name: '北京工人体育场', provinceId: 1, cityId: 11, districtId: 101, address: '北京市朝阳区工人体育场北路', latitude: '39.929985', longitude: '116.395645', province: { id: 1, name: '北京市', level: 1, code: '110000' }, city: { id: 11, name: '北京市', level: 2, code: '110100' }, district: { id: 101, name: '朝阳区', level: 3, code: '110105' } } }, { id: 2, name: '体育赛事', description: '足球比赛', startDate: '2025-11-02T15:00:00Z', endDate: '2025-11-02T17:00:00Z', venueLocationId: 2, venueLocation: { id: 2, name: '上海体育场', provinceId: 2, cityId: 22, districtId: 202, address: '上海市徐汇区天钥桥路666号', latitude: '31.230416', longitude: '121.473701', province: { id: 2, name: '上海市', level: 1, code: '310000' }, city: { id: 22, name: '上海市', level: 2, code: '310100' }, district: { id: 202, name: '徐汇区', level: 3, code: '310104' } } } ] // 模拟路线数据 const mockRoutes = [ { id: 1, activityId: 1, routeType: 'departure', startLocation: { id: 1, name: '北京南站', provinceId: 1, cityId: 11, districtId: 101, province: { id: 1, name: '北京市' }, city: { id: 11, name: '北京市' }, district: { id: 101, name: '朝阳区' } }, endLocation: { id: 2, name: '上海虹桥站', provinceId: 2, cityId: 22, districtId: 202, province: { id: 2, name: '上海市' }, city: { id: 22, name: '上海市' }, district: { id: 202, name: '徐汇区' } } }, { id: 2, activityId: 2, routeType: 'return', startLocation: { id: 2, name: '上海虹桥站', provinceId: 2, cityId: 22, districtId: 202, province: { id: 2, name: '上海市' }, city: { id: 22, name: '上海市' }, district: { id: 202, name: '徐汇区' } }, endLocation: { id: 1, name: '北京南站', provinceId: 1, cityId: 11, districtId: 101, province: { id: 1, name: '北京市' }, city: { id: 11, name: '北京市' }, district: { id: 101, name: '朝阳区' } } } ] describe('活动选择页面测试', () => { beforeEach(() => { jest.clearAllMocks() // 设置默认的路由参数 mockUseRouter.mockReturnValue({ params: { startAreaIds: JSON.stringify([1, 11, 101]), endAreaIds: JSON.stringify([2, 22, 202]), startAreaName: encodeURIComponent('北京市 北京市 朝阳区'), endAreaName: encodeURIComponent('上海市 上海市 徐汇区'), date: '2025-11-01', vehicleType: 'bus', travelMode: 'carpool' } }) // 设置默认的API响应 mockRouteClient.search.$get.mockResolvedValue({ status: 200, json: async () => ({ success: true, data: { routes: mockRoutes, activities: mockActivities } }) }) }) test('应该正确渲染活动选择页面', async () => { render( ) // 检查导航栏 await waitFor(() => { expect(screen.getByTestId('navbar')).toBeInTheDocument() expect(screen.getByTestId('navbar-title')).toHaveTextContent('选择活动') }) // 检查头部信息 expect(screen.getByText('北京市 北京市 朝阳区 → 上海市 上海市 徐汇区')).toBeInTheDocument() expect(screen.getByTestId('header-date')).toHaveTextContent('2025-11-01') // 检查页面标题 expect(screen.getByText('选择观看活动')).toBeInTheDocument() expect(screen.getByText('系统已根据您选择的地区自动匹配相关活动')).toBeInTheDocument() // 检查去程活动区域 expect(screen.getByText('去程活动')).toBeInTheDocument() expect(screen.getByText('前往上海市 上海市 徐汇区观看活动')).toBeInTheDocument() // 检查活动名称是否正确分配到对应的区域 expect(screen.getByTestId('departure-activity-name-1')).toHaveTextContent('音乐节活动') expect(screen.getByTestId('return-activity-name-2')).toHaveTextContent('体育赛事') // 检查返程活动区域 expect(screen.getByText('返程活动')).toBeInTheDocument() expect(screen.getByText('从北京市 北京市 朝阳区观看活动后返回')).toBeInTheDocument() }) test('应该正确显示活动列表', async () => { render( ) // 等待数据加载完成 await waitFor(() => { expect(screen.getByText('音乐节活动')).toBeInTheDocument() expect(screen.getByText('体育赛事')).toBeInTheDocument() }) // 检查活动信息完整显示 expect(screen.getByText('朝阳区 · 北京市 · 北京市')).toBeInTheDocument() expect(screen.getByText('北京市朝阳区工人体育场北路')).toBeInTheDocument() expect(screen.getByText('到达:上海市 上海市 徐汇区')).toBeInTheDocument() // 检查活动图片占位符已被注释掉,不显示 expect(screen.queryByText('活动图片')).not.toBeInTheDocument() }) test('应该正确处理活动选择', async () => { render( ) // 等待数据加载完成 await waitFor(() => { expect(screen.getByText('音乐节活动')).toBeInTheDocument() }) // 点击去程活动 const activityItem = screen.getByTestId('departure-activity-1') fireEvent.click(activityItem) // 检查导航被调用 expect(taroMock.navigateTo).toHaveBeenCalledWith({ url: expect.stringContaining('pages/schedule-list/ScheduleListPage') }) // 检查导航参数正确 const navigateCall = taroMock.navigateTo.mock.calls[0][0] expect(navigateCall.url).toContain('startAreaIds=[1,11,101]') expect(navigateCall.url).toContain('endAreaIds=[2,22,202]') expect(navigateCall.url).toContain('date=2025-11-01') expect(navigateCall.url).toContain('vehicleType=bus') expect(navigateCall.url).toContain('travelMode=carpool') expect(navigateCall.url).toContain('activityId=1') expect(navigateCall.url).toContain('routeType=departure') }) test('应该处理返程活动选择', async () => { render( ) // 等待数据加载完成 await waitFor(() => { expect(screen.getByText('体育赛事')).toBeInTheDocument() }) // 点击返程活动 const returnActivity = screen.getByTestId('return-activity-2') fireEvent.click(returnActivity) // 检查导航被调用 expect(taroMock.navigateTo).toHaveBeenCalledWith({ url: expect.stringContaining('pages/schedule-list/ScheduleListPage') }) // 检查导航参数正确 const navigateCall = taroMock.navigateTo.mock.calls[0][0] expect(navigateCall.url).toContain('routeType=return') expect(navigateCall.url).toContain('activityId=2') }) test('应该处理无活动的情况', async () => { // 模拟无活动数据 mockRouteClient.search.$get.mockResolvedValue({ status: 200, json: async () => ({ success: true, data: { routes: [], activities: [] } }) }) render( ) // 检查无活动状态显示 await waitFor(() => { expect(screen.getByText('暂无去程活动')).toBeInTheDocument() expect(screen.getByText('上海市 上海市 徐汇区当前没有相关活动')).toBeInTheDocument() expect(screen.getByText('暂无返程活动')).toBeInTheDocument() expect(screen.getByText('北京市 北京市 朝阳区当前没有相关活动')).toBeInTheDocument() }) }) test('应该处理加载状态', () => { // 模拟API延迟 mockRouteClient.search.$get.mockImplementation(() => new Promise(() => {})) render( ) // 检查加载状态显示 expect(screen.getByText('正在加载活动')).toBeInTheDocument() expect(screen.getByText('请稍候...')).toBeInTheDocument() }) test('应该处理API错误', async () => { // 模拟API错误 mockRouteClient.search.$get.mockRejectedValue(new Error('网络错误')) render( ) // 检查无活动状态显示(因为API错误导致无数据) await waitFor(() => { expect(screen.getByText('暂无去程活动')).toBeInTheDocument() expect(screen.getByText('暂无返程活动')).toBeInTheDocument() }) }) test('应该验证布局优化效果', async () => { render( ) // 等待数据加载完成 await waitFor(() => { expect(screen.getByText('音乐节活动')).toBeInTheDocument() }) // 验证图片占位符已被注释掉,不显示 expect(screen.queryByText('活动图片')).not.toBeInTheDocument() // 验证活动信息完整显示 expect(screen.getByText('音乐节活动')).toBeInTheDocument() expect(screen.getByText('朝阳区 · 北京市 · 北京市')).toBeInTheDocument() expect(screen.getByText('北京市朝阳区工人体育场北路')).toBeInTheDocument() expect(screen.getByText('到达:上海市 上海市 徐汇区')).toBeInTheDocument() // 验证头部日期显示正确 expect(screen.getByTestId('header-date')).toHaveTextContent('2025-11-01') }) test('应该处理返回按钮', async () => { render( ) // 等待数据加载完成 await waitFor(() => { expect(screen.getByTestId('navbar')).toBeInTheDocument() }) // 点击返回按钮 const backButton = screen.getByTestId('navbar-back') fireEvent.click(backButton) // 检查返回导航被调用 expect(mockNavigateBack).toHaveBeenCalled() }) test('应该处理不同的路由参数', async () => { // 设置不同的路由参数 mockUseRouter.mockReturnValue({ params: { startAreaIds: '[3,33,303]', endAreaIds: '[4,44,404]', startAreaName: encodeURIComponent('广州市 广东省 天河区'), endAreaName: encodeURIComponent('深圳市 广东省 福田区'), date: '2025-11-02', vehicleType: 'business', travelMode: 'charter' } }) render( ) // 检查头部信息显示正确的地区 await waitFor(() => { expect(screen.getByText('广州市 广东省 天河区 → 深圳市 广东省 福田区')).toBeInTheDocument() expect(screen.getByTestId('header-date')).toHaveTextContent('2025-11-02') }) }) })