Skip to content

Commit 99dcace

Browse files
committed
Merge branch 'develop' into abhi-template-in-console
2 parents 043e606 + c123105 commit 99dcace

File tree

110 files changed

+3735
-1577
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+3735
-1577
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as Sentry from '@sentry/browser';
2+
// eslint-disable-next-line import/no-duplicates
3+
import { thirdPartyErrorFilterIntegration } from '@sentry/browser';
4+
// eslint-disable-next-line import/no-duplicates
5+
import { captureConsoleIntegration } from '@sentry/browser';
6+
7+
// This is the code the bundler plugin would inject to mark the init bundle as a first party module:
8+
var _sentryModuleMetadataGlobal =
9+
typeof window !== 'undefined'
10+
? window
11+
: typeof global !== 'undefined'
12+
? global
13+
: typeof self !== 'undefined'
14+
? self
15+
: {};
16+
17+
_sentryModuleMetadataGlobal._sentryModuleMetadata = _sentryModuleMetadataGlobal._sentryModuleMetadata || {};
18+
19+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack] = Object.assign(
20+
{},
21+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack],
22+
{
23+
'_sentryBundlerPluginAppKey:my-app': true,
24+
},
25+
);
26+
27+
Sentry.init({
28+
dsn: 'https://[email protected]/1337',
29+
integrations: [
30+
thirdPartyErrorFilterIntegration({ behaviour: 'apply-tag-if-contains-third-party-frames', filterKeys: ['my-app'] }),
31+
captureConsoleIntegration({ levels: ['error'], handled: false }),
32+
],
33+
attachStacktrace: true,
34+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This is the code the bundler plugin would inject to mark the subject bundle as a first party module:
2+
var _sentryModuleMetadataGlobal =
3+
typeof window !== 'undefined'
4+
? window
5+
: typeof global !== 'undefined'
6+
? global
7+
: typeof self !== 'undefined'
8+
? self
9+
: {};
10+
11+
_sentryModuleMetadataGlobal._sentryModuleMetadata = _sentryModuleMetadataGlobal._sentryModuleMetadata || {};
12+
13+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack] = Object.assign(
14+
{},
15+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack],
16+
{
17+
'_sentryBundlerPluginAppKey:my-app': true,
18+
},
19+
);
20+
21+
const errorBtn = document.getElementById('errBtn');
22+
errorBtn.addEventListener('click', async () => {
23+
Promise.allSettled([Promise.reject('I am a first party Error')]).then(values =>
24+
values.forEach(value => {
25+
if (value.status === 'rejected') console.error(value.reason);
26+
}),
27+
);
28+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
</head>
6+
<body>
7+
<script src="thirdPartyScript.js"></script>
8+
<button id="errBtn">Throw 1st part yerror</button>
9+
</body>
10+
</html>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { readFileSync } from 'node:fs';
2+
import { join } from 'node:path';
3+
import { expect } from '@playwright/test';
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { envelopeRequestParser, waitForErrorRequest } from '../../../utils/helpers';
6+
7+
const bundle = process.env.PW_BUNDLE || '';
8+
// We only want to run this in non-CDN bundle mode because
9+
// thirdPartyErrorFilterIntegration is only available in the NPM package
10+
if (bundle.startsWith('bundle')) {
11+
sentryTest.skip();
12+
}
13+
14+
sentryTest('tags event if contains at least one third-party frame', async ({ getLocalTestUrl, page }) => {
15+
const url = await getLocalTestUrl({ testDir: __dirname });
16+
17+
const errorEventPromise = waitForErrorRequest(page, e => {
18+
return e.exception?.values?.[0]?.value === 'I am a third party Error';
19+
});
20+
21+
await page.route('**/thirdPartyScript.js', route =>
22+
route.fulfill({
23+
status: 200,
24+
body: readFileSync(join(__dirname, 'thirdPartyScript.js')),
25+
}),
26+
);
27+
28+
await page.goto(url);
29+
30+
const errorEvent = envelopeRequestParser(await errorEventPromise);
31+
expect(errorEvent.tags?.third_party_code).toBe(true);
32+
});
33+
34+
/**
35+
* This test seems a bit more complicated than necessary but this is intentional:
36+
* When using `captureConsoleIntegration` in combination with `thirdPartyErrorFilterIntegration`
37+
* and `attachStacktrace: true`, the stack trace includes native code stack frames which previously broke
38+
* the third party error filtering logic.
39+
*
40+
* see https://github.com/getsentry/sentry-javascript/issues/17674
41+
*/
42+
sentryTest(
43+
"doesn't tag event if doesn't contain third-party frames",
44+
async ({ getLocalTestUrl, page, browserName }) => {
45+
const url = await getLocalTestUrl({ testDir: __dirname });
46+
47+
const errorEventPromise = waitForErrorRequest(page, e => {
48+
return e.exception?.values?.[0]?.value === 'I am a first party Error';
49+
});
50+
51+
await page.route('**/thirdPartyScript.js', route =>
52+
route.fulfill({
53+
status: 200,
54+
body: readFileSync(join(__dirname, 'thirdPartyScript.js')),
55+
}),
56+
);
57+
58+
await page.goto(url);
59+
60+
await page.click('#errBtn');
61+
62+
const errorEvent = envelopeRequestParser(await errorEventPromise);
63+
64+
expect(errorEvent.tags?.third_party_code).toBeUndefined();
65+
66+
// ensure the stack trace includes native code stack frames which previously broke
67+
// the third party error filtering logic
68+
if (browserName === 'chromium') {
69+
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toContainEqual({
70+
filename: '<anonymous>',
71+
function: 'Array.forEach',
72+
in_app: true,
73+
});
74+
} else if (browserName === 'webkit') {
75+
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toContainEqual({
76+
filename: '[native code]',
77+
function: 'forEach',
78+
in_app: true,
79+
});
80+
}
81+
},
82+
);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
setTimeout(() => {
2+
throw new Error('I am a third party Error');
3+
}, 100);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window._testBaseTimestamp = performance.timeOrigin / 1000;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true })],
9+
tracesSampleRate: 1,
10+
debug: true,
11+
});
12+
13+
setTimeout(() => {
14+
Sentry.reportPageLoaded();
15+
}, 2500);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { expect } from '@playwright/test';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
7+
} from '@sentry/browser';
8+
import { sentryTest } from '../../../../../utils/fixtures';
9+
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';
10+
11+
sentryTest(
12+
'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true',
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
19+
20+
const url = await getLocalTestUrl({ testDir: __dirname });
21+
22+
await page.goto(url);
23+
24+
const eventData = envelopeRequestParser(await pageloadEventPromise);
25+
26+
const traceContextData = eventData.contexts?.trace?.data;
27+
const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!;
28+
29+
expect(traceContextData).toMatchObject({
30+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
31+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
32+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
33+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
34+
['sentry.idle_span_finish_reason']: 'reportPageLoaded',
35+
});
36+
37+
// We wait for 2.5 seconds before calling Sentry.reportPageLoaded()
38+
// the margins are to account for timing weirdness in CI to avoid flakes
39+
expect(spanDurationSeconds).toBeGreaterThan(2);
40+
expect(spanDurationSeconds).toBeLessThan(3);
41+
},
42+
);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window._testBaseTimestamp = performance.timeOrigin / 1000;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, finalTimeout: 3000 })],
9+
tracesSampleRate: 1,
10+
debug: true,
11+
});
12+
13+
// not calling Sentry.reportPageLoaded() on purpose!
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { expect } from '@playwright/test';
2+
import {
3+
SEMANTIC_ATTRIBUTE_SENTRY_OP,
4+
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
5+
SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE,
6+
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
7+
} from '@sentry/browser';
8+
import { sentryTest } from '../../../../../utils/fixtures';
9+
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers';
10+
11+
sentryTest(
12+
'final timeout cancels the pageload span even if `enableReportPageLoaded` is true',
13+
async ({ getLocalTestUrl, page }) => {
14+
if (shouldSkipTracingTest()) {
15+
sentryTest.skip();
16+
}
17+
18+
const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload');
19+
20+
const url = await getLocalTestUrl({ testDir: __dirname });
21+
22+
await page.goto(url);
23+
24+
const eventData = envelopeRequestParser(await pageloadEventPromise);
25+
26+
const traceContextData = eventData.contexts?.trace?.data;
27+
const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!;
28+
29+
expect(traceContextData).toMatchObject({
30+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.browser',
31+
[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1,
32+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url',
33+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
34+
['sentry.idle_span_finish_reason']: 'finalTimeout',
35+
});
36+
37+
// We wait for 3 seconds before calling Sentry.reportPageLoaded()
38+
// the margins are to account for timing weirdness in CI to avoid flakes
39+
expect(spanDurationSeconds).toBeGreaterThan(2.5);
40+
expect(spanDurationSeconds).toBeLessThan(3.5);
41+
},
42+
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
window._testBaseTimestamp = performance.timeOrigin / 1000;
5+
6+
Sentry.init({
7+
dsn: 'https://[email protected]/1337',
8+
integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, instrumentNavigation: false })],
9+
tracesSampleRate: 1,
10+
debug: true,
11+
});
12+
13+
setTimeout(() => {
14+
Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(), { name: 'custom_navigation' });
15+
}, 1000);
16+
17+
setTimeout(() => {
18+
Sentry.reportPageLoaded();
19+
}, 2500);

0 commit comments

Comments
 (0)