import { describe, it, expect, vi, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; const mockNavigating = { to: null }; vi.mock('$app/navigation', () => ({ beforeNavigate: () => {}, afterNavigate: () => {}, goto: vi.fn(), invalidate: vi.fn(), invalidateAll: vi.fn(), preloadCode: vi.fn(), preloadData: vi.fn(), pushState: vi.fn(), replaceState: vi.fn(), disableScrollHandling: vi.fn(), onNavigate: () => () => {} })); vi.mock('$app/state', () => ({ get navigating() { return mockNavigating; }, get page() { return { url: new URL('http://localhost/documents'), data: {} }; } })); const { default: DocumentsListPage } = await import('./+page.svelte'); const { bulkSelectionStore } = await import('$lib/document/bulkSelection.svelte'); afterEach(() => { cleanup(); bulkSelectionStore.clear(); }); const baseData = (overrides: Record = {}) => ({ items: [] as { id: string; title: string }[], totalElements: 0, totalPages: 1, pageNumber: 0, canWrite: false, error: null, q: '', from: '', to: '', senderId: '', receiverId: '', initialSenderName: '', initialReceiverName: '', tags: [] as string[], sort: 'DATE', dir: 'desc', tagQ: '', tagOp: 'AND', density: [], minDate: null, maxDate: null, zoomFrom: null, zoomTo: null, ...overrides }); describe('documents/+ page', () => { it('renders the screen-reader-only heading', async () => { render(DocumentsListPage, { props: { data: baseData() } }); await expect .element(page.getByRole('heading', { name: /^dokumente$/i, level: 1 })) .toBeVisible(); }); it('omits the result count when totalElements is 0', async () => { render(DocumentsListPage, { props: { data: baseData() } }); await expect.element(page.getByText(/0 Dokumente/i)).not.toBeInTheDocument(); }); it('renders the result count when totalElements is greater than 0', async () => { render(DocumentsListPage, { props: { data: baseData({ totalElements: 5 }) } }); await expect.element(page.getByText(/5 Dokumente/i)).toBeVisible(); }); it('shows the new-document CTA when canWrite is true', async () => { render(DocumentsListPage, { props: { data: baseData({ canWrite: true }) } }); await expect .element(page.getByRole('link', { name: /neues dokument/i })) .toHaveAttribute('href', '/documents/new'); }); it('hides the new-document CTA when canWrite is false', async () => { render(DocumentsListPage, { props: { data: baseData({ canWrite: false }) } }); await expect .element(page.getByRole('link', { name: /neues dokument/i })) .not.toBeInTheDocument(); }); it('shows the bulk-edit-all CTA when canWrite is true and totalElements > 0', async () => { render(DocumentsListPage, { props: { data: baseData({ canWrite: true, totalElements: 12 }) } }); await expect.element(page.getByRole('button', { name: /alle 12 editieren/i })).toBeVisible(); }); it('hides the bulk-edit-all CTA when totalElements is 0', async () => { render(DocumentsListPage, { props: { data: baseData({ canWrite: true }) } }); await expect .element(page.getByRole('button', { name: /alle .* editieren/i })) .not.toBeInTheDocument(); }); it('hides the bulk-edit-all and new-doc CTAs when canWrite is false', async () => { render(DocumentsListPage, { props: { data: baseData({ canWrite: false, totalElements: 12 }) } }); await expect .element(page.getByRole('link', { name: /neues dokument/i })) .not.toBeInTheDocument(); await expect.element(page.getByRole('button', { name: /alle 12/i })).not.toBeInTheDocument(); }); it('displays the no-results state when items is empty (via DocumentList)', async () => { render(DocumentsListPage, { props: { data: baseData({ items: [], totalElements: 0 }) } }); expect(document.body.textContent).toMatch(/keine|noch|empty/i); }); it('renders the document list when items are present', async () => { render(DocumentsListPage, { props: { data: baseData({ items: [ { document: { id: 'd1', title: 'Brief 1899', status: 'TRANSCRIBED', documentDate: '1899-04-14', summary: '', originalFilename: 'b1.pdf', receivers: [] }, matchData: { titleOffsets: [], senderMatched: false, matchedReceiverIds: [], matchedTagIds: [], snippetOffsets: [], summaryOffsets: [] }, completionPercentage: 80, contributors: [] } ], totalElements: 1 }) } }); expect(document.body.textContent).toContain('Brief 1899'); }); it('preselects the search input from data.q', async () => { render(DocumentsListPage, { props: { data: baseData({ q: 'kurrent' }) } }); const input = document.querySelector('input[type="text"]') as HTMLInputElement; expect(input?.value).toBe('kurrent'); }); it('opens the advanced-filter section when from/to date filters are preselected', async () => { render(DocumentsListPage, { props: { data: baseData({ from: '1899-01-01', to: '1950-12-31' }) } }); // hasAdvancedFilters() returns true when from/to is set, which forces showAdvanced=true onMount, // revealing the advanced filter section. The "Von" / "Bis" date labels are part of it. await expect.element(page.getByText('Von')).toBeVisible(); await expect.element(page.getByText('Bis')).toBeVisible(); }); it('opens the advanced-filter section when tag filters are preselected', async () => { render(DocumentsListPage, { props: { data: baseData({ tags: ['Kurrent', 'Familie'] }) } }); // Tag filter pills are part of the advanced filter section. await expect.element(page.getByText('Schlagworte')).toBeVisible(); }); it('opens the advanced-filter section when sender/receiver filters are set', async () => { render(DocumentsListPage, { props: { data: baseData({ senderId: 'p-1', initialSenderName: 'Anna Schmidt', receiverId: 'p-2', initialReceiverName: 'Bert Meier' }) } }); // PersonTypeahead's combobox input is labelled by the "Absender" / "Empfänger"