|
|
@@ -2,72 +2,60 @@
|
|
|
* 订单页面组件测试
|
|
|
*/
|
|
|
|
|
|
+import React from 'react'
|
|
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
|
import '@testing-library/jest-dom'
|
|
|
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
|
import OrderPage from '@/pages/order/index'
|
|
|
|
|
|
-// Mock Taro相关API
|
|
|
-const mockNavigateTo = jest.fn()
|
|
|
-const mockUseRouter = jest.fn()
|
|
|
-
|
|
|
-// Mock 封装的 toast 函数
|
|
|
-let mockShowToast: jest.Mock
|
|
|
-
|
|
|
-jest.mock('@tarojs/taro', () => ({
|
|
|
- navigateBack: jest.fn(),
|
|
|
- useRouter: () => mockUseRouter(),
|
|
|
- navigateTo: mockNavigateTo,
|
|
|
- requestPayment: jest.fn(),
|
|
|
- getSystemInfoSync: () => ({
|
|
|
- statusBarHeight: 20
|
|
|
- }),
|
|
|
- getMenuButtonBoundingClientRect: () => ({
|
|
|
- width: 87,
|
|
|
- height: 32,
|
|
|
- top: 48,
|
|
|
- right: 314,
|
|
|
- bottom: 80,
|
|
|
- left: 227
|
|
|
- })
|
|
|
-}))
|
|
|
+// 导入 Taro mock 函数
|
|
|
+import taroMock, { mockUseRouter } from '../../tests/__mocks__/taroMock'
|
|
|
|
|
|
-// Mock 封装的 toast 工具函数
|
|
|
-jest.mock('@/utils/toast', () => ({
|
|
|
- showToast: jest.fn()
|
|
|
-}))
|
|
|
|
|
|
-beforeAll(() => {
|
|
|
- mockShowToast = require('@/utils/toast').showToast
|
|
|
-})
|
|
|
|
|
|
// 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 React Query
|
|
|
+jest.mock('@tanstack/react-query', () => {
|
|
|
+ const actual = jest.requireActual('@tanstack/react-query')
|
|
|
+ return {
|
|
|
+ ...actual,
|
|
|
+ useQuery: (options: any) => mockUseQuery(options),
|
|
|
+ useMutation: (options: any) => mockUseMutation(options)
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 创建测试用的 QueryClient
|
|
|
+const createTestQueryClient = () => new QueryClient({
|
|
|
+ defaultOptions: {
|
|
|
+ queries: {
|
|
|
+ retry: 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'
|
|
|
-// }
|
|
|
-// }
|
|
|
+// 包装组件
|
|
|
+const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
|
|
+ const queryClient = createTestQueryClient()
|
|
|
+ return (
|
|
|
+ <QueryClientProvider client={queryClient}>
|
|
|
+ {children}
|
|
|
+ </QueryClientProvider>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// // Mock cn工具函数
|
|
|
+// jest.mock('@/utils/cn', () => ({
|
|
|
+// cn: (...inputs: any[]) => inputs.join(' ')
|
|
|
+// }))
|
|
|
+
|
|
|
+// // Mock platform工具
|
|
|
+// jest.mock('@/utils/platform', () => ({
|
|
|
+// isWeapp: () => false
|
|
|
// }))
|
|
|
|
|
|
// Mock Dialog组件
|
|
|
@@ -128,6 +116,8 @@ describe('OrderPage', () => {
|
|
|
]
|
|
|
|
|
|
beforeEach(() => {
|
|
|
+ jest.clearAllMocks()
|
|
|
+
|
|
|
mockUseRouter.mockReturnValue({
|
|
|
params: {
|
|
|
routeId: '1',
|
|
|
@@ -157,12 +147,22 @@ describe('OrderPage', () => {
|
|
|
isPending: false
|
|
|
}))
|
|
|
|
|
|
- mockNavigateTo.mockClear()
|
|
|
- mockShowToast.mockClear()
|
|
|
+ // 重置所有 Taro mock 调用记录
|
|
|
+ taroMock.showToast.mockClear()
|
|
|
+ taroMock.navigateTo.mockClear()
|
|
|
+ taroMock.requestPayment.mockClear()
|
|
|
+ taroMock.getEnv.mockClear()
|
|
|
+
|
|
|
+ // 设置默认环境为 WEB
|
|
|
+ taroMock.getEnv.mockReturnValue('WEB')
|
|
|
})
|
|
|
|
|
|
it('should render order page correctly', () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
expect(screen.getByTestId('order-navbar')).toBeInTheDocument()
|
|
|
expect(screen.getByTestId('activity-name')).toHaveTextContent('测试活动')
|
|
|
@@ -178,13 +178,21 @@ describe('OrderPage', () => {
|
|
|
return { data: null, isLoading: false }
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
expect(screen.getByText('加载中...')).toBeInTheDocument()
|
|
|
})
|
|
|
|
|
|
it('should handle phone number acquisition', async () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
const getPhoneButton = screen.getByTestId('get-phone-button')
|
|
|
expect(getPhoneButton).toBeInTheDocument()
|
|
|
@@ -196,7 +204,11 @@ describe('OrderPage', () => {
|
|
|
it('should handle passenger selection', async () => {
|
|
|
// 模拟已获取手机号的状态
|
|
|
// 由于组件内部状态难以直接模拟,我们测试乘客选择器的基本功能
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 测试乘客选择器Dialog组件是否正常工作
|
|
|
// 这里我们主要验证乘客选择器的渲染逻辑
|
|
|
@@ -211,7 +223,7 @@ describe('OrderPage', () => {
|
|
|
|
|
|
// 由于未获取手机号,应该显示提示
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -220,14 +232,18 @@ describe('OrderPage', () => {
|
|
|
})
|
|
|
|
|
|
it('should validate payment prerequisites', async () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
const payButton = screen.getByTestId('pay-button')
|
|
|
fireEvent.click(payButton)
|
|
|
|
|
|
// 应该显示需要获取手机号的提示
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -264,10 +280,13 @@ describe('OrderPage', () => {
|
|
|
}))
|
|
|
|
|
|
// Mock成功的微信支付
|
|
|
- const mockRequestPayment = require('@tarojs/taro').requestPayment
|
|
|
- mockRequestPayment.mockResolvedValue({})
|
|
|
+ taroMock.requestPayment.mockResolvedValue({})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 由于状态管理的复杂性,我们主要测试支付按钮的基本功能
|
|
|
const payButton = screen.getByTestId('pay-button')
|
|
|
@@ -277,7 +296,7 @@ describe('OrderPage', () => {
|
|
|
fireEvent.click(payButton)
|
|
|
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -294,14 +313,18 @@ describe('OrderPage', () => {
|
|
|
isPending: false
|
|
|
}))
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
const payButton = screen.getByTestId('pay-button')
|
|
|
fireEvent.click(payButton)
|
|
|
|
|
|
// 应该显示需要获取手机号的提示(因为未获取手机号)
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -311,19 +334,22 @@ describe('OrderPage', () => {
|
|
|
|
|
|
it('should handle user cancellation', async () => {
|
|
|
// Mock用户取消支付
|
|
|
- const mockRequestPayment = require('@tarojs/taro').requestPayment
|
|
|
- mockRequestPayment.mockRejectedValue({
|
|
|
+ taroMock.requestPayment.mockRejectedValue({
|
|
|
errMsg: 'requestPayment:fail cancel'
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
const payButton = screen.getByTestId('pay-button')
|
|
|
fireEvent.click(payButton)
|
|
|
|
|
|
// 应该显示需要获取手机号的提示(因为未获取手机号)
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -332,7 +358,11 @@ describe('OrderPage', () => {
|
|
|
})
|
|
|
|
|
|
it('should calculate total price correctly', () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 检查总价计算
|
|
|
// 包车模式下应该显示固定价格
|
|
|
@@ -372,7 +402,11 @@ describe('OrderPage', () => {
|
|
|
return { data: null, isLoading: false }
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证拼车模式下的座位限制显示
|
|
|
// 由于组件内部状态管理,我们主要验证基本功能
|
|
|
@@ -380,7 +414,11 @@ describe('OrderPage', () => {
|
|
|
})
|
|
|
|
|
|
it('should handle successful phone number acquisition', async () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 由于组件内部状态管理,我们主要验证获取手机号按钮的存在
|
|
|
const getPhoneButton = screen.getByTestId('get-phone-button')
|
|
|
@@ -391,7 +429,11 @@ describe('OrderPage', () => {
|
|
|
})
|
|
|
|
|
|
it('should handle phone number acquisition failure', async () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 由于组件内部状态管理,我们主要验证获取手机号按钮的存在
|
|
|
const getPhoneButton = screen.getByTestId('get-phone-button')
|
|
|
@@ -399,7 +441,11 @@ describe('OrderPage', () => {
|
|
|
})
|
|
|
|
|
|
it('should handle passenger deletion', async () => {
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 由于组件内部状态管理,我们主要验证删除按钮的存在
|
|
|
// 这里需要模拟有乘客的情况,但由于状态是内部的,我们简化测试
|
|
|
@@ -420,7 +466,11 @@ describe('OrderPage', () => {
|
|
|
return { data: null, isLoading: false }
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证组件能够处理错误情况而不崩溃
|
|
|
// 当路线数据加载失败时,组件应该显示加载状态
|
|
|
@@ -446,7 +496,11 @@ describe('OrderPage', () => {
|
|
|
return { data: null, isLoading: false }
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证组件能够处理错误情况而不崩溃
|
|
|
expect(screen.getByTestId('order-navbar')).toBeInTheDocument()
|
|
|
@@ -462,14 +516,18 @@ describe('OrderPage', () => {
|
|
|
isPending: false
|
|
|
}))
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
const payButton = screen.getByTestId('pay-button')
|
|
|
fireEvent.click(payButton)
|
|
|
|
|
|
// 应该显示需要获取手机号的提示(因为未获取手机号)
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -500,14 +558,18 @@ describe('OrderPage', () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
const payButton = screen.getByTestId('pay-button')
|
|
|
fireEvent.click(payButton)
|
|
|
|
|
|
// 应该显示需要获取手机号的提示(因为未获取手机号)
|
|
|
await waitFor(() => {
|
|
|
- expect(mockShowToast).toHaveBeenCalledWith({
|
|
|
+ expect(taroMock.showToast).toHaveBeenCalledWith({
|
|
|
title: '请先获取手机号',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
@@ -540,7 +602,11 @@ describe('OrderPage', () => {
|
|
|
return { data: null, isLoading: false }
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证拼车模式下的显示
|
|
|
expect(screen.getByTestId('service-type')).toHaveTextContent('班次信息')
|
|
|
@@ -557,7 +623,11 @@ describe('OrderPage', () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证包车模式下的显示
|
|
|
expect(screen.getByTestId('service-type')).toHaveTextContent('包车服务')
|
|
|
@@ -574,7 +644,11 @@ describe('OrderPage', () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证空活动名称时的默认显示
|
|
|
expect(screen.getByTestId('activity-name')).toHaveTextContent('活动')
|
|
|
@@ -591,7 +665,11 @@ describe('OrderPage', () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- render(<OrderPage />)
|
|
|
+ render(
|
|
|
+ <Wrapper>
|
|
|
+ <OrderPage />
|
|
|
+ </Wrapper>
|
|
|
+ )
|
|
|
|
|
|
// 验证URL编码的活动名称被正确解码
|
|
|
expect(screen.getByTestId('activity-name')).toHaveTextContent('测试活动名称')
|