Skip to content
Merged
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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@

- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott

## 10.3.0

- feat(core): MCP Server - Capture prompt results from prompt function calls (#17284)
- feat(bun): Export `skipOpenTelemetrySetup` option ([#17349](https://github.com/getsentry/sentry-javascript/pull/17349))
- feat(sveltekit): Streamline build logs ([#17306](https://github.com/getsentry/sentry-javascript/pull/17306))
- fix(browser): Handle data urls in errors caught by `globalHandlersIntegration` ([#17216](https://github.com/getsentry/sentry-javascript/pull/17216))
- fix(browser): Improve navigation vs. redirect detection ([#17275](https://github.com/getsentry/sentry-javascript/pull/17275))
- fix(react-router): Ensure source map upload fails silently if Sentry CLI fails ([#17081](https://github.com/getsentry/sentry-javascript/pull/17081))
- fix(react): Add support for React Router sub-routes from `handle` ([#17277](https://github.com/getsentry/sentry-javascript/pull/17277))

## 10.2.0

### Important Changes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const workerCode = `
self.addEventListener('message', (event) => {
if (event.data.type === 'error') {
throw new Error('Error thrown in worker');
}
});
`;

const worker = new Worker(`data:text/javascript;base64,${btoa(workerCode)}`);

worker.postMessage({ type: 'error' });
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../../utils/fixtures';
import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers';

/**
* Tests a special case where the `globalHandlersIntegration` itself creates a stack frame instead of using
* stack parsers. This is necessary because we don't always get an `error` object passed to `window.onerror`.
* @see `globalhandlers.ts#_enhanceEventWithInitialFrame`
*/
sentryTest('detects and handles data urls on first stack frame', async ({ getLocalTestUrl, page }) => {
const url = await getLocalTestUrl({ testDir: __dirname });

const errorEventPromise = waitForErrorRequest(page, e => {
return !!e.exception?.values;
});

await page.goto(url);

const errorEvent = envelopeRequestParser(await errorEventPromise);

expect(errorEvent?.exception?.values?.[0]).toEqual({
mechanism: {
handled: false,
synthetic: true,
type: 'auto.browser.global_handlers.onerror',
},
stacktrace: {
frames: [
{
colno: expect.any(Number), // webkit reports different colno than chromium
filename: '<data:text/javascript,base64>',
function: '?',
in_app: true,
lineno: 4,
},
],
},
type: 'Error',
value: expect.stringMatching(/(Uncaught )?Error: Error thrown in worker/), // webikt throws without "Uncaught "
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ window.Sentry = Sentry;

Sentry.init({
dsn: 'https://[email protected]/1337',
integrations: [Sentry.browserTracingIntegration({ idleTimeout: 2000 })],
integrations: [Sentry.browserTracingIntegration({ idleTimeout: 2000, detectRedirects: false })],
tracesSampleRate: 1,
});

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://[email protected]/1337',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 1,
});

window.history.pushState({}, '', '/sub-page-redirect-1');

setTimeout(() => {
window.history.pushState({}, '', '/sub-page-redirect-2');
}, 400);

setTimeout(() => {
window.history.pushState({}, '', '/sub-page-redirect-3');
}, 800);

document.getElementById('btn1').addEventListener('click', () => {
window.history.pushState({}, '', '/next-page');
});

setTimeout(() => {
document.getElementById('btn1').click();
// 1s is still within the 1.5s time window, but the click should trigger a new navigation root span
}, 1000);

setTimeout(() => {
window.history.pushState({}, '', '/next-page-redirect-1');
}, 1100);

setTimeout(() => {
window.history.pushState({}, '', '/next-page-redirect-2');
}, 2000);
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { expect } from '@playwright/test';
import {
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
} from '@sentry/core';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';

sentryTest(
'creates a pageload and navigation root spans each with multiple navigation.redirect childspans',
async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestUrl({ testDir: __dirname });

const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
const navigationRequestPromise = waitForTransactionRequest(
page,
event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/next-page',
);

await page.goto(url);

const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise);
const navigationRequest = envelopeRequestParser(await navigationRequestPromise);

expect(pageloadRequest.contexts?.trace?.op).toBe('pageload');

expect(pageloadRequest.contexts?.trace?.data).toMatchObject({
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
['sentry.idle_span_finish_reason']: 'cancelled',
});

expect(pageloadRequest.request).toEqual({
headers: {
'User-Agent': expect.any(String),
},
url: 'http://sentry-test.io/index.html',
});

const spans = pageloadRequest.spans || [];

const redirectSpans = spans.filter(span => span.op === 'navigation.redirect');
expect(redirectSpans).toHaveLength(3);

redirectSpans.forEach(redirectSpan => {
expect(redirectSpan?.timestamp).toEqual(redirectSpan?.start_timestamp);
expect(redirectSpan).toEqual({
data: {
'sentry.op': 'navigation.redirect',
'sentry.origin': 'auto.navigation.browser',
'sentry.source': 'url',
},
description: expect.stringContaining('/sub-page-redirect-'),
op: 'navigation.redirect',
origin: 'auto.navigation.browser',
parent_span_id: pageloadRequest.contexts!.trace!.span_id,
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: expect.any(String),
});
});

expect(navigationRequest.contexts?.trace?.op).toBe('navigation');
expect(navigationRequest.transaction).toEqual('/next-page');

// 2 subsequent redirects belonging to the navigation root span
expect(navigationRequest.spans?.filter(span => span.op === 'navigation.redirect')).toHaveLength(2);
},
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://[email protected]/1337',
integrations: [Sentry.browserTracingIntegration()],
tracesSampleRate: 1,
debug: true,
});

document.getElementById('btn1').addEventListener('click', () => {
window.history.pushState({}, '', '/sub-page');

// then trigger redirect inside of this navigation, which should not be detected as a redirect
// because the last click was less than 1.5s ago
setTimeout(() => {
document.getElementById('btn2').click();
}, 100);
});

document.getElementById('btn2').addEventListener('click', () => {
setTimeout(() => {
// navigation happens ~1100ms after the last navigation
window.history.pushState({}, '', '/sub-page-2');
}, 1000);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
</head>
<button id="btn1">Trigger navigation</button>
<button id="btn2">Trigger navigation 2</button>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { expect } from '@playwright/test';
import { sentryTest } from '../../../../../utils/fixtures';
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';

sentryTest(
'creates navigation root span if click happened within 1.5s of the last navigation',
async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestUrl({ testDir: __dirname });

const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
const navigationRequestPromise = waitForTransactionRequest(
page,
event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page',
);
const navigation2RequestPromise = waitForTransactionRequest(
page,
event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page-2',
);

await page.goto(url);

await pageloadRequestPromise;

// Now trigger navigation (since no span is active), and then a redirect in the navigation, with
await page.click('#btn1');

const navigationRequest = envelopeRequestParser(await navigationRequestPromise);
const navigation2Request = envelopeRequestParser(await navigation2RequestPromise);

expect(navigationRequest.contexts?.trace?.op).toBe('navigation');
expect(navigationRequest.transaction).toEqual('/sub-page');

const spans = (navigationRequest.spans || []).filter(s => s.op === 'navigation.redirect');

expect(spans).toHaveLength(0);

expect(navigation2Request.contexts?.trace?.op).toBe('navigation');
expect(navigation2Request.transaction).toEqual('/sub-page-2');

const spans2 = (navigation2Request.spans || []).filter(s => s.op === 'navigation.redirect');
expect(spans2).toHaveLength(0);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,10 @@ document.getElementById('btn1').addEventListener('click', () => {
}, 100);
}, 400);
});

document.getElementById('btn2').addEventListener('click', () => {
// Trigger navigation later than click, so the last click is more than 300ms ago
setTimeout(() => {
window.history.pushState({}, '', '/sub-page-2');
}, 400);
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
<meta charset="utf-8" />
</head>
<button id="btn1">Trigger navigation</button>
<button id="btn2">Trigger navigation</button>
</html>
Loading
Loading