CartContext.test.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import React from 'react'
  2. import { render } from '@testing-library/react'
  3. import { CartProvider, useCart, CartItem } from '@/contexts/CartContext'
  4. import { mockShowToast, mockGetStorageSync, mockSetStorageSync } from '~/__mocks__/taroMock'
  5. // Mock Taro API
  6. jest.mock('@tarojs/taro', () => jest.requireActual('~/__mocks__/taroMock'))
  7. // 测试组件用于访问购物车hook
  8. const TestComponent = ({ action, item }: { action: string; item?: CartItem }) => {
  9. const cart = useCart()
  10. React.useEffect(() => {
  11. console.log('TestComponent useEffect called, action:', action, 'item:', item, 'isLoading:', cart.isLoading)
  12. // 等待购物车加载完成后再添加商品
  13. if (!cart.isLoading && action === 'add' && item) {
  14. console.log('Calling addToCart with item:', item)
  15. cart.addToCart(item)
  16. }
  17. }, [action, item, cart.isLoading])
  18. console.log('TestComponent rendering, cart items:', cart.cart.items, 'isLoading:', cart.isLoading)
  19. return (
  20. <div>
  21. <div data-testid="items-count">{cart.cart.items.length}</div>
  22. <div data-testid="total-count">{cart.cart.totalCount}</div>
  23. <div data-testid="total-amount">{cart.cart.totalAmount}</div>
  24. {cart.cart.items.map((item, index) => (
  25. <div key={index} data-testid={`item-${index}`}>
  26. <span data-testid={`item-${index}-id`}>{item.id}</span>
  27. <span data-testid={`item-${index}-name`}>{item.name}</span>
  28. <span data-testid={`item-${index}-spec`}>{item.spec || ''}</span>
  29. <span data-testid={`item-${index}-quantity`}>{item.quantity}</span>
  30. <span data-testid={`item-${index}-stock`} style={{ display: 'none' }}>{item.stock}</span>
  31. </div>
  32. ))}
  33. </div>
  34. )
  35. }
  36. describe('CartContext - 规格支持', () => {
  37. beforeEach(() => {
  38. mockGetStorageSync.mockReturnValue(null)
  39. mockSetStorageSync.mockClear()
  40. mockShowToast.mockClear()
  41. })
  42. it('应该支持添加父商品到购物车', () => {
  43. const parentGoods: CartItem = {
  44. id: 1001,
  45. name: '测试父商品',
  46. price: 99.9,
  47. image: 'parent.jpg',
  48. stock: 10,
  49. quantity: 2,
  50. }
  51. const { getByTestId } = render(
  52. <CartProvider>
  53. <TestComponent action="add" item={parentGoods} />
  54. </CartProvider>
  55. )
  56. expect(getByTestId('items-count').textContent).toBe('1')
  57. expect(getByTestId('item-0-id').textContent).toBe('1001')
  58. expect(getByTestId('item-0-name').textContent).toBe('测试父商品')
  59. expect(mockSetStorageSync).toHaveBeenCalled()
  60. })
  61. it('应该支持添加子商品(带规格)到购物车', () => {
  62. const childGoods: CartItem = {
  63. id: 2001, // 子商品ID
  64. name: '测试父商品 - 红色/M', // 包含规格信息的完整名称
  65. price: 109.9,
  66. image: 'child.jpg',
  67. stock: 5,
  68. quantity: 1,
  69. spec: '红色/M', // 规格信息
  70. }
  71. const { getByTestId } = render(
  72. <CartProvider>
  73. <TestComponent action="add" item={childGoods} />
  74. </CartProvider>
  75. )
  76. expect(getByTestId('items-count').textContent).toBe('1')
  77. expect(getByTestId('item-0-id').textContent).toBe('2001')
  78. expect(getByTestId('item-0-name').textContent).toBe('测试父商品 - 红色/M')
  79. expect(getByTestId('item-0-spec').textContent).toBe('红色/M')
  80. expect(mockSetStorageSync).toHaveBeenCalled()
  81. })
  82. it('应该支持添加同一子商品多次(数量累加)', () => {
  83. const childGoods1: CartItem = {
  84. id: 3001,
  85. name: '测试商品 - 蓝色/L',
  86. price: 89.9,
  87. image: 'goods.jpg',
  88. stock: 10,
  89. quantity: 1,
  90. spec: '蓝色/L',
  91. }
  92. const childGoods2: CartItem = {
  93. id: 3001, // 同一子商品ID
  94. name: '测试商品 - 蓝色/L',
  95. price: 89.9,
  96. image: 'goods.jpg',
  97. stock: 10,
  98. quantity: 3,
  99. spec: '蓝色/L',
  100. }
  101. const { getByTestId, rerender } = render(
  102. <CartProvider>
  103. <TestComponent action="add" item={childGoods1} />
  104. </CartProvider>
  105. )
  106. expect(getByTestId('items-count').textContent).toBe('1')
  107. console.log('Item 0 id:', getByTestId('item-0-id').textContent)
  108. console.log('Item 0 name:', getByTestId('item-0-name').textContent)
  109. console.log('Item 0 spec:', getByTestId('item-0-spec').textContent)
  110. const quantityElement = getByTestId('item-0-quantity')
  111. console.log('Quantity element text:', quantityElement.textContent)
  112. // 修复:检查数量是否正确,应该是1而不是库存值10
  113. expect(parseInt(quantityElement.textContent || '0')).toBe(1)
  114. // 重新渲染添加更多数量
  115. rerender(
  116. <CartProvider>
  117. <TestComponent action="add" item={childGoods2} />
  118. </CartProvider>
  119. )
  120. expect(getByTestId('item-0-quantity').textContent).toBe('4') // 1 + 3
  121. })
  122. it('应该限制数量不超过库存', () => {
  123. const childGoods: CartItem = {
  124. id: 4001,
  125. name: '测试商品 - 黑色/XL',
  126. price: 129.9,
  127. image: 'goods.jpg',
  128. stock: 2, // 库存只有2
  129. quantity: 3, // 尝试购买3个
  130. spec: '黑色/XL',
  131. }
  132. const { getByTestId } = render(
  133. <CartProvider>
  134. <TestComponent action="add" item={childGoods} />
  135. </CartProvider>
  136. )
  137. // 应该显示库存不足提示
  138. expect(mockShowToast).toHaveBeenCalledWith(
  139. expect.objectContaining({ title: '库存不足' })
  140. )
  141. // 商品不应被添加
  142. expect(getByTestId('items-count').textContent).toBe('0')
  143. })
  144. it('应该支持同时添加父商品和不同子商品', () => {
  145. const parentGoods: CartItem = {
  146. id: 5001,
  147. name: '测试父商品',
  148. price: 199.9,
  149. image: 'parent.jpg',
  150. stock: 20,
  151. quantity: 1,
  152. }
  153. const childGoods1: CartItem = {
  154. id: 5002, // 子商品ID1
  155. name: '测试父商品 - 规格A',
  156. price: 219.9,
  157. image: 'child1.jpg',
  158. stock: 5,
  159. quantity: 2,
  160. spec: '规格A',
  161. }
  162. const childGoods2: CartItem = {
  163. id: 5003, // 子商品ID2
  164. name: '测试父商品 - 规格B',
  165. price: 229.9,
  166. image: 'child2.jpg',
  167. stock: 3,
  168. quantity: 1,
  169. spec: '规格B',
  170. }
  171. const { getByTestId, rerender } = render(
  172. <CartProvider>
  173. <TestComponent action="add" item={parentGoods} />
  174. </CartProvider>
  175. )
  176. expect(getByTestId('items-count').textContent).toBe('1')
  177. // 添加第一个子商品
  178. rerender(
  179. <CartProvider>
  180. <TestComponent action="add" item={childGoods1} />
  181. </CartProvider>
  182. )
  183. expect(getByTestId('items-count').textContent).toBe('2')
  184. // 添加第二个子商品
  185. rerender(
  186. <CartProvider>
  187. <TestComponent action="add" item={childGoods2} />
  188. </CartProvider>
  189. )
  190. expect(getByTestId('items-count').textContent).toBe('3')
  191. expect(getByTestId('item-0-id').textContent).toBe('5001')
  192. expect(getByTestId('item-1-id').textContent).toBe('5002')
  193. expect(getByTestId('item-2-id').textContent).toBe('5003')
  194. })
  195. })