import { render, screen } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import BedtimePlot from '../../../../src/components/stats/BedtimePlot'; import { useStats } from '../../../../src/hooks/useStats'; import { XAxis, YAxis, Tooltip } from 'recharts'; vi.mock('../../../../src/hooks/useStats', () => ({ useStats: vi.fn(), })); vi.mock('recharts', async () => { const actual = await vi.importActual('recharts'); return { ...actual, ResponsiveContainer: ({ children }: any) =>
{children}
, LineChart: ({ children }: any) =>
{children}
, XAxis: vi.fn(({ tickFormatter }) =>
), YAxis: vi.fn(({ tickFormatter }) =>
), CartesianGrid: vi.fn(), Tooltip: vi.fn(({ formatter, labelFormatter }) =>
), Line: vi.fn(), }; }); describe('BedtimePlot', () => { const props = { from_date: '2024-01-01', to_date: '2024-01-07', wakeup_ref: '07:00', bedtime_ref: '20:00', }; beforeEach(() => { vi.clearAllMocks(); }); it('renders loading state', () => { vi.mocked(useStats).mockReturnValue({ wakeupBedtime: undefined, isLoading: true, } as any); render(); expect(screen.getByText(/Loading plot.../i)).toBeInTheDocument(); }); it('renders empty state when no data is available', () => { vi.mocked(useStats).mockReturnValue({ wakeupBedtime: { days: [] }, isLoading: false, } as any); render(); expect(screen.getByText(/No data available for this period./i)).toBeInTheDocument(); }); it('renders chart and statistics correctly with data', () => { const today = new Date().toISOString().split('T')[0]; const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0]; vi.mocked(useStats).mockReturnValue({ wakeupBedtime: { days: [ { date: today, bedtime_minutes: 0 }, // Exactly at ref (20:00) { date: yesterday, bedtime_minutes: -60 }, // 19:00 { date: '2024-01-05', bedtime_minutes: 60 }, // 21:00 ], }, isLoading: false, } as any); render(); expect(screen.getByText(/Bedtime/i)).toBeInTheDocument(); // Verify statistical calculations expect(screen.getByText(/19:00/i)).toBeInTheDocument(); expect(screen.getByText(/21:00/i)).toBeInTheDocument(); const timeValues = screen.getAllByText(/20:00/i); expect(timeValues).toHaveLength(2); }); it('renders correctly when all data points are null', () => { const today = new Date().toISOString().split('T')[0]; vi.mocked(useStats).mockReturnValue({ wakeupBedtime: { days: [ { date: today, bedtime_minutes: null }, ], }, isLoading: false, } as any); render(); expect(screen.getByText(/Bedtime/i)).toBeInTheDocument(); expect(screen.queryByText(/Earliest/i)).not.toBeInTheDocument(); }); it('verifies YAxis tickFormatter handles numeric values', () => { const today = new Date().toISOString().split('T')[0]; vi.mocked(useStats).mockReturnValue({ wakeupBedtime: { days: [ { date: today, bedtime_minutes: 30 }, ], }, isLoading: false, } as any); render(); const yAxisCall = vi.mocked(YAxis).mock.calls[0][0]; expect(yAxisCall.tickFormatter(30)).toBe('20:30'); }); it('verifies Tooltip formatter handles various value types', () => { const today = new Date().toISOString().split('T')[0]; vi.mocked(useStats).mockReturnValue({ wakeupBedtime: { days: [{ date: today, bedtime_minutes: 30 }], }, isLoading: false, } as any); render(); const tooltipCall = vi.mocked(Tooltip).mock.calls[0][0]; expect(tooltipCall.formatter(30)).toEqual(['20:30', 'Bedtime']); expect(tooltipCall.formatter('N/A')).toEqual(['N/A', 'Bedtime']); }); });