Root cause: Cart.tsx used formatCurrency(819) -> '819,00' instead of formatCents(819) -> 'R$ 8,19' Changes: - Cart.tsx: Replace formatCurrency with formatCents at all 5 price display points - Cart.tsx: Add debug logging (logPrice) to trace price values - cartStore.ts: Add debug logging for addItem, updateQuantity, etc. - cartStore.ts: Document unitPrice as CENTS in interface - cartStore.test.ts: Add 7 new price conversion tests for cents handling All 95 tests pass.
326 lines
11 KiB
TypeScript
326 lines
11 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest'
|
|
import { useCartStore, selectGroupedCart, selectCartSummary, CartItem } from './cartStore'
|
|
|
|
const mockItem: Omit<CartItem, 'quantity'> = {
|
|
id: 'prod-1',
|
|
name: 'Produto Teste',
|
|
activeIngredient: 'Amoxicilina',
|
|
lab: 'EMS',
|
|
batch: 'L1001',
|
|
expiry: '2025-12',
|
|
vendorId: 'vendor-1',
|
|
vendorName: 'Distribuidora Norte',
|
|
unitPrice: 25.00
|
|
}
|
|
|
|
const mockItem2: Omit<CartItem, 'quantity'> = {
|
|
id: 'prod-2',
|
|
name: 'Produto Teste 2',
|
|
activeIngredient: 'Dipirona',
|
|
lab: 'Eurofarma',
|
|
batch: 'L2002',
|
|
expiry: '2026-01',
|
|
vendorId: 'vendor-2',
|
|
vendorName: 'Distribuidora Sul',
|
|
unitPrice: 15.00
|
|
}
|
|
|
|
describe('cartStore', () => {
|
|
beforeEach(() => {
|
|
// Reset the store before each test
|
|
useCartStore.setState({ items: [] })
|
|
})
|
|
|
|
describe('addItem', () => {
|
|
it('should add an item to the cart', () => {
|
|
const store = useCartStore.getState()
|
|
store.addItem(mockItem)
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items).toHaveLength(1)
|
|
expect(state.items[0]).toMatchObject({ ...mockItem, quantity: 1 })
|
|
})
|
|
|
|
it('should add item with custom quantity', () => {
|
|
const store = useCartStore.getState()
|
|
store.addItem(mockItem, 5)
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items[0].quantity).toBe(5)
|
|
})
|
|
|
|
it('should increment quantity if item already exists', () => {
|
|
const store = useCartStore.getState()
|
|
store.addItem(mockItem, 2)
|
|
store.addItem(mockItem, 3)
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items).toHaveLength(1)
|
|
expect(state.items[0].quantity).toBe(5)
|
|
})
|
|
})
|
|
|
|
describe('updateQuantity', () => {
|
|
it('should update quantity of existing item', () => {
|
|
useCartStore.getState().addItem(mockItem, 2)
|
|
useCartStore.getState().updateQuantity('prod-1', 10)
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items[0].quantity).toBe(10)
|
|
})
|
|
|
|
it('should not affect other items', () => {
|
|
useCartStore.getState().addItem(mockItem, 2)
|
|
useCartStore.getState().addItem(mockItem2, 3)
|
|
useCartStore.getState().updateQuantity('prod-1', 10)
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items[0].quantity).toBe(10)
|
|
expect(state.items[1].quantity).toBe(3)
|
|
})
|
|
})
|
|
|
|
describe('removeItem', () => {
|
|
it('should remove item from cart', () => {
|
|
useCartStore.getState().addItem(mockItem)
|
|
useCartStore.getState().addItem(mockItem2)
|
|
useCartStore.getState().removeItem('prod-1')
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items).toHaveLength(1)
|
|
expect(state.items[0].id).toBe('prod-2')
|
|
})
|
|
|
|
it('should handle removing non-existent item', () => {
|
|
useCartStore.getState().addItem(mockItem)
|
|
useCartStore.getState().removeItem('non-existent')
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items).toHaveLength(1)
|
|
})
|
|
})
|
|
|
|
describe('clearVendor', () => {
|
|
it('should remove all items from a specific vendor', () => {
|
|
const mockItemSameVendor: Omit<CartItem, 'quantity'> = {
|
|
...mockItem,
|
|
id: 'prod-3',
|
|
name: 'Outro Produto'
|
|
}
|
|
|
|
useCartStore.getState().addItem(mockItem)
|
|
useCartStore.getState().addItem(mockItemSameVendor)
|
|
useCartStore.getState().addItem(mockItem2)
|
|
useCartStore.getState().clearVendor('vendor-1')
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items).toHaveLength(1)
|
|
expect(state.items[0].vendorId).toBe('vendor-2')
|
|
})
|
|
})
|
|
|
|
describe('clearAll', () => {
|
|
it('should remove all items from cart', () => {
|
|
useCartStore.getState().addItem(mockItem)
|
|
useCartStore.getState().addItem(mockItem2)
|
|
useCartStore.getState().clearAll()
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items).toHaveLength(0)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('selectors', () => {
|
|
beforeEach(() => {
|
|
useCartStore.setState({ items: [] })
|
|
})
|
|
|
|
describe('selectGroupedCart', () => {
|
|
it('should group items by vendor', () => {
|
|
useCartStore.getState().addItem(mockItem, 2)
|
|
useCartStore.getState().addItem(mockItem2, 3)
|
|
|
|
const state = useCartStore.getState()
|
|
const groups = selectGroupedCart(state)
|
|
|
|
expect(Object.keys(groups)).toHaveLength(2)
|
|
expect(groups['vendor-1'].items).toHaveLength(1)
|
|
expect(groups['vendor-2'].items).toHaveLength(1)
|
|
})
|
|
|
|
it('should calculate vendor totals correctly', () => {
|
|
useCartStore.getState().addItem(mockItem, 2) // 2 * 25 = 50
|
|
useCartStore.getState().addItem(mockItem2, 3) // 3 * 15 = 45
|
|
|
|
const state = useCartStore.getState()
|
|
const groups = selectGroupedCart(state)
|
|
|
|
expect(groups['vendor-1'].total).toBe(50)
|
|
expect(groups['vendor-2'].total).toBe(45)
|
|
})
|
|
|
|
it('should return empty object for empty cart', () => {
|
|
const state = useCartStore.getState()
|
|
const groups = selectGroupedCart(state)
|
|
|
|
expect(Object.keys(groups)).toHaveLength(0)
|
|
})
|
|
})
|
|
|
|
describe('selectCartSummary', () => {
|
|
it('should calculate total items correctly', () => {
|
|
useCartStore.getState().addItem(mockItem, 2)
|
|
useCartStore.getState().addItem(mockItem2, 3)
|
|
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
expect(summary.totalItems).toBe(5)
|
|
})
|
|
|
|
it('should calculate total value correctly', () => {
|
|
useCartStore.getState().addItem(mockItem, 2) // 2 * 25 = 50
|
|
useCartStore.getState().addItem(mockItem2, 3) // 3 * 15 = 45
|
|
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
expect(summary.totalValue).toBe(95)
|
|
})
|
|
|
|
it('should return zeros for empty cart', () => {
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
expect(summary.totalItems).toBe(0)
|
|
expect(summary.totalValue).toBe(0)
|
|
})
|
|
})
|
|
})
|
|
|
|
/**
|
|
* PRICE CONVERSION TESTS
|
|
* These tests verify that prices are handled correctly in CENTS
|
|
* The API returns price_cents (e.g., 819 = R$ 8,19)
|
|
*/
|
|
describe('price conversion (cents)', () => {
|
|
const mockItemCents: Omit<CartItem, 'quantity'> = {
|
|
id: 'prod-cents-1',
|
|
name: 'Paracetamol 750mg',
|
|
activeIngredient: 'Paracetamol',
|
|
lab: 'EMS',
|
|
batch: 'L2025840',
|
|
expiry: '2027-09-15',
|
|
vendorId: 'vendor-anapolis',
|
|
vendorName: 'Anápolis/GO',
|
|
unitPrice: 819 // 819 cents = R$ 8,19
|
|
}
|
|
|
|
const mockItemCents2: Omit<CartItem, 'quantity'> = {
|
|
id: 'prod-cents-2',
|
|
name: 'Dipirona 500mg',
|
|
activeIngredient: 'Dipirona',
|
|
lab: 'Medley',
|
|
batch: 'L2025123',
|
|
expiry: '2026-06-30',
|
|
vendorId: 'vendor-anapolis',
|
|
vendorName: 'Anápolis/GO',
|
|
unitPrice: 1299 // 1299 cents = R$ 12,99
|
|
}
|
|
|
|
beforeEach(() => {
|
|
useCartStore.setState({ items: [] })
|
|
})
|
|
|
|
describe('storing prices in cents', () => {
|
|
it('should store unitPrice as cents (819 = R$ 8,19)', () => {
|
|
useCartStore.getState().addItem(mockItemCents)
|
|
|
|
const state = useCartStore.getState()
|
|
expect(state.items[0].unitPrice).toBe(819)
|
|
// Verify it's NOT stored as reais (8.19)
|
|
expect(state.items[0].unitPrice).not.toBe(8.19)
|
|
})
|
|
|
|
it('should preserve cents precision for subtotal calculation', () => {
|
|
useCartStore.getState().addItem(mockItemCents, 3)
|
|
|
|
const state = useCartStore.getState()
|
|
const item = state.items[0]
|
|
const subtotal = item.quantity * item.unitPrice
|
|
|
|
// 3 * 819 = 2457 cents = R$ 24,57
|
|
expect(subtotal).toBe(2457)
|
|
expect(subtotal / 100).toBe(24.57)
|
|
})
|
|
})
|
|
|
|
describe('cents calculations in selectors', () => {
|
|
it('should calculate vendor total in cents', () => {
|
|
// 819 cents * 2 = 1638 cents
|
|
useCartStore.getState().addItem(mockItemCents, 2)
|
|
// 1299 cents * 1 = 1299 cents
|
|
useCartStore.getState().addItem(mockItemCents2, 1)
|
|
|
|
const state = useCartStore.getState()
|
|
const groups = selectGroupedCart(state)
|
|
|
|
// Total: 1638 + 1299 = 2937 cents = R$ 29,37
|
|
expect(groups['vendor-anapolis'].total).toBe(2937)
|
|
expect(groups['vendor-anapolis'].total / 100).toBeCloseTo(29.37)
|
|
})
|
|
|
|
it('should calculate cart summary total in cents', () => {
|
|
useCartStore.getState().addItem(mockItemCents, 2) // 1638 cents
|
|
useCartStore.getState().addItem(mockItemCents2, 1) // 1299 cents
|
|
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
expect(summary.totalItems).toBe(3)
|
|
expect(summary.totalValue).toBe(2937) // cents
|
|
expect(summary.totalValue / 100).toBeCloseTo(29.37) // reais
|
|
})
|
|
})
|
|
|
|
describe('edge cases for cents', () => {
|
|
it('should handle single unit correctly', () => {
|
|
useCartStore.getState().addItem(mockItemCents, 1)
|
|
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
expect(summary.totalValue).toBe(819)
|
|
expect(summary.totalValue / 100).toBe(8.19)
|
|
})
|
|
|
|
it('should handle large quantities without overflow', () => {
|
|
useCartStore.getState().addItem(mockItemCents, 1000)
|
|
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
// 819 * 1000 = 819000 cents = R$ 8,190.00
|
|
expect(summary.totalValue).toBe(819000)
|
|
expect(summary.totalValue / 100).toBe(8190)
|
|
})
|
|
|
|
it('should handle small cent values (R$ 0,01)', () => {
|
|
const cheapItem: Omit<CartItem, 'quantity'> = {
|
|
...mockItemCents,
|
|
id: 'cheap-1',
|
|
unitPrice: 1 // 1 cent = R$ 0,01
|
|
}
|
|
|
|
useCartStore.getState().addItem(cheapItem, 10)
|
|
|
|
const state = useCartStore.getState()
|
|
const summary = selectCartSummary(state)
|
|
|
|
expect(summary.totalValue).toBe(10) // 10 cents
|
|
expect(summary.totalValue / 100).toBe(0.10)
|
|
})
|
|
})
|
|
})
|
|
|