From e307608acb7e758a1b1836f1b7280a1a6034d393 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 4 Apr 2024 10:22:50 -0400 Subject: [PATCH] test: Port captureException and captureMessage tests from karma runner (#11412) 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 1: `packages/browser/test/integration/suites/api.js` We have decent coverage around singular calls to these methods, so I just added the tests that validate calling `captureException` and `captureMessage` multiple times. I also fixed a spelling mistake with `dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrors/subject.js`, `linkedErrrors` -> `linkedErrors` ref https://github.com/getsentry/sentry-javascript/issues/11084 --- .../subject.js | 0 .../{linkedErrrors => linkedErrors}/test.ts | 0 .../subject.js | 41 +++++ .../multipleErrorsDifferentStacktrace/test.ts | 19 ++ .../multipleErrorsSameStacktrace/subject.js | 55 ++++++ .../multipleErrorsSameStacktrace/test.ts | 21 +++ .../multipleMessageAttachStacktrace/init.js | 8 + .../subject.js | 27 +++ .../multipleMessageAttachStacktrace/test.ts | 20 +++ .../browser/test/integration/suites/api.js | 162 ------------------ .../browser/test/integration/suites/shell.js | 1 - 11 files changed, 191 insertions(+), 163 deletions(-) rename dev-packages/browser-integration-tests/suites/public-api/captureException/{linkedErrrors => linkedErrors}/subject.js (100%) rename dev-packages/browser-integration-tests/suites/public-api/captureException/{linkedErrrors => linkedErrors}/test.ts (100%) create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/init.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/test.ts delete mode 100644 packages/browser/test/integration/suites/api.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrrors/subject.js b/dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrors/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrrors/subject.js rename to dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrors/subject.js diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrrors/test.ts b/dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrors/test.ts similarity index 100% rename from dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrrors/test.ts rename to dev-packages/browser-integration-tests/suites/public-api/captureException/linkedErrors/test.ts diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/subject.js b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/subject.js new file mode 100644 index 000000000000..d24f30aa2824 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/subject.js @@ -0,0 +1,41 @@ +function bar() { + baz(); +} + +function foo() { + bar(); +} + +function foo2() { + // identical to foo, but meant for testing + // different stack frame fns w/ same stack length + bar(); +} +// same error message, but different stacks means that these are considered +// different errors + +// stack: +// bar +try { + bar(); +} catch (e) { + Sentry.captureException(e); +} + +// stack (different # frames): +// bar +// foo +try { + foo(); +} catch (e) { + Sentry.captureException(e); +} + +// stack (same # frames, different frames): +// bar +// foo2 +try { + foo2(); +} catch (e) { + Sentry.captureException(e); +} diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/test.ts b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/test.ts new file mode 100644 index 000000000000..277c518d58f0 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsDifferentStacktrace/test.ts @@ -0,0 +1,19 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; + +sentryTest('should not reject back-to-back errors with different stack traces', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getMultipleSentryEnvelopeRequests(page, 3, { url }); + + // NOTE: regex because exact error message differs per-browser + expect(eventData[0].exception?.values?.[0].value).toMatch(/baz/); + expect(eventData[0].exception?.values?.[0].type).toMatch('ReferenceError'); + expect(eventData[1].exception?.values?.[0].value).toMatch(/baz/); + expect(eventData[1].exception?.values?.[0].type).toMatch('ReferenceError'); + expect(eventData[2].exception?.values?.[0].value).toMatch(/baz/); + expect(eventData[2].exception?.values?.[0].type).toMatch('ReferenceError'); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/subject.js b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/subject.js new file mode 100644 index 000000000000..5feab6646ccc --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/subject.js @@ -0,0 +1,55 @@ +function throwError(message) { + // eslint-disable-next-line no-param-reassign + message = message || 'foo'; + try { + throw new Error(message); + } catch (o_O) { + Sentry.captureException(o_O); + } +} + +function throwRandomError() { + try { + throw new Error('Exception no ' + (Date.now() + Math.random())); + } catch (o_O) { + Sentry.captureException(o_O); + } +} + +function createError() { + function nestedFunction() { + return new Error('created'); + } + + return nestedFunction(); +} + +function throwSameConsecutiveErrors(message) { + throwError(message); + throwError(message); +} + +// Different exceptions, don't dedupe +for (var i = 0; i < 2; i++) { + throwRandomError(); +} + +// Same exceptions and same stacktrace, dedupe +for (var j = 0; j < 2; j++) { + throwError(); +} + +const syntheticError = createError(); + +// Same exception, with transaction in between, dedupe +Sentry.captureException(syntheticError); +Sentry.captureEvent({ + event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', + message: 'someMessage', + transaction: 'wat', + type: 'transaction', +}); +Sentry.captureException(syntheticError); + +// Same exceptions, different stacktrace (different line number), don't dedupe +throwSameConsecutiveErrors('bar'); diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/test.ts b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/test.ts new file mode 100644 index 000000000000..acd7e12ed351 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureException/multipleErrorsSameStacktrace/test.ts @@ -0,0 +1,21 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; + +sentryTest('should reject duplicate, back-to-back errors from captureException', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getMultipleSentryEnvelopeRequests(page, 7, { url }); + + // NOTE: regex because exact error message differs per-browser + expect(eventData[0].exception?.values?.[0].value).toMatch(/Exception no \d+/); + expect(eventData[1].exception?.values?.[0].value).toMatch(/Exception no \d+/); + expect(eventData[2].exception?.values?.[0].value).toEqual('foo'); + // transaction so undefined + expect(eventData[3].exception?.values?.[0].value).toEqual('created'); + expect(eventData[4].exception?.values?.[0].value).toEqual(undefined); + expect(eventData[5].exception?.values?.[0].value).toEqual('bar'); + expect(eventData[6].exception?.values?.[0].value).toEqual('bar'); +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/init.js b/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/init.js new file mode 100644 index 000000000000..99ffd9f0ab31 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/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', + attachStacktrace: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/subject.js b/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/subject.js new file mode 100644 index 000000000000..f95ddde16833 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/subject.js @@ -0,0 +1,27 @@ +function captureMessage(message) { + // eslint-disable-next-line no-param-reassign + message = message || 'message'; + Sentry.captureMessage(message); +} + +function captureRandomMessage() { + Sentry.captureMessage('Message no ' + (Date.now() + Math.random())); +} + +function captureSameConsecutiveMessages(message) { + captureMessage(message); + captureMessage(message); +} + +// Different messages, don't dedupe +for (var i = 0; i < 2; i++) { + captureRandomMessage(); +} + +// Same messages and same stacktrace, dedupe +for (var j = 0; j < 3; j++) { + captureMessage('same message, same stacktrace'); +} + +// Same messages, different stacktrace (different line number), don't dedupe +captureSameConsecutiveMessages('same message, different stacktrace'); diff --git a/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/test.ts b/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/test.ts new file mode 100644 index 000000000000..01845d983feb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/public-api/captureMessage/multipleMessageAttachStacktrace/test.ts @@ -0,0 +1,20 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests } from '../../../../utils/helpers'; + +sentryTest( + 'should reject duplicate, back-to-back messages from captureMessage when it has stacktrace', + async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const eventData = await getMultipleSentryEnvelopeRequests(page, 5, { url }); + + expect(eventData[0].message).toMatch(/Message no \d+/); + expect(eventData[1].message).toMatch(/Message no \d+/); + expect(eventData[2].message).toMatch('same message, same stacktrace'); + expect(eventData[3].message).toMatch('same message, different stacktrace'); + expect(eventData[4].message).toMatch('same message, different stacktrace'); + }, +); diff --git a/packages/browser/test/integration/suites/api.js b/packages/browser/test/integration/suites/api.js deleted file mode 100644 index 462659e75ac7..000000000000 --- a/packages/browser/test/integration/suites/api.js +++ /dev/null @@ -1,162 +0,0 @@ -describe('API', function () { - it('should capture Sentry.captureMessage', function () { - return runInSandbox(sandbox, function () { - Sentry.captureMessage('Hello'); - }).then(function (summary) { - assert.equal(summary.events[0].message, 'Hello'); - }); - }); - - it('should capture Sentry.captureException', function () { - return runInSandbox(sandbox, function () { - try { - foo(); - } catch (e) { - Sentry.captureException(e); - } - }).then(function (summary) { - assert.isAtLeast(summary.events[0].exception.values[0].stacktrace.frames.length, 2); - assert.isAtMost(summary.events[0].exception.values[0].stacktrace.frames.length, 4); - }); - }); - - it('should generate a synthetic trace for captureException w/ non-errors', function () { - return runInSandbox(sandbox, function () { - throwNonError(); - }).then(function (summary) { - assert.isAtLeast(summary.events[0].exception.values[0].stacktrace.frames.length, 1); - assert.isAtMost(summary.events[0].exception.values[0].stacktrace.frames.length, 3); - }); - }); - - it('should have correct stacktrace order', function () { - return runInSandbox(sandbox, function () { - try { - foo(); - } catch (e) { - Sentry.captureException(e); - } - }).then(function (summary) { - assert.equal( - summary.events[0].exception.values[0].stacktrace.frames[ - summary.events[0].exception.values[0].stacktrace.frames.length - 1 - ].function, - 'bar', - ); - assert.isAtLeast(summary.events[0].exception.values[0].stacktrace.frames.length, 2); - assert.isAtMost(summary.events[0].exception.values[0].stacktrace.frames.length, 4); - }); - }); - - it('should have exception with type and value', function () { - return runInSandbox(sandbox, function () { - Sentry.captureException('this is my test exception'); - }).then(function (summary) { - assert.isNotEmpty(summary.events[0].exception.values[0].value); - assert.isNotEmpty(summary.events[0].exception.values[0].type); - }); - }); - - it('should reject duplicate, back-to-back errors from captureException', function () { - return runInSandbox(sandbox, function () { - // Different exceptions, don't dedupe - for (var i = 0; i < 2; i++) { - throwRandomError(); - } - - // Same exceptions and same stacktrace, dedupe - for (var j = 0; j < 2; j++) { - throwError(); - } - - // Same exceptions, different stacktrace (different line number), don't dedupe - throwSameConsecutiveErrors('bar'); - - // Same exception, with transaction in between, dedupe - throwError(); - Sentry.captureEvent({ - event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', - message: 'someMessage', - transaction: 'wat', - type: 'transaction', - }); - throwError(); - }).then(function (summary) { - // We have a length of one here since transactions don't go through beforeSend - // and we add events to summary in beforeSend - assert.equal(summary.events.length, 6); - assert.match(summary.events[0].exception.values[0].value, /Exception no \d+/); - assert.match(summary.events[1].exception.values[0].value, /Exception no \d+/); - assert.equal(summary.events[2].exception.values[0].value, 'foo'); - assert.equal(summary.events[3].exception.values[0].value, 'bar'); - assert.equal(summary.events[4].exception.values[0].value, 'bar'); - assert.equal(summary.events[5].exception.values[0].value, 'foo'); - }); - }); - - it('should not reject back-to-back errors with different stack traces', function () { - return runInSandbox(sandbox, function () { - // same error message, but different stacks means that these are considered - // different errors - - // stack: - // bar - try { - bar(); // declared in frame.html - } catch (e) { - Sentry.captureException(e); - } - - // stack (different # frames): - // bar - // foo - try { - foo(); // declared in frame.html - } catch (e) { - Sentry.captureException(e); - } - - // stack (same # frames, different frames): - // bar - // foo2 - try { - foo2(); // declared in frame.html - } catch (e) { - Sentry.captureException(e); - } - }).then(function (summary) { - // NOTE: regex because exact error message differs per-browser - assert.match(summary.events[0].exception.values[0].value, /baz/); - assert.equal(summary.events[0].exception.values[0].type, 'ReferenceError'); - assert.match(summary.events[1].exception.values[0].value, /baz/); - assert.equal(summary.events[1].exception.values[0].type, 'ReferenceError'); - assert.match(summary.events[2].exception.values[0].value, /baz/); - assert.equal(summary.events[2].exception.values[0].type, 'ReferenceError'); - }); - }); - - it('should reject duplicate, back-to-back messages from captureMessage', function () { - return runInSandbox(sandbox, function () { - // Different messages, don't dedupe - for (var i = 0; i < 2; i++) { - captureRandomMessage(); - } - - // Same messages and same stacktrace, dedupe - for (var j = 0; j < 2; j++) { - captureMessage('same message, same stacktrace'); - } - - // Same messages, different stacktrace (different line number), don't dedupe - captureSameConsecutiveMessages('same message, different stacktrace'); - }).then(function (summary) { - // On the async loader since we replay all messages from the same location, - // so we actually only receive 4 summary.events - assert.match(summary.events[0].message, /Message no \d+/); - assert.match(summary.events[1].message, /Message no \d+/); - assert.equal(summary.events[2].message, 'same message, same stacktrace'); - assert.equal(summary.events[3].message, 'same message, different stacktrace'); - !IS_LOADER && assert.equal(summary.events[4].message, 'same message, different stacktrace'); - }); - }); -}); diff --git a/packages/browser/test/integration/suites/shell.js b/packages/browser/test/integration/suites/shell.js index d3d89ed4b798..2a3ef36b5df5 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/config.js }} // biome-ignore format: No trailing commas - {{ suites/api.js }} // biome-ignore format: No trailing commas {{ suites/onerror.js }} // biome-ignore format: No trailing commas {{ suites/onunhandledrejection.js }} // biome-ignore format: No trailing commas {{ suites/builtins.js }} // biome-ignore format: No trailing commas