Files
babymonitor/frontend/tests/unit/components/stats/WakeupPlot.test.tsx
T

123 lines
4.1 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import WakeupPlot from '../../../../src/components/stats/WakeupPlot';
import { useStats } from '../../../../src/hooks/useStats';
import { YAxis, Tooltip } from 'recharts';
vi.mock('../../../../src/hooks/useStats', () => ({
useStats: vi.fn(),
}));
vi.mock('recharts', async () => {
const actual = await vi.importActual<typeof import('recharts')>('recharts');
return {
...actual,
ResponsiveContainer: ({ children }: any) => <div data-testid="responsive-container">{children}</div>,
LineChart: ({ children }: any) => <div data-testid="line-chart">{children}</div>,
XAxis: vi.fn(({ tickFormatter }) => <div data-testid="x-axis" data-tick-formatter={String(tickFormatter)} />),
YAxis: vi.fn(({ tickFormatter }) => <div data-testid="y-axis" data-tick-formatter={String(tickFormatter)} />),
CartesianGrid: vi.fn(),
Tooltip: vi.fn(({ formatter, labelFormatter }) => <div data-testid="tooltip" />),
Line: vi.fn(),
};
});
describe('WakeupPlot', () => {
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(<WakeupPlot {...props} />);
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(<WakeupPlot {...props} />);
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, wakeup_minutes: 0 }, // Today (7:00 + 0m = 07:00)
{ date: yesterday, wakeup_minutes: 60 }, // 1d ago (7:00 + 60m = 08:00)
],
},
isLoading: false,
} as any);
render(<WakeupPlot {...props} />);
expect(screen.getByText(/Wakeup Times/i)).toBeInTheDocument();
// Check stats labels exist
expect(screen.getByText(/Earliest/i)).toBeInTheDocument();
expect(screen.getByText(/Latest/i)).toBeInTheDocument();
expect(screen.getByText(/Average/i)).toBeInTheDocument();
expect(screen.getByText(/Median/i)).toBeInTheDocument();
// Verify statistical calculations
expect(screen.getByText(/07:00/i)).toBeInTheDocument(); // Earliest
expect(screen.getByText(/08:00/i)).toBeInTheDocument(); // Latest
// Since Average and Median both result in 07:30, we use getAllByText
const averageAndMedianTimes = screen.getAllByText(/07:30/i);
expect(averageAndMedianTimes).toHaveLength(2);
});
it('verifies YAxis tickFormatter handles numeric values', () => {
const today = new Date().toISOString().split('T')[0];
vi.mocked(useStats).mockReturnValue({
wakeupBedtime: {
days: [{ date: today, wakeup_minutes: 30 }],
},
isLoading: false,
} as any);
render(<WakeupPlot {...props} />);
const yAxisCall = vi.mocked(YAxis).mock.calls[0][0];
expect(yAxisCall.tickFormatter(30)).toBe('07: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, wakeup_minutes: 30 }],
},
isLoading: false,
} as any);
render(<WakeupPlot {...props} />);
const tooltipCall = vi.mocked(Tooltip).mock.calls[0][0];
expect(tooltipCall.formatter(30)).toEqual(['07:30', 'Wakeup']);
expect(tooltipCall.formatter('N/A')).toEqual(['N/A', 'Wakeup']);
});
});