|
|
@@ -0,0 +1,458 @@
|
|
|
+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 } 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.getByText('2025-11-01')).toBeInTheDocument()
|
|
|
+
|
|
|
+ // 检查页面标题
|
|
|
+ expect(screen.getByText('选择观看活动')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('系统已根据您选择的地区自动匹配相关活动')).toBeInTheDocument()
|
|
|
+
|
|
|
+ // 检查去程活动区域
|
|
|
+ expect(screen.getByText('去程活动')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('前往上海市 上海市 徐汇区观看活动')).toBeInTheDocument()
|
|
|
+
|
|
|
+ // 检查返程活动区域
|
|
|
+ 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('2025-11-01')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('朝阳区 · 北京市 · 北京市')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('北京市朝阳区工人体育场北路')).toBeInTheDocument()
|
|
|
+ expect(screen.getByText('到达:上海市 上海市 徐汇区')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ test('应该处理返回按钮', async () => {
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <ActivitySelectPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
+
|
|
|
+ // 等待数据加载完成
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(screen.getByTestId('navbar')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+
|
|
|
+ // 点击返回按钮
|
|
|
+ const backButton = screen.getByTestId('navbar-back')
|
|
|
+ fireEvent.click(backButton)
|
|
|
+
|
|
|
+ // 检查返回导航被调用
|
|
|
+ expect(taroMock.navigateBack).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.getByText('2025-11-02')).toBeInTheDocument()
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|