Feat/frontend tests (#62)
* test(frontend): add vitest test infrastructure - Add vitest, testing-library, jsdom dependencies - Configure vitest with jsdom environment - Add test setup with mocks for fetch, localStorage, matchMedia - Set 75% coverage threshold * Add frontend tests (#61) * Initial plan * Add frontend tests - utilities, hooks, and components (21% coverage) Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Add more component tests (Auth, AboutModal, ExportModal) and useRefill hook tests - 30% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Add useMedicationForm utility function tests - 30% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Add AppHeader tests and more schedule.ts tests - 32% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Changes before error encountered Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Fix page tests and add more tests - 326 tests passing, 34% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Add tests for ProfileModal, UserFilterModal, MedDetailModal - 361 tests, 36% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Add SharedSchedule tests - 366 tests, 39% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Expand page tests - 383 tests, 39% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Add MobileEditModal tests - 409 tests, 40% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Expand Dashboard and Schedule page tests - 427 tests, 40% coverage Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> * Fix code review issues - remove invalid remindEnabled property Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: DanielVolz <3275994+DanielVolz@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { MedDetailModal } from '../../components/MedDetailModal';
|
||||
import type { Medication, Coverage, StockThresholds, RefillEntry } from '../../types';
|
||||
|
||||
const defaultSettings: StockThresholds = {
|
||||
lowStockDays: 7,
|
||||
normalStockDays: 30,
|
||||
highStockDays: 90
|
||||
};
|
||||
|
||||
const mockMedication: Medication = {
|
||||
id: 1,
|
||||
name: 'Test Med',
|
||||
genericName: 'Generic Name',
|
||||
packCount: 1,
|
||||
blistersPerPack: 1,
|
||||
pillsPerBlister: 30,
|
||||
looseTablets: 0,
|
||||
takenBy: ['John'],
|
||||
blisters: [{ usage: 1, every: 1, start: '2024-01-01T09:00:00' }],
|
||||
updatedAt: null,
|
||||
expiryDate: '2025-12-31',
|
||||
notes: 'Test notes'
|
||||
};
|
||||
|
||||
const mockCoverage: Coverage = {
|
||||
name: 'Test Med',
|
||||
medsLeft: 25,
|
||||
daysLeft: 25,
|
||||
depletionDate: '2024-04-01',
|
||||
depletionTime: Date.now() + 25 * 86400000,
|
||||
nextDose: null
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
selectedMed: mockMedication,
|
||||
coverage: { all: [mockCoverage] },
|
||||
settings: defaultSettings,
|
||||
showImageLightbox: false,
|
||||
showRefillModal: false,
|
||||
showEditStockModal: false,
|
||||
onClose: vi.fn(),
|
||||
onOpenImageLightbox: vi.fn(),
|
||||
onCloseImageLightbox: vi.fn(),
|
||||
onOpenRefillModal: vi.fn(),
|
||||
onCloseRefillModal: vi.fn(),
|
||||
onOpenEditStockModal: vi.fn(),
|
||||
onCloseEditStockModal: vi.fn(),
|
||||
refillPacks: 0,
|
||||
onRefillPacksChange: vi.fn(),
|
||||
refillLoose: 0,
|
||||
onRefillLooseChange: vi.fn(),
|
||||
refillSaving: false,
|
||||
refillHistory: [] as RefillEntry[],
|
||||
refillHistoryExpanded: false,
|
||||
onRefillHistoryExpandedChange: vi.fn(),
|
||||
onSubmitRefill: vi.fn(),
|
||||
editStockFullBlisters: 0,
|
||||
onEditStockFullBlistersChange: vi.fn(),
|
||||
editStockPartialBlisterPills: 0,
|
||||
onEditStockPartialBlisterPillsChange: vi.fn(),
|
||||
editStockSaving: false,
|
||||
onSubmitStockCorrection: vi.fn()
|
||||
};
|
||||
|
||||
describe('MedDetailModal', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders nothing when selectedMed is null', () => {
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={null} />);
|
||||
|
||||
expect(screen.queryByText('Test Med')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders modal when medication is selected', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays medication name', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays generic name', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Generic Name')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders close button', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
const closeBtn = screen.getByText('×');
|
||||
expect(closeBtn).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onClose when close button clicked', () => {
|
||||
const onClose = vi.fn();
|
||||
render(<MedDetailModal {...defaultProps} onClose={onClose} />);
|
||||
|
||||
const closeBtn = screen.getByText('×');
|
||||
fireEvent.click(closeBtn);
|
||||
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls onClose when overlay clicked', () => {
|
||||
const onClose = vi.fn();
|
||||
render(<MedDetailModal {...defaultProps} onClose={onClose} />);
|
||||
|
||||
const overlay = document.querySelector('.modal-overlay');
|
||||
if (overlay) {
|
||||
fireEvent.click(overlay);
|
||||
}
|
||||
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not call onClose when modal content clicked', () => {
|
||||
const onClose = vi.fn();
|
||||
render(<MedDetailModal {...defaultProps} onClose={onClose} />);
|
||||
|
||||
const content = document.querySelector('.modal-content');
|
||||
if (content) {
|
||||
fireEvent.click(content);
|
||||
}
|
||||
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('displays notes when available', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
expect(screen.getByText('Test notes')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays schedule information', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
// Should have schedule section
|
||||
const scheduleSection = document.querySelector('.med-detail-schedules');
|
||||
expect(scheduleSection).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders med detail header', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
const header = document.querySelector('.med-detail-header');
|
||||
expect(header).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders med detail body', () => {
|
||||
render(<MedDetailModal {...defaultProps} />);
|
||||
|
||||
const body = document.querySelector('.med-detail-body');
|
||||
expect(body).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('MedDetailModal without coverage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('works without coverage data', () => {
|
||||
render(<MedDetailModal {...defaultProps} coverage={{ all: [] }} />);
|
||||
|
||||
// Should still render the medication name
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('MedDetailModal without optional fields', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('works without generic name', () => {
|
||||
const med = { ...mockMedication, genericName: null };
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={med} />);
|
||||
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('works without notes', () => {
|
||||
const med = { ...mockMedication, notes: null };
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={med} />);
|
||||
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('works without takenBy', () => {
|
||||
const med = { ...mockMedication, takenBy: [] };
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={med} />);
|
||||
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('works without expiryDate', () => {
|
||||
const med = { ...mockMedication, expiryDate: null };
|
||||
render(<MedDetailModal {...defaultProps} selectedMed={med} />);
|
||||
|
||||
expect(screen.getByText('Test Med')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user