diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index e21b40a86f27..2fa268c7332d 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -292,6 +292,10 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = { ...defaultRequestInstrumentationOptions, }; +// Bundlers can define these as `true` to tree-shake the root span error handlers and background tab detection +declare const __SENTRY_EXCLUDE_SPAN_ERROR_INSTRUMENTATION__: boolean | undefined; +declare const __SENTRY_EXCLUDE_BACKGROUND_TAB_DETECTION__: boolean | undefined; + /** * The Browser Tracing integration automatically instruments browser pageload/navigation * actions as transactions, and captures requests, metrics and errors as spans. @@ -431,7 +435,12 @@ export const browserTracingIntegration = ((options: Partial { ); }); + describe('tree-shakeable root span handlers', () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + function initClient(): void { + const client = new BrowserClient( + getDefaultBrowserClientOptions({ + tracesSampleRate: 1, + integrations: [browserTracingIntegration()], + }), + ); + setCurrentClient(client); + client.init(); + } + + it('registers span error instrumentation and background tab detection by default', () => { + const registerSpanErrorSpy = vi.spyOn(sentryCore, 'registerSpanErrorInstrumentation'); + const registerBackgroundTabSpy = vi.spyOn(backgroundTab, 'registerBackgroundTabDetection'); + + initClient(); + + expect(registerSpanErrorSpy).toHaveBeenCalledOnce(); + expect(registerBackgroundTabSpy).toHaveBeenCalledOnce(); + }); + + it("doesn't register span error instrumentation if `__SENTRY_EXCLUDE_SPAN_ERROR_INSTRUMENTATION__` is true", () => { + vi.stubGlobal('__SENTRY_EXCLUDE_SPAN_ERROR_INSTRUMENTATION__', true); + const registerSpanErrorSpy = vi.spyOn(sentryCore, 'registerSpanErrorInstrumentation'); + + initClient(); + + expect(registerSpanErrorSpy).not.toHaveBeenCalled(); + }); + + it("doesn't register background tab detection if `__SENTRY_EXCLUDE_BACKGROUND_TAB_DETECTION__` is true", () => { + vi.stubGlobal('__SENTRY_EXCLUDE_BACKGROUND_TAB_DETECTION__', true); + const registerBackgroundTabSpy = vi.spyOn(backgroundTab, 'registerBackgroundTabDetection'); + + initClient(); + + expect(registerBackgroundTabSpy).not.toHaveBeenCalled(); + }); + }); + describe('startBrowserTracingPageLoadSpan', () => { it('works without integration setup', () => { const client = new BrowserClient( diff --git a/packages/browser/test/tracing/request.test.ts b/packages/browser/test/tracing/request.test.ts index e0c8fe8f0947..ab7b78a95477 100644 --- a/packages/browser/test/tracing/request.test.ts +++ b/packages/browser/test/tracing/request.test.ts @@ -90,6 +90,46 @@ describe('instrumentOutgoingRequests', () => { expect(requestSpanJson.attributes[HTTP_REQUEST_METHOD]).toBe('QUERY'); }); + describe('HTTP timings', () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + function startFetchRequestSpan(): void { + let fetchHandler: ((data: utils.HandlerDataFetch) => void) | undefined; + vi.spyOn(utils, 'addFetchInstrumentationHandler').mockImplementation(handler => { + fetchHandler = handler; + }); + const tracingClient = new BrowserClient(getDefaultBrowserClientOptions({ tracesSampleRate: 1 })); + utils.setCurrentClient(tracingClient); + utils._INTERNAL_setSpanForScope(utils.getCurrentScope(), new utils.SentrySpan({ sampled: true })); + + instrumentOutgoingRequests(tracingClient, { traceXHR: false }); + fetchHandler?.({ + fetchData: { method: 'GET', url: 'https://example.com/api' }, + args: ['https://example.com/api'], + startTimestamp: Date.now(), + }); + } + + it('collects HTTP timings by default', () => { + const addPerformanceSpy = vi.spyOn(browserUtils, 'addPerformanceInstrumentationHandler'); + + startFetchRequestSpan(); + + expect(addPerformanceSpy).toHaveBeenCalledWith('resource', expect.any(Function)); + }); + + it("doesn't collect HTTP timings if `__SENTRY_EXCLUDE_HTTP_TIMINGS__` is true", () => { + vi.stubGlobal('__SENTRY_EXCLUDE_HTTP_TIMINGS__', true); + const addPerformanceSpy = vi.spyOn(browserUtils, 'addPerformanceInstrumentationHandler'); + + startFetchRequestSpan(); + + expect(addPerformanceSpy).not.toHaveBeenCalled(); + }); + }); + it('creates a QUERY XHR span with the QUERY method attribute', () => { let xhrHandler: ((data: browserUtils.HandlerDataXhr) => void) | undefined; let requestSpan: utils.Span | undefined;