diff --git a/app/scripts/lib/createTracingMiddleware.test.ts b/app/scripts/lib/createTracingMiddleware.test.ts index ceb87e0f3ac5..cafe97b71181 100644 --- a/app/scripts/lib/createTracingMiddleware.test.ts +++ b/app/scripts/lib/createTracingMiddleware.test.ts @@ -19,8 +19,8 @@ describe('createTracingMiddleware', () => { request = { ...REQUEST_MOCK }; - global.sentry = { - getMetaMetricsEnabled: () => Promise.resolve(true), + globalThis.sentry = { + withIsolationScope: jest.fn().mockReturnValue({}), }; }); diff --git a/app/scripts/lib/createTracingMiddleware.ts b/app/scripts/lib/createTracingMiddleware.ts index 99e0a73795b1..27e928a95199 100644 --- a/app/scripts/lib/createTracingMiddleware.ts +++ b/app/scripts/lib/createTracingMiddleware.ts @@ -1,4 +1,4 @@ -// Request and repsones are currently untyped. +// Request and responses are currently untyped. /* eslint-disable @typescript-eslint/no-explicit-any */ import { MESSAGE_TYPE } from '../../../shared/constants/app'; diff --git a/app/scripts/lib/ppom/ppom-middleware.test.ts b/app/scripts/lib/ppom/ppom-middleware.test.ts index cbd3674f65c8..a0188a96ae8a 100644 --- a/app/scripts/lib/ppom/ppom-middleware.test.ts +++ b/app/scripts/lib/ppom/ppom-middleware.test.ts @@ -111,6 +111,14 @@ describe('PPOMMiddleware', () => { generateSecurityAlertIdMock.mockReturnValue(SECURITY_ALERT_ID_MOCK); handlePPOMErrorMock.mockReturnValue(SECURITY_ALERT_RESPONSE_MOCK); isChainSupportedMock.mockResolvedValue(true); + + globalThis.sentry = { + withIsolationScope: jest + .fn() + .mockImplementation((fn) => fn({ setTags: jest.fn() })), + startSpan: jest.fn().mockImplementation((_, fn) => fn({})), + startSpanManual: jest.fn().mockImplementation((_, fn) => fn({})), + }; }); it('updates alert response after validating request', async () => { diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 8cd77b920d1e..401f95a21990 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -336,6 +336,10 @@ describe('MetaMaskController', () => { }, ]), ); + + globalThis.sentry = { + withIsolationScope: jest.fn(), + }; }); afterEach(() => { diff --git a/shared/lib/trace.test.ts b/shared/lib/trace.test.ts index fc3294924698..5154a930b7f9 100644 --- a/shared/lib/trace.test.ts +++ b/shared/lib/trace.test.ts @@ -37,6 +37,12 @@ describe('Trace', () => { beforeEach(() => { jest.resetAllMocks(); + globalThis.sentry = { + startSpan: startSpanMock, + startSpanManual: startSpanManualMock, + withIsolationScope: withIsolationScopeMock, + }; + startSpanMock.mockImplementation((_, fn) => fn({} as Span)); // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/shared/lib/trace.ts b/shared/lib/trace.ts index b9f59a2f2353..f5919bae74e0 100644 --- a/shared/lib/trace.ts +++ b/shared/lib/trace.ts @@ -114,7 +114,7 @@ function traceCallback(request: TraceRequest, fn: TraceCallback): T { }; return startSpan(request, (spanOptions) => - Sentry.startSpan(spanOptions, callback), + globalThis.sentry.startSpan(spanOptions, callback), ); } @@ -138,7 +138,7 @@ function startTrace(request: TraceRequest): TraceContext { }; return startSpan(request, (spanOptions) => - Sentry.startSpanManual(spanOptions, callback), + globalThis.sentry.startSpanManual(spanOptions, callback), ); } @@ -157,7 +157,7 @@ function startSpan( startTime, }; - return Sentry.withIsolationScope((scope) => { + return globalThis.sentry.withIsolationScope((scope: Sentry.Scope) => { scope.setTags(tags as Record); return callback(spanOptions); diff --git a/test/e2e/tests/metrics/traces.spec.ts b/test/e2e/tests/metrics/traces.spec.ts new file mode 100644 index 000000000000..62c4d7da9219 --- /dev/null +++ b/test/e2e/tests/metrics/traces.spec.ts @@ -0,0 +1,129 @@ +import { MockttpServer } from 'mockttp'; +import FixtureBuilder from '../../fixture-builder'; +import { + defaultGanacheOptions, + unlockWallet, + withFixtures, +} from '../../helpers'; +import { + expectMockRequest, + expectNoMockRequest, +} from '../../helpers/mock-server'; + +async function mockSentryCustomTrace(mockServer: MockttpServer) { + return [ + await mockServer + .forPost(/sentry/u) + .withBodyIncluding('"transaction":"UI Startup"') + .thenCallback(() => { + return { + statusCode: 200, + json: {}, + }; + }), + ]; +} + +async function mockSentryAutomatedTrace(mockServer: MockttpServer) { + return [ + await mockServer + .forPost(/sentry/u) + .withBodyIncluding('"transaction":"/home.html"') + .thenCallback(() => { + return { + statusCode: 200, + json: {}, + }; + }), + ]; +} + +describe('Traces', function () { + it('sends custom trace when opening UI if metrics enabled', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withMetaMetricsController({ + participateInMetaMetrics: true, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockSentryCustomTrace, + manifestFlags: { + doNotForceSentryForThisTest: true, + }, + }, + async ({ driver, mockedEndpoint }) => { + await unlockWallet(driver); + await expectMockRequest(driver, mockedEndpoint[0], { timeout: 3000 }); + }, + ); + }); + + it('does not send custom trace when opening UI if metrics disabled @no-mmi', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withMetaMetricsController({ + participateInMetaMetrics: false, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockSentryCustomTrace, + manifestFlags: { + doNotForceSentryForThisTest: true, + }, + }, + async ({ driver, mockedEndpoint }) => { + await unlockWallet(driver); + await expectNoMockRequest(driver, mockedEndpoint[0], { timeout: 3000 }); + }, + ); + }); + + it('sends automated trace when opening UI if metrics enabled', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withMetaMetricsController({ + participateInMetaMetrics: true, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockSentryAutomatedTrace, + manifestFlags: { + doNotForceSentryForThisTest: true, + }, + }, + async ({ driver, mockedEndpoint }) => { + await unlockWallet(driver); + await expectMockRequest(driver, mockedEndpoint[0], { timeout: 3000 }); + }, + ); + }); + + it('does not send automated trace when opening UI if metrics disabled @no-mmi', async function () { + await withFixtures( + { + fixtures: new FixtureBuilder() + .withMetaMetricsController({ + participateInMetaMetrics: false, + }) + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test?.fullTitle(), + testSpecificMock: mockSentryAutomatedTrace, + manifestFlags: { + doNotForceSentryForThisTest: true, + }, + }, + async ({ driver, mockedEndpoint }) => { + await unlockWallet(driver); + await expectNoMockRequest(driver, mockedEndpoint[0], { timeout: 3000 }); + }, + ); + }); +});