|
@@ -0,0 +1,251 @@
|
|
|
|
|
+import React from 'react'
|
|
|
|
|
+import { render, screen, waitFor } from '@testing-library/react'
|
|
|
|
|
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
|
|
|
+import ScheduleListPage from '../../src/pages/schedule-list/ScheduleListPage'
|
|
|
|
|
+
|
|
|
|
|
+// Mock Taro API
|
|
|
|
|
+jest.mock('@tarojs/taro', () => ({
|
|
|
|
|
+ useRouter: jest.fn(() => ({
|
|
|
|
|
+ params: {
|
|
|
|
|
+ startAreaIds: JSON.stringify([1, 2]),
|
|
|
|
|
+ endAreaIds: JSON.stringify([3, 4]),
|
|
|
|
|
+ date: '2025-10-31',
|
|
|
|
|
+ vehicleType: 'bus',
|
|
|
|
|
+ travelMode: 'carpool',
|
|
|
|
|
+ activityId: '1',
|
|
|
|
|
+ routeType: 'departure'
|
|
|
|
|
+ }
|
|
|
|
|
+ })),
|
|
|
|
|
+ navigateTo: jest.fn(),
|
|
|
|
|
+ navigateBack: jest.fn()
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+// Mock API client
|
|
|
|
|
+jest.mock('../../src/api', () => ({
|
|
|
|
|
+ routeClient: {
|
|
|
|
|
+ search: {
|
|
|
|
|
+ $get: jest.fn(() => Promise.resolve({
|
|
|
|
|
+ status: 200,
|
|
|
|
|
+ json: () => Promise.resolve({
|
|
|
|
|
+ data: {
|
|
|
|
|
+ routes: [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ name: '测试路线',
|
|
|
|
|
+ description: null,
|
|
|
|
|
+ startLocationId: 1,
|
|
|
|
|
+ endLocationId: 2,
|
|
|
|
|
+ startLocation: {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ name: '起点',
|
|
|
|
|
+ provinceId: 1,
|
|
|
|
|
+ cityId: 1,
|
|
|
|
|
+ districtId: 1,
|
|
|
|
|
+ address: '起点地址'
|
|
|
|
|
+ },
|
|
|
|
|
+ endLocation: {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ name: '终点',
|
|
|
|
|
+ provinceId: 2,
|
|
|
|
|
+ cityId: 2,
|
|
|
|
|
+ districtId: 2,
|
|
|
|
|
+ address: '终点地址'
|
|
|
|
|
+ },
|
|
|
|
|
+ pickupPoint: '上车点',
|
|
|
|
|
+ dropoffPoint: '下车点',
|
|
|
|
|
+ departureTime: '2025-10-31T08:00:00Z',
|
|
|
|
|
+ vehicleType: 'bus',
|
|
|
|
|
+ travelMode: 'carpool',
|
|
|
|
|
+ price: 100,
|
|
|
|
|
+ seatCount: 40,
|
|
|
|
|
+ availableSeats: 20,
|
|
|
|
|
+ activityId: 1,
|
|
|
|
|
+ activity: {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ name: '测试活动',
|
|
|
|
|
+ description: null,
|
|
|
|
|
+ venueLocationId: 3,
|
|
|
|
|
+ venueLocation: {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ name: '活动场地',
|
|
|
|
|
+ provinceId: 3,
|
|
|
|
|
+ cityId: 3,
|
|
|
|
|
+ districtId: 3,
|
|
|
|
|
+ address: '活动场地地址'
|
|
|
|
|
+ },
|
|
|
|
|
+ startDate: '2025-10-31',
|
|
|
|
|
+ endDate: '2025-11-01'
|
|
|
|
|
+ },
|
|
|
|
|
+ routeType: 'departure',
|
|
|
|
|
+ isDisabled: 0,
|
|
|
|
|
+ isDeleted: 0,
|
|
|
|
|
+ createdAt: '2025-10-31T00:00:00Z',
|
|
|
|
|
+ updatedAt: '2025-10-31T00:00:00Z'
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ activities: [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ name: '测试活动',
|
|
|
|
|
+ description: null,
|
|
|
|
|
+ venueLocationId: 3,
|
|
|
|
|
+ venueLocation: {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ name: '活动场地',
|
|
|
|
|
+ provinceId: 3,
|
|
|
|
|
+ cityId: 3,
|
|
|
|
|
+ districtId: 3,
|
|
|
|
|
+ address: '活动场地地址'
|
|
|
|
|
+ },
|
|
|
|
|
+ startDate: '2025-10-31',
|
|
|
|
|
+ endDate: '2025-11-01'
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+// Mock date-fns
|
|
|
|
|
+jest.mock('date-fns', () => ({
|
|
|
|
|
+ format: jest.fn(() => '08:00'),
|
|
|
|
|
+ zhCN: {}
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+// Mock components
|
|
|
|
|
+jest.mock('../../src/components/ui/navbar', () => ({
|
|
|
|
|
+ Navbar: ({ title, onClickLeft }: { title: string; onClickLeft: () => void }) => (
|
|
|
|
|
+ <div data-testid="navbar">
|
|
|
|
|
+ <div>{title}</div>
|
|
|
|
|
+ <button onClick={onClickLeft}>返回</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ),
|
|
|
|
|
+ NavbarPresets: {
|
|
|
|
|
+ primary: {}
|
|
|
|
|
+ }
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+const createTestQueryClient = () => new QueryClient({
|
|
|
|
|
+ defaultOptions: {
|
|
|
|
|
+ queries: { retry: false },
|
|
|
|
|
+ },
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
|
|
|
|
+ <QueryClientProvider client={createTestQueryClient()}>
|
|
|
|
|
+ {children}
|
|
|
|
|
+ </QueryClientProvider>
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+describe('ScheduleListPage', () => {
|
|
|
|
|
+ beforeEach(() => {
|
|
|
|
|
+ jest.clearAllMocks()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ test('应该正确渲染班次页面', async () => {
|
|
|
|
|
+ render(
|
|
|
|
|
+ <Wrapper>
|
|
|
|
|
+ <ScheduleListPage />
|
|
|
|
|
+ </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.getByText('可选班次')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('(1个班次)')).toBeInTheDocument()
|
|
|
|
|
+
|
|
|
|
|
+ // 验证班次详情显示
|
|
|
|
|
+ expect(screen.getByText('08:00')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('¥100/人')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('大巴拼车')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('拼车')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('上车点')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('下车点')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('剩余20/40座')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('空调')).toBeInTheDocument()
|
|
|
|
|
+ expect(screen.getByText('免费WiFi')).toBeInTheDocument()
|
|
|
|
|
+
|
|
|
|
|
+ // 验证预订按钮
|
|
|
|
|
+ expect(screen.getByText('立即购票')).toBeInTheDocument()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ test('应该隐藏日期选择功能', async () => {
|
|
|
|
|
+ render(
|
|
|
|
|
+ <Wrapper>
|
|
|
|
|
+ <ScheduleListPage />
|
|
|
|
|
+ </Wrapper>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ // 等待数据加载
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(screen.getByText('选择班次')).toBeInTheDocument()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 验证日期选择UI组件已隐藏
|
|
|
|
|
+ // 不应该显示"选择出发日期"文本
|
|
|
|
|
+ const dateSelectionText = screen.queryByText('选择出发日期')
|
|
|
|
|
+ expect(dateSelectionText).not.toBeInTheDocument()
|
|
|
|
|
+
|
|
|
|
|
+ // 不应该显示日期选项
|
|
|
|
|
+ const dateOptions = screen.queryByText('2025-10-31')
|
|
|
|
|
+ expect(dateOptions).not.toBeInTheDocument()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ test('应该显示加载状态', () => {
|
|
|
|
|
+ // Mock loading state
|
|
|
|
|
+ const mockApi = require('../../src/api')
|
|
|
|
|
+ mockApi.routeClient.search.$get.mockImplementationOnce(
|
|
|
|
|
+ () => new Promise(() => {}) // Never resolves to simulate loading
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ render(
|
|
|
|
|
+ <Wrapper>
|
|
|
|
|
+ <ScheduleListPage />
|
|
|
|
|
+ </Wrapper>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ expect(screen.getByText('加载中...')).toBeInTheDocument()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ test('应该显示无班次状态', async () => {
|
|
|
|
|
+ // Mock empty data
|
|
|
|
|
+ const mockApi = require('../../src/api')
|
|
|
|
|
+ mockApi.routeClient.search.$get.mockResolvedValueOnce({
|
|
|
|
|
+ status: 200,
|
|
|
|
|
+ json: () => Promise.resolve({
|
|
|
|
|
+ data: {
|
|
|
|
|
+ routes: [],
|
|
|
|
|
+ activities: []
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ render(
|
|
|
|
|
+ <Wrapper>
|
|
|
|
|
+ <ScheduleListPage />
|
|
|
|
|
+ </Wrapper>
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ await waitFor(() => {
|
|
|
|
|
+ expect(screen.getByText('暂无班次')).toBeInTheDocument()
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ expect(screen.getByText('请选择其他日期查看')).toBeInTheDocument()
|
|
|
|
|
+ })
|
|
|
|
|
+})
|