diff --git a/frontend/src/routes/conversations/page.svelte.spec.ts b/frontend/src/routes/conversations/page.svelte.spec.ts new file mode 100644 index 00000000..906a1dc4 --- /dev/null +++ b/frontend/src/routes/conversations/page.svelte.spec.ts @@ -0,0 +1,161 @@ +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 = { + canWrite: true, + documents: [], + initialValues: { senderName: '', receiverName: '' }, + filters: { senderId: '', receiverId: '', from: '', to: '', dir: 'DESC' as const } +}; + +const withPersons = { + ...baseData, + 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', + sender: { id: 'p1', firstName: 'Hans', lastName: 'Müller' }, + receivers: [{ id: 'p2', firstName: 'Anna', lastName: 'Schmidt' }], + tags: [], + transcription: null, + filePath: null, + createdAt: '1923-04-12T00:00:00Z', + updatedAt: '1923-04-12T00:00:00Z', + ...overrides +}); + +const withDocs = { + ...withPersons, + documents: [makeDoc()] +}; + +// ─── Empty state ────────────────────────────────────────────────────────────── + +describe('Conversations page – empty state', () => { + it('shows the "select two persons" prompt when no persons are selected', async () => { + render(Page, { data: baseData }); + await expect.element(page.getByText(/Wählen Sie zwei Personen aus/i)).toBeInTheDocument(); + }); + + it('does not show the swap button when no persons are selected', async () => { + render(Page, { data: baseData }); + await expect.element(page.getByTestId('conv-swap-btn')).not.toBeInTheDocument(); + }); + + it('does not show the new document link when no persons are selected', async () => { + render(Page, { data: baseData }); + await expect.element(page.getByTestId('conv-new-doc-link')).not.toBeInTheDocument(); + }); +}); + +// ─── No results ─────────────────────────────────────────────────────────────── + +describe('Conversations page – no results', () => { + it('shows "no documents found" when both persons are selected but there are no documents', async () => { + render(Page, { data: withPersons }); + await expect.element(page.getByText(/Keine Dokumente gefunden/i)).toBeInTheDocument(); + }); +}); + +// ─── Swap button ────────────────────────────────────────────────────────────── + +describe('Conversations page – swap button', () => { + it('shows the swap button when both persons are selected', async () => { + render(Page, { data: withPersons }); + await expect.element(page.getByTestId('conv-swap-btn')).toBeInTheDocument(); + }); + + 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 }); + await page.getByTestId('conv-swap-btn').click(); + expect(goto).toHaveBeenCalledWith(expect.stringContaining('senderId=p2'), expect.anything()); + expect(goto).toHaveBeenCalledWith(expect.stringContaining('receiverId=p1'), expect.anything()); + }); +}); + +// ─── Summary ────────────────────────────────────────────────────────────────── + +describe('Conversations page – summary', () => { + it('shows document count and year range when documents are loaded', async () => { + const data = { + ...withPersons, + documents: [ + makeDoc({ documentDate: '1923-04-12' }), + makeDoc({ id: 'd2', documentDate: '1965-08-03' }) + ] + }; + render(Page, { data }); + const summary = page.getByTestId('conv-summary'); + await expect.element(summary).toHaveTextContent('2'); + await expect.element(summary).toHaveTextContent('1923'); + await expect.element(summary).toHaveTextContent('1965'); + }); +}); + +// ─── Year dividers ──────────────────────────────────────────────────────────── + +describe('Conversations 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 }); + // Only one divider for 1923; 1965 divider should not appear + 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('Conversations page – new document link', () => { + it('shows the link with correct href for a write user', 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', '/documents/new?senderId=p1&receiverId=p2'); + }); + + 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(); + }); +});