Skip to content

Commit 6640038

Browse files
committed
add integration tests
1 parent 7730d9c commit 6640038

7 files changed

Lines changed: 151 additions & 3 deletions

File tree

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://public@dsn.ingest.sentry.io/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: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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+
sentryTest('tags event if contains at least one third-party frame', async ({ getLocalTestUrl, page }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
10+
const errorEventPromise = waitForErrorRequest(page, e => {
11+
return e.exception?.values?.[0]?.value === 'I am a third party Error';
12+
});
13+
14+
await page.route('**/thirdPartyScript.js', route =>
15+
route.fulfill({
16+
status: 200,
17+
body: readFileSync(join(__dirname, 'thirdPartyScript.js')),
18+
}),
19+
);
20+
21+
await page.goto(url);
22+
23+
const errorEvent = envelopeRequestParser(await errorEventPromise);
24+
expect(errorEvent.tags?.third_party_code).toBe(true);
25+
});
26+
27+
/**
28+
* This test seems a bit more complicated than necessary but this is intentional:
29+
* When using `captureConsoleIntegration` in combination with `thirdPartyErrorFilterIntegration`
30+
* and `attachStacktrace: true`, the stack trace includes native code stack frames which previously broke
31+
* the third party error filtering logic.
32+
*
33+
* see https://github.com/getsentry/sentry-javascript/issues/17674
34+
*/
35+
sentryTest(
36+
"doesn't tag event if doesn't contain third-party frames",
37+
async ({ getLocalTestUrl, page, browserName }) => {
38+
const url = await getLocalTestUrl({ testDir: __dirname });
39+
40+
const errorEventPromise = waitForErrorRequest(page, e => {
41+
return e.exception?.values?.[0]?.value === 'I am a first party Error';
42+
});
43+
44+
await page.route('**/thirdPartyScript.js', route =>
45+
route.fulfill({
46+
status: 200,
47+
body: readFileSync(join(__dirname, 'thirdPartyScript.js')),
48+
}),
49+
);
50+
51+
await page.goto(url);
52+
53+
await page.click('#errBtn');
54+
55+
const errorEvent = envelopeRequestParser(await errorEventPromise);
56+
57+
expect(errorEvent.tags?.third_party_code).toBeUndefined();
58+
59+
// ensure the stack trace includes native code stack frames which previously broke
60+
// the third party error filtering logic
61+
if (browserName === 'chromium') {
62+
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toContainEqual({
63+
filename: '<anonymous>',
64+
function: 'Array.forEach',
65+
in_app: true,
66+
});
67+
} else if (browserName === 'webkit') {
68+
expect(errorEvent.exception?.values?.[0]?.stacktrace?.frames).toContainEqual({
69+
filename: '[native code]',
70+
function: 'forEach',
71+
in_app: true,
72+
});
73+
}
74+
},
75+
);
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);

packages/core/src/integrations/third-party-errors-filter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function getBundleKeysForAllFramesWithFilenames(event: Event): string[][] | unde
110110
frames
111111
// Exclude frames without a filename or without lineno and colno,
112112
// since these are likely native code or built-ins
113-
.filter(frame => !!frame.filename && (frame.lineno != null || frame.colno != null))
113+
.filter(frame => !!frame.filename && (frame.lineno || frame.colno) != null)
114114
.map(frame => {
115115
if (frame.module_metadata) {
116116
return Object.keys(frame.module_metadata)

packages/core/test/lib/integrations/third-party-errors-filter.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,8 @@ const eventWithOnlyFirstPartyFrames: Event = {
6464
colno: 1,
6565
filename: __filename,
6666
function: 'function',
67-
lineno: 1,
6867
},
6968
{
70-
colno: 2,
7169
filename: __filename,
7270
function: 'function',
7371
lineno: 2,

0 commit comments

Comments
 (0)