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:
Daniel Volz
2026-01-22 10:25:11 +01:00
committed by GitHub
parent 8718311876
commit fd055a3a2a
36 changed files with 7602 additions and 3 deletions
@@ -0,0 +1,203 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { SchedulePage } from '../../pages/SchedulePage';
// Mock the context
vi.mock('../../context', () => ({
useAppContext: () => ({
meds: [],
settings: {
lowStockThreshold: 30,
criticalStockThreshold: 7,
expiryWarningDays: 30,
lowStockDays: 7,
normalStockDays: 30,
highStockDays: 90
},
scheduleDays: 30,
setScheduleDays: vi.fn(),
showPastDays: false,
setShowPastDays: vi.fn(),
pastDays: [],
futureDays: [],
takenDoses: new Set(),
markDoseTaken: vi.fn(),
undoDoseTaken: vi.fn(),
coverageByMed: {},
depletionByMed: {},
manuallyExpandedDays: new Set(),
toggleDayCollapse: vi.fn(),
openUserFilter: vi.fn()
})
}));
vi.mock('../../components/Auth', () => ({
useAuth: () => ({
user: { id: 1, username: 'testuser' }
})
}));
describe('SchedulePage', () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
});
it('renders schedule page', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
// Should render the schedule section
const section = document.querySelector('section.grid');
expect(section).toBeInTheDocument();
});
it('renders schedule title', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
expect(screen.getByText(/dashboard\.schedules\.title/i)).toBeInTheDocument();
});
it('renders day range selector', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
// Should have schedule days select dropdown
const select = document.querySelector('.schedule-days-select');
expect(select).toBeInTheDocument();
});
it('renders timeline section', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
// Should have timeline div
const timeline = document.querySelector('.timeline');
expect(timeline).toBeInTheDocument();
});
it('shows empty state when no medications', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
// With no meds, should show the schedule card but with empty timeline
const card = document.querySelector('.card.schedule-full');
expect(card).toBeInTheDocument();
});
it('renders card head', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const cardHead = document.querySelector('.card-head');
expect(cardHead).toBeInTheDocument();
});
it('renders schedule days options', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const select = document.querySelector('.schedule-days-select');
const options = select?.querySelectorAll('option');
expect(options?.length).toBe(3);
});
it('has 30, 90, 180 day options', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
expect(screen.getByText(/dashboard\.schedules\.1month/i)).toBeInTheDocument();
expect(screen.getByText(/dashboard\.schedules\.3months/i)).toBeInTheDocument();
expect(screen.getByText(/dashboard\.schedules\.6months/i)).toBeInTheDocument();
});
it('can change schedule days', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const select = document.querySelector('.schedule-days-select') as HTMLSelectElement;
expect(select).toBeInTheDocument();
fireEvent.change(select, { target: { value: '90' } });
});
});
describe('SchedulePage structure', () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
});
it('has heading element', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const heading = document.querySelector('h2');
expect(heading).toBeInTheDocument();
});
it('renders article element', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const article = document.querySelector('article');
expect(article).toBeInTheDocument();
});
it('renders section element', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const section = document.querySelector('section');
expect(section).toBeInTheDocument();
});
it('renders card with correct class', () => {
render(
<MemoryRouter>
<SchedulePage />
</MemoryRouter>
);
const card = document.querySelector('.card.schedule-full');
expect(card).toBeInTheDocument();
});
});