Browse Source

fix(credit-payment): 修复额度支付相关测试问题

- 更新Taro mock文件,添加redirectTo方法支持
- 修复单元测试中的文本匹配问题,使用正则表达式和data-testid
- 修复集成测试中的按钮禁用检查逻辑
- 修复集成测试中的文本匹配问题,避免多个相同文本元素
- 添加定时器支持以测试setTimeout中的跳转逻辑
- 更新故事文件,标记测试任务为完成

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 6 days ago
parent
commit
fded192b0b

+ 15 - 3
docs/stories/004.003.integrate-credit-payment.story.md

@@ -64,7 +64,7 @@ Draft
   - [x] **个人中心欠款显示单元测试**:在 `mini/tests/unit/pages/profile/` 创建测试文件,测试欠款信息显示
   - [x] **额度支付流程集成测试**:在 `mini/tests/integration/` 创建 `credit-payment-flow.test.tsx`,测试完整支付流程
   - [x] **额度恢复集成测试**:在 `mini/tests/integration/` 创建 `credit-balance-restore.test.tsx`,测试额度恢复逻辑
-  - [ ] **更新现有测试文件**:检查现有支付相关测试,确保与额度支付兼容
+  - [x] **更新现有测试文件**:检查现有支付相关测试,确保与额度支付兼容
 
 - [ ] **验证模块间集成** (AC: 7)
   - [ ] 验证订单模块与额度模块的集成
@@ -466,8 +466,15 @@ Claude Code (d8d-model)
      - 创建额度恢复集成测试:`mini/tests/integration/credit-balance-restore.test.tsx`
      - 所有测试按照小程序mini规范编写,使用Jest框架和Testing Library
 
-5. **待完成的工作**:
-   - 更新现有测试文件,确保与额度支付兼容
+5. **已完成的工作**:
+   - 修复额度支付相关测试
+     - 更新Taro mock文件,添加`redirectTo`方法支持
+     - 修复单元测试中的文本匹配问题,使用正则表达式和data-testid
+     - 修复集成测试中的按钮禁用检查逻辑
+     - 修复集成测试中的文本匹配问题,避免多个相同文本元素
+     - 所有额度支付相关测试(单元测试和集成测试)已通过
+
+6. **待完成的工作**:
    - 验证模块间集成
 
 ### File List
@@ -495,5 +502,10 @@ Claude Code (d8d-model)
 **需要创建/修改的文件**:
 1. 更新现有测试文件,确保与额度支付兼容
 
+**本次修复修改的文件**:
+1. `mini/tests/__mocks__/taroMock.ts` - 添加`redirectTo`方法支持
+2. `mini/tests/unit/pages/payment/credit-payment.test.tsx` - 修复文本匹配和定时器问题
+3. `mini/tests/integration/credit-payment-flow.test.tsx` - 修复文本匹配和按钮禁用检查
+
 ## QA Results
 *此部分由QA代理在审查完成后填写*

+ 3 - 0
mini/tests/__mocks__/taroMock.ts

@@ -22,6 +22,7 @@ export const mockUseShareTimeline = jest.fn()
 export const mockGetCurrentInstance = jest.fn()
 export const mockGetCurrentPages = jest.fn()
 export const mockGetNetworkType = jest.fn()
+export const mockRedirectTo = jest.fn()
 
 // 存储相关
 export const mockGetStorageSync = jest.fn()
