import { describe, it, expect, vi, beforeEach } from 'vitest'; import { renderHook, waitFor, act } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import React from 'react'; // Mock apiClient vi.mock('../../api/client', () => ({ default: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn(), }, })); import { useCustomers, useCreateCustomer, useUpdateCustomer, useDeleteCustomer, } from '../useCustomers'; import apiClient from '../../api/client'; // Create wrapper const createWrapper = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); return function Wrapper({ children }: { children: React.ReactNode }) { return React.createElement(QueryClientProvider, { client: queryClient }, children); }; }; describe('useCustomers hooks', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('useCustomers', () => { it('fetches customers and transforms data', async () => { const mockCustomers = [ { id: 1, name: 'John Doe', email: 'john@example.com', phone: '555-1234', total_spend: '150.00', status: 'Active', user_id: 10, }, { id: 2, user: { name: 'Jane Smith', email: 'jane@example.com' }, phone: '', total_spend: '0', status: 'Inactive', user: 20, }, ]; vi.mocked(apiClient.get).mockResolvedValue({ data: mockCustomers }); const { result } = renderHook(() => useCustomers(), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); expect(apiClient.get).toHaveBeenCalledWith('/customers/?'); expect(result.current.data).toHaveLength(2); expect(result.current.data?.[0]).toEqual(expect.objectContaining({ id: '1', name: 'John Doe', email: 'john@example.com', totalSpend: 150, status: 'Active', })); }); it('applies status filter', async () => { vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); renderHook(() => useCustomers({ status: 'Active' }), { wrapper: createWrapper(), }); await waitFor(() => { expect(apiClient.get).toHaveBeenCalledWith('/customers/?status=Active'); }); }); it('applies search filter', async () => { vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); renderHook(() => useCustomers({ search: 'john' }), { wrapper: createWrapper(), }); await waitFor(() => { expect(apiClient.get).toHaveBeenCalledWith('/customers/?search=john'); }); }); it('applies multiple filters', async () => { vi.mocked(apiClient.get).mockResolvedValue({ data: [] }); renderHook(() => useCustomers({ status: 'Blocked', search: 'test' }), { wrapper: createWrapper(), }); await waitFor(() => { expect(apiClient.get).toHaveBeenCalledWith('/customers/?status=Blocked&search=test'); }); }); it('handles customers with last_visit date', async () => { const mockCustomers = [ { id: 1, name: 'Customer', email: 'c@example.com', total_spend: '0', last_visit: '2024-01-15T10:00:00Z', user_id: 1, }, ]; vi.mocked(apiClient.get).mockResolvedValue({ data: mockCustomers }); const { result } = renderHook(() => useCustomers(), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.isSuccess).toBe(true); }); expect(result.current.data?.[0].lastVisit).toBeInstanceOf(Date); }); }); describe('useCreateCustomer', () => { it('creates customer with field mapping', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { id: 1 } }); const { result } = renderHook(() => useCreateCustomer(), { wrapper: createWrapper(), }); await act(async () => { await result.current.mutateAsync({ userId: '5', phone: '555-9999', city: 'Denver', state: 'CO', zip: '80202', status: 'Active', }); }); expect(apiClient.post).toHaveBeenCalledWith('/customers/', { user: 5, phone: '555-9999', city: 'Denver', state: 'CO', zip: '80202', status: 'Active', avatar_url: undefined, tags: undefined, }); }); }); describe('useUpdateCustomer', () => { it('updates customer with mapped fields', async () => { vi.mocked(apiClient.patch).mockResolvedValue({ data: {} }); const { result } = renderHook(() => useUpdateCustomer(), { wrapper: createWrapper(), }); await act(async () => { await result.current.mutateAsync({ id: '1', updates: { phone: '555-0000', status: 'Blocked', tags: ['vip'], }, }); }); expect(apiClient.patch).toHaveBeenCalledWith('/customers/1/', { phone: '555-0000', city: undefined, state: undefined, zip: undefined, status: 'Blocked', avatar_url: undefined, tags: ['vip'], }); }); }); describe('useDeleteCustomer', () => { it('deletes customer by id', async () => { vi.mocked(apiClient.delete).mockResolvedValue({}); const { result } = renderHook(() => useDeleteCustomer(), { wrapper: createWrapper(), }); await act(async () => { await result.current.mutateAsync('7'); }); expect(apiClient.delete).toHaveBeenCalledWith('/customers/7/'); }); }); });