import { afterEach, describe, expect, it, vi } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import Page from './+page.svelte'; vi.mock('$app/navigation', () => ({ goto: vi.fn() })); afterEach(cleanup); // ─── Test data ──────────────────────────────────────────────────────────────── const baseData = { user: undefined, canWrite: true, canAnnotate: false, documents: [], initialValues: { senderName: '', receiverName: '' }, filters: { senderId: '', receiverId: '', from: '', to: '', dir: 'DESC' as const } }; const withSender = { ...baseData, initialValues: { senderName: 'Hans Müller', receiverName: '' }, filters: { ...baseData.filters, senderId: 'p1' } }; const withPersons = { ...baseData, initialValues: { senderName: 'Hans Müller', receiverName: 'Anna Schmidt' }, filters: { ...baseData.filters, senderId: 'p1', receiverId: 'p2' } }; const makeDoc = (overrides: Record = {}) => ({ id: 'd1', title: 'Testbrief', originalFilename: 'testbrief.pdf', status: 'UPLOADED' as const, documentDate: '1923-04-12', location: 'Berlin', metadataComplete: false, scriptType: 'UNKNOWN' as const, sender: { id: 'p1', firstName: 'Hans', lastName: 'Müller' }, receivers: [{ id: 'p2', firstName: 'Anna', lastName: 'Schmidt' }], tags: [], transcription: undefined, filePath: undefined, createdAt: '1923-04-12T00:00:00Z', updatedAt: '1923-04-12T00:00:00Z', ...overrides }); const withDocs = { ...withPersons, documents: [makeDoc()] }; // ─── Hero state (no senderId) ──────────────────────────────────────────────── describe('Briefwechsel page – hero state', () => { it('shows the hero when no person is selected', async () => { render(Page, { data: baseData }); await expect.element(page.getByTestId('conv-hero')).toBeInTheDocument(); }); it('shows the discovery headline', async () => { render(Page, { data: baseData }); await expect.element(page.getByText(/Wessen Briefe möchten Sie lesen/i)).toBeInTheDocument(); }); it('does not show the person bar in hero state', async () => { render(Page, { data: baseData }); await expect.element(page.getByTestId('conv-hero')).toBeInTheDocument(); await expect.element(page.getByTestId('conv-person-bar')).not.toBeInTheDocument(); }); it('does not show filter controls in hero state', async () => { render(Page, { data: baseData }); await expect.element(page.getByTestId('conv-hero')).toBeInTheDocument(); await expect.element(page.getByTestId('conv-filter-controls')).not.toBeInTheDocument(); }); it('does not show the new document link when no person is selected', async () => { render(Page, { data: baseData }); await expect.element(page.getByTestId('conv-new-doc-link')).not.toBeInTheDocument(); }); it('does not show a year divider when no person is selected', async () => { render(Page, { data: baseData }); await expect.element(page.getByTestId('year-divider')).not.toBeInTheDocument(); }); }); // ─── Results state (senderId set) ──────────────────────────────────────────── describe('Briefwechsel page – results state', () => { it('does not show the hero when senderId is set', async () => { render(Page, { data: withSender }); await expect.element(page.getByTestId('conv-person-bar')).toBeInTheDocument(); await expect.element(page.getByTestId('conv-hero')).not.toBeInTheDocument(); }); it('shows the person bar when senderId is set', async () => { render(Page, { data: withSender }); await expect.element(page.getByTestId('conv-person-bar')).toBeInTheDocument(); }); it('hides filter controls by default (collapsible)', async () => { render(Page, { data: withSender }); await expect.element(page.getByTestId('conv-person-bar')).toBeInTheDocument(); await expect.element(page.getByTestId('conv-filter-controls')).not.toBeInTheDocument(); }); }); // ─── Recent persons chips ───────────────────────────────────────────────────── describe('Briefwechsel page – recent persons', () => { it('shows recent person chips from localStorage', async () => { localStorage.setItem( 'korrespondenz_recent_persons', JSON.stringify([{ id: 'r1', name: 'Clara Braun' }]) ); render(Page, { data: baseData }); await expect.element(page.getByText('Clara Braun')).toBeInTheDocument(); localStorage.removeItem('korrespondenz_recent_persons'); }); it('does not crash when localStorage contains corrupt JSON', async () => { localStorage.setItem('korrespondenz_recent_persons', '}{not valid json'); render(Page, { data: baseData }); await expect.element(page.getByText(/Wessen Briefe möchten Sie lesen/i)).toBeInTheDocument(); localStorage.removeItem('korrespondenz_recent_persons'); }); }); // ─── Single-person hint bar ─────────────────────────────────────────────────── describe('Briefwechsel page – single-person hint bar', () => { it('shows hint bar when only senderId is set', async () => { render(Page, { data: withSender }); await expect.element(page.getByText(/Alle Briefe von Hans Müller/i)).toBeInTheDocument(); }); it('does not show hint bar when both persons are set', async () => { render(Page, { data: { ...withPersons, documents: [makeDoc()] } }); await expect.element(page.getByText(/Alle Briefe von Hans Müller/i)).not.toBeInTheDocument(); }); it('does not show hint bar when no person is set', async () => { render(Page, { data: baseData }); await expect.element(page.getByText(/Alle Briefe von/i)).not.toBeInTheDocument(); }); }); // ─── Strip letter count ─────────────────────────────────────────────────────── describe('Briefwechsel page – strip letter count', () => { it('shows 0 Briefe when senderId is set but no documents', async () => { render(Page, { data: withSender }); await expect.element(page.getByTestId('conv-strip-count')).toHaveTextContent('0 Briefe'); }); it('shows correct count when documents are loaded', async () => { render(Page, { data: { ...withPersons, documents: [makeDoc()] } }); await expect.element(page.getByTestId('conv-strip-count')).toHaveTextContent('1 Briefe'); }); }); // ─── No results ─────────────────────────────────────────────────────────────── describe('Briefwechsel page – no results', () => { it('shows "no documents found" when a person is selected but there are no documents', async () => { render(Page, { data: withSender }); await expect.element(page.getByText(/Keine Dokumente gefunden/i)).toBeInTheDocument(); }); }); // ─── Swap button ────────────────────────────────────────────────────────────── describe('Briefwechsel page – swap button', () => { it('swap button is invisible when only one person is set', async () => { render(Page, { data: withSender }); const btn = document.querySelector('[data-testid="conv-swap-btn"]'); expect(btn).not.toBeNull(); expect(btn!.className).toMatch(/opacity-0/); }); it('swap button is visible when both persons are set', async () => { render(Page, { data: withPersons }); const btn = document.querySelector('[data-testid="conv-swap-btn"]'); expect(btn).not.toBeNull(); expect(btn!.className).not.toMatch(/opacity-0/); }); it('calls goto with swapped sender and receiver when clicked', async () => { const { goto } = await import('$app/navigation'); vi.mocked(goto).mockClear(); render(Page, { data: withPersons }); document.querySelector('[data-testid="conv-swap-btn"]')!.click(); expect(goto).toHaveBeenCalledWith(expect.stringContaining('senderId=p2'), expect.anything()); expect(goto).toHaveBeenCalledWith(expect.stringContaining('receiverId=p1'), expect.anything()); }); }); // ─── Year dividers ──────────────────────────────────────────────────────────── describe('Briefwechsel page – year dividers', () => { it('renders a year divider for the first document', async () => { render(Page, { data: withDocs }); await expect.element(page.getByTestId('year-divider').first()).toHaveTextContent('1923'); }); it('renders a divider for each new year in the document list', async () => { const data = { ...withPersons, documents: [ makeDoc({ documentDate: '1923-04-12' }), makeDoc({ id: 'd2', documentDate: '1965-08-03' }) ] }; render(Page, { data }); await expect.element(page.getByTestId('year-divider').first()).toHaveTextContent('1923'); await expect.element(page.getByTestId('year-divider').nth(1)).toHaveTextContent('1965'); }); it('does not render a second divider for documents from the same year', async () => { const data = { ...withPersons, documents: [ makeDoc({ documentDate: '1923-04-12' }), makeDoc({ id: 'd2', documentDate: '1923-09-01' }) ] }; render(Page, { data }); await expect.element(page.getByTestId('year-divider').first()).toHaveTextContent('1923'); await expect.element(page.getByTestId('year-divider').nth(1)).not.toBeInTheDocument(); }); }); // ─── New document link ──────────────────────────────────────────────────────── describe('Briefwechsel page – new document link', () => { it('shows the link with correct href for a write user (bilateral)', async () => { render(Page, { data: { ...withDocs, canWrite: true } }); const link = page.getByTestId('conv-new-doc-link'); await expect.element(link).toBeInTheDocument(); await expect.element(link).toHaveAttribute('href', expect.stringContaining('senderId=p1')); await expect.element(link).toHaveAttribute('href', expect.stringContaining('receiverId=p2')); }); it('shows the link with correct href for single-person mode', async () => { render(Page, { data: { ...withSender, documents: [makeDoc()], canWrite: true } }); const link = page.getByTestId('conv-new-doc-link'); await expect.element(link).toBeInTheDocument(); await expect.element(link).toHaveAttribute('href', expect.stringContaining('senderId=p1')); await expect.element(link).not.toHaveAttribute('href', expect.stringContaining('receiverId')); }); it('hides the link for a read-only user', async () => { render(Page, { data: { ...withDocs, canWrite: false } }); await expect.element(page.getByTestId('conv-new-doc-link')).not.toBeInTheDocument(); }); });