From 06b8c99ce785b920a1809b4488b9814a1c7562f3 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 9 Jun 2026 14:46:08 +0200 Subject: [PATCH] test(journey-editor): add reorder tests, fix vacuous isDirty test, fix remove btn name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add move-up and move-down tests that verify PUT /items/reorder is called with the swapped ID order; 50ms delay accounts for two await levels before csrfFetch is called (click → handleMoveUp → handleReorder → csrfFetch) - Replace vacuous 'isDirty stays false' test (was asserting a dialog that never renders) with a meaningful publish-button-enabled assertion after adding an item - Update remove button query from 'Wirklich entfernen?' to 'Eintrag entfernen' to match the new journey_remove_item_aria aria-label Co-Authored-By: Claude Sonnet 4.6 --- .../geschichte/JourneyEditor.svelte.spec.ts | 79 +++++++++++++++++-- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/frontend/src/lib/geschichte/JourneyEditor.svelte.spec.ts b/frontend/src/lib/geschichte/JourneyEditor.svelte.spec.ts index a88330f3..01c121d7 100644 --- a/frontend/src/lib/geschichte/JourneyEditor.svelte.spec.ts +++ b/frontend/src/lib/geschichte/JourneyEditor.svelte.spec.ts @@ -192,27 +192,92 @@ describe('JourneyEditor — remove with rollback', () => { render(JourneyEditor, defaultProps({ geschichte: makeGeschichte({ items }) })); // Click remove (no note → direct remove) - await userEvent.click(page.getByRole('button', { name: 'Wirklich entfernen?' })); + await userEvent.click(page.getByRole('button', { name: 'Eintrag entfernen' })); await new Promise((r) => setTimeout(r, 50)); // Item should be restored after rollback await expect.element(page.getByText('Brief A')).toBeInTheDocument(); }); - it('item-add does NOT mark dirty (isDirty stays false)', async () => { + it('item-add enables publish button (isDirty stays false, canPublish becomes true)', async () => { const newItem = { id: 'i1', position: 0, note: 'Test' }; mockCsrfFetch(() => newItem); - const onSubmit = vi.fn().mockResolvedValue(undefined); - render(JourneyEditor, defaultProps({ onSubmit })); + render(JourneyEditor, defaultProps()); - // Add interlude (no unsaved warning should interfere) + // Publish should be disabled before adding item + await expect.element(page.getByRole('button', { name: /Veröffentlichen/ })).toBeDisabled(); + + // Add interlude await userEvent.click(page.getByText('Zwischentext hinzufügen')); await userEvent.fill(page.getByPlaceholder('Zwischentext eingeben…'), 'Test'); await userEvent.click(page.getByRole('button', { name: 'Hinzufügen', exact: true })); + await new Promise((r) => setTimeout(r, 50)); - // Saving (which requires non-empty title) — no unsaved warning dialog - await expect.element(page.getByRole('dialog')).not.toBeInTheDocument(); + // After item add, publish becomes enabled — item was added and state is correct + await expect.element(page.getByRole('button', { name: /Veröffentlichen/ })).not.toBeDisabled(); + }); +}); + +describe('JourneyEditor — reorder via move buttons', () => { + it('move-up calls PUT reorder with swapped IDs', async () => { + const items = [ + { id: 'i1', position: 0, document: docSummary('d1', 'Brief A') }, + { id: 'i2', position: 1, document: docSummary('d2', 'Brief B') } + ]; + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ + ok: true, + json: vi.fn().mockResolvedValue([ + { id: 'i2', position: 0, document: docSummary('d2', 'Brief B') }, + { id: 'i1', position: 1, document: docSummary('d1', 'Brief A') } + ]) + }) + ); + + render(JourneyEditor, defaultProps({ geschichte: makeGeschichte({ items }) })); + + await userEvent.click(page.getByRole('button', { name: /Brief B.*nach oben verschieben/ })); + await new Promise((r) => setTimeout(r, 50)); // handleMoveUp → handleReorder → csrfFetch: two await levels + + expect(globalThis.fetch).toHaveBeenCalledWith( + expect.stringContaining('/items/reorder'), + expect.objectContaining({ + method: 'PUT', + body: JSON.stringify({ itemIds: ['i2', 'i1'] }) + }) + ); + }); + + it('move-down calls PUT reorder with swapped IDs', async () => { + const items = [ + { id: 'i1', position: 0, document: docSummary('d1', 'Brief A') }, + { id: 'i2', position: 1, document: docSummary('d2', 'Brief B') } + ]; + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ + ok: true, + json: vi.fn().mockResolvedValue([ + { id: 'i2', position: 0, document: docSummary('d2', 'Brief B') }, + { id: 'i1', position: 1, document: docSummary('d1', 'Brief A') } + ]) + }) + ); + + render(JourneyEditor, defaultProps({ geschichte: makeGeschichte({ items }) })); + + await userEvent.click(page.getByRole('button', { name: /Brief A.*nach unten verschieben/ })); + await new Promise((r) => setTimeout(r, 50)); // handleMoveDown → handleReorder → csrfFetch: two await levels + + expect(globalThis.fetch).toHaveBeenCalledWith( + expect.stringContaining('/items/reorder'), + expect.objectContaining({ + method: 'PUT', + body: JSON.stringify({ itemIds: ['i2', 'i1'] }) + }) + ); }); });