163 lines
5.6 KiB
TypeScript
163 lines
5.6 KiB
TypeScript
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 }) => (
|
|
<QueryClientProvider client={createTestQueryClient()}>
|
|
{children}
|
|
</QueryClientProvider>
|
|
);
|
|
|
|
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 }) => (
|
|
<QueryClientProvider client={createTestQueryClient()}>
|
|
{children}
|
|
</QueryClientProvider>
|
|
);
|
|
|
|
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);
|
|
});
|
|
});
|