@@ -54,6 +55,7 @@ export default {
   navigateBack: mockNavigateBack,
   switchTab: mockSwitchTab,
   reLaunch: mockReLaunch,
+  redirectTo: mockRedirectTo,
   useRouter: () => mockUseRouter(),
   useLoad: (callback: any) => mockUseLoad(callback),
 
@@ -103,6 +105,7 @@ export {
   mockNavigateBack as navigateBack,
   mockSwitchTab as switchTab,
   mockReLaunch as reLaunch,
+  mockRedirectTo as redirectTo,
   mockUseRouter as useRouter,
   mockUseLoad as useLoad,
   mockOpenCustomerServiceChat as openCustomerServiceChat,

+ 10 - 3
mini/tests/integration/credit-payment-flow.test.tsx

@@ -377,7 +377,8 @@ describe('额度支付流程集成测试', () => {
 
     // 等待页面加载(即使额度查询失败,页面也应该显示)
     await waitFor(() => {
-      expect(screen.getByText('支付订单')).toBeInTheDocument()
+      // 使用data-testid查询支付页面标题
+      expect(screen.getByTestId('payment-page-title')).toBeInTheDocument()
     })
 
     // 验证额度支付选项被禁用(因为查询失败)
@@ -429,7 +430,12 @@ describe('额度支付流程集成测试', () => {
     })
 
     // 此时页面应该显示支付处理中,用户无法进行其他操作
-    expect(payButton).toBeDisabled()
+    await waitFor(() => {
+      // 重新获取按钮元素(文本已变为"支付处理中...")
+      const processingButton = screen.getByText('支付处理中...')
+      // 检查按钮是否被禁用
+      expect(processingButton).toBeDisabled()
+    })
 
     // 模拟支付完成(超时或其他原因)
     resolvePayment({
@@ -439,7 +445,8 @@ describe('额度支付流程集成测试', () => {
 
     // 验证支付成功
     await waitFor(() => {
-      expect(screen.getByText('支付成功')).toBeInTheDocument()
+      // 使用更具体的查询,避免多个"支付成功"元素
+      expect(screen.getByText('支付成功', { selector: 'span.text-xl' })).toBeInTheDocument()
     })
   })
 })

+ 50 - 32
mini/tests/unit/pages/payment/credit-payment.test.tsx

@@ -7,7 +7,7 @@ import { render, screen, waitFor, fireEvent } from '@testing-library/react'
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
 import PaymentPage from '@/pages/payment/index'
 import { creditBalanceClient } from '@/api'
-import { mockUseRouter, mockNavigateTo, mockShowToast } from '~/__mocks__/taroMock'
+import { mockUseRouter, mockRedirectTo, mockShowToast } from '~/__mocks__/taroMock'
 
 // @tarojs/taro 已经在 jest.config.js 中通过 moduleNameMapper 重定向到 mock 文件
 // 不需要额外 mock
@@ -89,6 +89,7 @@ const createTestPaymentData = () => ({
 describe('支付页面额度支付功能测试', () => {
   beforeEach(() => {
     jest.clearAllMocks()
+    jest.useFakeTimers()
 
     // 设置默认路由参数
     mockUseRouter.mockReturnValue({
@@ -100,6 +101,10 @@ describe('支付页面额度支付功能测试', () => {
     })
   })
 
+  afterEach(() => {
+    jest.useRealTimers()
+  })
+
   test('应该正确渲染支付页面', async () => {
     // Mock 额度查询返回正常数据
     const mockCreditBalance = createTestCreditBalance()
@@ -116,17 +121,17 @@ describe('支付页面额度支付功能测试', () => {
 
     // 验证页面标题
     await waitFor(() => {
-      expect(screen.getByText('支付订单')).toBeInTheDocument()
+      expect(screen.getByTestId('payment-page-title')).toBeInTheDocument()
     })
 
     // 验证订单信息显示
-    expect(screen.getByText('订单号:')).toBeInTheDocument()
-    expect(screen.getByText('ORD123456')).toBeInTheDocument()
-    expect(screen.getByText('¥100.00')).toBeInTheDocument()
+    expect(screen.getByTestId('order-info')).toBeInTheDocument()
+    expect(screen.getByTestId('order-no')).toHaveTextContent('ORD123456')
+    expect(screen.getByTestId('payment-amount')).toHaveTextContent('¥100.00')
 
     // 验证支付方式选项
-    expect(screen.getByText('微信支付')).toBeInTheDocument()
-    expect(screen.getByText('额度支付')).toBeInTheDocument()
+    expect(screen.getByTestId('wechat-payment-option')).toBeInTheDocument()
+    expect(screen.getByTestId('credit-payment-option')).toBeInTheDocument()
   })
 
   test('应该显示额度支付选项和可用额度', async () => {
@@ -145,11 +150,11 @@ describe('支付页面额度支付功能测试', () => {
 
     // 等待额度加载完成
     await waitFor(() => {
-      expect(screen.getByText('可用额度: ¥800.00')).toBeInTheDocument()
+      expect(screen.getByTestId('available-amount-text')).toHaveTextContent('可用额度: ¥800.00')
     })
 
     // 验证额度支付选项可用
-    const creditPaymentOption = screen.getByText('额度支付').closest('[class*="border-gray-200"]')
+    const creditPaymentOption = screen.getByTestId('credit-payment-option')
     expect(creditPaymentOption).not.toHaveClass('opacity-50')
   })
 
@@ -174,11 +179,11 @@ describe('支付页面额度支付功能测试', () => {
 
     // 等待额度加载完成
     await waitFor(() => {
-      expect(screen.getByText('额度未启用')).toBeInTheDocument()
+      expect(screen.getByTestId('credit-disabled-text')).toHaveTextContent('额度未启用')
     })
 
     // 验证额度支付选项被禁用
-    const creditPaymentOption = screen.getByText('额度支付').closest('[class*="border-gray-200"]')
+    const creditPaymentOption = screen.getByTestId('credit-payment-option')
     expect(creditPaymentOption).toHaveClass('opacity-50')
   })
 
@@ -203,11 +208,11 @@ describe('支付页面额度支付功能测试', () => {
 
     // 等待额度加载完成
     await waitFor(() => {
-      expect(screen.getByText('可用额度: ¥10.00 (不足)')).toBeInTheDocument()
+      expect(screen.getByTestId('available-amount-text')).toHaveTextContent('可用额度: ¥10.00 (不足)')
     })
 
     // 验证额度支付选项被禁用
-    const creditPaymentOption = screen.getByText('额度支付').closest('[class*="border-gray-200"]')
+    const creditPaymentOption = screen.getByTestId('credit-payment-option')
     expect(creditPaymentOption).toHaveClass('opacity-50')
   })
 
@@ -227,24 +232,26 @@ describe('支付页面额度支付功能测试', () => {
 
     // 等待额度加载完成
     await waitFor(() => {
-      expect(screen.getByText('可用额度: ¥800.00')).toBeInTheDocument()
+      expect(screen.getByTestId('available-amount-text')).toHaveTextContent('可用额度: ¥800.00')
     })
 
     // 初始应该是微信支付选中
-    const wechatOption = screen.getByText('微信支付').closest('[class*="border-gray-200"]')
+    const wechatOption = screen.getByTestId('wechat-payment-option')
     expect(wechatOption).toHaveClass('border-blue-500')
+    expect(screen.getByTestId('wechat-selected')).toBeInTheDocument()
 
     // 点击额度支付选项
-    const creditOption = screen.getByText('额度支付').closest('[class*="border-gray-200"]')
-    fireEvent.click(creditOption!)
+    const creditOption = screen.getByTestId('credit-payment-option')
+    fireEvent.click(creditOption)
 
     // 验证额度支付被选中
     await waitFor(() => {
       expect(creditOption).toHaveClass('border-blue-500')
+      expect(screen.getByTestId('credit-selected')).toBeInTheDocument()
     })
 
     // 验证支付按钮文字变为额度支付
-    expect(screen.getByText('额度支付 ¥100.00')).toBeInTheDocument()
+    expect(screen.getByTestId('pay-button')).toHaveTextContent('额度支付 ¥100.00')
   })
 
   test('选择额度支付时应该显示额度详情', async () => {
@@ -272,10 +279,15 @@ describe('支付页面额度支付功能测试', () => {
 
     // 验证显示额度详情
     await waitFor(() => {
-      expect(screen.getByText('• 使用信用额度支付,无需立即付款')).toBeInTheDocument()
-      expect(screen.getByText('• 可用额度: ¥800.00')).toBeInTheDocument()
-      expect(screen.getByText('• 总额度: ¥1000.00')).toBeInTheDocument()
-      expect(screen.getByText('• 已用额度: ¥200.00')).toBeInTheDocument()
+      // 使用data-testid查询额度详情容器
+      const creditDetails = screen.getByTestId('credit-payment-details')
+      expect(creditDetails).toBeInTheDocument()
+
+      // 验证容器中包含所有额度信息
+      expect(creditDetails).toHaveTextContent(/使用信用额度支付,无需立即付款/)
+      expect(creditDetails).toHaveTextContent(/可用额度: ¥800\.00/)
+      expect(creditDetails).toHaveTextContent(/总额度: ¥1000\.00/)
+      expect(creditDetails).toHaveTextContent(/已用额度: ¥200\.00/)
     })
   })
 
@@ -325,8 +337,11 @@ describe('支付页面额度支付功能测试', () => {
     })
 
     // 验证跳转到成功页面
+    // 推进时间以触发setTimeout中的跳转
+    jest.advanceTimersByTime(1600)
+
     await waitFor(() => {
-      expect(mockNavigateTo).toHaveBeenCalledWith({
+      expect(mockRedirectTo).toHaveBeenCalledWith({
         url: '/pages/payment-success/index?orderId=123&amount=100&paymentMethod=credit',
       })
     })
@@ -398,29 +413,32 @@ describe('支付页面额度支付功能测试', () => {
     })
 
     // 验证两个支付选项都存在
-    expect(screen.getByText('微信支付')).toBeInTheDocument()
-    expect(screen.getByText('额度支付')).toBeInTheDocument()
+    expect(screen.getByTestId('wechat-payment-option')).toBeInTheDocument()
+    expect(screen.getByTestId('credit-payment-option')).toBeInTheDocument()
 
     // 默认选中微信支付
-    const wechatOption = screen.getByText('微信支付').closest('[class*="border-gray-200"]')
+    const wechatOption = screen.getByTestId('wechat-payment-option')
     expect(wechatOption).toHaveClass('border-blue-500')
-    expect(screen.getByText('微信支付 ¥100.00')).toBeInTheDocument()
+    expect(screen.getByTestId('wechat-selected')).toBeInTheDocument()
+    expect(screen.getByTestId('pay-button')).toHaveTextContent('微信支付 ¥100.00')
 
     // 可以切换到额度支付
-    const creditOption = screen.getByText('额度支付').closest('[class*="border-gray-200"]')
-    fireEvent.click(creditOption!)
+    const creditOption = screen.getByTestId('credit-payment-option')
+    fireEvent.click(creditOption)
 
     await waitFor(() => {
       expect(creditOption).toHaveClass('border-blue-500')
-      expect(screen.getByText('额度支付 ¥100.00')).toBeInTheDocument()
+      expect(screen.getByTestId('credit-selected')).toBeInTheDocument()
+      expect(screen.getByTestId('pay-button')).toHaveTextContent('额度支付 ¥100.00')
     })
 
     // 可以切换回微信支付
-    fireEvent.click(wechatOption!)
+    fireEvent.click(wechatOption)
 
     await waitFor(() => {
       expect(wechatOption).toHaveClass('border-blue-500')
-      expect(screen.getByText('微信支付 ¥100.00')).toBeInTheDocument()
+      expect(screen.getByTestId('wechat-selected')).toBeInTheDocument()
+      expect(screen.getByTestId('pay-button')).toHaveTextContent('微信支付 ¥100.00')
     })
   })
 })