From 9a4f6f58e3467eb115ec6d612aab11f1c3f41eb1 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:24:23 +0000 Subject: [PATCH] fix(v10/browser): Apply `urlQueryParams` to event URLs, static span names and LCP URLs Backport of: #25262 --- .../suites/integrations/httpContext/test.ts | 14 ++- .../init.js | 10 ++ .../template.html | 9 ++ .../test.ts | 33 ++++++ .../metrics/web-vitals-lcp/template.html | 2 +- .../tracing/metrics/web-vitals-lcp/test.ts | 5 +- .../utils/fixtures.ts | 2 +- .../src/metrics/browserMetrics.ts | 15 ++- packages/browser-utils/src/metrics/lcp.ts | 3 +- .../src/metrics/webVitalSpans.ts | 3 +- .../test/browser/browserMetrics.test.ts | 111 ++++++++++++------ .../browser-utils/test/metrics/lcp.test.ts | 4 +- .../test/metrics/webVitalSpans.test.ts | 4 +- .../browser/src/integrations/httpcontext.ts | 3 +- .../src/tracing/browserTracingIntegration.ts | 5 +- .../test/integrations/httpcontext.test.ts | 25 +++- .../pagesRouterInstrumentation.test.ts | 1 + 17 files changed, 191 insertions(+), 58 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts index dd8a870a3bad..b4837ecbb687 100644 --- a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; +import { envelopeRequestParser, getFirstSentryEnvelopeRequest, waitForErrorRequest } from '../../../utils/helpers'; sentryTest('httpContextIntegration captures user-agent and referrer', async ({ getLocalTestUrl, page }) => { const url = await getLocalTestUrl({ testDir: __dirname }); @@ -23,3 +23,15 @@ sentryTest('httpContextIntegration captures user-agent and referrer', async ({ g url: expect.any(String), }); }); + +sentryTest('httpContextIntegration filters sensitive query params in the URL', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + + const errorEventPromise = waitForErrorRequest(page); + + await page.goto(`${url}?jwt=sensitive-jwt-value&page=1`); + + const errorEvent = envelopeRequestParser(await errorEventPromise); + + expect(errorEvent.request?.url).toBe(`${url}?jwt=[Filtered]&page=1`); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/init.js new file mode 100644 index 000000000000..bb6d4918d1bf --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/init.js @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration()], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/template.html new file mode 100644 index 000000000000..3d3b49a9951f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts new file mode 100644 index 000000000000..2408a08b6e53 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-sensitive-query-params-static/test.ts @@ -0,0 +1,33 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; + +sentryTest( + 'filters sensitive query params in the pageload transaction URL and span descriptions', + async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); + + await page.route('https://sentry-test-site.example/path/to/image.svg?*', route => + route.fulfill({ body: '', contentType: 'image/svg+xml' }), + ); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + await page.goto(`${url}?jwt=sensitive-jwt-value&page=1`); + const eventData = envelopeRequestParser(await pageloadRequestPromise); + + expect(eventData.request?.url).toBe(`${url}?jwt=[Filtered]&page=1`); + expect(eventData.spans?.find(({ op }) => op === 'browser.request')?.description).toBe( + `${url}?jwt=[Filtered]&page=1`, + ); + expect(eventData.spans?.find(({ op }) => op === 'resource.img')?.description).toBe( + 'https://sentry-test-site.example/path/to/image.svg?token=[Filtered]&size=2', + ); + + const serializedEvent = JSON.stringify(eventData); + expect(serializedEvent).not.toContain('sensitive-jwt-value'); + expect(serializedEvent).not.toContain('sensitive-token-value'); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html index b613a556aca4..164dbf51f486 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/template.html @@ -5,6 +5,6 @@
- + diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts index 4d61904c58ac..e822c6b046a3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp/test.ts @@ -10,7 +10,7 @@ sentryTest('captures LCP vitals with element details', async ({ browserName, get } page.route('**', route => route.continue()); - page.route('**/my/image.png', async (route: Route) => { + page.route('**/my/image.png?*', async (route: Route) => { return route.fulfill({ path: `${__dirname}/assets/sentry-logo-600x179.png`, }); @@ -26,6 +26,9 @@ sentryTest('captures LCP vitals with element details', async ({ browserName, get expect(eventData.contexts?.trace?.data?.['lcp.size']).toBeGreaterThan(0); expect(eventData.contexts?.trace?.data?.['lcp.loadTime']).toBeGreaterThan(0); expect(eventData.contexts?.trace?.data?.['lcp.renderTime']).toBeGreaterThan(0); + expect(eventData.contexts?.trace?.data?.['lcp.url']).toBe( + 'https://sentry-test-site.example/my/image.png?token=[Filtered]&v=1', + ); // The LCP value should be the renderTime because the renderTime is set expect(eventData.measurements?.lcp?.value).toBeCloseTo(eventData.contexts?.trace?.data?.['lcp.renderTime']); diff --git a/dev-packages/browser-integration-tests/utils/fixtures.ts b/dev-packages/browser-integration-tests/utils/fixtures.ts index f856fa64ce23..7436f2c35d9a 100644 --- a/dev-packages/browser-integration-tests/utils/fixtures.ts +++ b/dev-packages/browser-integration-tests/utils/fixtures.ts @@ -90,7 +90,7 @@ const sentryTest = base.extend({ } await page.route(`${TEST_HOST}/*.*`, route => { - const file = route.request().url().split('/').pop(); + const file = new URL(route.request().url()).pathname.split('/').pop(); const filePath = path.resolve(tmpDir, `./${file}`); return fs.existsSync(filePath) diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index 2d07c4d07f1e..67314a2da6e2 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -690,7 +690,7 @@ function _addPerformanceNavigationTiming( } startAndEndSpan(span, timeOrigin + msToSec(start), timeOrigin + msToSec(end), { op: `browser.${name}`, - name: entry.name, + name: filterCollectedUrl(entry.name), attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', ...(event === 'redirect' && entry.redirectCount != null ? { 'http.redirect_count': entry.redirectCount } : {}), @@ -718,9 +718,11 @@ function _addRequest(span: Span, entry: PerformanceNavigationTiming, timeOrigin: // In this case, ie. when the document request hasn't finished yet, `entry.responseEnd` will be 0. // In order not to produce faulty spans, where the end timestamp is before the start timestamp, we will only collect // these spans when the responseEnd value is available. The backend (Relay) would drop the entire span if it contained faulty spans. + const filteredUrl = filterCollectedUrl(entry.name); + startAndEndSpan(span, requestStartTimestamp, responseEndTimestamp, { op: 'browser.request', - name: entry.name, + name: filteredUrl, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, @@ -728,7 +730,7 @@ function _addRequest(span: Span, entry: PerformanceNavigationTiming, timeOrigin: startAndEndSpan(span, responseStartTimestamp, responseEndTimestamp, { op: 'browser.response', - name: entry.name, + name: filteredUrl, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, @@ -776,7 +778,8 @@ export function _addResourceSpans( attributes['url.same_origin'] = resourceUrl.includes(WINDOW.location.origin); - attributes[URL_FULL] = filterCollectedUrl(resourceUrl); + const filteredUrl = filterCollectedUrl(resourceUrl); + attributes[URL_FULL] = filteredUrl; _setResourceRequestAttributes(entry, attributes, [ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus @@ -799,7 +802,7 @@ export function _addResourceSpans( const endTimestamp = startTimestamp + duration; startAndEndSpan(span, startTimestamp, endTimestamp, { - name: resourceUrl.replace(WINDOW.location.origin, ''), + name: filteredUrl.replace(WINDOW.location.origin, ''), op, attributes: attributesWithResourceTiming, }); @@ -873,7 +876,7 @@ function _setWebVitalAttributes(span: Span, options: AddWebVitalsToSpanOptions): if (_lcpEntry.url) { // Trim URL to the first 200 characters. - span.setAttribute('lcp.url', _lcpEntry.url.trim().slice(0, 200)); + span.setAttribute('lcp.url', filterCollectedUrl(_lcpEntry.url.trim()).slice(0, 200)); } if (_lcpEntry.loadTime != null) { diff --git a/packages/browser-utils/src/metrics/lcp.ts b/packages/browser-utils/src/metrics/lcp.ts index bcd065e94cf0..bfbd3ab476b9 100644 --- a/packages/browser-utils/src/metrics/lcp.ts +++ b/packages/browser-utils/src/metrics/lcp.ts @@ -2,6 +2,7 @@ import type { Client, SpanAttributes } from '@sentry/core'; import { browserPerformanceTimeOrigin, debug, + filterCollectedUrl, getCurrentScope, SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME, SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_UNIT, @@ -90,7 +91,7 @@ export function _sendStandaloneLcpSpan( entry.element && (attributes['lcp.element'] = htmlTreeAsString(entry.element)); entry.id && (attributes['lcp.id'] = entry.id); - entry.url && (attributes['lcp.url'] = entry.url); + entry.url && (attributes['lcp.url'] = filterCollectedUrl(entry.url)); // loadTime is the time of LCP that's related to receiving the LCP element response.. entry.loadTime != null && (attributes['lcp.loadTime'] = entry.loadTime); diff --git a/packages/browser-utils/src/metrics/webVitalSpans.ts b/packages/browser-utils/src/metrics/webVitalSpans.ts index e5e6af09355f..a24422ae77b7 100644 --- a/packages/browser-utils/src/metrics/webVitalSpans.ts +++ b/packages/browser-utils/src/metrics/webVitalSpans.ts @@ -2,6 +2,7 @@ import type { Client, Span, SpanAttributes } from '@sentry/core'; import { browserPerformanceTimeOrigin, debug, + filterCollectedUrl, getActiveSpan, getCurrentScope, getRootSpan, @@ -162,7 +163,7 @@ export function _sendLcpSpan( entry?.element && (attributes['browser.web_vital.lcp.element'] = htmlTreeAsString(entry.element)); entry?.id && (attributes['browser.web_vital.lcp.id'] = entry.id); - entry?.url && (attributes['browser.web_vital.lcp.url'] = entry.url); + entry?.url && (attributes['browser.web_vital.lcp.url'] = filterCollectedUrl(entry.url)); entry?.loadTime != null && (attributes['browser.web_vital.lcp.load_time'] = entry.loadTime); entry?.renderTime != null && (attributes['browser.web_vital.lcp.render_time'] = entry.renderTime); entry?.size != null && (attributes['browser.web_vital.lcp.size'] = entry.size); diff --git a/packages/browser-utils/test/browser/browserMetrics.test.ts b/packages/browser-utils/test/browser/browserMetrics.test.ts index ef7bbd26f75d..998992a1cd9f 100644 --- a/packages/browser-utils/test/browser/browserMetrics.test.ts +++ b/packages/browser-utils/test/browser/browserMetrics.test.ts @@ -512,6 +512,26 @@ describe('_addResourceSpans', () => { expect(json.data['url.full']).toBe('https://example.com/assets/app.js?v=42#main'); }); + it('filters sensitive query params in the description and url.full', () => { + const spans: Span[] = []; + + getClient()?.on('spanEnd', span => { + spans.push(span); + }); + + const entry = mockPerformanceResourceTiming({ + initiatorType: 'script', + nextHopProtocol: 'http/1.1', + }); + + _addResourceSpans(span, entry, 'https://example.com/assets/app.js?token=abc&v=42', 100, 23, 345); + + expect(spans).toHaveLength(1); + const json = spanToJSON(spans[0]!); + expect(json.description).toBe('/assets/app.js?token=[Filtered]&v=42'); + expect(json.data['url.full']).toBe('https://example.com/assets/app.js?token=[Filtered]&v=42'); + }); + it('sets url.full to the full cross-origin URL', () => { const spans: Span[] = []; @@ -818,45 +838,46 @@ describe('_addNavigationSpans', () => { client.init(); }); + // entry taken from a real entry via browser dev tools + const entry: PerformanceNavigationTiming = { + name: 'https://santry.com/test', + entryType: 'navigation', + startTime: 0, + duration: 546.1000000014901, + initiatorType: 'navigation', + nextHopProtocol: 'h2', + workerStart: 0, + redirectStart: 7.5, + redirectEnd: 20.5, + redirectCount: 2, + fetchStart: 4.9000000059604645, + domainLookupStart: 4.9000000059604645, + domainLookupEnd: 4.9000000059604645, + connectStart: 4.9000000059604645, + secureConnectionStart: 4.9000000059604645, + connectEnd: 4.9000000059604645, + requestStart: 7.9000000059604645, + responseStart: 396.80000000447035, + responseEnd: 416.40000000596046, + transferSize: 14726, + encodedBodySize: 14426, + decodedBodySize: 67232, + responseStatus: 200, + serverTiming: [], + unloadEventStart: 0, + unloadEventEnd: 0, + domInteractive: 473.20000000298023, + domContentLoadedEventStart: 480.1000000014901, + domContentLoadedEventEnd: 480.30000000447035, + domComplete: 546, + loadEventStart: 546, + loadEventEnd: 546.1000000014901, + type: 'navigate', + activationStart: 0, + toJSON: () => ({}), + }; + it('adds navigation spans based on the navigation performance entry', () => { - // entry taken from a real entry via browser dev tools - const entry: PerformanceNavigationTiming = { - name: 'https://santry.com/test', - entryType: 'navigation', - startTime: 0, - duration: 546.1000000014901, - initiatorType: 'navigation', - nextHopProtocol: 'h2', - workerStart: 0, - redirectStart: 7.5, - redirectEnd: 20.5, - redirectCount: 2, - fetchStart: 4.9000000059604645, - domainLookupStart: 4.9000000059604645, - domainLookupEnd: 4.9000000059604645, - connectStart: 4.9000000059604645, - secureConnectionStart: 4.9000000059604645, - connectEnd: 4.9000000059604645, - requestStart: 7.9000000059604645, - responseStart: 396.80000000447035, - responseEnd: 416.40000000596046, - transferSize: 14726, - encodedBodySize: 14426, - decodedBodySize: 67232, - responseStatus: 200, - serverTiming: [], - unloadEventStart: 0, - unloadEventEnd: 0, - domInteractive: 473.20000000298023, - domContentLoadedEventStart: 480.1000000014901, - domContentLoadedEventEnd: 480.30000000447035, - domComplete: 546, - loadEventStart: 546, - loadEventEnd: 546.1000000014901, - type: 'navigate', - activationStart: 0, - toJSON: () => ({}), - }; const spans: Span[] = []; getClient()?.on('spanEnd', span => { @@ -974,6 +995,20 @@ describe('_addNavigationSpans', () => { ]), ); }); + + it('filters sensitive query params in span names', () => { + const spans: Span[] = []; + + getClient()?.on('spanEnd', span => { + spans.push(span); + }); + + _addNavigationSpans(pageloadSpan, { ...entry, name: 'https://santry.com/test?jwt=baz&q=1' }, 999); + + expect(spans.map(spanToJSON)).toEqual( + Array(9).fill(expect.objectContaining({ description: 'https://santry.com/test?jwt=[Filtered]&q=1' })), + ); + }); }); describe('_setResourceRequestAttributes', () => { diff --git a/packages/browser-utils/test/metrics/lcp.test.ts b/packages/browser-utils/test/metrics/lcp.test.ts index baa7cd5de052..d90608d42c3f 100644 --- a/packages/browser-utils/test/metrics/lcp.test.ts +++ b/packages/browser-utils/test/metrics/lcp.test.ts @@ -63,7 +63,7 @@ describe('_sendStandaloneLcpSpan', () => { startTime: 100, duration: 0, id: 'image', - url: 'https://example.com/image.png', + url: 'https://example.com/image.png?token=abc&size=2', size: 1234, loadTime: 95, renderTime: 100, @@ -84,7 +84,7 @@ describe('_sendStandaloneLcpSpan', () => { 'sentry.report_event': 'navigation', 'lcp.element': '', 'lcp.id': 'image', - 'lcp.url': 'https://example.com/image.png', + 'lcp.url': 'https://example.com/image.png?token=[Filtered]&size=2', 'lcp.loadTime': 95, 'lcp.renderTime': 100, 'lcp.size': 1234, diff --git a/packages/browser-utils/test/metrics/webVitalSpans.test.ts b/packages/browser-utils/test/metrics/webVitalSpans.test.ts index 7995fb411c3a..f27bb00bc7e2 100644 --- a/packages/browser-utils/test/metrics/webVitalSpans.test.ts +++ b/packages/browser-utils/test/metrics/webVitalSpans.test.ts @@ -230,7 +230,7 @@ describe('_sendLcpSpan', () => { const mockEntry = { element: { tagName: 'img' } as Element, id: 'hero', - url: 'https://example.com/hero.jpg', + url: 'https://example.com/hero.jpg?token=abc&size=2', loadTime: 100, renderTime: 150, size: 50000, @@ -251,7 +251,7 @@ describe('_sendLcpSpan', () => { 'sentry.pageload.span_id': 'pageload-123', 'browser.web_vital.lcp.element': '', 'browser.web_vital.lcp.id': 'hero', - 'browser.web_vital.lcp.url': 'https://example.com/hero.jpg', + 'browser.web_vital.lcp.url': 'https://example.com/hero.jpg?token=[Filtered]&size=2', 'browser.web_vital.lcp.load_time': 100, 'browser.web_vital.lcp.render_time': 150, 'browser.web_vital.lcp.size': 50000, diff --git a/packages/browser/src/integrations/httpcontext.ts b/packages/browser/src/integrations/httpcontext.ts index 55d135034e89..d389e9236399 100644 --- a/packages/browser/src/integrations/httpcontext.ts +++ b/packages/browser/src/integrations/httpcontext.ts @@ -9,7 +9,7 @@ import { HTTP_REQUEST_HEADER_KEY_BASE, SENTRY_OP, URL_FULL, USER_AGENT_ORIGINAL export const httpContextIntegration = defineIntegration(() => { return { name: 'HttpContext' as const, - preprocessEvent(event) { + preprocessEvent(event, _hint, client) { // if none of the information we want exists, don't bother if (!WINDOW.navigator && !WINDOW.location && !WINDOW.document) { return; @@ -23,6 +23,7 @@ export const httpContextIntegration = defineIntegration(() => { event.request = { ...reqData, + url: filterCollectedUrl(reqData.url, client), ...event.request, headers, }; diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts index 90bba3fdd514..b19d569654e3 100644 --- a/packages/browser/src/tracing/browserTracingIntegration.ts +++ b/packages/browser/src/tracing/browserTracingIntegration.ts @@ -636,8 +636,9 @@ export const browserTracingIntegration = ((options: Partial { 'user_agent.original': USER_AGENT, }); }); + + it('filters sensitive query params in the URL attached to events', () => { + const client = new BrowserClient(getDefaultBrowserClientOptions()); + const event: Event = {}; + + document.location.href = 'https://example.com/?jwt=baz&page=1'; + try { + httpContextIntegration().preprocessEvent!(event, {}, client); + } finally { + document.location.href = 'https://example.com'; + } + + expect(event.request?.url).toBe('https://example.com/?jwt=[Filtered]&page=1'); + }); + + it('leaves a user-supplied event URL untouched', () => { + const client = new BrowserClient(getDefaultBrowserClientOptions()); + const event: Event = { request: { url: 'https://example.com/?jwt=user-set' } }; + + httpContextIntegration().preprocessEvent!(event, {}, client); + + expect(event.request?.url).toBe('https://example.com/?jwt=user-set'); + }); }); diff --git a/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts b/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts index 16eeb3469ad5..13ce69e9bb2d 100644 --- a/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts +++ b/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts @@ -320,6 +320,7 @@ describe('pagesRouterInstrumentNavigation', () => { const client = { emit, getOptions: () => ({}), + getDataCollectionOptions: () => ({ urlQueryParams: true }), } as unknown as Client; pagesRouterInstrumentNavigation(client);