import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen } from '@testing-library/react'; const { mockUseApi } = vi.hoisted(() => ({ mockUseApi: vi.fn() })); const { mockUseSWR } = vi.hoisted(() => ({ mockUseSWR: vi.fn() })); vi.mock('@/lib/useApi', () => ({ useApi: mockUseApi })); vi.mock('swr', () => ({ default: mockUseSWR })); vi.mock('@/services/cabinet', () => ({ getCabinetSummary: vi.fn(), })); vi.mock('@/services/regimens', () => ({ getBurnRates: vi.fn(), })); vi.mock('@/services/purchases', () => ({ listPurchases: vi.fn(), })); vi.mock('@/services/refills', () => ({ getRefillAlerts: vi.fn(), })); vi.mock('@/services/cabinet-events', () => ({ listCabinetEvents: vi.fn(), })); vi.mock('next/link', () => ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any default: (props: any) => {props.children}, })); import DashboardPage from '../../../../src/app/(dashboard)/dashboard/page'; beforeEach(() => { vi.clearAllMocks(); mockUseApi.mockReturnValue({ householdId: null, isLoading: true, profile: undefined }); mockUseSWR.mockReturnValue({ data: undefined }); }); describe(DashboardPage.name, () => { it('renders heading', () => { render(); expect(screen.getByText('Dashboard')).toBeInTheDocument(); }); it('shows loading skeleton when session loading', () => { render(); expect(screen.getByText('Dashboard')).toBeInTheDocument(); }); it('renders greeting with user name', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'John Doe' }, }); render(); expect(screen.getByText(/John/)).toBeInTheDocument(); }); it('renders generic greeting without profile', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: null }); render(); expect(screen.getByText(/there/)).toBeInTheDocument(); }); it('renders cabinet items that are in active regimens', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); const summaryData = { data: [ { medicineId: '1', medicineName: 'Aspirin', totalQuantity: 50, unit: 'tablets' }, { medicineId: '2', medicineName: 'Ibuprofen', totalQuantity: 100, unit: 'tablets' }, ], }; const burnRateData = { data: [ { medicineId: '1', medicineName: 'Aspirin', daysUntilEmpty: 10 }, ], }; mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: summaryData }; if (key.includes('burn-rates')) return { data: burnRateData }; if (key.includes('refill-alerts')) return { data: { data: [] } }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Aspirin')).toBeInTheDocument(); // Ibuprofen should NOT be in the document because it has no burn rate expect(screen.queryByText('Ibuprofen')).not.toBeInTheDocument(); }); it('shows empty states when no data', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: { data: [] } }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('No active medicines in regimens.')).toBeInTheDocument(); expect(screen.getByText('No alerts — all stocked.')).toBeInTheDocument(); expect(screen.getByText('No pending orders.')).toBeInTheDocument(); expect(screen.getByText('No recent activity.')).toBeInTheDocument(); }); it('shows refill alerts with days left', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); const refillData = { data: [{ medicineId: 'm1', medicineName: 'Vitamin C', daysUntilEmpty: 5 }], }; mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [{ medicineId: '1' }] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: refillData }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Vitamin C')).toBeInTheDocument(); expect(screen.getByText('5d')).toBeInTheDocument(); expect(screen.getByText(/critically low/)).toBeInTheDocument(); }); it('shows pending purchases', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); const purchaseData = { data: [ { _id: 'p1', storeName: 'Pharmacy Plus', items: [{ name: 'A' }, { name: 'B' }], status: 'ordered', }, ], }; mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: { data: [] } }; if (key.includes('purchases')) return { data: purchaseData }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Pharmacy Plus')).toBeInTheDocument(); expect(screen.getByText('2 items')).toBeInTheDocument(); expect(screen.getByText('ordered')).toBeInTheDocument(); }); it('shows recent events', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); const eventData = { data: [ { _id: 'e1', eventType: 'consumed', medicineName: 'Aspirin', createdAt: '2026-01-15T10:00:00.000Z', }, ], }; mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: { data: [] } }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: eventData }; return { data: undefined }; }); render(); expect(screen.getByText('consumed')).toBeInTheDocument(); expect(screen.getByText('Aspirin')).toBeInTheDocument(); }); it('shows "good shape" message when no critical alerts', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [{ medicineId: '1' }] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: { data: [{ daysUntilEmpty: 20 }] } }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Your cabinet is in good shape.')).toBeInTheDocument(); }); it('shows stat badges with summary data', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [{ medicineId: '1' }, { medicineId: '2' }, { medicineId: '3' }] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: { data: [{ daysUntilEmpty: 5 }, { daysUntilEmpty: 3 }] } }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Total medicines')).toBeInTheDocument(); expect(screen.getByText('3')).toBeInTheDocument(); expect(screen.getAllByText('Running low')).toHaveLength(2); expect(screen.getByText('2')).toBeInTheDocument(); }); it('handles store without name', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); const purchaseData = { data: [{ _id: 'p1', storeName: undefined, items: [{ name: 'A' }], status: 'ordered' }], }; mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: { data: [] } }; if (key.includes('burn-rates')) return { data: { data: [] } }; if (key.includes('refill-alerts')) return { data: { data: [] } }; if (key.includes('purchases')) return { data: purchaseData }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Unknown store')).toBeInTheDocument(); }); it('handles cabinet item with no name', () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: { displayName: 'Jane' }, }); const summaryData = { data: [{ medicineId: 'c1', medicineName: undefined, totalQuantity: 10, unit: 'pills' }], }; const burnRateData = { data: [{ medicineId: 'c1', medicineName: undefined, daysUntilEmpty: 5 }], }; mockUseSWR.mockImplementation((key: string) => { if (!key) return { data: undefined }; if (key.includes('cabinet-summary')) return { data: summaryData }; if (key.includes('burn-rates')) return { data: burnRateData }; if (key.includes('refill-alerts')) return { data: { data: [] } }; if (key.includes('purchases')) return { data: { data: [] } }; if (key.includes('cabinet-events')) return { data: { data: [] } }; return { data: undefined }; }); render(); expect(screen.getByText('Unknown')).toBeInTheDocument(); }); });