Files
medassist-ng/frontend/src/test/components/MedDetailModal.test.tsx
T
Daniel Volz fd055a3a2a 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>
2026-01-22 10:25:11 +01:00

212 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
});
});