Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ function waitForPageloadSpan() {
});
}

// The throw still bubbles to the page after the worker forwards it, but
// the integration makes globalHandlers skip that frameless copy. So the
// first error event to arrive must be the forwarded one.
const WORKER_MECHANISM = 'auto.browser.web_worker.onerror';

test('captures an error with debug ids and pageload trace context', async ({ page }) => {
const errorEventPromise = waitForError('browser-webworker-vite', async event => {
return !event.type && !!event.exception?.values?.[0];
Expand All @@ -24,9 +29,16 @@ test('captures an error with debug ids and pageload trace context', async ({ pag
const pageloadSpan = await pageloadSpanPromise;

expect(errorEvent.exception?.values).toHaveLength(1);
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught Error: Uncaught error in worker');
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1);
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(/worker-.+\.js$/);
expect(errorEvent.exception?.values?.[0]?.mechanism?.type).toBe(WORKER_MECHANISM);
expect(errorEvent.exception?.values?.[0]?.type).toBe('Error');
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught error in worker');
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toEqual(
expect.arrayContaining([expect.objectContaining({ filename: expect.stringMatching(/worker-.+\.js$/) })]),
);

expect(errorEvent.contexts?.worker).toEqual({
filename: expect.stringMatching(/worker-.+\.js$/),
});

expect(errorEvent.transaction).toBe('/');
expect(pageloadSpan.name).toBe('Pageload');
Expand All @@ -52,6 +64,38 @@ test('captures an error with debug ids and pageload trace context', async ({ pag
});
});

test('emits exactly one event for an uncaught worker error', async ({ page }) => {
const mechanisms: Array<string | undefined> = [];
// Never resolves. It only records every error event that arrives, so
// the global handler's copy of a throw would show up here.
void waitForError('browser-webworker-vite', event => {
if (!event.type && event.exception?.values?.[0]) {
mechanisms.push(event.exception.values[0].mechanism?.type);
}
return false;
});

const firstErrorPromise = waitForError('browser-webworker-vite', event => {
return event.exception?.values?.[0]?.value === 'Uncaught error in worker';
});
const secondErrorPromise = waitForError('browser-webworker-vite', event => {
return event.exception?.values?.[0]?.value === 'Uncaught error in worker 2';
});

await page.goto('/');

await page.locator('#trigger-error').click();
await firstErrorPromise;

// The bubbled copy of the first throw is queued right behind the forwarded
// one, so the second worker's event arriving without it in between is the
// signal that it was suppressed.
await page.locator('#trigger-error-2').click();
await secondErrorPromise;

expect(mechanisms).toEqual([WORKER_MECHANISM, WORKER_MECHANISM]);
});
Comment thread
cursor[bot] marked this conversation as resolved.

test("user worker message handlers don't trigger for sentry messages", async ({ page }) => {
const workerReadyPromise = new Promise<number>(resolve => {
let workerMessageCount = 0;
Expand Down Expand Up @@ -90,9 +134,11 @@ test('captures an error from the second eagerly added worker', async ({ page })
const pageloadSpan = await pageloadSpanPromise;

expect(errorEvent.exception?.values).toHaveLength(1);
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught Error: Uncaught error in worker 2');
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1);
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(/worker2-.+\.js$/);
expect(errorEvent.exception?.values?.[0]?.mechanism?.type).toBe(WORKER_MECHANISM);
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught error in worker 2');
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toEqual(
expect.arrayContaining([expect.objectContaining({ filename: expect.stringMatching(/worker2-.+\.js$/) })]),
);

expect(errorEvent.transaction).toBe('/');
expect(pageloadSpan.name).toBe('Pageload');
Expand Down Expand Up @@ -135,9 +181,11 @@ test('captures an error from the third lazily added worker', async ({ page }) =>
const pageloadSpan = await pageloadSpanPromise;

expect(errorEvent.exception?.values).toHaveLength(1);
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught Error: Uncaught error in worker 3');
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toHaveLength(1);
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames?.[0]?.filename).toMatch(/worker3-.+\.js$/);
expect(errorEvent.exception?.values?.[0]?.mechanism?.type).toBe(WORKER_MECHANISM);
expect(errorEvent.exception?.values?.[0]?.value).toBe('Uncaught error in worker 3');
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toEqual(
expect.arrayContaining([expect.objectContaining({ filename: expect.stringMatching(/worker3-.+\.js$/) })]),
);

expect(errorEvent.transaction).toBe('/');
expect(pageloadSpan.name).toBe('Pageload');
Expand Down Expand Up @@ -165,7 +213,7 @@ test('captures an error from the third lazily added worker', async ({ page }) =>

test('worker errors are not tagged as third-party when module metadata is present', async ({ page }) => {
const errorEventPromise = waitForError('browser-webworker-vite', async event => {
return !event.type && event.exception?.values?.[0]?.value === 'Uncaught Error: Uncaught error in worker';
return !event.type && event.exception?.values?.[0]?.value === 'Uncaught error in worker';
});

await page.goto('/');
Expand Down
5 changes: 4 additions & 1 deletion packages/browser/src/integrations/globalhandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ export function _eventFromRejectionWithPrimitive(reason: Primitive): Event {
};
}

function _enhanceEventWithInitialFrame(
/**
* Adds a frame built from the error location when the event has none.
*/
export function _enhanceEventWithInitialFrame(
event: Event,
url: string | undefined,
lineno: number | undefined,
Expand Down
Loading
Loading