Add Point of Sale system and tax rate lookup integration
POS System: - Full POS interface with product grid, cart panel, and payment flow - Product and category management with barcode scanning support - Cash drawer operations and shift management - Order history and receipt generation - Thermal printer integration (ESC/POS protocol) - Gift card support with purchase and redemption - Inventory tracking with low stock alerts - Customer selection and walk-in support Tax Rate Integration: - ZIP-to-state mapping for automatic state detection - SST boundary data import for 24 member states - Static rates for uniform-rate states (IN, MA, CT, etc.) - Statewide jurisdiction fallback for simple lookups - Tax rate suggestion in location editor with auto-apply - Multiple data sources: SST, CDTFA, TX Comptroller, Avalara UI Improvements: - POS renders full-screen outside BusinessLayout - Clear cart button prominently in cart header - Tax rate limited to 2 decimal places - Location tax rate field with suggestion UI 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
696
frontend/src/pos/__tests__/POSContext.test.tsx
Normal file
696
frontend/src/pos/__tests__/POSContext.test.tsx
Normal file
@@ -0,0 +1,696 @@
|
||||
/**
|
||||
* Tests for POSContext operations
|
||||
*
|
||||
* Tests cover cart operations including adding items, removing items,
|
||||
* updating quantities, applying discounts, and calculating totals.
|
||||
*/
|
||||
|
||||
import React, { type ReactNode } from 'react';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { POSProvider, usePOS } from '../context/POSContext';
|
||||
import type { POSProduct, POSService, POSDiscount } from '../types';
|
||||
|
||||
// Clear localStorage before each test to prevent state leakage
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
// Create wrapper component for hooks
|
||||
const createWrapper = (initialLocationId?: number) => {
|
||||
return ({ children }: { children: ReactNode }) => (
|
||||
<POSProvider initialLocationId={initialLocationId ?? null}>{children}</POSProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// Mock product - matches POSProduct interface from types.ts
|
||||
const mockProduct: POSProduct = {
|
||||
id: 1,
|
||||
name: 'Test Product',
|
||||
sku: 'TEST-001',
|
||||
barcode: '123456789',
|
||||
description: 'A test product',
|
||||
price_cents: 1000,
|
||||
cost_cents: 500,
|
||||
tax_rate: 0.08,
|
||||
is_taxable: true,
|
||||
category_id: 1,
|
||||
display_order: 1,
|
||||
image_url: null,
|
||||
color: '#3B82F6',
|
||||
status: 'active',
|
||||
track_inventory: true,
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
updated_at: '2024-01-01T00:00:00Z',
|
||||
};
|
||||
|
||||
// Mock service - matches POSService interface from types.ts
|
||||
const mockService: POSService = {
|
||||
id: 2,
|
||||
name: 'Test Service',
|
||||
description: 'A test service',
|
||||
price_cents: 2500,
|
||||
duration_minutes: 60,
|
||||
};
|
||||
|
||||
describe('POSContext - Initial State', () => {
|
||||
it('should start with empty cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(0);
|
||||
expect(result.current.isCartEmpty).toBe(true);
|
||||
expect(result.current.itemCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should start with zero totals', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.subtotalCents).toBe(0);
|
||||
expect(result.current.state.cart.taxCents).toBe(0);
|
||||
expect(result.current.state.cart.tipCents).toBe(0);
|
||||
expect(result.current.state.cart.discountCents).toBe(0);
|
||||
expect(result.current.state.cart.totalCents).toBe(0);
|
||||
});
|
||||
|
||||
it('should accept initial location ID', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(42),
|
||||
});
|
||||
|
||||
expect(result.current.state.selectedLocationId).toBe(42);
|
||||
});
|
||||
|
||||
it('should start with no customer', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.customer).toBeNull();
|
||||
});
|
||||
|
||||
it('should start with no active shift', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.state.activeShift).toBeNull();
|
||||
});
|
||||
|
||||
it('should start with printer disconnected', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
expect(result.current.state.printerStatus).toBe('disconnected');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Adding Items', () => {
|
||||
it('should add a product to cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(1);
|
||||
expect(result.current.state.cart.items[0].name).toBe('Test Product');
|
||||
expect(result.current.state.cart.items[0].unitPriceCents).toBe(1000);
|
||||
expect(result.current.state.cart.items[0].quantity).toBe(1);
|
||||
});
|
||||
|
||||
it('should add a service to cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockService, 1, 'service');
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(1);
|
||||
expect(result.current.state.cart.items[0].name).toBe('Test Service');
|
||||
expect(result.current.state.cart.items[0].unitPriceCents).toBe(2500);
|
||||
expect(result.current.state.cart.items[0].itemType).toBe('service');
|
||||
});
|
||||
|
||||
it('should increment quantity for existing item', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
// Should still be one item with quantity 2
|
||||
expect(result.current.state.cart.items).toHaveLength(1);
|
||||
expect(result.current.state.cart.items[0].quantity).toBe(2);
|
||||
});
|
||||
|
||||
it('should add multiple quantities at once', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 5, 'product');
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items[0].quantity).toBe(5);
|
||||
expect(result.current.itemCount).toBe(5);
|
||||
});
|
||||
|
||||
it('should update item count correctly', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product');
|
||||
result.current.addItem(mockService, 1, 'service');
|
||||
});
|
||||
|
||||
expect(result.current.itemCount).toBe(3);
|
||||
expect(result.current.isCartEmpty).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Removing Items', () => {
|
||||
it('should remove item from cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
const itemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.removeItem(itemId);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(0);
|
||||
expect(result.current.isCartEmpty).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle removing non-existent item gracefully', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.removeItem('non-existent-id');
|
||||
});
|
||||
|
||||
// Should still have the original item
|
||||
expect(result.current.state.cart.items).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should recalculate totals after removal', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
result.current.addItem(mockService, 1, 'service');
|
||||
});
|
||||
|
||||
const productItemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.removeItem(productItemId);
|
||||
});
|
||||
|
||||
// Should only have service now
|
||||
expect(result.current.state.cart.subtotalCents).toBe(2500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Updating Quantities', () => {
|
||||
it('should update item quantity', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
const itemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.updateQuantity(itemId, 5);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items[0].quantity).toBe(5);
|
||||
expect(result.current.itemCount).toBe(5);
|
||||
});
|
||||
|
||||
it('should remove item when quantity set to zero', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 3, 'product');
|
||||
});
|
||||
|
||||
const itemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.updateQuantity(itemId, 0);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should remove item when quantity is negative', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product');
|
||||
});
|
||||
|
||||
const itemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.updateQuantity(itemId, -1);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should recalculate totals after quantity update', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
const initialSubtotal = result.current.state.cart.subtotalCents;
|
||||
const itemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.updateQuantity(itemId, 3);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.subtotalCents).toBe(initialSubtotal * 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Applying Discounts', () => {
|
||||
it('should apply percentage discount to cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product');
|
||||
});
|
||||
|
||||
const discount: POSDiscount = {
|
||||
percent: 10,
|
||||
reason: 'Loyalty discount',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.applyDiscount(discount);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.discount).toEqual(discount);
|
||||
// 10% of $20.00 = $2.00 = 200 cents
|
||||
expect(result.current.state.cart.discountCents).toBe(200);
|
||||
});
|
||||
|
||||
it('should apply fixed amount discount to cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product');
|
||||
});
|
||||
|
||||
const discount: POSDiscount = {
|
||||
amountCents: 500,
|
||||
reason: 'Promo code',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.applyDiscount(discount);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.discountCents).toBe(500);
|
||||
});
|
||||
|
||||
it('should clear discount', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product');
|
||||
result.current.applyDiscount({ percent: 10 });
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.clearDiscount();
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.discount).toBeNull();
|
||||
expect(result.current.state.cart.discountCents).toBe(0);
|
||||
});
|
||||
|
||||
it('should apply item-level discount', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
const itemId = result.current.state.cart.items[0].id;
|
||||
|
||||
act(() => {
|
||||
result.current.setItemDiscount(itemId, undefined, 20); // 20% off
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items[0].discountPercent).toBe(20);
|
||||
// Subtotal should reflect the discount
|
||||
expect(result.current.state.cart.subtotalCents).toBe(800); // $10 - 20% = $8
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Setting Tip', () => {
|
||||
it('should set tip amount', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setTip(200);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.tipCents).toBe(200);
|
||||
});
|
||||
|
||||
it('should include tip in total', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
});
|
||||
|
||||
const totalBeforeTip = result.current.state.cart.totalCents;
|
||||
|
||||
act(() => {
|
||||
result.current.setTip(300);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.totalCents).toBe(totalBeforeTip + 300);
|
||||
});
|
||||
|
||||
it('should handle zero tip', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product');
|
||||
result.current.setTip(500);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setTip(0);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.tipCents).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Calculating Totals', () => {
|
||||
it('should calculate subtotal correctly', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product'); // $10 x 2 = $20
|
||||
result.current.addItem(mockService, 1, 'service'); // $25
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.subtotalCents).toBe(4500); // $45
|
||||
});
|
||||
|
||||
it('should calculate tax correctly', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product'); // $10 @ 8% = $0.80 tax
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.taxCents).toBe(80);
|
||||
});
|
||||
|
||||
it('should calculate total correctly', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product'); // $10 + $0.80 tax
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.totalCents).toBe(1080);
|
||||
});
|
||||
|
||||
it('should calculate total with discount and tip', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product'); // $20 subtotal + $1.60 tax
|
||||
result.current.applyDiscount({ amountCents: 500 }); // -$5 discount
|
||||
result.current.setTip(300); // +$3 tip
|
||||
});
|
||||
|
||||
// Total: $20 + $1.60 - $5 + $3 = $19.60
|
||||
expect(result.current.state.cart.totalCents).toBe(1960);
|
||||
});
|
||||
|
||||
it('should not allow negative total', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 1, 'product'); // $10.80 total
|
||||
result.current.applyDiscount({ amountCents: 2000 }); // $20 discount (exceeds total)
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.totalCents).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Customer Management', () => {
|
||||
it('should set customer', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
const customer = {
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
phone: '555-123-4567',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.setCustomer(customer);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.customer).toEqual(customer);
|
||||
});
|
||||
|
||||
it('should clear customer', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setCustomer({ id: 1, name: 'John', email: '', phone: '' });
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setCustomer(null);
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.customer).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Clearing Cart', () => {
|
||||
it('should clear all items from cart', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.addItem(mockProduct, 2, 'product');
|
||||
result.current.addItem(mockService, 1, 'service');
|
||||
result.current.setTip(500);
|
||||
result.current.applyDiscount({ percent: 10 });
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.clearCart();
|
||||
});
|
||||
|
||||
expect(result.current.state.cart.items).toHaveLength(0);
|
||||
expect(result.current.state.cart.subtotalCents).toBe(0);
|
||||
expect(result.current.state.cart.taxCents).toBe(0);
|
||||
expect(result.current.state.cart.tipCents).toBe(0);
|
||||
expect(result.current.state.cart.discountCents).toBe(0);
|
||||
expect(result.current.state.cart.totalCents).toBe(0);
|
||||
expect(result.current.state.cart.discount).toBeNull();
|
||||
expect(result.current.state.cart.customer).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Active Shift', () => {
|
||||
it('should set active shift', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
const shift = {
|
||||
id: 1,
|
||||
location: 1,
|
||||
opened_by: 1,
|
||||
closed_by: null,
|
||||
opening_balance_cents: 10000,
|
||||
expected_balance_cents: 15000,
|
||||
actual_balance_cents: null,
|
||||
variance_cents: null,
|
||||
cash_breakdown: {},
|
||||
status: 'open' as const,
|
||||
opened_at: '2024-01-01T09:00:00Z',
|
||||
closed_at: null,
|
||||
opening_notes: '',
|
||||
closing_notes: '',
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.setActiveShift(shift);
|
||||
});
|
||||
|
||||
expect(result.current.state.activeShift).toEqual(shift);
|
||||
});
|
||||
|
||||
it('should clear active shift', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setActiveShift({ id: 1 } as any);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setActiveShift(null);
|
||||
});
|
||||
|
||||
expect(result.current.state.activeShift).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Printer Status', () => {
|
||||
it('should update printer status', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setPrinterStatus('connecting');
|
||||
});
|
||||
|
||||
expect(result.current.state.printerStatus).toBe('connecting');
|
||||
|
||||
act(() => {
|
||||
result.current.setPrinterStatus('connected');
|
||||
});
|
||||
|
||||
expect(result.current.state.printerStatus).toBe('connected');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - Location', () => {
|
||||
it('should set location', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setLocation(123);
|
||||
});
|
||||
|
||||
expect(result.current.state.selectedLocationId).toBe(123);
|
||||
});
|
||||
|
||||
it('should clear location', () => {
|
||||
const { result } = renderHook(() => usePOS(), {
|
||||
wrapper: createWrapper(42),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.setLocation(null);
|
||||
});
|
||||
|
||||
expect(result.current.state.selectedLocationId).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('POSContext - usePOS Hook Error', () => {
|
||||
it('should throw error when used outside provider', () => {
|
||||
// Suppress console.error for this test
|
||||
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
expect(() => {
|
||||
renderHook(() => usePOS());
|
||||
}).toThrow('usePOS must be used within a POSProvider');
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user