| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- /**
- * 订单页面组件测试
- */
- import React from 'react'
- import { render, screen, fireEvent, waitFor } from '@testing-library/react'
- import '@testing-library/jest-dom'
- import OrderPage from '@/pages/order/index'
- // Mock Taro相关API
- const mockNavigateTo = jest.fn()
- const mockShowToast = jest.fn()
- const mockUseRouter = jest.fn()
- jest.mock('@tarojs/taro', () => ({
- useRouter: () => mockUseRouter(),
- navigateTo: mockNavigateTo,
- showToast: mockShowToast,
- requestPayment: jest.fn(),
- getSystemInfoSync: () => ({
- statusBarHeight: 20
- }),
- getMenuButtonBoundingClientRect: () => ({
- width: 87,
- height: 32,
- top: 48,
- right: 314,
- bottom: 80,
- left: 227
- }),
- navigateBack: jest.fn()
- }))
- // Mock React Query
- const mockUseQuery = jest.fn()
- const mockUseMutation = jest.fn()
- jest.mock('@tanstack/react-query', () => ({
- useQuery: (options: any) => mockUseQuery(options),
- useMutation: (options: any) => mockUseMutation(options)
- }))
- // Mock cn工具函数
- jest.mock('@/utils/cn', () => ({
- cn: (...inputs: any[]) => inputs.join(' ')
- }))
- // Mock platform工具
- jest.mock('@/utils/platform', () => ({
- isWeapp: () => false
- }))
- // Mock navbar组件
- // jest.mock('@/components/ui/navbar', () => ({
- // Navbar: ({ children }: any) => <div data-testid="navbar">{children}</div>,
- // NavbarPresets: {
- // primary: {
- // backgroundColor: 'bg-primary-600',
- // textColor: 'text-white'
- // }
- // }
- // }))
- // Mock API客户端
- jest.mock('@/api', () => ({
- orderClient: {
- $post: jest.fn()
- },
- paymentClient: {
- $post: jest.fn()
- },
- routeClient: {
- ':id': {
- $get: jest.fn()
- }
- },
- passengerClient: {
- $get: jest.fn()
- }
- }))
- describe('OrderPage', () => {
- const mockRouteData = {
- id: 1,
- name: '测试路线',
- pickupPoint: '上车地点',
- dropoffPoint: '下车地点',
- departureTime: '2025-10-24 10:00:00',
- price: 100,
- vehicleType: '商务车',
- travelMode: 'charter',
- availableSeats: 10
- }
- const mockPassengers = [
- {
- id: 1,
- name: '张三',
- idType: '身份证',
- idNumber: '110101199001011234',
- phone: '13800138000'
- }
- ]
- beforeEach(() => {
- mockUseRouter.mockReturnValue({
- params: {
- routeId: '1',
- activityName: '测试活动',
- type: 'business-charter'
- }
- })
- mockUseQuery.mockImplementation((options) => {
- if (options.queryKey?.[0] === 'route') {
- return {
- data: mockRouteData,
- isLoading: false
- }
- }
- if (options.queryKey?.[0] === 'passengers') {
- return {
- data: mockPassengers,
- isLoading: false
- }
- }
- return { data: null, isLoading: false }
- })
- mockUseMutation.mockImplementation((options) => ({
- mutateAsync: options.mutationFn,
- isPending: false
- }))
- mockNavigateTo.mockClear()
- mockShowToast.mockClear()
- })
- it('should render order page correctly', () => {
- render(<OrderPage />)
- expect(screen.getByTestId('order-navbar')).toBeInTheDocument()
- expect(screen.getByTestId('activity-name')).toHaveTextContent('测试活动')
- expect(screen.getByTestId('service-type')).toHaveTextContent('包车服务')
- expect(screen.getByTestId('price-per-unit')).toHaveTextContent('¥100/车')
- })
- it('should show loading state', () => {
- mockUseQuery.mockImplementation((options) => {
- if (options.queryKey?.[0] === 'route') {
- return { data: null, isLoading: true }
- }
- return { data: null, isLoading: false }
- })
- render(<OrderPage />)
- expect(screen.getByText('加载中...')).toBeInTheDocument()
- })
- it('should handle phone number acquisition', async () => {
- render(<OrderPage />)
- const getPhoneButton = screen.getByTestId('get-phone-button')
- expect(getPhoneButton).toBeInTheDocument()
- // 这里可以模拟获取手机号的交互
- // 由于Taro API的限制,实际测试可能需要更复杂的模拟
- })
- it('should handle passenger selection', async () => {
- render(<OrderPage />)
- const addPassengerButton = screen.getByTestId('add-passenger-button')
- fireEvent.click(addPassengerButton)
- // 应该显示乘客选择器
- await waitFor(() => {
- expect(screen.getByText('选择乘车人')).toBeInTheDocument()
- })
- // 选择乘客
- const passengerCard = screen.getByText('张三')
- fireEvent.click(passengerCard)
- // 应该显示乘客已添加的提示
- await waitFor(() => {
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '乘客添加成功',
- icon: 'success',
- duration: 1500
- })
- })
- })
- it('should validate payment prerequisites', async () => {
- const { mutateAsync: createOrderMutation } = mockUseMutation()
- render(<OrderPage />)
- const payButton = screen.getByTestId('pay-button')
- fireEvent.click(payButton)
- // 应该显示需要获取手机号的提示
- await waitFor(() => {
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '请先获取手机号',
- icon: 'none',
- duration: 2000
- })
- })
- // 应该显示需要添加乘车人的提示
- // 这里需要模拟已获取手机号但未添加乘客的情况
- })
- it('should handle successful payment flow', async () => {
- // Mock成功的订单创建
- const mockOrderResponse = { id: 123 }
- const mockPaymentResponse = {
- timeStamp: '1234567890',
- nonceStr: 'abcdefghijklmnopqrstuvwxyz',
- package: 'prepay_id=wx1234567890',
- signType: 'RSA',
- paySign: 'abcdefghijklmnopqrstuvwxyz1234567890'
- }
- mockUseMutation.mockImplementation((options) => ({
- mutateAsync: async (data: any) => {
- if (options.mutationFn === expect.any(Function)) {
- if (data.routeId) {
- // 订单创建
- return mockOrderResponse
- } else if (data.orderId) {
- // 支付创建
- return mockPaymentResponse
- }
- }
- return null
- },
- isPending: false
- }))
- // Mock成功的微信支付
- const mockRequestPayment = require('@tarojs/taro').requestPayment
- mockRequestPayment.mockResolvedValue({})
- render(<OrderPage />)
- // 这里需要模拟已获取手机号和添加乘客的状态
- // 由于状态管理的复杂性,这个测试可能需要更详细的设置
- const payButton = screen.getByTestId('pay-button')
- fireEvent.click(payButton)
- // 应该调用订单创建和支付创建
- await waitFor(() => {
- expect(mockRequestPayment).toHaveBeenCalled()
- })
- // 应该跳转到支付成功页面
- await waitFor(() => {
- expect(mockNavigateTo).toHaveBeenCalledWith({
- url: '/pages/pay-success/index?orderId=123&totalPrice=100&passengerCount=0'
- })
- })
- })
- it('should handle payment failure', async () => {
- // Mock失败的订单创建
- mockUseMutation.mockImplementation((options) => ({
- mutateAsync: async () => {
- throw new Error('支付创建失败')
- },
- isPending: false
- }))
- render(<OrderPage />)
- const payButton = screen.getByTestId('pay-button')
- fireEvent.click(payButton)
- // 应该显示支付失败的提示
- await waitFor(() => {
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '支付失败,请重试',
- icon: 'error',
- duration: 2000
- })
- })
- })
- it('should handle user cancellation', async () => {
- // Mock用户取消支付
- const mockRequestPayment = require('@tarojs/taro').requestPayment
- mockRequestPayment.mockRejectedValue({
- errMsg: 'requestPayment:fail cancel'
- })
- render(<OrderPage />)
- const payButton = screen.getByTestId('pay-button')
- fireEvent.click(payButton)
- // 应该显示支付已取消的提示
- await waitFor(() => {
- expect(mockShowToast).toHaveBeenCalledWith({
- title: '支付已取消',
- icon: 'none',
- duration: 2000
- })
- })
- })
- it('should calculate total price correctly', () => {
- render(<OrderPage />)
- // 检查总价计算
- // 包车模式下应该显示固定价格
- expect(screen.getByTestId('total-price')).toHaveTextContent('¥100')
- })
- it('should validate seat availability', async () => {
- // 测试拼车模式的座位验证
- mockUseRouter.mockReturnValue({
- params: {
- routeId: '1',
- activityName: '测试活动',
- type: 'carpool' // 拼车模式
- }
- })
- render(<OrderPage />)
- // 这里需要模拟超过座位数量的乘客添加
- // 然后测试支付时的验证逻辑
- })
- })
|