import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import DocumentMetadataDrawer from './DocumentMetadataDrawer.svelte'; afterEach(cleanup); const sender = { id: 's1', firstName: 'Anna', lastName: 'Schmidt', displayName: 'Anna Schmidt' }; const receiver = (id: string, name: string) => ({ id, firstName: name.split(' ')[0], lastName: name.split(' ').slice(1).join(' ') || name, displayName: name }); const baseProps = { documentDate: '1923-04-15' as string | null, location: 'Berlin' as string | null, status: 'UPLOADED', sender: null as typeof sender | null, receivers: [] as ReturnType[], tags: [] as { id: string; name: string }[], inferredRelationship: null, geschichten: [] as { id: string; title: string; publishedAt?: string; author?: { firstName?: string; lastName?: string; email: string }; }[], documentId: 'doc-1', canBlogWrite: false }; describe('DocumentMetadataDrawer', () => { it('renders the three default section headings', async () => { render(DocumentMetadataDrawer, { props: baseProps }); await expect.element(page.getByRole('heading', { name: 'Details' })).toBeVisible(); await expect.element(page.getByRole('heading', { name: 'Personen' })).toBeVisible(); await expect.element(page.getByRole('heading', { name: 'Schlagwörter' })).toBeVisible(); }); it('renders the formatted long date when documentDate is provided', async () => { render(DocumentMetadataDrawer, { props: baseProps }); // formatDate default ('long') format is "15. April 1923" in de-DE. await expect.element(page.getByText(/1923/)).toBeVisible(); }); it('renders an em-dash when documentDate is null', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, documentDate: null } }); // The dash appears in date AND location AND geschichten — multiple matches expected const dashes = document.querySelectorAll('dd, p'); const dashTexts = Array.from(dashes) .map((el) => el.textContent?.trim()) .filter((t) => t === '—'); expect(dashTexts.length).toBeGreaterThan(0); }); it('renders the no-persons placeholder when sender and receivers are empty', async () => { render(DocumentMetadataDrawer, { props: baseProps }); await expect.element(page.getByText('Keine Personen zugeordnet')).toBeVisible(); }); it('renders the sender and inferred relationship label when both are present', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, sender, inferredRelationship: { labelFromA: 'Vater', labelFromB: 'Tochter' } } }); await expect.element(page.getByText('Anna Schmidt')).toBeVisible(); }); it('renders the receivers list with up to five visible by default', async () => { const receivers = Array.from({ length: 7 }, (_, i) => receiver(`r${i}`, `Person ${i}`)); render(DocumentMetadataDrawer, { props: { ...baseProps, sender, receivers } }); await expect.element(page.getByText('Person 0')).toBeVisible(); await expect.element(page.getByText('Person 4')).toBeVisible(); await expect.element(page.getByText('Person 5')).not.toBeInTheDocument(); }); it('renders the +N more button when there are more than five receivers', async () => { const receivers = Array.from({ length: 8 }, (_, i) => receiver(`r${i}`, `Person ${i}`)); render(DocumentMetadataDrawer, { props: { ...baseProps, sender, receivers } }); await expect.element(page.getByRole('button', { name: /\+3 weitere/i })).toBeVisible(); }); it('expands the receiver list when the +N more button is clicked', async () => { const receivers = Array.from({ length: 8 }, (_, i) => receiver(`r${i}`, `Person ${i}`)); render(DocumentMetadataDrawer, { props: { ...baseProps, sender, receivers } }); await page.getByRole('button', { name: /\+3 weitere/i }).click(); await expect.element(page.getByText('Person 7')).toBeVisible(); }); it('renders the no-tags placeholder when tags is empty', async () => { render(DocumentMetadataDrawer, { props: baseProps }); await expect.element(page.getByText('Keine Schlagwörter zugeordnet')).toBeVisible(); }); it('renders one anchor per tag when tags are present', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, tags: [ { id: 't1', name: 'Familie' }, { id: 't2', name: 'Reise' } ] } }); await expect .element(page.getByRole('link', { name: 'Familie' })) .toHaveAttribute('href', '/?tag=Familie'); await expect .element(page.getByRole('link', { name: 'Reise' })) .toHaveAttribute('href', '/?tag=Reise'); }); it('hides the geschichten column when there are no stories and no canBlogWrite', async () => { render(DocumentMetadataDrawer, { props: baseProps }); await expect .element(page.getByRole('heading', { name: 'Geschichten' })) .not.toBeInTheDocument(); }); it('shows the geschichten column when canBlogWrite is true even with no stories', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, canBlogWrite: true } }); await expect.element(page.getByRole('heading', { name: 'Geschichten' })).toBeVisible(); }); it('renders the attach link to the new-geschichte route when canBlogWrite + documentId', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, canBlogWrite: true, documentId: 'doc-42' } }); const links = document.querySelectorAll('a[href*="/geschichten/new?documentId="]'); expect(links.length).toBe(1); expect((links[0] as HTMLAnchorElement).href).toContain('documentId=doc-42'); }); it('renders the geschichten list when stories are present', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, geschichten: [ { id: 'g1', title: 'Reise nach Berlin', publishedAt: '2026-04-15T10:00:00Z', author: { firstName: 'Anna', lastName: 'Schmidt', email: 'anna@x' } } ] } }); await expect.element(page.getByRole('link', { name: /reise nach berlin/i })).toBeVisible(); }); it('renders the show-all geschichten link when there are at least three stories', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, geschichten: Array.from({ length: 3 }, (_, i) => ({ id: `g${i}`, title: `Geschichte ${i}`, publishedAt: '2026-04-15T10:00:00Z', author: { firstName: 'Anna', lastName: 'Schmidt', email: 'anna@x' } })) } }); await expect.element(page.getByText(/zeige alle|alle/i)).toBeVisible(); }); it('renders the receiver-only inferred relationship pill only when there is exactly one receiver', async () => { render(DocumentMetadataDrawer, { props: { ...baseProps, sender, receivers: [receiver('r1', 'Bert Meier')], inferredRelationship: { labelFromA: 'Vater', labelFromB: 'Tochter' } } }); // Both labels should be visible — Vater for sender, Tochter for the single receiver await expect.element(page.getByText(/vater/i)).toBeVisible(); await expect.element(page.getByText(/tochter/i)).toBeVisible(); }); });