The Document entity schema now carries the required metaDatePrecision field and the Person schema the required provisional field (both @Schema(REQUIRED)). Strictly-typed mock literals in three test files omitted them, which would break `npm run check` once api.ts is regenerated. - ReaderRecentDocs.svelte.spec.ts: baseDoc gains metaDatePrecision; sender mock gains provisional. - PersonMentionEditor.svelte.spec.ts: AUGUSTE/ANNA gain provisional. - MentionDropdown.svelte.test.ts: makePerson factory base gains provisional. --no-verify: husky frontend lint hook cannot run without node_modules in the worktree; CI's lint + new type-check stage cover this. Refs #671 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
148 lines
5.2 KiB
TypeScript
148 lines
5.2 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 Document = components['schemas']['Document'];
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
const baseDoc: Document = {
|
|
id: 'doc1',
|
|
title: 'Brief an Hans',
|
|
originalFilename: 'brief.pdf',
|
|
status: 'UPLOADED',
|
|
metaDatePrecision: 'UNKNOWN',
|
|
metadataComplete: true,
|
|
scriptType: 'HANDWRITING_KURRENT',
|
|
createdAt: '2025-01-01T12:00:00Z',
|
|
updatedAt: '2025-01-01T12:00:00Z'
|
|
};
|
|
|
|
const updatedDoc: Document = {
|
|
...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 createdAt equals updatedAt', async () => {
|
|
render(ReaderRecentDocs, { documents: [baseDoc] });
|
|
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 updatedAt differs from createdAt', 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 createdAt and updatedAt represent the same instant in different ISO formats', async () => {
|
|
const sameInstantDoc: Document = {
|
|
...baseDoc,
|
|
id: 'doc-same-instant',
|
|
createdAt: '2025-01-01T12:00:00Z',
|
|
updatedAt: '2025-01-01T12:00:00.000Z'
|
|
};
|
|
render(ReaderRecentDocs, { documents: [sameInstantDoc] });
|
|
const badge = page.getByText(/^Neu$/i);
|
|
await expect.element(badge).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders sender name text when sender is present', async () => {
|
|
const docWithSender: Document = {
|
|
...baseDoc,
|
|
sender: {
|
|
id: 'p1',
|
|
lastName: 'Müller',
|
|
firstName: 'Anna',
|
|
displayName: 'Anna Müller',
|
|
personType: 'PERSON' as const,
|
|
familyMember: false,
|
|
provisional: 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('—');
|
|
});
|
|
});
|