import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import ReaderRecentDocs from './ReaderRecentDocs.svelte'; import type { components } from '$lib/generated/api'; type DocumentListItem = components['schemas']['DocumentListItem']; afterEach(() => { cleanup(); }); const baseDoc: DocumentListItem = { id: 'doc1', title: 'Brief an Hans', originalFilename: 'brief.pdf', completionPercentage: 0, receivers: [], tags: [], contributors: [], matchData: { titleOffsets: [], senderMatched: false, matchedReceiverIds: [], matchedTagIds: [], snippetOffsets: [], summaryOffsets: [] }, createdAt: '2025-01-01T12:00:00Z', updatedAt: '2025-01-01T12:00:00Z' }; const updatedDoc: DocumentListItem = { ...baseDoc, id: 'doc2', title: 'Urkunde 1920', createdAt: '2025-01-01T12:00:00Z', updatedAt: '2025-03-01T12:00:00Z' }; describe('ReaderRecentDocs', () => { it('renders a link to /documents/{id} for each document', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const link = page.getByRole('link', { name: /Brief an Hans/ }); await expect.element(link).toHaveAttribute('href', '/documents/doc1'); }); it('card has overflow-hidden and flex-col classes (no p-6, no shadow-sm)', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const heading = page.getByRole('heading', { level: 3 }); const card = (await heading.element())?.closest('div'); const rootCard = card?.parentElement; const cls = rootCard?.className ?? ''; expect(cls).toMatch(/overflow-hidden/); expect(cls).toMatch(/flex-col/); expect(cls).not.toMatch(/\bp-6\b/); expect(cls).not.toMatch(/shadow-sm/); }); it('card-head contains an h3 (not h2)', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const h3 = page.getByRole('heading', { level: 3 }); await expect.element(h3).toBeInTheDocument(); const h2 = page.getByRole('heading', { level: 2 }); await expect.element(h2).not.toBeInTheDocument(); }); it('"Alle Dokumente" link in card-head points to /documents', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const link = page.getByRole('link', { name: /Alle Dokumente/i }); await expect.element(link).toHaveAttribute('href', '/documents'); }); it('"Alle Dokumente" link has min-h-[44px]', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const link = page.getByRole('link', { name: /Alle Dokumente/i }); const cls = ((await link.element()) as HTMLElement).className; expect(cls).toMatch(/min-h-\[44px\]/); }); it('doc-row link has min-h-[44px] touch target', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const link = page.getByRole('link', { name: /Brief an Hans/ }); const cls = ((await link.element()) as HTMLElement).className; expect(cls).toMatch(/min-h-\[44px\]/); }); it('thumb element has correct classes', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const link = page.getByRole('link', { name: /Brief an Hans/ }); const el = (await link.element()) as HTMLElement; const thumb = el.querySelector('[class*="w-5"][class*="h-6"]'); expect(thumb).not.toBeNull(); expect(thumb!.className).toMatch(/bg-canvas/); expect(thumb!.className).toMatch(/border-line/); expect(thumb!.className).toMatch(/rounded-/); }); it('shows "Neu" accent-pill badge when document was created within the last 7 days', async () => { const recentDoc: DocumentListItem = { ...baseDoc, id: 'doc-recent', createdAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(), updatedAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString() }; render(ReaderRecentDocs, { documents: [recentDoc] }); const badge = page.getByText(/^Neu$/i); await expect.element(badge).toBeInTheDocument(); const cls = ((await badge.element()) as HTMLElement).className; expect(cls).toMatch(/bg-accent-bg/); expect(cls).toMatch(/rounded-full/); expect(cls).toMatch(/\btext-ink\b/); }); it('shows no badge when document was created more than 7 days ago', async () => { render(ReaderRecentDocs, { documents: [updatedDoc] }); const badge = page.getByText(/^Neu$/i); await expect.element(badge).not.toBeInTheDocument(); const updatedBadge = page.getByText(/^Aktualisiert$/i); await expect.element(updatedBadge).not.toBeInTheDocument(); }); it('shows "Neu" badge when document was created 6 days ago', async () => { const almostOldDoc: DocumentListItem = { ...baseDoc, id: 'doc-almost-old', createdAt: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString(), updatedAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString() }; render(ReaderRecentDocs, { documents: [almostOldDoc] }); const badge = page.getByText(/^Neu$/i); await expect.element(badge).toBeInTheDocument(); }); it('renders sender name text when sender is present', async () => { const docWithSender: DocumentListItem = { ...baseDoc, sender: { id: 'p1', lastName: 'Müller', firstName: 'Anna', displayName: 'Anna Müller', personType: 'PERSON' as const, familyMember: false } }; render(ReaderRecentDocs, { documents: [docWithSender] }); const link = page.getByRole('link', { name: /Brief an Hans/ }); const el = (await link.element()) as HTMLElement; expect(el.textContent).toContain('Anna Müller'); }); it('shows em-dash when sender is absent', async () => { render(ReaderRecentDocs, { documents: [baseDoc] }); const link = page.getByRole('link', { name: /Brief an Hans/ }); const el = (await link.element()) as HTMLElement; expect(el.textContent).toContain('—'); }); });