feat(worker): Geschiedenis screen with session list and CSV export
This commit is contained in:
104
apps/worker/src/screens/History.test.tsx
Normal file
104
apps/worker/src/screens/History.test.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
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,
|
||||
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('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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user