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,93 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { ShareDialog } from '../../components/ShareDialog';
describe('ShareDialog', () => {
const defaultProps = {
show: true,
sharePeople: ['Alice', 'Bob'],
shareSelectedPerson: 'Alice',
onShareSelectedPersonChange: vi.fn(),
shareSelectedDays: 30,
onShareSelectedDaysChange: vi.fn(),
shareGenerating: false,
shareLink: null,
onShareLinkChange: vi.fn(),
shareCopied: false,
onShareCopiedChange: vi.fn(),
onClose: vi.fn(),
onGenerateShareLink: vi.fn(),
onCopyShareLink: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
});
it('returns null when show is false', () => {
const { container } = render(<ShareDialog {...defaultProps} show={false} />);
expect(container.firstChild).toBeNull();
});
it('renders dialog when show is true', () => {
render(<ShareDialog {...defaultProps} />);
expect(screen.getByText(/share\.title/i)).toBeInTheDocument();
});
it('renders no people message when sharePeople is empty', () => {
render(<ShareDialog {...defaultProps} sharePeople={[]} />);
expect(screen.getByText(/share\.noPeople/i)).toBeInTheDocument();
});
it('renders person selection dropdown', () => {
render(<ShareDialog {...defaultProps} />);
expect(screen.getByRole('option', { name: 'Alice' })).toBeInTheDocument();
expect(screen.getByRole('option', { name: 'Bob' })).toBeInTheDocument();
});
it('renders period selection dropdown', () => {
render(<ShareDialog {...defaultProps} />);
// The dropdown renders with 3 options for time periods
const options = screen.getAllByRole('option');
expect(options.length).toBeGreaterThanOrEqual(3);
});
it('calls onClose when close button is clicked', () => {
render(<ShareDialog {...defaultProps} />);
fireEvent.click(screen.getByText('×'));
expect(defaultProps.onClose).toHaveBeenCalled();
});
it('calls onClose when overlay is clicked', () => {
const { container } = render(<ShareDialog {...defaultProps} />);
const overlay = container.querySelector('.modal-overlay');
fireEvent.click(overlay!);
expect(defaultProps.onClose).toHaveBeenCalled();
});
it('shows generated link', () => {
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" />);
const input = screen.getByRole('textbox');
expect(input).toHaveValue('http://example.com/share/abc123');
});
it('calls onCopyShareLink when copy button is clicked', () => {
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" />);
fireEvent.click(screen.getByText('📋'));
expect(defaultProps.onCopyShareLink).toHaveBeenCalled();
});
it('shows copied indicator after copy', () => {
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" shareCopied={true} />);
expect(screen.getByText('✓')).toBeInTheDocument();
});
it('selects link text when input is clicked', () => {
render(<ShareDialog {...defaultProps} shareLink="http://example.com/share/abc123" />);
const input = screen.getByRole('textbox') as HTMLInputElement;
const selectMock = vi.fn();
input.select = selectMock;
fireEvent.click(input);
expect(selectMock).toHaveBeenCalled();
});
});