// src/hooks/queries/useFlyerItemsQuery.test.tsx import { renderHook, waitFor, act } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import type { ReactNode } from 'react'; import { useFlyerItemsQuery } from './useFlyerItemsQuery'; import * as apiClient from '../../services/apiClient'; vi.mock('../../services/apiClient'); const mockedApiClient = vi.mocked(apiClient); describe('useFlyerItemsQuery', () => { let queryClient: QueryClient; const wrapper = ({ children }: { children: ReactNode }) => ( {children} ); beforeEach(() => { vi.resetAllMocks(); queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, }, }); }); it('should fetch flyer items when flyerId is provided', async () => { const mockFlyerItems = [ { item_id: 1, name: 'Milk', price: 3.99, flyer_id: 42 }, { item_id: 2, name: 'Bread', price: 2.49, flyer_id: 42 }, ]; mockedApiClient.fetchFlyerItems.mockResolvedValue({ ok: true, json: () => Promise.resolve({ items: mockFlyerItems }), } as Response); const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(mockedApiClient.fetchFlyerItems).toHaveBeenCalledWith(42); expect(result.current.data).toEqual(mockFlyerItems); }); it('should not fetch when flyerId is undefined', async () => { const { result } = renderHook(() => useFlyerItemsQuery(undefined), { wrapper }); // Wait a bit to ensure the query doesn't run await new Promise((resolve) => setTimeout(resolve, 100)); expect(mockedApiClient.fetchFlyerItems).not.toHaveBeenCalled(); expect(result.current.isLoading).toBe(false); expect(result.current.isFetching).toBe(false); }); it('should handle API error with error message', async () => { mockedApiClient.fetchFlyerItems.mockResolvedValue({ ok: false, status: 404, json: () => Promise.resolve({ message: 'Flyer not found' }), } as Response); const { result } = renderHook(() => useFlyerItemsQuery(999), { wrapper }); await waitFor(() => expect(result.current.isError).toBe(true)); expect(result.current.error?.message).toBe('Flyer not found'); }); it('should handle API error without message', async () => { mockedApiClient.fetchFlyerItems.mockResolvedValue({ ok: false, status: 500, json: () => Promise.reject(new Error('Parse error')), } as Response); const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper }); await waitFor(() => expect(result.current.isError).toBe(true)); expect(result.current.error?.message).toBe('Request failed with status 500'); }); it('should use fallback message when error.message is empty', async () => { mockedApiClient.fetchFlyerItems.mockResolvedValue({ ok: false, status: 500, json: () => Promise.resolve({ message: '' }), } as Response); const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper }); await waitFor(() => expect(result.current.isError).toBe(true)); expect(result.current.error?.message).toBe('Failed to fetch flyer items'); }); it('should throw error when refetch is called without flyerId', async () => { // This tests the internal guard in queryFn that throws if flyerId is undefined // We call refetch() manually to force the queryFn to execute even when disabled const { result } = renderHook(() => useFlyerItemsQuery(undefined), { wrapper }); // Force the query to run by calling refetch await act(async () => { await result.current.refetch(); }); await waitFor(() => expect(result.current.isError).toBe(true)); expect(result.current.error?.message).toBe('Flyer ID is required'); }); it('should return empty array when API returns no items', async () => { mockedApiClient.fetchFlyerItems.mockResolvedValue({ ok: true, json: () => Promise.resolve({ items: [] }), } as Response); const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(result.current.data).toEqual([]); }); it('should handle response without items property', async () => { mockedApiClient.fetchFlyerItems.mockResolvedValue({ ok: true, json: () => Promise.resolve({}), } as Response); const { result } = renderHook(() => useFlyerItemsQuery(42), { wrapper }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(result.current.data).toEqual([]); }); });