diff --git a/frontend/src/routes/documents/[id]/edit/SaveBar.svelte b/frontend/src/routes/documents/[id]/edit/SaveBar.svelte index 17195ce5..477f3582 100644 --- a/frontend/src/routes/documents/[id]/edit/SaveBar.svelte +++ b/frontend/src/routes/documents/[id]/edit/SaveBar.svelte @@ -1,9 +1,20 @@
- +
- {#if confirmDelete} - {m.doc_delete_confirm()} -
- - -
- {:else} - - - {m.btn_cancel()} - - {/if} + + + + + + {m.btn_delete()} + + + {m.btn_cancel()} +
diff --git a/frontend/src/routes/documents/[id]/edit/SaveBar.svelte.spec.ts b/frontend/src/routes/documents/[id]/edit/SaveBar.svelte.spec.ts new file mode 100644 index 00000000..d1ad0d97 --- /dev/null +++ b/frontend/src/routes/documents/[id]/edit/SaveBar.svelte.spec.ts @@ -0,0 +1,92 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import SaveBar from './SaveBar.svelte'; +import { createConfirmService, CONFIRM_KEY } from '$lib/services/confirm.svelte.js'; + +let appendedForms: HTMLFormElement[] = []; + +afterEach(() => { + cleanup(); + appendedForms.forEach((f) => f.remove()); + appendedForms = []; +}); + +function renderSaveBar(docId = 'doc-1') { + const service = createConfirmService(); + + // Mount a dummy delete form so SaveBar can find it via document.getElementById + const deleteForm = document.createElement('form'); + deleteForm.id = 'delete-form'; + document.body.appendChild(deleteForm); + appendedForms.push(deleteForm); + + const result = render(SaveBar, { + props: { docId }, + context: new Map([[CONFIRM_KEY, service]]) + }); + + return { ...result, service, deleteForm }; +} + +// ─── Rendering ──────────────────────────────────────────────────────────────── + +describe('SaveBar — rendering', () => { + it('renders save button', async () => { + renderSaveBar(); + await expect.element(page.getByRole('button', { name: /Speichern/i })).toBeInTheDocument(); + }); + + it('renders delete button', async () => { + renderSaveBar(); + // The delete button should be type="button" (async confirm flow) + const deleteBtn = document.querySelector('button[type="button"]'); + expect(deleteBtn).not.toBeNull(); + }); + + it('renders cancel link pointing to /documents/doc-1', async () => { + renderSaveBar(); + await expect + .element(page.getByRole('link', { name: /Abbrechen/i })) + .toHaveAttribute('href', '/documents/doc-1'); + }); +}); + +// ─── Delete confirmation ────────────────────────────────────────────────────── + +describe('SaveBar — delete confirmation', () => { + it('opens confirm dialog when delete button is clicked', async () => { + const { service } = renderSaveBar(); + const deleteBtn = document.querySelectorAll('button[type="button"]')[0]; + deleteBtn.click(); + await vi.waitFor(() => expect(service.options).not.toBeNull()); + expect(service.options?.destructive).toBe(true); + service.settle(false); + }); + + it('submits delete form when user confirms', async () => { + const { service, deleteForm } = renderSaveBar(); + const requestSubmit = vi.spyOn(deleteForm, 'requestSubmit').mockImplementation(() => {}); + + const deleteBtn = document.querySelectorAll('button[type="button"]')[0]; + deleteBtn.click(); + await vi.waitFor(() => expect(service.options).not.toBeNull()); + service.settle(true); + await vi.waitFor(() => expect(service.options).toBeNull()); + + expect(requestSubmit).toHaveBeenCalledOnce(); + }); + + it('does not submit delete form when user cancels', async () => { + const { service, deleteForm } = renderSaveBar(); + const requestSubmit = vi.spyOn(deleteForm, 'requestSubmit').mockImplementation(() => {}); + + const deleteBtn = document.querySelectorAll('button[type="button"]')[0]; + deleteBtn.click(); + await vi.waitFor(() => expect(service.options).not.toBeNull()); + service.settle(false); + await vi.waitFor(() => expect(service.options).toBeNull()); + + expect(requestSubmit).not.toHaveBeenCalled(); + }); +});