fix(bulk-upload): skip navigation when any chunk fails to upload

goto('/documents') fired unconditionally, discarding error chips and
leaving the user with no feedback on which files failed. Now only
navigates when hadErrors is false after all chunks complete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-24 21:18:17 +02:00
committed by marcel
parent 4229e952fb
commit fc118f7032
2 changed files with 26 additions and 5 deletions

View File

@@ -1,11 +1,15 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { goto } from '$app/navigation';
import { cleanup, render } from 'vitest-browser-svelte';
import { page, userEvent } from 'vitest/browser';
import BulkDocumentEditLayout from './BulkDocumentEditLayout.svelte';
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
afterEach(() => {
cleanup();
vi.unstubAllGlobals();
vi.clearAllMocks();
});
function makeFile(name: string): File {
@@ -80,9 +84,6 @@ describe('BulkDocumentEditLayout', () => {
});
vi.stubGlobal('fetch', mockFetch);
// Also stub goto to prevent navigation errors in test
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
const { container } = render(BulkDocumentEditLayout, {});
const files = Array.from({ length: 12 }, (_, i) => makeFile(`f${i}.pdf`));
await addFilesViaInput(container, files);
@@ -103,7 +104,6 @@ describe('BulkDocumentEditLayout', () => {
json: async () => ({ errors: [{ filename: 'f0.pdf', code: 'FILE_UPLOAD_FAILED' }] })
});
vi.stubGlobal('fetch', mockFetch);
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
const { container } = render(BulkDocumentEditLayout, {});
await addFilesViaInput(container, [makeFile('f0.pdf')]);
@@ -140,6 +140,25 @@ describe('BulkDocumentEditLayout', () => {
expect(metadataJson).toHaveProperty('tagNames');
});
it('save() does not navigate when chunk returns non-ok response', async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: false,
json: async () => ({ errors: [{ filename: 'f0.pdf', code: 'FILE_UPLOAD_FAILED' }] })
});
vi.stubGlobal('fetch', mockFetch);
const { container } = render(BulkDocumentEditLayout, {});
await addFilesViaInput(container, [makeFile('f0.pdf')]);
const saveBtn = container.querySelector(
'button[data-testid="bulk-save-btn"]'
) as HTMLButtonElement;
saveBtn.click();
await vi.waitFor(() => expect(mockFetch).toHaveBeenCalledTimes(1), { timeout: 3000 });
expect(goto).not.toHaveBeenCalled();
});
it('discard-all resets to N=0 state and shows drop zone', async () => {
const { container } = render(BulkDocumentEditLayout, {});
await addFilesViaInput(container, [makeFile('a.pdf'), makeFile('b.pdf')]);