Skip to content

Commit 658898c

Browse files
authored
test: Port breadcrumb tests from karma runner (#11436)
I want to remove the karma/mocha based tests in the browser package. To accomplish this, I'll be porting 1 test suite a day from the old integration tests to playwright. Today is Day 2: `packages/browser/test/integration/suites/breadcrumbs.js` This adds console and history breadcrumb tests (which didn't exist before), and expands upon the dom and xhr/fetch tests, and cleans up some code here and there as well. ref #11084 day 1: #11412
1 parent dd4c9fe commit 658898c

File tree

20 files changed

+317
-791
lines changed

20 files changed

+317
-791
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
console.log('One');
2+
console.warn('Two', { a: 1 });
3+
console.error('Error 2', { b: { c: [] } });
4+
5+
// Passed assertions _should not_ be captured
6+
console.assert(1 + 1 === 2, 'math works');
7+
// Failed assertions _should_ be captured
8+
console.assert(1 + 1 === 3, 'math broke');
9+
10+
Sentry.captureException('test exception');
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/browser';
3+
4+
import { sentryTest } from '../../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';
6+
7+
sentryTest('should capture console breadcrumbs', async ({ getLocalTestUrl, page }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
10+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
11+
12+
expect(eventData.breadcrumbs).toEqual([
13+
{
14+
category: 'console',
15+
data: { arguments: ['One'], logger: 'console' },
16+
level: 'log',
17+
message: 'One',
18+
timestamp: expect.any(Number),
19+
},
20+
{
21+
category: 'console',
22+
data: { arguments: ['Two', { a: 1 }], logger: 'console' },
23+
level: 'warning',
24+
message: 'Two [object Object]',
25+
timestamp: expect.any(Number),
26+
},
27+
{
28+
category: 'console',
29+
data: { arguments: ['Error 2', { b: '[Object]' }], logger: 'console' },
30+
level: 'error',
31+
message: 'Error 2 [object Object]',
32+
timestamp: expect.any(Number),
33+
},
34+
{
35+
category: 'console',
36+
data: {
37+
arguments: ['math broke'],
38+
logger: 'console',
39+
},
40+
level: 'log',
41+
message: 'Assertion failed: math broke',
42+
timestamp: expect.any(Number),
43+
},
44+
]);
45+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
defaultIntegrations: false,
8+
integrations: [Sentry.breadcrumbsIntegration()],
9+
sampleRate: 1,
10+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const click = new MouseEvent('click');
2+
function kaboom() {
3+
throw new Error('lol');
4+
}
5+
Object.defineProperty(click, 'target', { get: kaboom });
6+
const input = document.getElementById('input1');
7+
input.dispatchEvent(click);
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+
<title></title>
6+
</head>
7+
<body>
8+
<input id="input1" type="text" />
9+
</body>
10+
</html>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/browser';
3+
4+
import { sentryTest } from '../../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';
6+
7+
// see: https://github.com/getsentry/sentry-javascript/issues/768
8+
sentryTest(
9+
'should record breadcrumb if accessing the target property of an event throws an exception',
10+
async ({ getLocalTestUrl, page }) => {
11+
const url = await getLocalTestUrl({ testDir: __dirname });
12+
13+
await page.goto(url);
14+
15+
const promise = getFirstSentryEnvelopeRequest<Event>(page);
16+
17+
await page.locator('#input1').pressSequentially('test', { delay: 1 });
18+
19+
await page.evaluate('Sentry.captureException("test exception")');
20+
21+
const eventData = await promise;
22+
23+
expect(eventData.breadcrumbs).toHaveLength(1);
24+
expect(eventData.breadcrumbs).toEqual([
25+
{
26+
category: 'ui.input',
27+
message: 'body > input#input1[type="text"]',
28+
timestamp: expect.any(Number),
29+
},
30+
]);
31+
},
32+
);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const input = document.getElementsByTagName('input')[0];
2+
input.addEventListener('build', function (evt) {
3+
evt.stopPropagation();
4+
});
5+
6+
const customEvent = new CustomEvent('build', { detail: 1 });
7+
input.dispatchEvent(customEvent);
8+
9+
Sentry.captureException('test exception');
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title></title>
6+
</head>
7+
<body>
8+
<input id="input1" type="text" />
9+
<input id="input2" type="text" />
10+
<input id="annotated-input" data-sentry-component="AnnotatedInput" type="text" />
11+
</body>
12+
</html>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';
6+
7+
sentryTest('breadcrumbs listener should not fail with custom event', async ({ getLocalTestUrl, page }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
10+
let error = undefined;
11+
page.on('pageerror', err => {
12+
error = err;
13+
});
14+
15+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
16+
expect(eventData.exception?.values).toHaveLength(1);
17+
expect(eventData.breadcrumbs).toBeUndefined();
18+
expect(error).toBeUndefined();
19+
});
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+
<title></title>
6+
</head>
7+
<body>
8+
<input id="input1" type="text" />
9+
</body>
10+
</html>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/browser';
3+
4+
import { sentryTest } from '../../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';
6+
7+
sentryTest(
8+
'should correctly capture multiple consecutive breadcrumbs if they are of different type',
9+
async ({ getLocalTestUrl, page }) => {
10+
const url = await getLocalTestUrl({ testDir: __dirname });
11+
12+
await page.goto(url);
13+
14+
const promise = getFirstSentryEnvelopeRequest<Event>(page);
15+
16+
// These inputs will be debounced
17+
await page.locator('#input1').pressSequentially('abc', { delay: 1 });
18+
await page.locator('#input1').pressSequentially('def', { delay: 1 });
19+
await page.locator('#input1').pressSequentially('ghi', { delay: 1 });
20+
21+
await page.locator('#input1').click();
22+
await page.locator('#input1').click();
23+
await page.locator('#input1').click();
24+
25+
// This input should not be debounced
26+
await page.locator('#input1').pressSequentially('jkl', { delay: 1 });
27+
28+
await page.evaluate('Sentry.captureException("test exception")');
29+
30+
const eventData = await promise;
31+
32+
expect(eventData.breadcrumbs).toEqual([
33+
{
34+
category: 'ui.input',
35+
message: 'body > input#input1[type="text"]',
36+
timestamp: expect.any(Number),
37+
},
38+
{
39+
category: 'ui.click',
40+
message: 'body > input#input1[type="text"]',
41+
timestamp: expect.any(Number),
42+
},
43+
{
44+
category: 'ui.input',
45+
message: 'body > input#input1[type="text"]',
46+
timestamp: expect.any(Number),
47+
},
48+
]);
49+
},
50+
);
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const xhr = new XMLHttpRequest();
2-
31
fetch('http://localhost:7654/foo').then(() => {
42
Sentry.captureException('test error');
53
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fetch(new Request('http://localhost:7654/foo')).then(() => {
2+
Sentry.captureException('test error');
3+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';
6+
7+
sentryTest('captures Breadcrumb for basic GET request that uses request object', async ({ getLocalTestUrl, page }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
10+
await page.route('**/foo', route => {
11+
return route.fulfill({
12+
status: 200,
13+
body: JSON.stringify({
14+
userNames: ['John', 'Jane'],
15+
}),
16+
headers: {
17+
'Content-Type': 'application/json',
18+
},
19+
});
20+
});
21+
22+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
23+
24+
expect(eventData.exception?.values).toHaveLength(1);
25+
26+
expect(eventData?.breadcrumbs?.length).toBe(1);
27+
expect(eventData!.breadcrumbs![0]).toEqual({
28+
timestamp: expect.any(Number),
29+
category: 'fetch',
30+
type: 'http',
31+
data: {
32+
method: 'GET',
33+
status_code: 200,
34+
url: 'http://localhost:7654/foo',
35+
},
36+
});
37+
});

dev-packages/browser-integration-tests/suites/integrations/Breadcrumbs/fetch/post/subject.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const xhr = new XMLHttpRequest();
2-
31
fetch('http://localhost:7654/foo', {
42
method: 'POST',
53
body: '{"my":"body"}',
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
defaultIntegrations: false,
8+
integrations: [Sentry.breadcrumbsIntegration()],
9+
sampleRate: 1,
10+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
history.pushState({}, '', '/foo');
2+
history.pushState({}, '', '/bar?a=1#fragment');
3+
history.pushState({}, '', {});
4+
history.pushState({}, '', null);
5+
history.replaceState({}, '', '/bar?a=1#fragment');
6+
7+
Sentry.captureException('test exception');
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/browser';
3+
4+
import { sentryTest } from '../../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers';
6+
7+
sentryTest('should record history changes as navigation breadcrumbs', async ({ getLocalTestUrl, page }) => {
8+
const url = await getLocalTestUrl({ testDir: __dirname });
9+
10+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
11+
12+
expect(eventData.breadcrumbs).toEqual([
13+
{
14+
category: 'navigation',
15+
data: {
16+
from: '/index.html',
17+
to: '/foo',
18+
},
19+
timestamp: expect.any(Number),
20+
},
21+
{
22+
category: 'navigation',
23+
data: {
24+
from: '/foo',
25+
to: '/bar?a=1#fragment',
26+
},
27+
timestamp: expect.any(Number),
28+
},
29+
{
30+
category: 'navigation',
31+
data: {
32+
from: '/bar?a=1#fragment',
33+
to: '[object Object]',
34+
},
35+
timestamp: expect.any(Number),
36+
},
37+
{
38+
category: 'navigation',
39+
data: {
40+
from: '[object Object]',
41+
to: '/bar?a=1#fragment',
42+
},
43+
timestamp: expect.any(Number),
44+
},
45+
]);
46+
});

0 commit comments

Comments
 (0)