import React from 'react'
import { render } from '@testing-library/react'
import { CartProvider, useCart, CartItem } from '@/contexts/CartContext'
import { mockShowToast, mockGetStorageSync, mockSetStorageSync } from '~/__mocks__/taroMock'
// Mock Taro API
jest.mock('@tarojs/taro', () => jest.requireActual('~/__mocks__/taroMock'))
// 测试组件用于访问购物车hook
const TestComponent = ({ action, item }: { action: string; item?: CartItem }) => {
const cart = useCart()
React.useEffect(() => {
console.log('TestComponent useEffect called, action:', action, 'item:', item, 'isLoading:', cart.isLoading)
// 等待购物车加载完成后再添加商品
if (!cart.isLoading && action === 'add' && item) {
console.log('Calling addToCart with item:', item)
cart.addToCart(item)
}
}, [action, item, cart.isLoading])
console.log('TestComponent rendering, cart items:', cart.cart.items, 'isLoading:', cart.isLoading)
return (
{cart.cart.items.length}
{cart.cart.totalCount}
{cart.cart.totalAmount}
{cart.cart.items.map((item, index) => (
{item.id}
{item.name}
{/* spec字段已移除 */}
{item.quantity}
{item.stock}
))}
)
}
describe('CartContext - 规格支持', () => {
beforeEach(() => {
mockGetStorageSync.mockReturnValue(null)
mockSetStorageSync.mockClear()
mockShowToast.mockClear()
})
it('应该支持添加父商品到购物车', () => {
const parentGoods: CartItem = {
id: 1001,
parentGoodsId: 0, // 父商品,无父商品
name: '测试父商品',
price: 99.9,
image: 'parent.jpg',
stock: 10,
quantity: 2,
}
const { getByTestId } = render(
)
expect(getByTestId('items-count').textContent).toBe('1')
expect(getByTestId('item-0-id').textContent).toBe('1001')
expect(getByTestId('item-0-name').textContent).toBe('测试父商品')
expect(mockSetStorageSync).toHaveBeenCalled()
})
it('应该支持添加子商品(带规格)到购物车', () => {
const childGoods: CartItem = {
id: 2001, // 子商品ID
parentGoodsId: 2000, // 父商品ID
name: '红色/M', // 规格名称
price: 109.9,
image: 'child.jpg',
stock: 5,
quantity: 1,
}
const { getByTestId } = render(
)
expect(getByTestId('items-count').textContent).toBe('1')
expect(getByTestId('item-0-id').textContent).toBe('2001')
expect(getByTestId('item-0-name').textContent).toBe('红色/M')
expect(mockSetStorageSync).toHaveBeenCalled()
})
it('应该支持添加同一子商品多次(数量累加)', () => {
const childGoods1: CartItem = {
id: 3001,
parentGoodsId: 3000, // 父商品ID
name: '蓝色/L', // 规格名称
price: 89.9,
image: 'goods.jpg',
stock: 10,
quantity: 1,
}
const childGoods2: CartItem = {
id: 3001, // 同一子商品ID
parentGoodsId: 3000, // 父商品ID
name: '蓝色/L', // 规格名称
price: 89.9,
image: 'goods.jpg',
stock: 10,
quantity: 3,
}
const { getByTestId, rerender } = render(
)
expect(getByTestId('items-count').textContent).toBe('1')
console.log('Item 0 id:', getByTestId('item-0-id').textContent)
console.log('Item 0 name:', getByTestId('item-0-name').textContent)
const quantityElement = getByTestId('item-0-quantity')
console.log('Quantity element text:', quantityElement.textContent)
// 修复:检查数量是否正确,应该是1而不是库存值10
expect(parseInt(quantityElement.textContent || '0')).toBe(1)
// 重新渲染添加更多数量
rerender(
)
expect(getByTestId('item-0-quantity').textContent).toBe('4') // 1 + 3
})
it('应该限制数量不超过库存', () => {
const childGoods: CartItem = {
id: 4001,
parentGoodsId: 4000, // 父商品ID
name: '测试商品 - 黑色/XL',
price: 129.9,
image: 'goods.jpg',
stock: 2, // 库存只有2
quantity: 3, // 尝试购买3个
}
const { getByTestId } = render(
)
// 应该显示库存不足提示
expect(mockShowToast).toHaveBeenCalledWith(
expect.objectContaining({ title: '库存不足' })
)
// 商品不应被添加
expect(getByTestId('items-count').textContent).toBe('0')
})
it('应该支持同时添加父商品和不同子商品', () => {
const parentGoods: CartItem = {
id: 5001,
parentGoodsId: 0, // 父商品,无父商品
name: '测试父商品',
price: 199.9,
image: 'parent.jpg',
stock: 20,
quantity: 1,
}
const childGoods1: CartItem = {
id: 5002, // 子商品ID1
parentGoodsId: 5001, // 父商品ID
name: '测试父商品 - 规格A',
price: 219.9,
image: 'child1.jpg',
stock: 5,
quantity: 2,
}
const childGoods2: CartItem = {
id: 5003, // 子商品ID2
parentGoodsId: 5001, // 父商品ID
name: '测试父商品 - 规格B',
price: 229.9,
image: 'child2.jpg',
stock: 3,
quantity: 1,
}
const { getByTestId, rerender } = render(
)
expect(getByTestId('items-count').textContent).toBe('1')
// 添加第一个子商品
rerender(
)
expect(getByTestId('items-count').textContent).toBe('2')
// 添加第二个子商品
rerender(
)
expect(getByTestId('items-count').textContent).toBe('3')
expect(getByTestId('item-0-id').textContent).toBe('5001')
expect(getByTestId('item-1-id').textContent).toBe('5002')
expect(getByTestId('item-2-id').textContent).toBe('5003')
})
it('应该支持切换购物车项规格', () => {
// 首先添加一个子商品到购物车
const childGoods: CartItem = {
id: 6001,
parentGoodsId: 6000, // 父商品ID
name: '测试父商品 - 规格A',
price: 99.9,
image: 'child1.jpg',
stock: 10,
quantity: 2,
}
// 创建一个新的测试组件来测试switchSpec
const TestSwitchSpecComponent = ({ cartItemId, newChildGoods }: {
cartItemId?: number,
newChildGoods?: { id: number; name: string; price: number; stock: number; image?: string }
}) => {
const cart = useCart()
React.useEffect(() => {
if (!cart.isLoading && cartItemId && newChildGoods) {
cart.switchSpec(cartItemId, newChildGoods)
}
}, [cart.isLoading, cartItemId, newChildGoods])
return (
{cart.cart.items.length}
{cart.cart.items.map((item, index) => (
{item.id}
{item.name}
{/* spec字段已移除 */}
{item.quantity}
{item.price}
))}
)
}
const { getByTestId, rerender } = render(
)
expect(getByTestId('items-count').textContent).toBe('1')
expect(getByTestId('item-0-id').textContent).toBe('6001')
expect(getByTestId('item-0-name').textContent).toBe('测试父商品 - 规格A')
// 切换到新规格
const newChildGoods = {
id: 6002,
name: '规格B', // 规格名称
price: 119.9,
stock: 5,
image: 'child2.jpg',
}
rerender(
)
// 验证规格已切换
expect(getByTestId('items-count').textContent).toBe('1')
expect(getByTestId('item-0-id').textContent).toBe('6002') // ID已更新
expect(getByTestId('item-0-name').textContent).toBe('规格B') // 规格名称
expect(getByTestId('item-0-price').textContent).toBe('119.9')
expect(getByTestId('item-0-quantity').textContent).toBe('2') // 数量保持不变
})
it('切换规格时应该验证库存', () => {
const childGoods: CartItem = {
id: 7001,
parentGoodsId: 7000,
name: '测试商品 - 规格A',
price: 50,
image: 'test.jpg',
stock: 10,
quantity: 8, // 当前数量8
}
const TestSwitchSpecComponent = ({ cartItemId, newChildGoods }: {
cartItemId?: number,
newChildGoods?: { id: number; name: string; price: number; stock: number; image?: string }
}) => {
const cart = useCart()
React.useEffect(() => {
if (!cart.isLoading && cartItemId && newChildGoods) {
cart.switchSpec(cartItemId, newChildGoods)
}
}, [cart.isLoading, cartItemId, newChildGoods])
return {mockShowToast.mock.calls.length}
}
// 添加商品到购物车
const { getByTestId, rerender } = render(
)
// 尝试切换到库存不足的规格(库存只有5,但当前数量是8)
const newChildGoods = {
id: 7002,
name: '测试商品 - 规格B',
price: 60,
stock: 5, // 库存不足
image: 'test2.jpg',
}
rerender(
)
// 应该显示库存不足提示
expect(mockShowToast).toHaveBeenCalledWith(
expect.objectContaining({ title: expect.stringContaining('库存不足') })
)
})
it('单规格商品不应该支持切换规格', () => {
const singleSpecGoods: CartItem = {
id: 8001,
parentGoodsId: 0, // 单规格商品
name: '单规格商品',
price: 30,
image: 'single.jpg',
stock: 10,
quantity: 1,
}
const TestSwitchSpecComponent = ({ cartItemId, newChildGoods }: {
cartItemId?: number,
newChildGoods?: { id: number; name: string; price: number; stock: number; image?: string }
}) => {
const cart = useCart()
React.useEffect(() => {
if (!cart.isLoading && cartItemId && newChildGoods) {
cart.switchSpec(cartItemId, newChildGoods)
}
}, [cart.isLoading, cartItemId, newChildGoods])
return Test
}
// 添加单规格商品
const { rerender } = render(
)
const newChildGoods = {
id: 8002,
name: '新规格',
price: 40,
stock: 5,
}
rerender(
)
// 应该显示不支持切换的提示
expect(mockShowToast).toHaveBeenCalledWith(
expect.objectContaining({ title: '该商品不支持切换规格' })
)
})
})