diff --git a/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.test.ts b/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.test.ts index 254ba084..c0b6c9f0 100644 --- a/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.test.ts +++ b/frontend/src/lib/document/transcription/TranscriptionEditView.svelte.test.ts @@ -2,9 +2,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; -vi.mock('$lib/shared/services/confirm.svelte', () => ({ - getConfirmService: () => ({ confirm: async () => false }) -})); +import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js'; const { default: TranscriptionEditView } = await import('./TranscriptionEditView.svelte'); import type { TranscriptionBlockData } from '$lib/shared/types'; @@ -37,7 +35,10 @@ const baseProps = (overrides: Record = {}) => ({ describe('TranscriptionEditView', () => { it('renders the empty-state coach when there are no blocks', async () => { - render(TranscriptionEditView, { props: baseProps() }); + render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: baseProps() + }); // TranscribeCoachEmptyState renders some German text expect(document.body.textContent).toMatch(/markier|block|transkrip/i); @@ -45,6 +46,7 @@ describe('TranscriptionEditView', () => { it('renders the review progress counter when there are blocks', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock({ id: 'b1', reviewed: false }), baseBlock({ id: 'b2', reviewed: true })] }) @@ -55,6 +57,7 @@ describe('TranscriptionEditView', () => { it('shows the "alle als fertig markieren" button when onMarkAllReviewed is provided', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], onMarkAllReviewed: async () => {} @@ -66,6 +69,7 @@ describe('TranscriptionEditView', () => { it('disables the mark-all-reviewed button when all blocks are reviewed', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock({ reviewed: true })], onMarkAllReviewed: async () => {} @@ -80,6 +84,7 @@ describe('TranscriptionEditView', () => { it('enables the mark-all-reviewed button when not all blocks are reviewed', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock({ reviewed: false })], onMarkAllReviewed: async () => {} @@ -93,7 +98,10 @@ describe('TranscriptionEditView', () => { }); it('hides the mark-all-reviewed button when onMarkAllReviewed is not provided', async () => { - render(TranscriptionEditView, { props: baseProps({ blocks: [baseBlock()] }) }); + render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: baseProps({ blocks: [baseBlock()] }) + }); await expect .element(page.getByRole('button', { name: /alle als fertig/i })) @@ -102,6 +110,7 @@ describe('TranscriptionEditView', () => { it('renders the OcrTrigger only when canRunOcr is true and onTriggerOcr is provided', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canRunOcr: true, @@ -116,6 +125,7 @@ describe('TranscriptionEditView', () => { it('hides the OcrTrigger when canRunOcr is false', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canRunOcr: false, @@ -129,6 +139,7 @@ describe('TranscriptionEditView', () => { it('renders the training-label chips when canWrite=true and there are blocks', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canWrite: true, @@ -143,6 +154,7 @@ describe('TranscriptionEditView', () => { it('hides the training-label section when canWrite is false', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canWrite: false @@ -155,6 +167,7 @@ describe('TranscriptionEditView', () => { it('toggles the training label chip when clicked', async () => { const onToggleTrainingLabel = vi.fn().mockResolvedValue(undefined); render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canWrite: true, @@ -174,6 +187,7 @@ describe('TranscriptionEditView', () => { it('renders blocks sorted by sortOrder', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [ baseBlock({ id: 'b3', sortOrder: 3, text: 'Third' }), @@ -193,6 +207,7 @@ describe('TranscriptionEditView', () => { it('renders both blocks with their text after rerender with a new activeAnnotationId', async () => { const { rerender } = render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [ baseBlock({ id: 'b1', annotationId: 'ann-1', sortOrder: 1, text: 'First' }), @@ -223,6 +238,7 @@ describe('TranscriptionEditView', () => { it('handleMarkAllReviewed calls onMarkAllReviewed when clicked', async () => { const onMarkAllReviewed = vi.fn().mockResolvedValue(undefined); render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock({ reviewed: false })], onMarkAllReviewed @@ -238,6 +254,7 @@ describe('TranscriptionEditView', () => { it('renders all blocks with their text', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [ baseBlock({ id: 'b1', text: 'Erster Block' }), @@ -252,6 +269,7 @@ describe('TranscriptionEditView', () => { it('shows the next-block CTA when there are blocks', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()] }) @@ -263,6 +281,7 @@ describe('TranscriptionEditView', () => { it('shows the active training label highlighted when included in trainingLabels', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canWrite: true, @@ -281,6 +300,7 @@ describe('TranscriptionEditView', () => { it('renders the inactive training-label chip class when not in trainingLabels', async () => { render(TranscriptionEditView, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: baseProps({ blocks: [baseBlock()], canWrite: true, diff --git a/frontend/src/routes/admin/groups/[id]/page.svelte.test.ts b/frontend/src/routes/admin/groups/[id]/page.svelte.test.ts index 35f9b110..9f9213d1 100644 --- a/frontend/src/routes/admin/groups/[id]/page.svelte.test.ts +++ b/frontend/src/routes/admin/groups/[id]/page.svelte.test.ts @@ -1,10 +1,8 @@ -import { describe, it, expect, vi, afterEach } from 'vitest'; +import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; -vi.mock('$lib/shared/services/confirm.svelte', () => ({ - getConfirmService: () => ({ confirm: async () => false }) -})); +import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js'; const { default: AdminGroupEditPage } = await import('./+page.svelte'); @@ -19,13 +17,17 @@ const baseGroup = (overrides: Record = {}) => ({ describe('admin/groups/[id] page', () => { it('renders the edit heading with the group name', async () => { - render(AdminGroupEditPage, { props: { data: { group: baseGroup() }, form: undefined } }); + render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: { group: baseGroup() }, form: undefined } + }); await expect.element(page.getByRole('heading', { name: /familie/i })).toBeVisible(); }); it('hydrates the name input from data.group.name', async () => { render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: { group: baseGroup({ name: 'Admins' }) }, form: undefined } }); @@ -35,6 +37,7 @@ describe('admin/groups/[id] page', () => { it('checks the permission checkboxes that are in data.group.permissions', async () => { render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: { group: baseGroup({ permissions: ['READ_ALL', 'ADMIN_TAG'] }) }, form: undefined @@ -57,6 +60,7 @@ describe('admin/groups/[id] page', () => { it('shows the success banner when form.success is true', async () => { render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: { group: baseGroup() }, form: { success: true } } }); @@ -65,6 +69,7 @@ describe('admin/groups/[id] page', () => { it('shows the error banner when form.error is set', async () => { render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: { group: baseGroup() }, form: { error: 'Name darf nicht leer sein.' } @@ -75,21 +80,30 @@ describe('admin/groups/[id] page', () => { }); it('renders the cancel link to /admin/groups', async () => { - render(AdminGroupEditPage, { props: { data: { group: baseGroup() }, form: undefined } }); + render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: { group: baseGroup() }, form: undefined } + }); const links = document.querySelectorAll('a[href="/admin/groups"]'); expect(links.length).toBeGreaterThan(0); }); it('renders the delete and save buttons', async () => { - render(AdminGroupEditPage, { props: { data: { group: baseGroup() }, form: undefined } }); + render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: { group: baseGroup() }, form: undefined } + }); await expect.element(page.getByRole('button', { name: /löschen/i })).toBeVisible(); await expect.element(page.getByRole('button', { name: /speichern/i })).toBeVisible(); }); it('does not render success banner when form is undefined', async () => { - render(AdminGroupEditPage, { props: { data: { group: baseGroup() }, form: undefined } }); + render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: { group: baseGroup() }, form: undefined } + }); const banner = document.querySelector('.bg-green-50'); expect(banner).toBeNull(); @@ -97,6 +111,7 @@ describe('admin/groups/[id] page', () => { it('does not render error-banner div when form.success is true (success path only)', async () => { render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: { group: baseGroup() }, form: { success: true } } }); @@ -106,7 +121,10 @@ describe('admin/groups/[id] page', () => { }); it('renders all 8 permission checkboxes (4 standard + 4 admin)', async () => { - render(AdminGroupEditPage, { props: { data: { group: baseGroup() }, form: undefined } }); + render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: { group: baseGroup() }, form: undefined } + }); const checkboxes = document.querySelectorAll('input[name="permissions"]'); expect(checkboxes.length).toBe(8); @@ -114,6 +132,7 @@ describe('admin/groups/[id] page', () => { it('handles a group with empty permissions array', async () => { render(AdminGroupEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: { group: baseGroup({ permissions: [] }) }, form: undefined } }); diff --git a/frontend/src/routes/admin/users/[id]/page.svelte.test.ts b/frontend/src/routes/admin/users/[id]/page.svelte.test.ts index 33da7c50..31514db9 100644 --- a/frontend/src/routes/admin/users/[id]/page.svelte.test.ts +++ b/frontend/src/routes/admin/users/[id]/page.svelte.test.ts @@ -1,10 +1,8 @@ -import { describe, it, expect, vi, afterEach } from 'vitest'; +import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; -vi.mock('$lib/shared/services/confirm.svelte', () => ({ - getConfirmService: () => ({ confirm: async () => false }) -})); +import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js'; const { default: AdminUserEditPage } = await import('./+page.svelte'); @@ -32,13 +30,19 @@ const baseData = (overrides: Record = {}) => ({ describe('admin/users/[id] page', () => { it('renders the edit heading with the user email', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: undefined } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); await expect.element(page.getByRole('heading', { name: /anna@example/i })).toBeVisible(); }); it('renders all three card sections', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: undefined } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); await expect.element(page.getByRole('heading', { name: /persönliche daten/i })).toBeVisible(); await expect.element(page.getByRole('heading', { name: /^gruppen$/i })).toBeVisible(); @@ -46,13 +50,17 @@ describe('admin/users/[id] page', () => { }); it('shows the update success banner when form.success is true', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: { success: true } } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: { success: true } } + }); await expect.element(page.getByText('Änderungen gespeichert.')).toBeVisible(); }); it('shows the update error banner when form.error is set', async () => { render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData(), form: { error: 'E-Mail bereits vergeben' } } }); @@ -60,7 +68,10 @@ describe('admin/users/[id] page', () => { }); it('preselects the user groups in UserGroupsSection', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: undefined } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); const checkboxes = Array.from( document.querySelectorAll('input[name="groupIds"]') @@ -70,27 +81,39 @@ describe('admin/users/[id] page', () => { }); it('renders cancel link to /admin/users', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: undefined } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); const cancel = document.querySelector('a[href="/admin/users"]'); expect(cancel).not.toBeNull(); }); it('renders the delete button', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: undefined } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); await expect.element(page.getByRole('button', { name: /löschen/i })).toBeVisible(); }); it('does not show success banner when form is undefined', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: undefined } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); const banner = document.querySelector('.bg-green-50'); expect(banner).toBeNull(); }); it('does not show error banner when form.error is undefined', async () => { - render(AdminUserEditPage, { props: { data: baseData(), form: { success: false } } }); + render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: { success: false } } + }); // The error banner has both border-red-200 AND text-red-700 — the delete button has red-50 // background but is a button, not a div. Look for the specific error
. @@ -100,6 +123,7 @@ describe('admin/users/[id] page', () => { it('handles a user with empty groups list (selectedGroupIds defaults to [])', async () => { render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ editUser: { ...baseEditUser, groups: [] } }), form: undefined @@ -117,6 +141,7 @@ describe('admin/users/[id] page', () => { const editUser = { ...baseEditUser } as typeof baseEditUser & { groups?: undefined }; delete (editUser as { groups?: unknown }).groups; render(AdminUserEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ editUser }), form: undefined } }); diff --git a/frontend/src/routes/documents/[id]/edit/page.svelte.test.ts b/frontend/src/routes/documents/[id]/edit/page.svelte.test.ts index 9aacf88f..023cbec7 100644 --- a/frontend/src/routes/documents/[id]/edit/page.svelte.test.ts +++ b/frontend/src/routes/documents/[id]/edit/page.svelte.test.ts @@ -1,9 +1,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; -vi.mock('$lib/shared/services/confirm.svelte', () => ({ - getConfirmService: () => ({ confirm: async () => false }) -})); +import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js'; const { default: DocumentEditPage } = await import('./+page.svelte'); @@ -31,7 +29,10 @@ const baseData = (overrides: Record = {}) => ({ describe('documents/[id]/edit page', () => { it('renders the page with the DocumentEditLayout', async () => { - render(DocumentEditPage, { props: { data: baseData(), form: undefined } }); + render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); // At minimum, the body has content const main = document.body.firstElementChild; @@ -39,7 +40,10 @@ describe('documents/[id]/edit page', () => { }); it('renders both hidden submit-target forms', async () => { - render(DocumentEditPage, { props: { data: baseData(), form: undefined } }); + render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); const reviewForm = document.querySelector('form#mark-for-review-form'); const deleteForm = document.querySelector('form#delete-form'); @@ -48,7 +52,10 @@ describe('documents/[id]/edit page', () => { }); it('renders the action bar with delete, cancel, mark-for-review, and save buttons/links', async () => { - render(DocumentEditPage, { props: { data: baseData(), form: undefined } }); + render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); // Find delete button const deleteBtn = Array.from(document.querySelectorAll('button')).find((b) => @@ -58,13 +65,17 @@ describe('documents/[id]/edit page', () => { }); it('uses doc.title in the document title when set', async () => { - render(DocumentEditPage, { props: { data: baseData(), form: undefined } }); + render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); await vi.waitFor(() => expect(document.title).toContain('Brief an Helene')); }); it('falls back to originalFilename when title is empty', async () => { render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, title: '', originalFilename: 'fallback.pdf' } }), form: undefined @@ -75,7 +86,10 @@ describe('documents/[id]/edit page', () => { }); it('renders the cancel link to the document detail page', async () => { - render(DocumentEditPage, { props: { data: baseData(), form: undefined } }); + render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData(), form: undefined } + }); const link = document.querySelector('a[href="/documents/d1"]'); expect(link).not.toBeNull(); @@ -83,6 +97,7 @@ describe('documents/[id]/edit page', () => { it('passes form.error to DocumentEditLayout when form is set', async () => { render(DocumentEditPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData(), form: { error: 'Save failed' } } }); diff --git a/frontend/src/routes/documents/[id]/page.svelte.test.ts b/frontend/src/routes/documents/[id]/page.svelte.test.ts index c34b26f5..98bc6bd7 100644 --- a/frontend/src/routes/documents/[id]/page.svelte.test.ts +++ b/frontend/src/routes/documents/[id]/page.svelte.test.ts @@ -27,9 +27,7 @@ vi.mock('$app/navigation', () => ({ onNavigate: () => () => {} })); -vi.mock('$lib/shared/services/confirm.svelte', () => ({ - getConfirmService: () => ({ confirm: async () => false }) -})); +import { createConfirmService, CONFIRM_KEY } from '$lib/shared/services/confirm.svelte.js'; const { default: DocumentDetailPage } = await import('./+page.svelte'); @@ -63,7 +61,10 @@ const baseData = (overrides: Record = {}) => ({ describe('documents/[id] page', () => { it('renders the DocumentTopBar and resolves the document title in svelte:head', async () => { mockPage.url = new URL('http://localhost/documents/d1'); - render(DocumentDetailPage, { props: { data: baseData() } }); + render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData() } + }); expect(document.querySelector('[data-topbar]')).not.toBeNull(); await vi.waitFor(() => expect(document.title).toContain('Brief an Helene')); @@ -71,7 +72,10 @@ describe('documents/[id] page', () => { it('mounts the page region with the [data-hydrated] container', async () => { mockPage.url = new URL('http://localhost/documents/d1'); - render(DocumentDetailPage, { props: { data: baseData() } }); + render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData() } + }); expect(document.querySelector('[data-hydrated]')).not.toBeNull(); }); @@ -79,7 +83,10 @@ describe('documents/[id] page', () => { 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() } }); + render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData() } + }); await vi.waitFor(() => { const stored = localStorage.getItem('familienarchiv.lastVisited'); @@ -89,7 +96,10 @@ describe('documents/[id] page', () => { it('uses doc.title as the document title when set', async () => { mockPage.url = new URL('http://localhost/documents/d1'); - render(DocumentDetailPage, { props: { data: baseData() } }); + render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData() } + }); await vi.waitFor(() => expect(document.title).toContain('Brief an Helene')); }); @@ -97,6 +107,7 @@ describe('documents/[id] page', () => { it('falls back to originalFilename when title is empty', async () => { mockPage.url = new URL('http://localhost/documents/d2'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd2', title: '', originalFilename: 'fallback.pdf' } @@ -110,6 +121,7 @@ describe('documents/[id] page', () => { it('falls back to "Dokument" when title and originalFilename are empty', async () => { mockPage.url = new URL('http://localhost/documents/d3'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd3', title: '', originalFilename: '' } @@ -122,7 +134,10 @@ describe('documents/[id] page', () => { it('renders the topbar Edit-link affordance when canWrite is true', async () => { mockPage.url = new URL('http://localhost/documents/d4'); - render(DocumentDetailPage, { props: { data: baseData({ canWrite: true }) } }); + render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), + props: { data: baseData({ canWrite: true }) } + }); await expect.element(browserPage.getByRole('link', { name: 'Bearbeiten' })).toBeVisible(); }); @@ -130,6 +145,7 @@ describe('documents/[id] page', () => { it('renders the topbar when geschichten and inferredRelationship are passed through', async () => { mockPage.url = new URL('http://localhost/documents/d5'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ geschichten: [{ id: 'g1', title: 'Story', publishedAt: null }], @@ -146,6 +162,7 @@ describe('documents/[id] page', () => { it('renders the topbar even when doc.id is empty (defensive)', async () => { mockPage.url = new URL('http://localhost/documents/d-empty'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: '', title: 'No ID' } }) } }); @@ -156,6 +173,7 @@ describe('documents/[id] page', () => { it('renders sender data in the metadata drawer when sender is populated', async () => { mockPage.url = new URL('http://localhost/documents/d7'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { @@ -176,6 +194,7 @@ describe('documents/[id] page', () => { it('renders the topbar when filePath is set on the document', async () => { mockPage.url = new URL('http://localhost/documents/d8'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { @@ -195,6 +214,7 @@ describe('documents/[id] page', () => { it('renders the topbar with a complete user object passed through', async () => { mockPage.url = new URL('http://localhost/documents/d9'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd9' }, @@ -209,6 +229,7 @@ describe('documents/[id] page', () => { it('Escape keydown leaves the transcribe panel hidden when not already in transcribe mode', async () => { mockPage.url = new URL('http://localhost/documents/d10'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd10' } }) } }); @@ -220,6 +241,7 @@ describe('documents/[id] page', () => { it('non-Escape keydown does not affect the transcribe panel state', async () => { mockPage.url = new URL('http://localhost/documents/d11'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd11' } }) } }); @@ -232,6 +254,7 @@ describe('documents/[id] page', () => { it('renders the topbar with a deep-link comment query param', async () => { mockPage.url = new URL('http://localhost/documents/d12?comment=c-abc'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd12' } }) } }); @@ -241,6 +264,7 @@ describe('documents/[id] page', () => { it('renders sender name and Edit affordance with all metadata populated', async () => { mockPage.url = new URL('http://localhost/documents/d-meta'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { @@ -278,6 +302,7 @@ describe('documents/[id] page', () => { it('enters transcribe mode and shows the panel close button when ?task=transcribe is set', async () => { mockPage.url = new URL('http://localhost/documents/d-task?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-task' }, @@ -296,6 +321,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-fail?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-fail' } }) } @@ -328,6 +354,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-blocks?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-blocks' } }) } }); await expect.element(browserPage.getByText('Erster')).toBeVisible(); @@ -343,6 +370,7 @@ describe('documents/[id] page', () => { ); mockPage.url = new URL('http://localhost/documents/d-new'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-new', title: 'New Doc' } }) } @@ -360,6 +388,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-ocr-fail?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ canWrite: true, document: { ...baseDoc, id: 'd-ocr-fail' } }) } }); await vi.waitFor(() => { @@ -391,6 +420,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-ocr-run?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ canWrite: true, document: { ...baseDoc, id: 'd-ocr-run' } }) } }); await expect.element(browserPage.getByText('OCR läuft')).toBeVisible(); @@ -402,6 +432,7 @@ describe('documents/[id] page', () => { it('renders the topbar when the document has all OCR-relevant fields populated', async () => { mockPage.url = new URL('http://localhost/documents/d-ocr-meta?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { @@ -424,6 +455,7 @@ describe('documents/[id] page', () => { it('treats undefined geschichten as the empty array (geschichten ?? [] branch)', async () => { mockPage.url = new URL('http://localhost/documents/d-no-stories'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-no-stories' }, @@ -449,6 +481,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-ocr-done?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-ocr-done' } }) } }); await vi.waitFor(() => { @@ -474,6 +507,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-ocr-no-job?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-ocr-no-job' } }) } }); await vi.waitFor(() => { @@ -496,6 +530,7 @@ describe('documents/[id] page', () => { try { mockPage.url = new URL('http://localhost/documents/d-ocr-throw?task=transcribe'); render(DocumentDetailPage, { + context: new Map([[CONFIRM_KEY, createConfirmService()]]), props: { data: baseData({ document: { ...baseDoc, id: 'd-ocr-throw' } }) } }); await vi.waitFor(() => {