Tests refactor
This commit is contained in:
parent
245520fb50
commit
99134d8556
165 changed files with 911 additions and 531 deletions
337
packages/web/tests/app/(dashboard)/dashboard/page.test.tsx
Normal file
337
packages/web/tests/app/(dashboard)/dashboard/page.test.tsx
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
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) => <a href={props.href}>{props.children}</a>,
|
||||
}));
|
||||
|
||||
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(<DashboardPage />);
|
||||
expect(screen.getByText('Dashboard')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows loading skeleton when session loading', () => {
|
||||
render(<DashboardPage />);
|
||||
expect(screen.getByText('Dashboard')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders greeting with user name', () => {
|
||||
mockUseApi.mockReturnValue({
|
||||
householdId: 'hh1',
|
||||
isLoading: false,
|
||||
profile: { displayName: 'John Doe' },
|
||||
});
|
||||
|
||||
render(<DashboardPage />);
|
||||
|
||||
expect(screen.getByText(/John/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders generic greeting without profile', () => {
|
||||
mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false, profile: null });
|
||||
|
||||
render(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
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(<DashboardPage />);
|
||||
|
||||
expect(screen.getByText('Unknown')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue