diff --git a/frontend/src/routes/documents/page.svelte.test.ts b/frontend/src/routes/documents/page.svelte.test.ts new file mode 100644 index 00000000..e56a2309 --- /dev/null +++ b/frontend/src/routes/documents/page.svelte.test.ts @@ -0,0 +1,115 @@ +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'); + +afterEach(cleanup); + +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(); + }); +});