import { describe, it, expect, vi, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; const mockPage = { url: new URL('http://localhost/documents/d1'), state: {} }; vi.mock('$app/state', () => ({ get page() { return mockPage; } })); vi.mock('$app/navigation', () => ({ beforeNavigate: () => {}, afterNavigate: () => {}, goto: vi.fn(), invalidate: vi.fn(), invalidateAll: vi.fn(), preloadCode: vi.fn(), preloadData: vi.fn(), pushState: vi.fn(), replaceState: vi.fn(), disableScrollHandling: vi.fn(), onNavigate: () => () => {} })); vi.mock('$lib/shared/services/confirm.svelte', () => ({ getConfirmService: () => ({ confirm: async () => false }) })); vi.mock('$lib/shared/services/confirm.svelte.js', () => ({ getConfirmService: () => ({ confirm: async () => false }) })); const { default: DocumentDetailPage } = await import('./+page.svelte'); afterEach(cleanup); const baseDoc = { id: 'd1', title: 'Brief an Helene', originalFilename: 'brief.pdf', documentDate: '1923-04-15', sender: null, receivers: [], tags: [], filePath: null, contentType: null, location: null, status: 'UPLOADED', fileHash: null }; const baseData = (overrides: Record = {}) => ({ document: baseDoc, canWrite: false, canBlogWrite: false, user: null, geschichten: [], inferredRelationship: null, ...overrides }); describe('documents/[id] page', () => { it('renders the DocumentTopBar with the document title', async () => { mockPage.url = new URL('http://localhost/documents/d1'); render(DocumentDetailPage, { props: { data: baseData() } }); // Just verify the page mounts and renders the top bar const topbar = document.querySelector('[data-topbar]'); expect(topbar).not.toBeNull(); }); it('renders the DocumentViewer in the page', async () => { mockPage.url = new URL('http://localhost/documents/d1'); render(DocumentDetailPage, { props: { data: baseData() } }); // DocumentViewer renders an absolute container; just check the page mounted const main = document.body.firstElementChild; expect(main).not.toBeNull(); }); it('persists last-visited document ID to localStorage on mount', async () => { localStorage.removeItem('familienarchiv.lastVisited'); mockPage.url = new URL('http://localhost/documents/d1'); render(DocumentDetailPage, { props: { data: baseData() } }); await new Promise((r) => setTimeout(r, 50)); const stored = localStorage.getItem('familienarchiv.lastVisited'); expect(stored).toContain('d1'); }); it('uses doc.title as the document title when set', async () => { mockPage.url = new URL('http://localhost/documents/d1'); render(DocumentDetailPage, { props: { data: baseData() } }); // The browser reflects doc.title when set await new Promise((r) => setTimeout(r, 30)); expect(document.title).toContain('Brief an Helene'); }); it('falls back to originalFilename when title is empty', async () => { mockPage.url = new URL('http://localhost/documents/d2'); render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd2', title: '', originalFilename: 'fallback.pdf' } }) } }); await new Promise((r) => setTimeout(r, 30)); expect(document.title).toContain('fallback.pdf'); }); it('falls back to "Dokument" when title and originalFilename are empty', async () => { mockPage.url = new URL('http://localhost/documents/d3'); render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd3', title: '', originalFilename: '' } }) } }); await new Promise((r) => setTimeout(r, 30)); expect(document.title).toContain('Dokument'); }); it('renders without throwing when canWrite is true', async () => { mockPage.url = new URL('http://localhost/documents/d4'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ canWrite: true }) } }) ).not.toThrow(); }); it('renders without throwing when geschichten and inferredRelationship are set', async () => { mockPage.url = new URL('http://localhost/documents/d5'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ geschichten: [{ id: 'g1', title: 'Story', publishedAt: null }], inferredRelationship: { label: 'PARENT_OF', from: 'p1', to: 'p2' }, canBlogWrite: true }) } }) ).not.toThrow(); }); it('renders without throwing when doc.id is empty', async () => { mockPage.url = new URL('http://localhost/documents/d-empty'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: '', title: 'No ID' } }) } }) ).not.toThrow(); }); it('renders with task=transcribe in the URL without throwing', async () => { mockPage.url = new URL('http://localhost/documents/d6?task=transcribe'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd6' } }) } }) ).not.toThrow(); }); it('renders without throwing with sender/receivers populated', async () => { mockPage.url = new URL('http://localhost/documents/d7'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd7', sender: { id: 's1', displayName: 'Anna Schmidt' }, receivers: [{ id: 'r1', displayName: 'Bert Meier' }] } }) } }) ).not.toThrow(); }); it('renders without throwing when filePath is set on the document', async () => { mockPage.url = new URL('http://localhost/documents/d8'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd8', filePath: 's3://bucket/file.pdf', contentType: 'application/pdf' } }) } }) ).not.toThrow(); }); it('renders without throwing with a complete user object', async () => { mockPage.url = new URL('http://localhost/documents/d9'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd9' }, user: { id: 'u1', firstName: 'Anna', lastName: 'S', email: 'a@x' } }) } }) ).not.toThrow(); }); it('handles Escape keydown without throwing (close transcribe path)', async () => { mockPage.url = new URL('http://localhost/documents/d10'); render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd10' } }) } }); expect(() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })) ).not.toThrow(); }); it('handles non-Escape keydown without firing close handler', async () => { mockPage.url = new URL('http://localhost/documents/d11'); render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd11' } }) } }); expect(() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true })) ).not.toThrow(); }); it('renders without throwing with a deep-link comment query param', async () => { mockPage.url = new URL('http://localhost/documents/d12?comment=c-abc'); expect(() => render(DocumentDetailPage, { props: { data: baseData({ document: { ...baseDoc, id: 'd12' } }) } }) ).not.toThrow(); }); });