diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/init.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/init.js new file mode 100644 index 000000000000..d8c94f36fdd0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/init.js @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/instrumentation-behaviour/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/subject.js rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/instrumentation-behaviour/subject.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/instrumentation-behaviour/test.ts similarity index 93% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/instrumentation-behaviour/test.ts index b6d2e6fa9231..557211e5b668 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-instrumentation-behaviour/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/instrumentation-behaviour/test.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; +import { sentryTest } from '../../../../../utils/fixtures'; sentryTest( 'Event listener instrumentation should attach the same event listener only once', diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/named-function/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/named-function/subject.js new file mode 100644 index 000000000000..2e0caf7fb352 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/named-function/subject.js @@ -0,0 +1,7 @@ +function clickHandler() { + throw new Error('event_listener_error'); +} + +window.addEventListener('click', clickHandler); + +document.body.click(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/named-function/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/named-function/test.ts new file mode 100644 index 000000000000..a61e582254ee --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/named-function/test.ts @@ -0,0 +1,29 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('should capture built-in handlers fn name in mechanism data', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'Error', + value: 'event_listener_error', + mechanism: { + type: 'instrument', + handled: false, + data: { + function: 'addEventListener', + handler: 'clickHandler', + target: 'EventTarget', + }, + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/init.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/init.js new file mode 100644 index 000000000000..95845a563c1e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/init.js @@ -0,0 +1,12 @@ +// store references to original, unwrapped built-ins in order to make assertions re: wrapped functions +window.originalBuiltIns = { + addEventListener: document.addEventListener, +}; + +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/subject.js new file mode 100644 index 000000000000..221c7a906eaf --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/subject.js @@ -0,0 +1,12 @@ +const div = document.createElement('div'); +document.body.appendChild(div); +window.capturedCall = false; +const captureFn = function () { + window.capturedCall = true; +}; +// Use original addEventListener to simulate non-wrapped behavior (callback is attached without __sentry_wrapped__) +window.originalBuiltIns.addEventListener.call(div, 'click', captureFn); +// Then attach the same callback again, but with already wrapped method +div.addEventListener('click', captureFn); +div.removeEventListener('click', captureFn); +div.dispatchEvent(new MouseEvent('click')); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/test.ts new file mode 100644 index 000000000000..82866a35105a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/original-callback/test.ts @@ -0,0 +1,19 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; + +sentryTest( + 'should remove the original callback if it was registered before Sentry initialized (w. original method)', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + + const capturedCalled = await page.evaluate(() => { + // @ts-expect-error defined in subject.js + return window.capturedCall; + }); + + expect(capturedCalled).toBe(false); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/remove/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/remove/subject.js new file mode 100644 index 000000000000..f7671e35fe64 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/remove/subject.js @@ -0,0 +1,12 @@ +const div = document.createElement('div'); +document.body.appendChild(div); +const fooFn = function () { + throw new Error('foo'); +}; +const barFn = function () { + throw new Error('bar'); +}; +div.addEventListener('click', fooFn); +div.addEventListener('click', barFn); +div.removeEventListener('click', barFn); +div.dispatchEvent(new MouseEvent('click')); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/remove/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/remove/test.ts new file mode 100644 index 000000000000..8451472568a8 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/remove/test.ts @@ -0,0 +1,24 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('should transparently remove event listeners from wrapped functions', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'Error', + value: 'foo', + mechanism: { + type: 'instrument', + handled: false, + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/this-preservation/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/subject.js rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/this-preservation/subject.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/this-preservation/test.ts similarity index 92% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/this-preservation/test.ts index e6304858eed4..462db45c6052 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-this-preservation/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/this-preservation/test.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; +import { sentryTest } from '../../../../../utils/fixtures'; sentryTest('Event listener instrumentation preserves "this" context', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/thrown-error/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/subject.js rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/thrown-error/subject.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/thrown-error/test.ts similarity index 72% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/test.ts rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/thrown-error/test.ts index d7b9f75a13f2..1c0bbb66fa12 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/thrown-error/test.ts @@ -1,8 +1,8 @@ import { expect } from '@playwright/test'; import type { Event } from '@sentry/types'; -import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; sentryTest( 'Event listener instrumentation should capture an error thrown in an event handler', @@ -18,6 +18,11 @@ sentryTest( mechanism: { type: 'instrument', handled: false, + data: { + function: 'addEventListener', + handler: '', + target: 'EventTarget', + }, }, stacktrace: { frames: expect.any(Array), diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/wrapping/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/subject.js rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/wrapping/subject.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/wrapping/test.ts similarity index 95% rename from dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts rename to dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/wrapping/test.ts index cf77132c98df..def52f9ce465 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener-wrapping/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/eventListener/wrapping/test.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; +import { sentryTest } from '../../../../../utils/fixtures'; sentryTest( 'Event listener instrumentation should not wrap event listeners multiple times', diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/callback/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/callback/test.ts new file mode 100644 index 000000000000..0840789c759d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/callback/test.ts @@ -0,0 +1,45 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; + +sentryTest( + 'wrapped callback should preserve correct context - window (not-bound)', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + + const { outsideCtx, requestAnimationFrameCtx } = (await page.evaluate(() => { + return new Promise(resolve => { + const outsideCtx = window as any; + requestAnimationFrame(function () { + // @ts-expect-error re-assigning this + resolve({ outsideCtx, requestAnimationFrameCtx: this as any }); + }); + }); + })) as any; + expect(requestAnimationFrameCtx).toBe(outsideCtx); + }, +); + +sentryTest( + 'wrapped callback should preserve correct context - `bind` bound method', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + + const requestAnimationFrameCtx = (await page.evaluate(() => { + return new Promise(resolve => { + function foo() { + // @ts-expect-error re-assigning this + resolve(this); + } + + requestAnimationFrame(foo.bind({ magicNumber: 42 })); + }); + })) as any; + + expect(requestAnimationFrameCtx.magicNumber).toBe(42); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/init.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/init.js new file mode 100644 index 000000000000..d8c94f36fdd0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/init.js @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/thrown-errors/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/thrown-errors/subject.js new file mode 100644 index 000000000000..5068286be114 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/thrown-errors/subject.js @@ -0,0 +1,3 @@ +requestAnimationFrame(function () { + throw new Error('requestAnimationFrame_error'); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/thrown-errors/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/thrown-errors/test.ts new file mode 100644 index 000000000000..df437f34c0ef --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/requestAnimationFrame/thrown-errors/test.ts @@ -0,0 +1,24 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest('should capture exceptions inside callback', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'Error', + value: 'requestAnimationFrame_error', + mechanism: { + type: 'instrument', + handled: false, + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setInterval/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setInterval/subject.js new file mode 100644 index 000000000000..1c4488651ebb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setInterval/subject.js @@ -0,0 +1,4 @@ +let exceptionInterval = setInterval(function () { + clearInterval(exceptionInterval); + throw new Error('setInterval_error'); +}, 0); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setInterval/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setInterval/test.ts new file mode 100644 index 000000000000..6da1e21dea1e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setInterval/test.ts @@ -0,0 +1,24 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; + +sentryTest('Instrumentation should capture errors in setInterval', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'Error', + value: 'setInterval_error', + mechanism: { + type: 'instrument', + handled: false, + }, + stacktrace: { + frames: expect.any(Array), + }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setTimeout/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setTimeout/test.ts index 808e15285cb1..fce63e657b49 100644 --- a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/setTimeout/test.ts @@ -16,6 +16,9 @@ sentryTest('Instrumentation should capture errors in setTimeout', async ({ getLo mechanism: { type: 'instrument', handled: false, + data: { + function: 'setTimeout', + }, }, stacktrace: { frames: expect.any(Array), diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/init.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/init.js new file mode 100644 index 000000000000..d8c94f36fdd0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/init.js @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/onreadystatechange/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/onreadystatechange/subject.js new file mode 100644 index 000000000000..f88672f09214 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/onreadystatechange/subject.js @@ -0,0 +1,7 @@ +window.calls = {}; +const xhr = new XMLHttpRequest(); +xhr.open('GET', 'test'); +xhr.onreadystatechange = function wat() { + window.calls[xhr.readyState] = window.calls[xhr.readyState] ? window.calls[xhr.readyState] + 1 : 1; +}; +xhr.send(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/onreadystatechange/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/onreadystatechange/test.ts new file mode 100644 index 000000000000..faec510f8f47 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/onreadystatechange/test.ts @@ -0,0 +1,19 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../../../utils/fixtures'; + +sentryTest( + 'should not call XMLHttpRequest onreadystatechange more than once per state', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + + const calls = await page.evaluate(() => { + // @ts-expect-error window.calls defined in subject.js + return window.calls; + }); + + expect(calls).toEqual({ '4': 1 }); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/thrown-error/subject.js b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/thrown-error/subject.js new file mode 100644 index 000000000000..407ead95010e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/thrown-error/subject.js @@ -0,0 +1,10 @@ +const xhr = new XMLHttpRequest(); +xhr.open('GET', 'test'); +// intentionally assign event handlers *after* open, since this is what jQuery does +xhr.onreadystatechange = function wat() { + // replace onreadystatechange with no-op so exception doesn't + // fire more than once as XHR changes loading state + xhr.onreadystatechange = function () {}; + throw new Error('xhr_error'); +}; +xhr.send(); diff --git a/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/thrown-error/test.ts b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/thrown-error/test.ts new file mode 100644 index 000000000000..1b213b2fb6bc --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/instrumentation/xhr/thrown-error/test.ts @@ -0,0 +1,30 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../../../utils/helpers'; + +sentryTest( + 'should capture exceptions from XMLHttpRequest event handlers (e.g. onreadystatechange)', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + expect(eventData.exception?.values).toHaveLength(1); + expect(eventData.exception?.values?.[0]).toMatchObject({ + type: 'Error', + value: 'xhr_error', + mechanism: { + type: 'instrument', + handled: false, + data: { + function: 'onreadystatechange', + }, + }, + stacktrace: { + frames: expect.any(Array), + }, + }); + }, +); diff --git a/packages/browser/test/integration/suites/builtins.js b/packages/browser/test/integration/suites/builtins.js deleted file mode 100644 index 54f86cd4f636..000000000000 --- a/packages/browser/test/integration/suites/builtins.js +++ /dev/null @@ -1,326 +0,0 @@ -describe('wrapped built-ins', function () { - it('should capture exceptions from event listeners', function () { - return runInSandbox(sandbox, function () { - var div = document.createElement('div'); - document.body.appendChild(div); - div.addEventListener( - 'click', - function () { - window.element = div; - window.context = this; - foo(); - }, - false, - ); - var click = new MouseEvent('click'); - div.dispatchEvent(click); - }).then(function (summary) { - // Make sure we preserve the correct context - assert.equal(summary.window.element, summary.window.context); - delete summary.window.element; - delete summary.window.context; - assert.match(summary.events[0].exception.values[0].value, /baz/); - }); - }); - - it('should transparently remove event listeners from wrapped functions', function () { - return runInSandbox(sandbox, function () { - var div = document.createElement('div'); - document.body.appendChild(div); - var fooFn = function () { - foo(); - }; - var barFn = function () { - bar(); - }; - div.addEventListener('click', fooFn); - div.addEventListener('click', barFn); - div.removeEventListener('click', barFn); - div.dispatchEvent(new MouseEvent('click')); - }).then(function (summary) { - assert.lengthOf(summary.events, 1); - }); - }); - - it('should remove the original callback if it was registered before Sentry initialized (w. original method)', function () { - return runInSandbox(sandbox, function () { - var div = document.createElement('div'); - document.body.appendChild(div); - window.capturedCall = false; - var captureFn = function () { - window.capturedCall = true; - }; - // Use original addEventListener to simulate non-wrapped behavior (callback is attached without __sentry_wrapped__) - window.originalBuiltIns.addEventListener.call(div, 'click', captureFn); - // Then attach the same callback again, but with already wrapped method - div.addEventListener('click', captureFn); - div.removeEventListener('click', captureFn); - div.dispatchEvent(new MouseEvent('click')); - }).then(function (summary) { - assert.equal(summary.window.capturedCall, false); - delete summary.window.capturedCalls; - }); - }); - - it('should capture exceptions inside setTimeout', function () { - return runInSandbox(sandbox, function () { - setTimeout(function () { - foo(); - }); - }).then(function (summary) { - assert.match(summary.events[0].exception.values[0].value, /baz/); - }); - }); - - it('should capture exceptions inside setInterval', function () { - return runInSandbox(sandbox, function () { - var exceptionInterval = setInterval(function () { - clearInterval(exceptionInterval); - foo(); - }, 0); - }).then(function (summary) { - assert.match(summary.events[0].exception.values[0].value, /baz/); - }); - }); - - describe('requestAnimationFrame', function () { - it('should capture exceptions inside callback', function () { - // needs to be visible or requestAnimationFrame won't ever fire - sandbox.style.display = 'block'; - - return runInSandbox(sandbox, { manual: true }, function () { - requestAnimationFrame(function () { - window.finalizeManualTest(); - foo(); - }); - }).then(function (summary) { - assert.match(summary.events[0].exception.values[0].value, /baz/); - }); - }); - - it('wrapped callback should preserve correct context - window (not-bound)', function () { - // needs to be visible or requestAnimationFrame won't ever fire - sandbox.style.display = 'block'; - return runInSandbox(sandbox, { manual: true }, function () { - requestAnimationFrame(function () { - window.capturedCtx = this; - window.finalizeManualTest(); - }); - }).then(function (summary) { - assert.strictEqual(summary.window.capturedCtx, summary.window); - delete summary.window.capturedCtx; - }); - }); - - it('wrapped callback should preserve correct context - class bound method', function () { - // needs to be visible or requestAnimationFrame won't ever fire - sandbox.style.display = 'block'; - return runInSandbox(sandbox, { manual: true }, function () { - // TypeScript-transpiled class syntax - var Foo = (function () { - function Foo() { - var _this = this; - this.magicNumber = 42; - this.getThis = function () { - window.capturedCtx = _this; - window.finalizeManualTest(); - }; - } - return Foo; - })(); - var foo = new Foo(); - requestAnimationFrame(foo.getThis); - }).then(function (summary) { - assert.strictEqual(summary.window.capturedCtx.magicNumber, 42); - delete summary.window.capturedCtx; - }); - }); - - it('wrapped callback should preserve correct context - `bind` bound method', function () { - // needs to be visible or requestAnimationFrame won't ever fire - sandbox.style.display = 'block'; - return runInSandbox(sandbox, { manual: true }, function () { - function foo() { - window.capturedCtx = this; - window.finalizeManualTest(); - } - requestAnimationFrame(foo.bind({ magicNumber: 42 })); - }).then(function (summary) { - assert.strictEqual(summary.window.capturedCtx.magicNumber, 42); - delete summary.window.capturedCtx; - }); - }); - }); - - it('should capture exceptions from XMLHttpRequest event handlers (e.g. onreadystatechange)', function () { - return runInSandbox(sandbox, { manual: true }, function () { - var xhr = new XMLHttpRequest(); - xhr.open('GET', '/base/subjects/example.json'); - // intentionally assign event handlers *after* open, since this is what jQuery does - xhr.onreadystatechange = function wat() { - window.finalizeManualTest(); - // replace onreadystatechange with no-op so exception doesn't - // fire more than once as XHR changes loading state - xhr.onreadystatechange = function () {}; - foo(); - }; - xhr.send(); - }).then(function (summary) { - assert.match(summary.events[0].exception.values[0].value, /baz/); - - if (IS_LOADER) { - assert.ok(summary.events[0].exception.values[0].mechanism); - } else { - var handler = summary.events[0].exception.values[0].mechanism.data.handler; - delete summary.events[0].exception.values[0].mechanism.data.handler; - - if (summary.window.canReadFunctionName()) { - assert.equal(handler, 'wat'); - } else { - assert.equal(handler, ''); - } - - assert.deepEqual(summary.events[0].exception.values[0].mechanism, { - type: 'instrument', - handled: false, - data: { - function: 'onreadystatechange', - }, - }); - } - }); - }); - - it('should not call XMLHttpRequest onreadystatechange more than once per state', function () { - return runInSandbox(sandbox, { manual: true }, function () { - window.calls = {}; - var xhr = new XMLHttpRequest(); - xhr.open('GET', '/base/subjects/example.json'); - xhr.onreadystatechange = function wat() { - window.calls[xhr.readyState] = window.calls[xhr.readyState] ? window.calls[xhr.readyState] + 1 : 1; - if (xhr.readyState === 4) { - window.finalizeManualTest(); - } - }; - xhr.send(); - }).then(function (summary) { - for (var state in summary.window.calls) { - assert.equal(summary.window.calls[state], 1); - } - // IE Triggers all states (1-4), including 1 (open), despite it being assigned before - // the `onreadystatechange` handler. - assert.isAtLeast(Object.keys(summary.window.calls).length, 3); - assert.isAtMost(Object.keys(summary.window.calls).length, 4); - delete summary.window.calls; - }); - }); - - it(optional("should capture built-in's mechanism type as instrument", IS_LOADER), function () { - return runInSandbox(sandbox, function () { - setTimeout(function () { - foo(); - }); - }).then(function (summary) { - if (IS_LOADER) { - // The async loader doesn't wrap setTimeout - // so we don't receive the full mechanism - assert.ok(summary.events[0].exception.values[0].mechanism); - } else { - var fn = summary.events[0].exception.values[0].mechanism.data.function; - delete summary.events[0].exception.values[0].mechanism.data; - - if (summary.window.canReadFunctionName()) { - assert.equal(fn, 'setTimeout'); - } else { - assert.equal(fn, ''); - } - - assert.deepEqual(summary.events[0].exception.values[0].mechanism, { - type: 'instrument', - handled: false, - }); - } - }); - }); - - it(optional("should capture built-in's handlers fn name in mechanism data", IS_LOADER), function () { - return runInSandbox(sandbox, function () { - var div = document.createElement('div'); - document.body.appendChild(div); - div.addEventListener( - 'click', - function namedFunction() { - foo(); - }, - false, - ); - var click = new MouseEvent('click'); - div.dispatchEvent(click); - }).then(function (summary) { - if (IS_LOADER) { - // The async loader doesn't wrap addEventListener - // so we don't receive the full mechanism - assert.ok(summary.events[0].exception.values[0].mechanism); - } else { - var handler = summary.events[0].exception.values[0].mechanism.data.handler; - delete summary.events[0].exception.values[0].mechanism.data.handler; - var target = summary.events[0].exception.values[0].mechanism.data.target; - delete summary.events[0].exception.values[0].mechanism.data.target; - - if (summary.window.canReadFunctionName()) { - assert.equal(handler, 'namedFunction'); - } else { - assert.equal(handler, ''); - } - - // IE vs. Rest of the world - assert.oneOf(target, ['Node', 'EventTarget']); - assert.deepEqual(summary.events[0].exception.values[0].mechanism, { - type: 'instrument', - handled: false, - data: { - function: 'addEventListener', - }, - }); - } - }); - }); - - it( - optional('should fallback to fn name in mechanism data if one is unavailable', IS_LOADER), - function () { - return runInSandbox(sandbox, function () { - var div = document.createElement('div'); - document.body.appendChild(div); - div.addEventListener( - 'click', - function () { - foo(); - }, - false, - ); - var click = new MouseEvent('click'); - div.dispatchEvent(click); - }).then(function (summary) { - if (IS_LOADER) { - // The async loader doesn't wrap - assert.ok(summary.events[0].exception.values[0].mechanism); - } else { - var target = summary.events[0].exception.values[0].mechanism.data.target; - delete summary.events[0].exception.values[0].mechanism.data.target; - - // IE vs. Rest of the world - assert.oneOf(target, ['Node', 'EventTarget']); - assert.deepEqual(summary.events[0].exception.values[0].mechanism, { - type: 'instrument', - handled: false, - data: { - function: 'addEventListener', - handler: '', - }, - }); - } - }); - }, - ); -}); diff --git a/packages/browser/test/integration/suites/shell.js b/packages/browser/test/integration/suites/shell.js index c8dcdb490104..07ecf0248f73 100644 --- a/packages/browser/test/integration/suites/shell.js +++ b/packages/browser/test/integration/suites/shell.js @@ -23,7 +23,6 @@ function runVariant(variant) { * The test runner will replace each of these placeholders with the contents of the corresponding file. */ {{ suites/onunhandledrejection.js }} // biome-ignore format: No trailing commas - {{ suites/builtins.js }} // biome-ignore format: No trailing commas }); }