import { renderHook, waitFor } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { useStats, useSummary } from '../../../src/hooks/useStats'; import * as api from '../../../src/api/client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; // Mock the api client vi.mock('../../../src/api/client', () => ({ fetchDailySummary: vi.fn(), fetchTimetable: vi.fn(), fetchWakeSleepTimes: vi.fn(), fetchSummary: vi.fn(), })); const createTestQueryClient = () => new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0, }, }, }); describe('useStats', () => { const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); beforeEach(() => { vi.clearAllMocks(); }); it('should return data and correctly transform daily stats on success', async () => { const mockDailySummary = { days: [ { date: '2024-01-01', total_minutes: 480, day_minutes: 240, night_minutes: 240 }, { date: '2024-01-02', total_minutes: 540, day_minutes: 300, night_minutes: 240 }, ], }; const mockTimetable = { slots: [] }; const mockWakeupBedtime = { days: [] }; vi.mocked(api.fetchDailySummary).mockResolvedValueOnce(mockDailySummary); vi.mocked(api.fetchTimetable).mockResolvedValueOnce(mockTimetable); vi.mocked(api.fetchWakeSleepTimes).mockResolvedValueOnce(mockWakeupBedtime); const { result } = renderHook(() => useStats('2024-01-01', '2024-01-02'), { wrapper }); // Initially loading expect(result.current.isLoading).toBe(true); await waitFor(() => expect(result.current.isLoading).toBe(false)); // Verify raw data expect(result.current.dailySummary).toEqual(mockDailySummary); // Verify transformation (total, day/night split in hours) expect(result.current.transformedDailySummary).toEqual([ { date: '2024-01-01', daySleep: 4, nightSleep: 4, total: 8 }, { date: '2024-01-02', daySleep: 5, nightSleep: 4, total: 9 }, ]); expect(result.current.timetable).toEqual(mockTimetable); expect(result.current.wakeupBedtime).toEqual(mockWakeupBedtime); expect(result.current.error).toBeNull(); }); it('should handle empty data from the API', async () => { const mockEmptyDailySummary = { days: [] }; const mockTimetable = { slots: [] }; const mockWakeupBedtime = { days: [] }; vi.mocked(api.fetchDailySummary).mockResolvedValueOnce(mockEmptyDailySummary); vi.mocked(api.fetchTimetable).mockResolvedValueOnce(mockTimetable); vi.mocked(api.fetchWakeSleepTimes).mockResolvedValueOnce(mockWakeupBedtime); const { result } = renderHook(() => useStats('2024-01-01', '2024-01-02'), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBe(false)); expect(result.current.dailySummary).toEqual(mockEmptyDailySummary); expect(result.current.transformedDailySummary).toEqual([]); expect(result.current.error).toBeNull(); }); it('should handle error state when the API request fails', async () => { const error = new Error('API Failure'); vi.mocked(api.fetchDailySummary).mockRejectedValueOnce(error); vi.mocked(api.fetchTimetable).mockResolvedValueOnce({ slots: [] }); vi.mocked(api.fetchWakeSleepTimes).mockResolvedValueOnce({ days: [] }); const { result } = renderHook(() => useStats('2024-01-01', '2024-01-02'), { wrapper }); await waitFor(() => expect(result.current.isLoading).toBe(false)); expect(result.current.error).toBeDefined(); expect(result.current.error).toEqual(error); }); it('should handle loading state transitions', async () => { let resolvePromise: (value: any) => void; const pendingPromise = new Promise((resolve) => { resolvePromise = resolve; }); vi.mocked(api.fetchDailySummary).mockReturnValueOnce(pendingPromise as any); vi.mocked(api.fetchTimetable).mockResolvedValueOnce({ slots: [] }); vi.mocked(api.fetchWakeSleepTimes).mockResolvedValueOnce({ days: [] }); const { result } = renderHook(() => useStats('2024-01-01', '2024-01-02'), { wrapper }); // Should be loading while promise is pending expect(result.current.isLoading).toBe(true); // Resolve the promise resolvePromise!({ days: [] }); await waitFor(() => expect(result.current.isLoading).toBe(false)); }); }); describe('useSummary', () => { const wrapper = ({ children }: { children: React.ReactNode }) => ( {children} ); beforeEach(() => { vi.clearAllMocks(); }); it('should return data on success', async () => { const mockSummary = { total_sleeps: 10, avg_sleeps_per_day: 2, total_sleeps_all_time: 100, avg_sleeps_per_day_all_time: 2.5, }; vi.mocked(api.fetchSummary).mockResolvedValueOnce(mockSummary); const { result } = renderHook(() => useSummary('2024-01-01', '2024-01-02'), { wrapper }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); expect(result.current.data).toEqual(mockSummary); expect(api.fetchSummary).toHaveBeenCalledWith('2024-01-01', '2024-01-02'); }); it('should handle error on failure', async () => { const error = new Error('API Failure'); vi.mocked(api.fetchSummary).mockRejectedValueOnce(error); const { result } = renderHook(() => useSummary('2024-01-01', '2024-01-02'), { wrapper }); await waitFor(() => expect(result.current.isError).toBe(true)); expect(result.current.error).toEqual(error); }); });