|
|
@@ -32,7 +32,8 @@ const mockCartItems = [
|
|
|
},
|
|
|
]
|
|
|
|
|
|
-// Mock API客户端
|
|
|
+
|
|
|
+// mock数据
|
|
|
const mockGoodsData = {
|
|
|
1: {
|
|
|
id: 1,
|
|
|
@@ -78,36 +79,36 @@ const mockGoodsData = {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const mockGoodsClient = {
|
|
|
- ':id': {
|
|
|
- $get: jest.fn(({ param }: any) => {
|
|
|
- const goodsId = param?.id
|
|
|
- const goodsData = mockGoodsData[goodsId as keyof typeof mockGoodsData] || mockGoodsData[1]
|
|
|
- return Promise.resolve({
|
|
|
- status: 200,
|
|
|
- json: () => Promise.resolve(goodsData)
|
|
|
- })
|
|
|
- }),
|
|
|
- children: {
|
|
|
- $get: jest.fn()
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+// 使用getter延迟创建mockGoodsClient
|
|
|
+let mockGoodsClient
|
|
|
|
|
|
jest.mock('@/api', () => {
|
|
|
- // 如果mockGoodsClient已经定义,使用它;否则创建默认mock
|
|
|
- const goodsClientMock = typeof mockGoodsClient !== 'undefined' ? mockGoodsClient : {
|
|
|
- ':id': {
|
|
|
- $get: jest.fn(),
|
|
|
- children: {
|
|
|
- $get: jest.fn()
|
|
|
+ return {
|
|
|
+ get goodsClient() {
|
|
|
+ if (!mockGoodsClient) {
|
|
|
+ // 第一次访问时创建mock
|
|
|
+ mockGoodsClient = {
|
|
|
+ ':id': {
|
|
|
+ $get: jest.fn(({ param }: any) => {
|
|
|
+ const goodsId = param?.id
|
|
|
+ const idNum = Number(goodsId)
|
|
|
+ const goodsData = mockGoodsData[idNum] || mockGoodsData[1]
|
|
|
+ return Promise.resolve({
|
|
|
+ status: 200,
|
|
|
+ json: () => Promise.resolve(goodsData)
|
|
|
+ })
|
|
|
+ }),
|
|
|
+ children: {
|
|
|
+ $get: jest.fn()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ return mockGoodsClient
|
|
|
}
|
|
|
}
|
|
|
- return { goodsClient: goodsClientMock }
|
|
|
})
|
|
|
|
|
|
-
|
|
|
// Mock布局组件
|
|
|
jest.mock('@/layouts/tab-bar-layout', () => ({
|
|
|
TabBarLayout: ({ children }: any) => <div>{children}</div>,
|
|
|
@@ -139,7 +140,7 @@ jest.mock('@/components/ui/image', () => ({
|
|
|
),
|
|
|
}))
|
|
|
|
|
|
-// 移除对规格选择器组件的mock,使用真实组件
|
|
|
+
|
|
|
// 移除对useQueries的mock,使用真实hook
|
|
|
|
|
|
// 创建测试用的QueryClient
|
|
|
@@ -167,22 +168,25 @@ const renderWithProviders = (ui: React.ReactElement) => {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+// 导入api模块以触发mock初始化
|
|
|
+import * as api from '@/api'
|
|
|
+
|
|
|
describe('购物车页面', () => {
|
|
|
beforeEach(() => {
|
|
|
jest.clearAllMocks()
|
|
|
// 设置默认购物车数据(包含2个商品)
|
|
|
- mockGetStorageSync.mockReturnValue({ items: mockCartItems })
|
|
|
- mockShowModal.mockImplementation(() => Promise.resolve({ confirm: true }))
|
|
|
- mockGoodsClient[':id'].$get.mockClear()
|
|
|
- // 设置默认mock实现
|
|
|
- mockGoodsClient[':id'].$get.mockImplementation(({ param }: any) => {
|
|
|
- const goodsId = param?.id
|
|
|
- const goodsData = mockGoodsData[goodsId as keyof typeof mockGoodsData] || mockGoodsData[1]
|
|
|
- return Promise.resolve({
|
|
|
- status: 200,
|
|
|
- json: () => Promise.resolve(goodsData)
|
|
|
- })
|
|
|
+ mockGetStorageSync.mockImplementation((key) => {
|
|
|
+ if (key === 'mini_cart') {
|
|
|
+ return { items: mockCartItems }
|
|
|
+ }
|
|
|
+ return null
|
|
|
})
|
|
|
+ mockShowModal.mockImplementation(() => Promise.resolve({ confirm: true }))
|
|
|
+ // 触发goodsClient getter以确保mock被创建
|
|
|
+ // 访问api.goodsClient会触发getter,创建mockGoodsClient
|
|
|
+ if (api.goodsClient) {
|
|
|
+ // mock已经被创建,jest.clearAllMocks()已经清除了调用记录
|
|
|
+ }
|
|
|
mockRequest.mockClear()
|
|
|
})
|
|
|
|
|
|
@@ -193,6 +197,13 @@ describe('购物车页面', () => {
|
|
|
|
|
|
it('应该显示购物车中的商品列表', async () => {
|
|
|
const { findByText } = renderWithProviders(<CartPage />)
|
|
|
+
|
|
|
+ // 等待商品API被调用
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(api.goodsClient[':id'].$get).toHaveBeenCalled()
|
|
|
+ })
|
|
|
+
|
|
|
+ // 等待查询完成,商品名称应该显示父商品名称
|
|
|
expect(await findByText('测试商品1')).toBeDefined()
|
|
|
expect(await findByText('测试商品2')).toBeDefined()
|
|
|
expect(await findByText('¥29.90')).toBeDefined()
|
|
|
@@ -381,7 +392,7 @@ describe('购物车页面', () => {
|
|
|
mockGetStorageSync.mockReturnValue({ items: [] })
|
|
|
// 确保其他mock被清除
|
|
|
mockShowModal.mockImplementation(() => Promise.resolve({ confirm: true }))
|
|
|
- mockGoodsClient[':id'].$get.mockClear()
|
|
|
+ api.goodsClient[':id'].$get.mockClear()
|
|
|
mockRequest.mockClear()
|
|
|
})
|
|
|
|
|
|
@@ -445,7 +456,7 @@ describe('购物车页面', () => {
|
|
|
})
|
|
|
|
|
|
it('规格区域应该可点击并打开规格选择器', async () => {
|
|
|
- const { findByText } = renderWithProviders(<CartPage />)
|
|
|
+ const { findByText, container } = renderWithProviders(<CartPage />)
|
|
|
|
|
|
// 获取规格元素
|
|
|
const specElement = await findByText('红色/M')
|
|
|
@@ -453,11 +464,16 @@ describe('购物车页面', () => {
|
|
|
// 验证元素存在
|
|
|
expect(specElement).toBeDefined()
|
|
|
|
|
|
- // 点击规格区域
|
|
|
- fireEvent.click(specElement)
|
|
|
+ // 点击规格区域 - 点击规格文本的父元素(div.goods-specs)
|
|
|
+ const specContainer = container.querySelector('.goods-specs')
|
|
|
+ fireEvent.click(specContainer || specElement)
|
|
|
|
|
|
// 验证规格选择器应该显示(通过检查规格选择器组件是否被渲染)
|
|
|
// 由于GoodsSpecSelector组件是真实组件,我们需要检查其props
|
|
|
+ // 规格选择器标题"选择规格"应该显示
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(container.querySelector('.spec-modal-title')).toBeDefined()
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
it('应该加载子商品数据并显示规格选择器', async () => {
|
|
|
@@ -487,8 +503,7 @@ describe('购物车页面', () => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- // Mock goodsClient的children API
|
|
|
- const api = require('@/api')
|
|
|
+ // Mock goodsClient的children API - 使用导入的api模块
|
|
|
const childrenSpy = jest.spyOn(api.goodsClient[':id'].children, '$get')
|
|
|
childrenSpy.mockImplementation(({ param, query }: any) => {
|
|
|
return Promise.resolve(mockChildGoodsResponse)
|
|
|
@@ -496,7 +511,15 @@ describe('购物车页面', () => {
|
|
|
|
|
|
const { findByText, container } = renderWithProviders(<CartPage />)
|
|
|
|
|
|
- // 点击规格区域打开选择器
|
|
|
+ // 首先等待商品API被调用,确保商品数据加载
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(api.goodsClient[':id'].$get).toHaveBeenCalled()
|
|
|
+ })
|
|
|
+
|
|
|
+ // 等待商品名称显示
|
|
|
+ await findByText(/测试商品1/)
|
|
|
+
|
|
|
+ // 点击规格区域打开选择器 - 规格区域显示的是规格名称"红色/M"
|
|
|
const specElement = await findByText('红色/M')
|
|
|
fireEvent.click(specElement)
|
|
|
|
|
|
@@ -546,9 +569,10 @@ describe('购物车页面', () => {
|
|
|
// Mock switchSpec调用
|
|
|
const { getByText, container } = renderWithProviders(<CartPage />)
|
|
|
|
|
|
- // 点击规格区域打开选择器
|
|
|
+ // 点击规格区域打开选择器 - 点击规格文本的父元素(div.goods-specs)
|
|
|
const specElement = getByText('红色/M')
|
|
|
- fireEvent.click(specElement)
|
|
|
+ const specContainer = container.querySelector('.goods-specs')
|
|
|
+ fireEvent.click(specContainer || specElement)
|
|
|
|
|
|
// 等待API调用
|
|
|
await waitFor(() => {
|
|
|
@@ -615,9 +639,10 @@ describe('购物车页面', () => {
|
|
|
|
|
|
const { getByText, container } = renderWithProviders(<CartPage />)
|
|
|
|
|
|
- // 点击规格区域
|
|
|
+ // 点击规格区域 - 点击规格文本的父元素(div.goods-specs)
|
|
|
const specElement = getByText('红色/M')
|
|
|
- fireEvent.click(specElement)
|
|
|
+ const specContainer = container.querySelector('.goods-specs')
|
|
|
+ fireEvent.click(specContainer || specElement)
|
|
|
|
|
|
// 验证API被调用
|
|
|
await waitFor(() => {
|
|
|
@@ -643,11 +668,18 @@ describe('购物车页面', () => {
|
|
|
return Promise.resolve(mockErrorResponse)
|
|
|
})
|
|
|
|
|
|
- const { getByText, findByText } = renderWithProviders(<CartPage />)
|
|
|
+ const { getByText, findByText, container } = renderWithProviders(<CartPage />)
|
|
|
|
|
|
- // 点击规格区域打开选择器
|
|
|
+ // 点击规格区域打开选择器 - 点击规格文本的父元素(div.goods-specs)
|
|
|
const specElement = getByText('红色/M')
|
|
|
- fireEvent.click(specElement)
|
|
|
+ // 查找父元素div.goods-specs
|
|
|
+ const specContainer = container.querySelector('.goods-specs')
|
|
|
+ fireEvent.click(specContainer || specElement)
|
|
|
+
|
|
|
+ // 等待规格选择器显示 - 精确匹配标题
|
|
|
+ await waitFor(() => {
|
|
|
+ expect(getByText('选择规格', { exact: true })).toBeDefined()
|
|
|
+ })
|
|
|
|
|
|
// 验证API被调用
|
|
|
await waitFor(() => {
|
|
|
@@ -677,7 +709,7 @@ describe('购物车页面', () => {
|
|
|
]
|
|
|
mockGetStorageSync.mockReturnValue({ items: singleSpecCartItems })
|
|
|
// Mock goodsClient 返回单规格商品数据(无parent对象)
|
|
|
- mockGoodsClient[':id'].$get.mockImplementation(({ param }: any) => {
|
|
|
+ api.goodsClient[':id'].$get.mockImplementation(({ param }: any) => {
|
|
|
const goodsId = param?.id
|
|
|
if (goodsId === 300) {
|
|
|
const singleSpecGoodsData = {
|