test(docs): add DocumentRow unit tests — title, snippet, tags, sender, progress ring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
166
frontend/src/lib/components/DocumentRow.svelte.spec.ts
Normal file
166
frontend/src/lib/components/DocumentRow.svelte.spec.ts
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import { cleanup, render } from 'vitest-browser-svelte';
|
||||||
|
import { page } from 'vitest/browser';
|
||||||
|
import DocumentRow from './DocumentRow.svelte';
|
||||||
|
import type { components } from '$lib/generated/api';
|
||||||
|
|
||||||
|
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||||
|
|
||||||
|
afterEach(() => cleanup());
|
||||||
|
|
||||||
|
type DocumentSearchItem = components['schemas']['DocumentSearchItem'];
|
||||||
|
|
||||||
|
function makeItem(overrides: Partial<DocumentSearchItem> = {}): DocumentSearchItem {
|
||||||
|
return {
|
||||||
|
document: {
|
||||||
|
id: '1',
|
||||||
|
title: 'Testbrief',
|
||||||
|
originalFilename: 'testbrief.pdf',
|
||||||
|
status: 'UPLOADED',
|
||||||
|
documentDate: '2024-03-15',
|
||||||
|
sender: null,
|
||||||
|
receivers: [],
|
||||||
|
tags: [],
|
||||||
|
createdAt: '2024-01-01T00:00:00Z',
|
||||||
|
updatedAt: '2024-01-01T00:00:00Z',
|
||||||
|
metadataComplete: false,
|
||||||
|
scriptType: 'UNKNOWN'
|
||||||
|
},
|
||||||
|
matchData: {
|
||||||
|
titleOffsets: [],
|
||||||
|
senderMatched: false,
|
||||||
|
matchedReceiverIds: [],
|
||||||
|
matchedTagIds: [],
|
||||||
|
snippetOffsets: [],
|
||||||
|
summaryOffsets: []
|
||||||
|
},
|
||||||
|
completionPercentage: 0,
|
||||||
|
contributors: [],
|
||||||
|
...overrides
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Title ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('DocumentRow – title', () => {
|
||||||
|
it('renders document title', async () => {
|
||||||
|
render(DocumentRow, { item: makeItem() });
|
||||||
|
await expect.element(page.getByRole('heading', { name: 'Testbrief' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to originalFilename when title is null', async () => {
|
||||||
|
const item = makeItem({ document: { ...makeItem().document, title: null } });
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByRole('heading', { name: 'testbrief.pdf' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a mark element for highlighted title offsets', async () => {
|
||||||
|
const item = makeItem({
|
||||||
|
document: { ...makeItem().document, title: 'Brief an Anna' },
|
||||||
|
matchData: {
|
||||||
|
titleOffsets: [{ start: 0, length: 5 }],
|
||||||
|
senderMatched: false,
|
||||||
|
matchedReceiverIds: [],
|
||||||
|
matchedTagIds: [],
|
||||||
|
snippetOffsets: [],
|
||||||
|
summaryOffsets: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
const mark = page.getByRole('mark');
|
||||||
|
await expect.element(mark).toBeInTheDocument();
|
||||||
|
await expect.element(mark).toHaveTextContent('Brief');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Snippet ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('DocumentRow – snippet', () => {
|
||||||
|
it('shows transcription snippet when present', async () => {
|
||||||
|
const item = makeItem({
|
||||||
|
matchData: {
|
||||||
|
transcriptionSnippet: 'Er schrieb einen langen Brief',
|
||||||
|
titleOffsets: [],
|
||||||
|
senderMatched: false,
|
||||||
|
matchedReceiverIds: [],
|
||||||
|
matchedTagIds: [],
|
||||||
|
snippetOffsets: [],
|
||||||
|
summaryOffsets: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText('Er schrieb einen langen Brief')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not render snippet section when no snippet', async () => {
|
||||||
|
render(DocumentRow, { item: makeItem() });
|
||||||
|
await expect.element(page.getByTestId('search-snippet')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Sender / receivers ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('DocumentRow – sender', () => {
|
||||||
|
it('shows sender display name', async () => {
|
||||||
|
const item = makeItem({
|
||||||
|
document: {
|
||||||
|
...makeItem().document,
|
||||||
|
sender: { id: 's1', displayName: 'Großmutter Maria' }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText('Großmutter Maria').first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows unknown fallback when sender is null', async () => {
|
||||||
|
render(DocumentRow, { item: makeItem() });
|
||||||
|
const unknownElements = page.getByText('Unbekannt');
|
||||||
|
await expect.element(unknownElements.first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Tags ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('DocumentRow – tags', () => {
|
||||||
|
it('renders tag buttons', async () => {
|
||||||
|
const item = makeItem({
|
||||||
|
document: {
|
||||||
|
...makeItem().document,
|
||||||
|
tags: [{ id: 't1', name: 'Familie', color: null, parentId: null }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByRole('button', { name: 'Familie' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('navigates to /documents?tag=… on tag click', async () => {
|
||||||
|
const { goto } = await import('$app/navigation');
|
||||||
|
const item = makeItem({
|
||||||
|
document: {
|
||||||
|
...makeItem().document,
|
||||||
|
tags: [{ id: 't1', name: 'Urlaub & Reise', color: null, parentId: null }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await page.getByRole('button', { name: 'Urlaub & Reise' }).click();
|
||||||
|
expect(goto).toHaveBeenCalledWith('/documents?tag=Urlaub%20%26%20Reise');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── ProgressRing & ContributorStack ─────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('DocumentRow – progress ring and contributors', () => {
|
||||||
|
it('renders the completion percentage label', async () => {
|
||||||
|
const item = makeItem({ completionPercentage: 42 });
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText('42%').first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders contributor initials when contributors present', async () => {
|
||||||
|
const item = makeItem({
|
||||||
|
contributors: [{ initials: 'AR', color: '#4a90e2', name: 'Anna Raddatz' }]
|
||||||
|
});
|
||||||
|
render(DocumentRow, { item });
|
||||||
|
await expect.element(page.getByText('AR').first()).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user