Skip to content

Commit b77ec37

Browse files
authored
test(browser): Add integration tests for sessions. (#4500)
1 parent e00b419 commit b77ec37

File tree

7 files changed

+139
-6
lines changed

7 files changed

+139
-6
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
release: '0.1',
8+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<html>
2+
<head>
3+
<meta charset='utf-8' />
4+
<title></title>
5+
<script src='{{htmlWebpackPlugin.options.initialization}}'></script>
6+
</head>
7+
<body>
8+
<a id='navigate' href="foo">Navigate</button>
9+
<script src='{{htmlWebpackPlugin.options.subject}}'></script>
10+
</body>
11+
</html>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { expect, Route } from '@playwright/test';
2+
import { SessionContext } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
6+
7+
sentryTest('should start a new session on pageload.', async ({ getLocalTestPath, page }) => {
8+
const url = await getLocalTestPath({ testDir: __dirname });
9+
const session = await getFirstSentryEnvelopeRequest<SessionContext>(page, url);
10+
11+
expect(session).toBeDefined();
12+
expect(session.init).toBe(true);
13+
expect(session.errors).toBe(0);
14+
expect(session.status).toBe('ok');
15+
});
16+
17+
sentryTest('should start a new session with navigation.', async ({ getLocalTestPath, page, browserName }) => {
18+
// Navigations get CORS error on Firefox and WebKit as we're using `file://` protocol.
19+
if (browserName !== 'chromium') {
20+
sentryTest.skip();
21+
}
22+
23+
const url = await getLocalTestPath({ testDir: __dirname });
24+
await page.route('**/foo', (route: Route) => route.fulfill({ path: `${__dirname}/dist/index.html` }));
25+
26+
const initSession = await getFirstSentryEnvelopeRequest<SessionContext>(page, url);
27+
28+
await page.click('#navigate');
29+
30+
const newSession = await getFirstSentryEnvelopeRequest<SessionContext>(page, url);
31+
32+
expect(newSession).toBeDefined();
33+
expect(newSession.init).toBe(true);
34+
expect(newSession.errors).toBe(0);
35+
expect(newSession.status).toBe('ok');
36+
expect(newSession.sid).toBeDefined();
37+
expect(initSession.sid).not.toBe(newSession.sid);
38+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
document.getElementById('throw-error').addEventListener('click', () => {
2+
throw new Error('test');
3+
});
4+
5+
document.getElementById('capture-exception').addEventListener('click', () => {
6+
Sentry.captureException('test');
7+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<html>
2+
<head>
3+
<meta charset='utf-8' />
4+
<title></title>
5+
<script src='{{htmlWebpackPlugin.options.initialization}}'></script>
6+
</head>
7+
<body>
8+
<button id='throw-error'>Throw Error</button>
9+
<button id='capture-exception'>Capture Exception</button>
10+
<script src='{{htmlWebpackPlugin.options.subject}}'></script>
11+
</body>
12+
</html>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { expect } from '@playwright/test';
2+
import { SessionContext } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers';
6+
7+
sentryTest('should update session when an error is thrown.', async ({ getLocalTestPath, page }) => {
8+
const url = await getLocalTestPath({ testDir: __dirname });
9+
const pageloadSession = await getFirstSentryEnvelopeRequest<SessionContext>(page, url);
10+
const updatedSession = (
11+
await Promise.all([page.click('#throw-error'), getFirstSentryEnvelopeRequest<SessionContext>(page)])
12+
)[1];
13+
14+
expect(pageloadSession).toBeDefined();
15+
expect(pageloadSession.init).toBe(true);
16+
expect(pageloadSession.errors).toBe(0);
17+
expect(updatedSession).toBeDefined();
18+
expect(updatedSession.init).toBe(false);
19+
expect(updatedSession.errors).toBe(1);
20+
expect(updatedSession.status).toBe('ok');
21+
expect(pageloadSession.sid).toBe(updatedSession.sid);
22+
});
23+
24+
sentryTest('should update session when an exception is captured.', async ({ getLocalTestPath, page }) => {
25+
const url = await getLocalTestPath({ testDir: __dirname });
26+
27+
const pageloadSession = await getFirstSentryEnvelopeRequest<SessionContext>(page, url);
28+
const updatedSession = (
29+
await Promise.all([page.click('#capture-exception'), getFirstSentryEnvelopeRequest<SessionContext>(page)])
30+
)[1];
31+
32+
expect(pageloadSession).toBeDefined();
33+
expect(pageloadSession.init).toBe(true);
34+
expect(pageloadSession.errors).toBe(0);
35+
expect(updatedSession).toBeDefined();
36+
expect(updatedSession.init).toBe(false);
37+
expect(updatedSession.errors).toBe(1);
38+
expect(updatedSession.status).toBe('ok');
39+
expect(pageloadSession.sid).toBe(updatedSession.sid);
40+
});

packages/integration-tests/utils/helpers.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ async function getSentryRequest(page: Page, url?: string): Promise<Event> {
4343
* @return {*} {Promise<Event>}
4444
*/
4545
async function getSentryTransactionRequest(page: Page, url?: string): Promise<Event> {
46-
return (await getMultipleSentryTransactionRequests(page, 1, url))[0];
46+
// TODO: Remove this and update all usages in favour of `getFirstSentryEnvelopeRequest` and `getMultipleSentryEnvelopeRequests`
47+
return (await getMultipleSentryEnvelopeRequests<Event>(page, 1, url))[0];
4748
}
4849

4950
/**
@@ -135,15 +136,30 @@ async function getMultipleSentryRequests(page: Page, count: number, url?: string
135136
}
136137

137138
/**
138-
* Wait and get multiple transaction requests at the given URL, or the current page
139+
* Wait and get multiple envelope requests at the given URL, or the current page
139140
*
141+
* @template T
140142
* @param {Page} page
141143
* @param {number} count
142144
* @param {string} [url]
143-
* @return {*} {Promise<Event>}
145+
* @return {*} {Promise<T[]>}
146+
*/
147+
async function getMultipleSentryEnvelopeRequests<T>(page: Page, count: number, url?: string): Promise<T[]> {
148+
// TODO: This is not currently checking the type of envelope, just casting for now.
149+
// We can update this to include optional type-guarding when we have types for Envelope.
150+
return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, url) as Promise<T[]>;
151+
}
152+
153+
/**
154+
* Wait and get the first envelope request at the given URL, or the current page
155+
*
156+
* @template T
157+
* @param {Page} page
158+
* @param {string} [url]
159+
* @return {*} {Promise<T>}
144160
*/
145-
async function getMultipleSentryTransactionRequests(page: Page, count: number, url?: string): Promise<Event[]> {
146-
return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, url);
161+
async function getFirstSentryEnvelopeRequest<T>(page: Page, url?: string): Promise<T> {
162+
return (await getMultipleSentryEnvelopeRequests<T>(page, 1, url))[0];
147163
}
148164

149165
/**
@@ -166,7 +182,8 @@ async function injectScriptAndGetEvents(page: Page, url: string, scriptPath: str
166182
export {
167183
runScriptInSandbox,
168184
getMultipleSentryRequests,
169-
getMultipleSentryTransactionRequests,
185+
getMultipleSentryEnvelopeRequests,
186+
getFirstSentryEnvelopeRequest,
170187
getSentryRequest,
171188
getSentryTransactionRequest,
172189
getSentryEvents,

0 commit comments

Comments
 (0)