| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- 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 }) => (
- <div data-testid="navbar">
- <div data-testid="navbar-title">{title}</div>
- <button data-testid="navbar-back" onClick={onClickLeft}>返回</button>
- </div>
- )),
- NavbarPresets: {
- primary: {}
- }
- }))
- // 创建测试用的 QueryClient
- const createTestQueryClient = () => new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
- })
- // 包装组件
- const Wrapper = ({ children }: { children: React.ReactNode }) => {
- const queryClient = createTestQueryClient()
- return (
- <QueryClientProvider client={queryClient}>
- {children}
- </QueryClientProvider>
- )
- }
- // 模拟活动数据
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 检查导航栏
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 等待数据加载完成
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 等待数据加载完成
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 等待数据加载完成
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 检查无活动状态显示
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 检查加载状态显示
- expect(screen.getByText('正在加载活动')).toBeInTheDocument()
- expect(screen.getByText('请稍候...')).toBeInTheDocument()
- })
- test('应该处理API错误', async () => {
- // 模拟API错误
- mockRouteClient.search.$get.mockRejectedValue(new Error('网络错误'))
- render(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 检查无活动状态显示(因为API错误导致无数据)
- await waitFor(() => {
- expect(screen.getByText('暂无去程活动')).toBeInTheDocument()
- expect(screen.getByText('暂无返程活动')).toBeInTheDocument()
- })
- })
- test('应该验证布局优化效果', async () => {
- render(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 等待数据加载完成
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 等待数据加载完成
- 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(
- <Wrapper>
- <ActivitySelectPage />
- </Wrapper>
- )
- // 检查头部信息显示正确的地区
- await waitFor(() => {
- expect(screen.getByText('广州市 广东省 天河区 → 深圳市 广东省 福田区')).toBeInTheDocument()
- expect(screen.getByTestId('header-date')).toHaveTextContent('2025-11-02')
- })
- })
- })
|