Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 3m23s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / Backend Unit Tests (pull_request) Successful in 3m55s
CI / fail2ban Regex (pull_request) Successful in 45s
CI / Semgrep Security Scan (pull_request) Successful in 24s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m5s
Replace manual edits to api.ts with a proper `npm run generate:api` run — the generated output is identical for DocumentListItem (createdAt/updatedAt were already correct), so this just removes the drift risk flagged in review. Fix ReaderRecentDocs.svelte.spec.ts to use DocumentListItem instead of Document for all test fixtures, matching the component's actual prop type. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
5.6 KiB
TypeScript
161 lines
5.6 KiB
TypeScript
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('—');
|
|
});
|
|
});
|