feat(observability): guard navigator.clipboard and handle rejection in copyId
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>
This commit is contained in:
@@ -7,10 +7,16 @@ let copied = $state(false);
|
|||||||
function copyId() {
|
function copyId() {
|
||||||
const id = page.error?.errorId;
|
const id = page.error?.errorId;
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
navigator.clipboard.writeText(id).then(() => {
|
if (!navigator.clipboard) return;
|
||||||
copied = true;
|
navigator.clipboard.writeText(id).then(
|
||||||
setTimeout(() => (copied = false), 2000);
|
() => {
|
||||||
});
|
copied = true;
|
||||||
|
setTimeout(() => (copied = false), 2000);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
/* clipboard denied or unavailable — select-all on the <code> element remains */
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -94,4 +94,38 @@ describe('+error.svelte', () => {
|
|||||||
|
|
||||||
await expect.element(browserPage.getByText('Fehler-ID')).not.toBeInTheDocument();
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user