diff --git a/packages/integration-tests/suites/sessions/init.js b/packages/integration-tests/suites/sessions/init.js new file mode 100644 index 000000000000..af2df91a7ceb --- /dev/null +++ b/packages/integration-tests/suites/sessions/init.js @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '0.1', +}); diff --git a/packages/integration-tests/suites/sessions/start-session/template.hbs b/packages/integration-tests/suites/sessions/start-session/template.hbs new file mode 100644 index 000000000000..49ff0beab214 --- /dev/null +++ b/packages/integration-tests/suites/sessions/start-session/template.hbs @@ -0,0 +1,11 @@ + + + + + + + + Navigate + + + diff --git a/packages/integration-tests/suites/sessions/start-session/test.ts b/packages/integration-tests/suites/sessions/start-session/test.ts new file mode 100644 index 000000000000..e4469bd369c1 --- /dev/null +++ b/packages/integration-tests/suites/sessions/start-session/test.ts @@ -0,0 +1,38 @@ +import { expect, Route } from '@playwright/test'; +import { SessionContext } from '@sentry/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; + +sentryTest('should start a new session on pageload.', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + const session = await getFirstSentryEnvelopeRequest(page, url); + + expect(session).toBeDefined(); + expect(session.init).toBe(true); + expect(session.errors).toBe(0); + expect(session.status).toBe('ok'); +}); + +sentryTest('should start a new session with navigation.', async ({ getLocalTestPath, page, browserName }) => { + // Navigations get CORS error on Firefox and WebKit as we're using `file://` protocol. + if (browserName !== 'chromium') { + sentryTest.skip(); + } + + const url = await getLocalTestPath({ testDir: __dirname }); + await page.route('**/foo', (route: Route) => route.fulfill({ path: `${__dirname}/dist/index.html` })); + + const initSession = await getFirstSentryEnvelopeRequest(page, url); + + await page.click('#navigate'); + + const newSession = await getFirstSentryEnvelopeRequest(page, url); + + expect(newSession).toBeDefined(); + expect(newSession.init).toBe(true); + expect(newSession.errors).toBe(0); + expect(newSession.status).toBe('ok'); + expect(newSession.sid).toBeDefined(); + expect(initSession.sid).not.toBe(newSession.sid); +}); diff --git a/packages/integration-tests/suites/sessions/update-session/subject.js b/packages/integration-tests/suites/sessions/update-session/subject.js new file mode 100644 index 000000000000..04575f47e1bd --- /dev/null +++ b/packages/integration-tests/suites/sessions/update-session/subject.js @@ -0,0 +1,7 @@ +document.getElementById('throw-error').addEventListener('click', () => { + throw new Error('test'); +}); + +document.getElementById('capture-exception').addEventListener('click', () => { + Sentry.captureException('test'); +}); diff --git a/packages/integration-tests/suites/sessions/update-session/template.hbs b/packages/integration-tests/suites/sessions/update-session/template.hbs new file mode 100644 index 000000000000..114ce1f75f83 --- /dev/null +++ b/packages/integration-tests/suites/sessions/update-session/template.hbs @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/integration-tests/suites/sessions/update-session/test.ts b/packages/integration-tests/suites/sessions/update-session/test.ts new file mode 100644 index 000000000000..9634e66c360e --- /dev/null +++ b/packages/integration-tests/suites/sessions/update-session/test.ts @@ -0,0 +1,40 @@ +import { expect } from '@playwright/test'; +import { SessionContext } from '@sentry/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; + +sentryTest('should update session when an error is thrown.', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + const pageloadSession = await getFirstSentryEnvelopeRequest(page, url); + const updatedSession = ( + await Promise.all([page.click('#throw-error'), getFirstSentryEnvelopeRequest(page)]) + )[1]; + + expect(pageloadSession).toBeDefined(); + expect(pageloadSession.init).toBe(true); + expect(pageloadSession.errors).toBe(0); + expect(updatedSession).toBeDefined(); + expect(updatedSession.init).toBe(false); + expect(updatedSession.errors).toBe(1); + expect(updatedSession.status).toBe('ok'); + expect(pageloadSession.sid).toBe(updatedSession.sid); +}); + +sentryTest('should update session when an exception is captured.', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const pageloadSession = await getFirstSentryEnvelopeRequest(page, url); + const updatedSession = ( + await Promise.all([page.click('#capture-exception'), getFirstSentryEnvelopeRequest(page)]) + )[1]; + + expect(pageloadSession).toBeDefined(); + expect(pageloadSession.init).toBe(true); + expect(pageloadSession.errors).toBe(0); + expect(updatedSession).toBeDefined(); + expect(updatedSession.init).toBe(false); + expect(updatedSession.errors).toBe(1); + expect(updatedSession.status).toBe('ok'); + expect(pageloadSession.sid).toBe(updatedSession.sid); +}); diff --git a/packages/integration-tests/utils/helpers.ts b/packages/integration-tests/utils/helpers.ts index 90365b11228d..84ee3f8b3ddc 100644 --- a/packages/integration-tests/utils/helpers.ts +++ b/packages/integration-tests/utils/helpers.ts @@ -43,7 +43,8 @@ async function getSentryRequest(page: Page, url?: string): Promise { * @return {*} {Promise} */ async function getSentryTransactionRequest(page: Page, url?: string): Promise { - return (await getMultipleSentryTransactionRequests(page, 1, url))[0]; + // TODO: Remove this and update all usages in favour of `getFirstSentryEnvelopeRequest` and `getMultipleSentryEnvelopeRequests` + return (await getMultipleSentryEnvelopeRequests(page, 1, url))[0]; } /** @@ -135,15 +136,30 @@ async function getMultipleSentryRequests(page: Page, count: number, url?: string } /** - * Wait and get multiple transaction requests at the given URL, or the current page + * Wait and get multiple envelope requests at the given URL, or the current page * + * @template T * @param {Page} page * @param {number} count * @param {string} [url] - * @return {*} {Promise} + * @return {*} {Promise} + */ +async function getMultipleSentryEnvelopeRequests(page: Page, count: number, url?: string): Promise { + // TODO: This is not currently checking the type of envelope, just casting for now. + // We can update this to include optional type-guarding when we have types for Envelope. + return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, url) as Promise; +} + +/** + * Wait and get the first envelope request at the given URL, or the current page + * + * @template T + * @param {Page} page + * @param {string} [url] + * @return {*} {Promise} */ -async function getMultipleSentryTransactionRequests(page: Page, count: number, url?: string): Promise { - return getMultipleRequests(page, count, envelopeUrlRegex, envelopeRequestParser, url); +async function getFirstSentryEnvelopeRequest(page: Page, url?: string): Promise { + return (await getMultipleSentryEnvelopeRequests(page, 1, url))[0]; } /** @@ -166,7 +182,8 @@ async function injectScriptAndGetEvents(page: Page, url: string, scriptPath: str export { runScriptInSandbox, getMultipleSentryRequests, - getMultipleSentryTransactionRequests, + getMultipleSentryEnvelopeRequests, + getFirstSentryEnvelopeRequest, getSentryRequest, getSentryTransactionRequest, getSentryEvents,