Files
solelog/apps/worker/src/screens/History.test.tsx
Bas van Rossem 70ac27ec8e
All checks were successful
Build and Push Docker Image / build (push) Successful in 28s
style: align oxfmt to trailing-comma 'all' and normalize code
The repo was authored prettier-style (trailing-comma 'all') but .oxfmtrc.json
was set to 'es5', so every formatted file diverged. Switch the config to 'all'
to match the existing code, ignore docs/** and **/drizzle/** (prose + generated
snapshots the formatter should not own), and reformat the source tree once for
consistency. No behavioural change; all suites green (api 60, worker 28, admin 21).
2026-06-17 21:36:18 +02:00

124 lines
4.0 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { WorkSession } from '@solelog/shared';
import History from './History';
import { apiFetch } from '../lib/api';
import { downloadExport } from '../lib/export';
// Mock the network layer so no real requests are made.
vi.mock('../lib/api', () => ({
apiFetch: vi.fn(),
}));
// Mock the authenticated CSV download helper.
vi.mock('../lib/export', () => ({
downloadExport: vi.fn(),
}));
const mockedApiFetch = vi.mocked(apiFetch);
const mockedDownloadExport = vi.mocked(downloadExport);
function session(overrides: Partial<WorkSession> = {}): WorkSession {
return {
id: 1,
user_id: 'u1',
activity_id: 1,
activity_name: 'Frezen',
insole_type: 'Kurk',
pair_count: 2,
start_time: '2026-01-02T08:30:00.000Z',
end_time: '2026-01-02T09:31:01.000Z',
duration_seconds: 3661,
paused_seconds: 0,
paused_at: null,
status: 'completed',
source: 'app',
notes: null,
created_at: '2026-01-02T08:30:00.000Z',
...overrides,
};
}
function renderHistory() {
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
return render(
<QueryClientProvider client={queryClient}>
<History />
</QueryClientProvider>,
);
}
describe('History', () => {
beforeEach(() => {
mockedApiFetch.mockReset();
mockedDownloadExport.mockReset();
mockedApiFetch.mockResolvedValue([]);
mockedDownloadExport.mockResolvedValue(undefined);
});
afterEach(() => {
vi.clearAllMocks();
});
it('renders the header and export button in Dutch', () => {
renderHistory();
expect(screen.getByText('Geschiedenis')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Exporteer CSV' })).toBeInTheDocument();
});
it('shows the empty state when there are no sessions', async () => {
mockedApiFetch.mockResolvedValue([]);
renderHistory();
expect(await screen.findByText('Nog geen opgeslagen sessies.')).toBeInTheDocument();
});
it('renders a session card with activity name, type, count and duration', async () => {
mockedApiFetch.mockResolvedValue([session({ duration_seconds: 3661 })]);
renderHistory();
expect(await screen.findByText('Frezen')).toBeInTheDocument();
const card = screen.getByText('Frezen').closest('.session-card') as HTMLElement;
expect(card).not.toBeNull();
expect(card.textContent).toContain('Kurk');
expect(card.textContent).toContain('2 inlegzolen');
// 3661s -> "1h 1m" (hours present).
expect(card.textContent).toContain('1h 1m');
});
it('uses the singular noun for a count of 1', async () => {
mockedApiFetch.mockResolvedValue([session({ pair_count: 1 })]);
renderHistory();
const card = (await screen.findByText('Frezen')).closest('.session-card') as HTMLElement;
expect(card.textContent).toContain('1 inlegzool');
expect(card.textContent).not.toContain('1 inlegzolen');
});
it('renders a Pauze line when paused_seconds > 0', async () => {
mockedApiFetch.mockResolvedValue([session({ paused_seconds: 125 })]);
renderHistory();
const card = (await screen.findByText('Frezen')).closest('.session-card') as HTMLElement;
// 125s -> "2m 5s".
expect(card.textContent).toContain('Pauze 2m 5s');
});
it('does not render a Pauze line when paused_seconds is 0', async () => {
mockedApiFetch.mockResolvedValue([session({ paused_seconds: 0 })]);
renderHistory();
const card = (await screen.findByText('Frezen')).closest('.session-card') as HTMLElement;
expect(card.textContent).not.toContain('Pauze');
});
it('triggers the CSV download on Exporteer CSV', async () => {
const user = userEvent.setup();
renderHistory();
await user.click(screen.getByRole('button', { name: 'Exporteer CSV' }));
expect(mockedDownloadExport).toHaveBeenCalledTimes(1);
});
});