Adds availability guard (navigator.clipboard may be undefined in non-HTTPS contexts) and a rejection handler so clipboard-denied errors are silently caught rather than becoming unhandled promise rejections. Tests cover the success feedback and the silent-failure path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
132 lines
3.7 KiB
TypeScript
132 lines
3.7 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
import { cleanup, render } from 'vitest-browser-svelte';
|
|
import { page as browserPage } from 'vitest/browser';
|
|
|
|
const mockPage = {
|
|
status: 500,
|
|
error: { message: 'Internal Error', errorId: undefined } as {
|
|
message: string;
|
|
errorId?: string;
|
|
} | null
|
|
};
|
|
|
|
vi.mock('$app/state', () => ({
|
|
get page() {
|
|
return mockPage;
|
|
}
|
|
}));
|
|
|
|
vi.mock('$lib/paraglide/messages.js', () => ({
|
|
m: {
|
|
page_title_error: () => 'Es ist etwas schiefgelaufen.',
|
|
error_internal_error: () => 'Ein unerwarteter Fehler ist aufgetreten.',
|
|
error_page_id_label: () => 'Fehler-ID',
|
|
error_copy_id_label: () => 'ID kopieren',
|
|
error_copied: () => 'Kopiert!'
|
|
}
|
|
}));
|
|
|
|
afterEach(cleanup);
|
|
|
|
async function loadComponent() {
|
|
return (await import('./+error.svelte')).default;
|
|
}
|
|
|
|
describe('+error.svelte', () => {
|
|
it('renders the page status code', async () => {
|
|
mockPage.status = 404;
|
|
mockPage.error = { message: 'Not Found' };
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await expect.element(browserPage.getByText('404')).toBeVisible();
|
|
});
|
|
|
|
it('renders the error message text from page.error.message', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = { message: 'Database unavailable' };
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await expect.element(browserPage.getByText('Database unavailable')).toBeVisible();
|
|
});
|
|
|
|
it('falls back to error_internal_error message when page.error is null', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = null;
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await expect
|
|
.element(browserPage.getByText('Ein unerwarteter Fehler ist aufgetreten.'))
|
|
.toBeVisible();
|
|
});
|
|
|
|
it('shows errorId when page.error.errorId is set', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = { message: 'Something broke', errorId: 'abc-123-def' };
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await expect.element(browserPage.getByText('abc-123-def')).toBeVisible();
|
|
});
|
|
|
|
it('shows copy button when errorId is present', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = { message: 'Something broke', errorId: 'abc-123-def' };
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await expect.element(browserPage.getByRole('button', { name: 'ID kopieren' })).toBeVisible();
|
|
});
|
|
|
|
it('does not render errorId section when errorId is absent', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = { message: 'Something broke' };
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await expect.element(browserPage.getByText('Fehler-ID')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('shows "Kopiert!" after clicking the copy button', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = { message: 'Something broke', errorId: 'abc-123-def' };
|
|
|
|
Object.defineProperty(navigator, 'clipboard', {
|
|
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await browserPage.getByRole('button', { name: 'ID kopieren' }).click();
|
|
await expect.element(browserPage.getByText('Kopiert!')).toBeVisible();
|
|
});
|
|
|
|
it('does not show "Kopiert!" when clipboard write is rejected', async () => {
|
|
mockPage.status = 500;
|
|
mockPage.error = { message: 'Something broke', errorId: 'abc-123-def' };
|
|
|
|
Object.defineProperty(navigator, 'clipboard', {
|
|
value: { writeText: vi.fn().mockRejectedValue(new Error('denied')) },
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
|
|
const ErrorPage = await loadComponent();
|
|
render(ErrorPage);
|
|
|
|
await browserPage.getByRole('button', { name: 'ID kopieren' }).click();
|
|
await expect.element(browserPage.getByText('Kopiert!')).not.toBeInTheDocument();
|
|
});
|
|
});
|