diff --git a/packages/angular/package.json b/packages/angular/package.json index f57026360765..f938f545862b 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -53,10 +53,32 @@ "fix": "run-s fix:eslint fix:prettier", "fix:prettier": "prettier --write \"{src,test}/**/*.ts\"", "fix:eslint": "eslint . --format stylish --fix", + "test": "jest", + "test:watch": "jest --watch", "pack": "npm pack" }, "volta": { "extends": "../../package.json" }, + "jest": { + "collectCoverage": true, + "transform": { + "^.+\\.ts$": "ts-jest" + }, + "moduleFileExtensions": [ + "js", + "ts" + ], + "testEnvironment": "jsdom", + "testMatch": [ + "**/*.test.ts" + ], + "globals": { + "ts-jest": { + "tsConfig": "./tsconfig.json", + "diagnostics": false + } + } + }, "sideEffects": false } diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index b746af148c1f..41c46421dfea 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -1,3 +1,6 @@ +import { SDK_VERSION } from '@sentry/browser'; +import { setSDKInfo } from '@sentry/utils'; + export * from '@sentry/browser'; export { createErrorHandler, ErrorHandlerOptions } from './errorhandler'; export { @@ -8,3 +11,5 @@ export { TraceDirective, TraceService, } from './tracing'; + +setSDKInfo('sentry.javascript.angular', 'npm:@sentry/angular', SDK_VERSION); diff --git a/packages/angular/test/init.test.ts b/packages/angular/test/init.test.ts new file mode 100644 index 000000000000..59d0218b267f --- /dev/null +++ b/packages/angular/test/init.test.ts @@ -0,0 +1,23 @@ +import { getGlobalObject } from '@sentry/utils'; + +import * as Sentry from '../src/index'; + +const global = getGlobalObject(); + +describe('global SDK metadata', () => { + it('sets correct SDK data', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts` - it sets the angular part itself, + // and the browser part gets set when it imports from @sentry/browser - so no action is necessary here before we run + // the `expect`s + + expect(global.__SENTRY__?.sdkInfo).toBeDefined(); + expect(global.__SENTRY__?.sdkInfo?.name).toEqual('sentry.javascript.angular'); + expect(global.__SENTRY__?.sdkInfo?.version).toEqual(Sentry.SDK_VERSION); + expect(global.__SENTRY__?.sdkInfo?.packages).toEqual( + expect.arrayContaining([ + { name: 'npm:@sentry/angular', version: Sentry.SDK_VERSION }, + { name: 'npm:@sentry/browser', version: Sentry.SDK_VERSION }, + ]), + ); + }); +}); diff --git a/packages/angular/tsconfig.json b/packages/angular/tsconfig.json index b38e47c761a5..7e01ba4e464b 100644 --- a/packages/angular/tsconfig.json +++ b/packages/angular/tsconfig.json @@ -1,8 +1,9 @@ { "extends": "./tsconfig.build.json", - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "test/**/*.ts"], "exclude": ["dist"], "compilerOptions": { - "rootDir": "." + "rootDir": ".", + "types": ["jest"] } } diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 0b94c020592f..8eb936bd6c4c 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,10 +1,13 @@ export * from './exports'; import { Integrations as CoreIntegrations } from '@sentry/core'; -import { getGlobalObject } from '@sentry/utils'; +import { getGlobalObject, setSDKInfo } from '@sentry/utils'; import * as BrowserIntegrations from './integrations'; import * as Transports from './transports'; +import { SDK_VERSION } from './version'; + +setSDKInfo('sentry.javascript.browser', 'npm:@sentry/browser', SDK_VERSION, false); let windowIntegrations = {}; diff --git a/packages/browser/test/unit/index.test.ts b/packages/browser/test/unit/index.test.ts index 24c3ebb6cc7d..d71bf8be6f99 100644 --- a/packages/browser/test/unit/index.test.ts +++ b/packages/browser/test/unit/index.test.ts @@ -14,6 +14,7 @@ import { init, Integrations, Scope, + SDK_VERSION, wrap, } from '../../src'; import { SimpleTransport } from './mocks/simpletransport'; @@ -171,6 +172,16 @@ describe('SentryBrowser', () => { }); describe('SentryBrowser initialization', () => { + it('should set SDK info globally', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts`, so no action is necessary here + // before we run the `expect`s + + expect(global.__SENTRY__.sdkInfo).to.exist; + expect(global.__SENTRY__.sdkInfo.name).to.equal('sentry.javascript.browser'); + expect(global.__SENTRY__.sdkInfo.version).to.equal(SDK_VERSION); + expect(global.__SENTRY__.sdkInfo.packages).to.deep.equal([{ name: 'npm:@sentry/browser', version: SDK_VERSION }]); + }); + it('should use window.SENTRY_RELEASE to set release on initialization if available', () => { global.SENTRY_RELEASE = { id: 'foobar' }; init({ dsn }); diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts index 14084d942c33..06d91a89e887 100644 --- a/packages/core/src/request.ts +++ b/packages/core/src/request.ts @@ -1,11 +1,14 @@ import { Event, SentryRequest, Session } from '@sentry/types'; +import { getGlobalObject } from '@sentry/utils'; import { API } from './api'; /** Creates a SentryRequest from an event. */ export function sessionToSentryRequest(session: Session, api: API): SentryRequest { + const { name, version } = getGlobalObject().__SENTRY__?.sdkInfo || {}; const envelopeHeaders = JSON.stringify({ sent_at: new Date().toISOString(), + sdk: { name, version }, }); const itemHeaders = JSON.stringify({ type: 'session', @@ -39,9 +42,12 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest { // deserialization. Instead, we only implement a minimal subset of the spec to // serialize events inline here. if (useEnvelope) { + const { name, version } = getGlobalObject().__SENTRY__?.sdkInfo || {}; + const envelopeHeaders = JSON.stringify({ event_id: event.event_id, sent_at: new Date().toISOString(), + sdk: { name, version }, }); const itemHeaders = JSON.stringify({ type: event.type, diff --git a/packages/core/test/lib/request.test.ts b/packages/core/test/lib/request.test.ts index be71d72cc8b8..e31db8b78a5d 100644 --- a/packages/core/test/lib/request.test.ts +++ b/packages/core/test/lib/request.test.ts @@ -1,4 +1,5 @@ import { Event, TransactionSamplingMethod } from '@sentry/types'; +import { getGlobalObject } from '@sentry/utils'; import { API } from '../../src/api'; import { eventToSentryRequest } from '../../src/request'; @@ -53,4 +54,22 @@ describe('eventToSentryRequest', () => { expect('dog' in envelope.event.tags).toBe(true); }); }); + + it('adds sdk metadata to envelope header', () => { + getGlobalObject().__SENTRY__ = { sdkInfo: { name: 'sentry.javascript.browser', version: '12.31.12' } } as any; + + const result = eventToSentryRequest(event as Event, api); + + const [envelopeHeaderString, itemHeaderString, eventString] = result.body.split('\n'); + + const envelope = { + envelopeHeader: JSON.parse(envelopeHeaderString), + itemHeader: JSON.parse(itemHeaderString), + event: JSON.parse(eventString), + }; + + expect(envelope.envelopeHeader).toEqual( + expect.objectContaining({ sdk: { name: 'sentry.javascript.browser', version: '12.31.12' } }), + ); + }); }); diff --git a/packages/ember/addon/index.ts b/packages/ember/addon/index.ts index d65e13faa4d0..92bc6580cf41 100644 --- a/packages/ember/addon/index.ts +++ b/packages/ember/addon/index.ts @@ -5,7 +5,7 @@ import { macroCondition, isDevelopingApp } from '@embroider/macros'; import { next } from '@ember/runloop'; import { assert, warn } from '@ember/debug'; import Ember from 'ember'; -import { timestampWithMs } from '@sentry/utils'; +import { setSDKInfo, timestampWithMs } from '@sentry/utils'; declare module '@ember/debug' { export function assert(desc: string, test: unknown): void; @@ -19,6 +19,7 @@ export function InitSentryForEmber(_runtimeConfig: BrowserOptions | undefined) { const initConfig = Object.assign({}, config.sentry, _runtimeConfig || {}); createEmberEventProcessor(); + setSDKInfo('sentry.javascript.ember', 'npm:@sentry/ember', SDK_VERSION); Sentry.init(initConfig); diff --git a/packages/ember/tests/unit/init-test.ts b/packages/ember/tests/unit/init-test.ts new file mode 100644 index 000000000000..cdb8325a985f --- /dev/null +++ b/packages/ember/tests/unit/init-test.ts @@ -0,0 +1,20 @@ +import { module, test } from 'qunit'; +import * as Sentry from '@sentry/ember'; +import { getGlobalObject } from '@sentry/utils'; + +const global = getGlobalObject(); + +module('Unit | SDK initialization', function() { + test('adds SDK metadata globally', function(assert) { + // the SDK data is set when we import from @sentry/ember (and therefore run `addon/index.ts`) - it sets the ember + // part itself, and the browser part gets set when it imports from @sentry/browser - so no action is necessary here + // before we run the `assert`s + + assert.equal(global.__SENTRY__?.sdkInfo?.name, 'sentry.javascript.ember'); + assert.equal(global.__SENTRY__?.sdkInfo?.version, Sentry.SDK_VERSION); + assert.deepEqual(global.__SENTRY__?.sdkInfo?.packages, [ + { name: 'npm:@sentry/browser', version: Sentry.SDK_VERSION }, + { name: 'npm:@sentry/ember', version: Sentry.SDK_VERSION }, + ]); + }); +}); diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 6429ce3a75c9..de6b23696874 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -42,6 +42,12 @@ export { NodeClient } from './client'; export { defaultIntegrations, init, lastEventId, flush, close } from './sdk'; export { SDK_NAME, SDK_VERSION } from './version'; +import { setSDKInfo } from '@sentry/utils'; + +import { SDK_VERSION } from './version'; + +setSDKInfo('sentry.javascript.node', 'npm:@sentry/node', SDK_VERSION, false); + import { Integrations as CoreIntegrations } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; import * as domain from 'domain'; diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 97296a6432da..4c6fdb74d38f 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -11,6 +11,7 @@ import { init, NodeClient, Scope, + SDK_VERSION, } from '../src'; import { NodeBackend } from '../src/backend'; @@ -270,6 +271,16 @@ describe('SentryNode', () => { }); describe('SentryNode initialization', () => { + test('sets SDK info globally', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts`, so no action is necessary here + // before we run the `expect`s + + expect(global.__SENTRY__.sdkInfo).toBeDefined(); + expect(global.__SENTRY__.sdkInfo.name).toEqual('sentry.javascript.node'); + expect(global.__SENTRY__.sdkInfo.version).toEqual(SDK_VERSION); + expect(global.__SENTRY__.sdkInfo.packages).toEqual([{ name: 'npm:@sentry/node', version: SDK_VERSION }]); + }); + test('global.SENTRY_RELEASE is used to set release on initialization if available', () => { global.SENTRY_RELEASE = { id: 'foobar' }; init({ dsn }); diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 57a37862d85d..411940c631d5 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,4 +1,7 @@ import { addGlobalEventProcessor, SDK_VERSION } from '@sentry/browser'; +import { setSDKInfo } from '@sentry/utils'; + +setSDKInfo('sentry.javascript.react', 'npm:@sentry/react', SDK_VERSION); /** * A global side effect that makes sure Sentry events that user diff --git a/packages/react/test/init.test.tsx b/packages/react/test/init.test.tsx new file mode 100644 index 000000000000..35e9cac457fa --- /dev/null +++ b/packages/react/test/init.test.tsx @@ -0,0 +1,23 @@ +import { getGlobalObject } from '@sentry/utils'; + +import * as Sentry from '../src/index'; + +const global = getGlobalObject(); + +describe('event SDK info', () => { + it('adds SDK metadata globally', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts` - it sets the react part itself, + // and the browser part gets set when it imports from @sentry/browser - so no action is necessary here before we run + // the `expect`s + + expect(global.__SENTRY__?.sdkInfo).toBeDefined(); + expect(global.__SENTRY__?.sdkInfo?.name).toEqual('sentry.javascript.react'); + expect(global.__SENTRY__?.sdkInfo?.version).toEqual(Sentry.SDK_VERSION); + expect(global.__SENTRY__?.sdkInfo?.packages).toEqual( + expect.arrayContaining([ + { name: 'npm:@sentry/react', version: Sentry.SDK_VERSION }, + { name: 'npm:@sentry/browser', version: Sentry.SDK_VERSION }, + ]), + ); + }); +}); diff --git a/packages/serverless/src/index.ts b/packages/serverless/src/index.ts index d3eabf5dc269..3130d2fc04e9 100644 --- a/packages/serverless/src/index.ts +++ b/packages/serverless/src/index.ts @@ -5,3 +5,8 @@ export { AWSLambda, GCPFunction }; export * from './awsservices'; export * from '@sentry/node'; + +import { SDK_VERSION } from '@sentry/node'; +import { setSDKInfo } from '@sentry/utils'; + +setSDKInfo('sentry.javascript.serverless', 'npm:@sentry/serverless', SDK_VERSION); diff --git a/packages/serverless/test/__mocks__/@sentry/node.ts b/packages/serverless/test/__mocks__/@sentry/node.ts index 769d22f6e2f6..7fae24342742 100644 --- a/packages/serverless/test/__mocks__/@sentry/node.ts +++ b/packages/serverless/test/__mocks__/@sentry/node.ts @@ -1,7 +1,7 @@ const origSentry = jest.requireActual('@sentry/node'); export const defaultIntegrations = origSentry.defaultIntegrations; // eslint-disable-line @typescript-eslint/no-unsafe-member-access export const Handlers = origSentry.Handlers; // eslint-disable-line @typescript-eslint/no-unsafe-member-access -export const SDK_VERSION = '6.6.6'; +export const SDK_VERSION = origSentry.SDK_VERSION; // eslint-disable-line @typescript-eslint/no-unsafe-member-access export const Severity = { Warning: 'warning', }; diff --git a/packages/serverless/test/awslambda.test.ts b/packages/serverless/test/awslambda.test.ts index e80b79f2515d..56a0aec13d43 100644 --- a/packages/serverless/test/awslambda.test.ts +++ b/packages/serverless/test/awslambda.test.ts @@ -1,4 +1,5 @@ import { Event } from '@sentry/types'; +import { getGlobalObject } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import { Callback, Handler } from 'aws-lambda'; @@ -341,6 +342,24 @@ describe('AWSLambda', () => { }); describe('init()', () => { + it('sets SDK data globally', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts` - it sets the serverless part itself, + // and the node part gets set when it imports from @sentry/node - so no action is necessary here before we run + // the `expect`s + + const global = getGlobalObject(); + + expect(global.__SENTRY__?.sdkInfo).toBeDefined(); + expect(global.__SENTRY__?.sdkInfo?.name).toEqual('sentry.javascript.serverless'); + expect(global.__SENTRY__?.sdkInfo?.version).toEqual(Sentry.SDK_VERSION); + expect(global.__SENTRY__?.sdkInfo?.packages).toEqual( + expect.arrayContaining([ + { name: 'npm:@sentry/serverless', version: Sentry.SDK_VERSION }, + { name: 'npm:@sentry/node', version: Sentry.SDK_VERSION }, + ]), + ); + }); + test('enhance event with SDK info and correct mechanism value', async () => { expect.assertions(1); @@ -381,10 +400,10 @@ describe('AWSLambda', () => { }, { name: 'npm:@sentry/serverless', - version: '6.6.6', + version: Sentry.SDK_VERSION, }, ], - version: '6.6.6', + version: Sentry.SDK_VERSION, }, }); }); @@ -416,10 +435,10 @@ describe('AWSLambda', () => { packages: [ { name: 'npm:@sentry/serverless', - version: '6.6.6', + version: Sentry.SDK_VERSION, }, ], - version: '6.6.6', + version: Sentry.SDK_VERSION, }, }); }); diff --git a/packages/serverless/test/gcpfunction.test.ts b/packages/serverless/test/gcpfunction.test.ts index 0f7903df3240..5053ab9fe4c2 100644 --- a/packages/serverless/test/gcpfunction.test.ts +++ b/packages/serverless/test/gcpfunction.test.ts @@ -1,4 +1,5 @@ import { Event } from '@sentry/types'; +import { getGlobalObject } from '@sentry/utils'; import * as domain from 'domain'; import * as Sentry from '../src'; @@ -361,6 +362,26 @@ describe('GCPFunction', () => { }); describe('init()', () => { + // Note: this is the same test that's in the AWS test file, but putting it here also in case AWS and GCP ever get + // split + it('sets SDK data globally', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts` - it sets the serverless part itself, + // and the node part gets set when it imports from @sentry/node - so no action is necessary here before we run + // the `expect`s + + const global = getGlobalObject(); + + expect(global.__SENTRY__?.sdkInfo).toBeDefined(); + expect(global.__SENTRY__?.sdkInfo?.name).toEqual('sentry.javascript.serverless'); + expect(global.__SENTRY__?.sdkInfo?.version).toEqual(Sentry.SDK_VERSION); + expect(global.__SENTRY__?.sdkInfo?.packages).toEqual( + expect.arrayContaining([ + { name: 'npm:@sentry/serverless', version: Sentry.SDK_VERSION }, + { name: 'npm:@sentry/node', version: Sentry.SDK_VERSION }, + ]), + ); + }); + test('enhance event with SDK info and correct mechanism value', async () => { expect.assertions(1); @@ -401,10 +422,10 @@ describe('GCPFunction', () => { }, { name: 'npm:@sentry/serverless', - version: '6.6.6', + version: Sentry.SDK_VERSION, }, ], - version: '6.6.6', + version: Sentry.SDK_VERSION, }, }); }); @@ -436,10 +457,10 @@ describe('GCPFunction', () => { packages: [ { name: 'npm:@sentry/serverless', - version: '6.6.6', + version: Sentry.SDK_VERSION, }, ], - version: '6.6.6', + version: Sentry.SDK_VERSION, }, }); }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index fc288847b41a..02ce77acda57 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -13,7 +13,6 @@ export { LogLevel } from './loglevel'; export { Mechanism } from './mechanism'; export { ExtractedNodeRequestData, Primitive, WorkerLocation } from './misc'; export { Options } from './options'; -export { Package } from './package'; export { Request, SentryRequest, SentryRequestType } from './request'; export { Response } from './response'; export { Runtime } from './runtime'; diff --git a/packages/types/src/package.ts b/packages/types/src/package.ts deleted file mode 100644 index c44d66e62950..000000000000 --- a/packages/types/src/package.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** JSDoc */ -export interface Package { - name: string; - version: string; -} diff --git a/packages/types/src/sdkinfo.ts b/packages/types/src/sdkinfo.ts index 45500cad5482..b35935229bfe 100644 --- a/packages/types/src/sdkinfo.ts +++ b/packages/types/src/sdkinfo.ts @@ -1,4 +1,7 @@ -import { Package } from './package'; +interface Package { + name: string; + version: string; +} /** JSDoc */ export interface SdkInfo { diff --git a/packages/utils/src/misc.ts b/packages/utils/src/misc.ts index 7c7a568db51d..6da99d97c23c 100644 --- a/packages/utils/src/misc.ts +++ b/packages/utils/src/misc.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Event, Integration, StackFrame, WrappedFunction } from '@sentry/types'; +import { Event, Integration, SdkInfo, StackFrame, WrappedFunction } from '@sentry/types'; import { isNodeEnv } from './node'; import { snipLine } from './string'; @@ -18,6 +18,7 @@ interface SentryGlobal { globalEventProcessors: any; hub: any; logger: any; + sdkInfo: SdkInfo; }; } @@ -37,6 +38,34 @@ export function getGlobalObject(): T & SentryGlobal { ? self : fallbackGlobalObject) as T & SentryGlobal; } +/** + * Adds SDK info to global object + * + * @param name The name of the SDK, like 'sentry.javascript.browser' + * @param packageName The name of the package in npm or elsewhere + * @param version The SDK version + * @param overwriteMainSDK Should already-existing main SDK data be overwritten? Used to ensure that wrapper packages + * (like @sentry/react) show up as themselves rather than as the packages they wrap + */ +export function setSDKInfo(name: string, packageName: string, version: string, overwriteMainSDK: boolean = true): void { + const global = getGlobalObject(); + global.__SENTRY__ = global.__SENTRY__ || {}; + + // we're the first ones here + if (global.__SENTRY__.sdkInfo === undefined) { + global.__SENTRY__.sdkInfo = { name, version, packages: [{ name: packageName, version }] }; + return; + } + + // add on to rather than replace package list (so that @sentry/react gets @sentry/react and @sentry/browser, for ex) + global.__SENTRY__.sdkInfo.packages = global.__SENTRY__.sdkInfo.packages || []; + global.__SENTRY__.sdkInfo.packages.push({ name: packageName, version }); + + if (overwriteMainSDK) { + global.__SENTRY__.sdkInfo.name = name; + global.__SENTRY__.sdkInfo.version = version; + } +} /** * Extended Window interface that allows for Crypto API usage in IE browsers diff --git a/packages/vue/package.json b/packages/vue/package.json index 2801369609d4..5c94b12b15d0 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -61,7 +61,7 @@ "fix": "run-s fix:eslint fix:prettier", "fix:prettier": "prettier --write \"{src,test}/**/*.ts\"", "fix:eslint": "eslint . --format stylish --fix", - "test": "jest --passWithNoTests", + "test": "jest", "test:watch": "jest --watch", "pack": "npm pack" }, diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 9d5015322d99..7a037835cfdd 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -17,7 +17,13 @@ export { setUser, startTransaction, withScope, + SDK_VERSION, } from '@sentry/browser'; export { init } from './sdk'; export { vueRouterInstrumentation } from './vuerouter'; + +import { SDK_VERSION } from '@sentry/browser'; +import { setSDKInfo } from '@sentry/utils'; + +setSDKInfo('sentry.javascript.vue', 'npm:@sentry/vue', SDK_VERSION); diff --git a/packages/vue/test/init.test.ts b/packages/vue/test/init.test.ts new file mode 100644 index 000000000000..f173a6a5519b --- /dev/null +++ b/packages/vue/test/init.test.ts @@ -0,0 +1,23 @@ +import { getGlobalObject } from '@sentry/utils'; + +import * as Sentry from '../src/index'; + +const global = getGlobalObject(); + +describe('event SDK info', () => { + it('adds SDK metadata globally', () => { + // the SDK data is set when we import from (and therefore run) `../src/index.ts` - it sets the vue part itself, + // and the browser part gets set when it imports from @sentry/browser - so no action is necessary here before we run + // the `expect`s + + expect(global.__SENTRY__?.sdkInfo).toBeDefined(); + expect(global.__SENTRY__?.sdkInfo?.name).toEqual('sentry.javascript.vue'); + expect(global.__SENTRY__?.sdkInfo?.version).toEqual(Sentry.SDK_VERSION); + expect(global.__SENTRY__?.sdkInfo?.packages).toEqual( + expect.arrayContaining([ + { name: 'npm:@sentry/vue', version: Sentry.SDK_VERSION }, + { name: 'npm:@sentry/browser', version: Sentry.SDK_VERSION }, + ]), + ); + }); +});