try/finally without catch swallowed TypeError network failures silently. Added catch block that sets errorMessage so the UI shows role=alert. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
90 lines
2.6 KiB
TypeScript
90 lines
2.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import { cleanup, render } from 'vitest-browser-svelte';
|
|
import { page, userEvent } from 'vitest/browser';
|
|
import { m } from '$lib/paraglide/messages.js';
|
|
import { getErrorMessage } from '$lib/shared/errors';
|
|
|
|
vi.mock('$app/navigation', () => ({
|
|
goto: vi.fn()
|
|
}));
|
|
|
|
const { default: JourneyCreate } = await import('./JourneyCreate.svelte');
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe('JourneyCreate — failure path', () => {
|
|
it('renders the mapped error message when POST /api/geschichten fails with a code', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn().mockResolvedValue({
|
|
ok: false,
|
|
json: vi.fn().mockResolvedValue({ code: 'VALIDATION_ERROR' })
|
|
})
|
|
);
|
|
|
|
render(JourneyCreate, {});
|
|
|
|
await userEvent.fill(
|
|
page.getByRole('textbox', { name: m.journey_title_aria_label() }),
|
|
'Meine Lesereise'
|
|
);
|
|
await userEvent.click(page.getByRole('button', { name: m.journey_create_submit() }));
|
|
|
|
const alert = page.getByRole('alert');
|
|
await expect.element(alert).toBeInTheDocument();
|
|
await expect.element(alert).toHaveTextContent(getErrorMessage('VALIDATION_ERROR'));
|
|
});
|
|
|
|
it('navigates to the edit page on success', async () => {
|
|
const { goto } = await import('$app/navigation');
|
|
vi.mocked(goto).mockClear();
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn().mockResolvedValue({
|
|
ok: true,
|
|
json: vi.fn().mockResolvedValue({ id: 'g-new' })
|
|
})
|
|
);
|
|
|
|
render(JourneyCreate, {});
|
|
|
|
await userEvent.fill(
|
|
page.getByRole('textbox', { name: m.journey_title_aria_label() }),
|
|
'Meine Lesereise'
|
|
);
|
|
await userEvent.click(page.getByRole('button', { name: m.journey_create_submit() }));
|
|
|
|
await vi.waitFor(() => {
|
|
expect(goto).toHaveBeenCalledWith('/geschichten/g-new/edit');
|
|
});
|
|
});
|
|
|
|
it('shows an error alert when the network request rejects (no crash)', async () => {
|
|
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new TypeError('network down')));
|
|
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
|
|
render(JourneyCreate, {});
|
|
|
|
await userEvent.fill(
|
|
page.getByRole('textbox', { name: m.journey_title_aria_label() }),
|
|
'Meine Lesereise'
|
|
);
|
|
await userEvent.click(page.getByRole('button', { name: m.journey_create_submit() }));
|
|
|
|
await expect.element(page.getByRole('alert')).toBeInTheDocument();
|
|
consoleError.mockRestore();
|
|
});
|
|
|
|
it('has an accessible label on the title input', async () => {
|
|
vi.stubGlobal('fetch', vi.fn());
|
|
render(JourneyCreate, {});
|
|
|
|
await expect
|
|
.element(page.getByRole('textbox', { name: m.journey_title_aria_label() }))
|
|
.toBeInTheDocument();
|
|
});
|
|
});
|