From 131bc912b1b4ce72877dff1177e406c78290e501 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 30 Sep 2026 14:14:44 +0200 Subject: [PATCH 1/7] test(browser): port browser tracing suites to span streaming Co-Authored-By: GPT-6 --- .../async-spans/init.js | 1 - .../async-spans/test.ts | 6 +- .../backgroundtab-custom/init.js | 1 - .../backgroundtab-custom/test.ts | 2 +- .../subject.js | 8 - .../template.html | 9 - .../backgroundtab-pageload-streamed/test.ts | 18 - .../init.js | 2 +- .../backgroundtab-pageload/test.ts | 20 +- .../browserTracingIntegration/error/test.ts | 52 +-- .../http-timings-streamed/init.js | 18 - .../http-timings-streamed/subject.js | 3 - .../http-timings-streamed/test.ts | 64 ---- .../http-timings/init.js | 1 - .../http-timings/test.ts | 146 ++++--- .../tracing/browserTracingIntegration/init.js | 1 - .../consistent-sampling/default/init.js | 22 -- .../consistent-sampling/default/subject.js | 17 - .../consistent-sampling/default/template.html | 8 - .../consistent-sampling/default/test.ts | 154 -------- .../consistent-sampling/meta-negative/init.js | 19 - .../meta-negative/subject.js | 17 - .../meta-negative/template.html | 13 - .../consistent-sampling/meta-negative/test.ts | 97 ----- .../meta-precedence/init.js | 20 - .../meta-precedence/page-1.html | 15 - .../meta-precedence/page-2.html | 10 - .../meta-precedence/subject.js | 17 - .../meta-precedence/template.html | 14 - .../meta-precedence/test.ts | 117 ------ .../consistent-sampling/meta/init.js | 18 - .../consistent-sampling/meta/subject.js | 17 - .../consistent-sampling/meta/template.html | 13 - .../consistent-sampling/meta/test.ts | 171 --------- .../tracesSampler-precedence/init.js | 29 -- .../tracesSampler-precedence/subject.js | 17 - .../tracesSampler-precedence/template.html | 8 - .../tracesSampler-precedence/test.ts | 151 -------- .../custom-trace/subject.js | 14 - .../custom-trace/template.html | 8 - .../custom-trace/test.ts | 63 ---- .../linked-traces-streamed/default/test.ts | 95 ----- .../linked-traces-streamed/init.js | 10 - .../interaction-spans/init.js | 10 - .../interaction-spans/template.html | 7 - .../interaction-spans/test.ts | 79 ---- .../linked-traces-streamed/meta/template.html | 11 - .../linked-traces-streamed/meta/test.ts | 50 --- .../negatively-sampled/init.js | 15 - .../negatively-sampled/test.ts | 44 --- .../session-storage/init.js | 12 - .../session-storage/test.ts | 42 --- .../consistent-sampling/default/init.js | 1 - .../consistent-sampling/default/test.ts | 139 +++---- .../consistent-sampling/meta-negative/init.js | 1 - .../consistent-sampling/meta-negative/test.ts | 37 +- .../meta-precedence/init.js | 1 - .../meta-precedence/test.ts | 51 +-- .../consistent-sampling/meta/init.js | 1 - .../consistent-sampling/meta/test.ts | 143 ++++--- .../tracesSampler-precedence/init.js | 1 - .../tracesSampler-precedence/test.ts | 91 +++-- .../linked-traces/custom-trace/test.ts | 60 +-- .../linked-traces/default/test.ts | 93 +++-- .../linked-traces/init.js | 1 - .../linked-traces/interaction-spans/init.js | 1 - .../linked-traces/interaction-spans/test.ts | 73 ++-- .../linked-traces/meta/test.ts | 45 ++- .../linked-traces/negatively-sampled/init.js | 1 - .../linked-traces/negatively-sampled/test.ts | 33 +- .../linked-traces/session-storage/init.js | 1 - .../linked-traces/session-storage/test.ts | 44 ++- .../init.js | 17 - .../subject.js | 18 - .../template.html | 11 - .../test.ts | 25 -- .../init.js | 1 - .../test.ts | 25 +- .../assets/script.js | 12 - .../init.js | 15 - .../template.html | 10 - .../test.ts | 28 -- .../long-animation-frame-disabled/init.js | 1 - .../long-animation-frame-disabled/test.ts | 22 +- .../assets/script.js | 25 -- .../init.js | 15 - .../template.html | 11 - .../test.ts | 108 ------ .../long-animation-frame-enabled/init.js | 1 - .../long-animation-frame-enabled/test.ts | 132 ++++--- .../long-animation-frame-non-chromium/init.js | 1 - .../long-animation-frame-non-chromium/test.ts | 10 +- .../assets/script.js | 25 -- .../init.js | 15 - .../template.html | 11 - .../test.ts | 110 ------ .../init.js | 1 - .../test.ts | 132 ++++--- .../init.js | 18 - .../subject.js | 17 - .../template.html | 12 - .../test.ts | 29 -- .../long-tasks-before-navigation/init.js | 1 - .../long-tasks-before-navigation/test.ts | 24 +- .../assets/script.js | 12 - .../long-tasks-disabled-streamed/init.js | 15 - .../template.html | 10 - .../long-tasks-disabled-streamed/test.ts | 23 -- .../long-tasks-disabled/init.js | 1 - .../long-tasks-disabled/test.ts | 22 +- .../assets/script.js | 12 - .../long-tasks-enabled-streamed/init.js | 14 - .../long-tasks-enabled-streamed/template.html | 10 - .../long-tasks-enabled-streamed/test.ts | 42 --- .../long-tasks-enabled/init.js | 1 - .../long-tasks-enabled/test.ts | 35 +- .../long-tasks-no-animation-frame/init.js | 1 - .../long-tasks-no-animation-frame/test.ts | 26 +- .../browserTracingIntegration/meta/init.js | 1 - .../browserTracingIntegration/meta/test.ts | 115 +++--- .../navigation-aborting-pageload/init.js | 1 - .../navigation-aborting-pageload/test.ts | 124 +++--- .../multiple-redirects/init.js | 1 - .../multiple-redirects/test.ts | 77 ++-- .../navigation-navigation-click/init.js | 1 - .../navigation-navigation-click/test.ts | 39 +- .../navigation-navigation-keypress/init.js | 1 - .../navigation-navigation-keypress/test.ts | 42 ++- .../navigation-redirect/init.js | 1 - .../navigation-redirect/test.ts | 30 +- .../navigation-redirect/opt-out/init.js | 1 - .../navigation-redirect/opt-out/test.ts | 20 +- .../pageload-navigation-click/init.js | 1 - .../pageload-navigation-click/test.ts | 22 +- .../pageload-navigation-keypress/init.js | 1 - .../pageload-navigation-keypress/test.ts | 20 +- .../pageload-navigation-time/init.js | 1 - .../pageload-navigation-time/test.ts | 20 +- .../pageload-redirect/init.js | 1 - .../pageload-redirect/test.ts | 62 +-- .../navigation-streamed/test.ts | 274 -------------- .../init.js | 3 +- .../navigation/test.ts | 357 ++++++++++++------ .../on-request-span-end/init.js | 1 - .../on-request-span-end/test.ts | 30 +- .../on-request-span-start/init.js | 1 - .../on-request-span-start/test.ts | 26 +- .../pageload-streamed/init.js | 11 - .../pageload-streamed/test.ts | 192 ---------- .../pageload-update-txn-name/init.js | 1 - .../pageload-update-txn-name/test.ts | 39 +- .../pageload-updateSpanName/init.js | 1 - .../pageload-updateSpanName/test.ts | 39 +- .../pageload/init.js | 1 - .../pageload/test.ts | 211 +++++++++-- .../pageloadDelayed/init.js | 1 - .../pageloadDelayed/test.ts | 22 +- .../pageloadWithChildSpanTimeout/init.js | 1 - .../pageloadWithChildSpanTimeout/test.ts | 36 +- .../reportPageLoaded-streamed/default/init.js | 15 - .../reportPageLoaded-streamed/default/test.ts | 41 -- .../finalTimeout/init.js | 16 - .../finalTimeout/test.ts | 36 -- .../navigation/init.js | 22 -- .../navigation/test.ts | 34 -- .../reportPageLoaded/default/init.js | 1 - .../reportPageLoaded/default/test.ts | 36 +- .../reportPageLoaded/finalTimeout/init.js | 1 - .../reportPageLoaded/finalTimeout/test.ts | 28 +- .../reportPageLoaded/navigation/init.js | 1 - .../reportPageLoaded/navigation/test.ts | 28 +- .../resource-spans-ignored/init.js | 1 - .../resource-spans-ignored/test.ts | 10 +- .../init.js | 17 - .../subject.js | 12 - .../template.html | 12 - .../test.ts | 65 ---- .../spotlight-interaction-filter/init.js | 1 - .../spotlight-interaction-filter/test.ts | 102 +++-- .../customTargets/init.js | 1 - .../defaultTargetsMatch/init.js | 1 - .../defaultTargetsNoMatch/init.js | 1 - .../twp-errors-meta/init.js | 1 - .../twp-errors/init.js | 1 - 184 files changed, 1688 insertions(+), 4326 deletions(-) delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts rename dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/{navigation-streamed => backgroundtab-pageload}/init.js (64%) delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-1.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-2.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/assets/script.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/assets/script.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/assets/script.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/assets/script.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/assets/script.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts rename dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/{backgroundtab-pageload-streamed => navigation}/init.js (60%) delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/init.js index bb6d4918d1bf..83076460599f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/init.js @@ -3,7 +3,6 @@ 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/async-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/test.ts index 5a2f34abecba..24516c270dba 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/async-spans/test.ts @@ -1,7 +1,6 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; type WindowWithSpan = Window & { firstWaitingSpan: any; @@ -19,8 +18,7 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); await page.goto(url); - const envelope = await getFirstSentryEnvelopeRequest(page); - expect(envelope).toBeDefined(); + await page.waitForFunction(() => !!(window as unknown as WindowWithSpan).secondWaitingSpan); const firstWaitingSpanValue = await page.evaluate( () => (window as unknown as WindowWithSpan).firstWaitingSpan.name, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/init.js index 1c1791241ab7..9a11fe3682bb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts index b6661c2638e2..ba3af4ced459 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts @@ -3,7 +3,7 @@ import type { StreamedSpanJSON } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; -sentryTest('should finish a custom transaction when the page goes background', async ({ getLocalTestUrl, page }) => { +sentryTest('should finish a custom span when the page goes background', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); } diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/subject.js deleted file mode 100644 index b657f38ac009..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/subject.js +++ /dev/null @@ -1,8 +0,0 @@ -document.getElementById('go-background').addEventListener('click', () => { - setTimeout(() => { - Object.defineProperty(document, 'hidden', { value: true, writable: true }); - const ev = document.createEvent('Event'); - ev.initEvent('visibilitychange'); - document.dispatchEvent(ev); - }, 250); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/template.html deleted file mode 100644 index 8083ddc80694..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/template.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts deleted file mode 100644 index f33f988be946..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; - -sentryTest('finishes streamed pageload span when the page goes background', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - - await page.goto(url); - await page.locator('#go-background').click(); - const pageloadSpan = await pageloadSpanPromise; - - // TODO: Is this what we want? - expect(pageloadSpan.status).toBe('ok'); - expect(pageloadSpan.attributes['sentry.cancellation_reason']?.value).toBe('document.hidden'); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/init.js similarity index 64% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/init.js index a93fc742bafb..83076460599f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/init.js @@ -4,6 +4,6 @@ window.Sentry = Sentry; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], + integrations: [Sentry.browserTracingIntegration()], tracesSampleRate: 1, }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts index 80ea1f5fa09c..5915b17d78eb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts @@ -1,20 +1,18 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; -sentryTest('should finish pageload transaction when the page goes background', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } +sentryTest('finishes streamed pageload span when the page goes background', async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); await page.locator('#go-background').click(); + const pageloadSpan = await pageloadSpanPromise; - const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); - - expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); - expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); - expect(pageloadTransaction.contexts?.trace?.data?.['sentry.cancellation_reason']).toBe('document.hidden'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(pageloadSpan.status).toBe('ok'); + expect(pageloadSpan.attributes['sentry.cancellation_reason']?.value).toBe('document.hidden'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts index bf54209f053a..8af1a0f80935 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts @@ -2,46 +2,32 @@ import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; import { - getMultipleSentryEnvelopeRequests, + envelopeRequestParser, + waitForErrorRequest, runScriptInSandbox, shouldSkipTracingTest, } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( - 'should put the pageload transaction name onto an error event caught during pageload', + 'puts the page path onto an error caught during pageload', async ({ getLocalTestUrl, page, browserName }) => { - if (browserName === 'webkit') { - // This test fails on Webkit as errors thrown from `runScriptInSandbox` are Script Errors and skipped by Sentry - sentryTest.skip(); - } - - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - + // WebKit treats errors from runScriptInSandbox as Script Errors, which Sentry skips. + sentryTest.skip(shouldSkipTracingTest() || browserName === 'webkit'); const url = await getLocalTestUrl({ testDir: __dirname }); - - const errorEventsPromise = getMultipleSentryEnvelopeRequests(page, 2); - + const spans = collectStreamedSpans(page); + const pageloadPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const errorPromise = waitForErrorRequest(page); await page.goto(url); - - await runScriptInSandbox(page, { - content: ` - throw new Error('Error during pageload'); - `, - }); - - const [e1, e2] = await errorEventsPromise; - - const pageloadTxnEvent = e1.type === 'transaction' ? e1 : e2; - const errorEvent = e1.type === 'transaction' ? e2 : e1; - - expect(pageloadTxnEvent.contexts?.trace?.op).toEqual('pageload'); - expect(pageloadTxnEvent.spans?.length).toBeGreaterThan(0); - expect(errorEvent.exception?.values?.[0]).toBeDefined(); - - expect(pageloadTxnEvent.transaction?.endsWith('index.html')).toBe(true); - - expect(errorEvent.transaction).toEqual(pageloadTxnEvent.transaction); + await runScriptInSandbox(page, { content: "throw new Error('Error during pageload');" }); + const [pageload, errorRequest] = await Promise.all([pageloadPromise, errorPromise]); + await page.evaluate(() => (window as any).Sentry.flush()); + expect(getSpanOp(pageload)).toBe('pageload'); + expect(spans.filter(span => !span.is_segment)).not.toHaveLength(0); + const error = envelopeRequestParser(errorRequest); + expect(error.exception?.values?.[0]).toBeDefined(); + expect(pageload.name).toBe('Pageload'); + expect(error.transaction).toBe('/index.html'); + expect(error.transaction).toBe(pageload.attributes['url.path']?.value); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/init.js deleted file mode 100644 index 8ec045483e0c..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/init.js +++ /dev/null @@ -1,18 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - _experiments: { - enableHTTPTimings: true, - }, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, - traceLifecycle: 'stream', - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/subject.js deleted file mode 100644 index e19cc07e28f5..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/subject.js +++ /dev/null @@ -1,3 +0,0 @@ -fetch('http://sentry-test-site.example/0').then( - fetch('http://sentry-test-site.example/1').then(fetch('http://sentry-test-site.example/2')), -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts deleted file mode 100644 index 7c02a63fc41c..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest( - 'adds http timing to http.client spans in span streaming mode', - async ({ browserName, getLocalTestUrl, page }) => { - const supportedBrowsers = ['chromium', 'firefox']; - - sentryTest.skip(shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)); - - await page.route('http://sentry-test-site.example/*', async route => { - const request = route.request(); - const postData = await request.postDataJSON(); - - await route.fulfill({ - status: 200, - contentType: 'application/json', - body: JSON.stringify(Object.assign({ id: 1 }, postData)), - }); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'http.client')); - await page.goto(url); - - const requestSpans = (await spansPromise).filter(s => getSpanOp(s) === 'http.client'); - const pageloadSpan = (await spansPromise).find(s => getSpanOp(s) === 'pageload'); - - expect(pageloadSpan).toBeDefined(); - expect(requestSpans).toHaveLength(3); - - requestSpans?.forEach(span => - expect(span).toMatchObject({ - // Streamed span names drop the high-cardinality URL path. - name: 'GET sentry-test-site.example', - parent_span_id: pageloadSpan?.span_id, - span_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - end_timestamp: expect.any(Number), - trace_id: pageloadSpan?.trace_id, - status: 'ok', - attributes: expect.objectContaining({ - 'http.request.redirect_start': expect.any(Object), - 'http.request.redirect_end': expect.any(Object), - 'http.request.worker_start': expect.any(Object), - 'http.request.fetch_start': expect.any(Object), - 'http.request.domain_lookup_start': expect.any(Object), - 'http.request.domain_lookup_end': expect.any(Object), - 'http.request.connect_start': expect.any(Object), - 'http.request.secure_connection_start': expect.any(Object), - 'http.request.connection_end': expect.any(Object), - 'http.request.request_start': expect.any(Object), - 'http.request.response_start': expect.any(Object), - 'http.request.response_end': expect.any(Object), - 'http.request.time_to_first_byte': expect.any(Object), - 'network.protocol.version': expect.any(Object), - }), - }), - ); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/init.js index 5e521633ad12..575e3fa65693 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts index 6894d7407349..162aebbd4e01 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts @@ -1,59 +1,95 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; - -sentryTest('creates fetch spans with http timing', async ({ browserName, getLocalTestUrl, page }) => { - const supportedBrowsers = ['chromium', 'firefox']; - - if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { - sentryTest.skip(); - } - await page.route('http://sentry-test-site.example/*', async route => { - const request = route.request(); - const postData = await request.postDataJSON(); - - await route.fulfill({ - status: 200, - contentType: 'application/json', - body: JSON.stringify(Object.assign({ id: 1 }, postData)), +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; + +sentryTest( + 'adds http timing to http.client spans in span streaming mode', + async ({ browserName, getLocalTestUrl, page }) => { + const supportedBrowsers = ['chromium', 'firefox']; + + sentryTest.skip(shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)); + + await page.route('http://sentry-test-site.example/*', async route => { + const request = route.request(); + const postData = await request.postDataJSON(); + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(Object.assign({ id: 1 }, postData)), + }); }); - }); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); - const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers - - const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); - - expect(requestSpans).toHaveLength(3); - - await page.pause(); - requestSpans?.forEach((span, index) => - expect(span).toMatchObject({ - description: `GET http://sentry-test-site.example/${index}`, - parent_span_id: tracingEvent.contexts?.trace?.span_id, - span_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: tracingEvent.contexts?.trace?.trace_id, - data: expect.objectContaining({ - 'http.request.redirect_start': expect.any(Number), - 'http.request.redirect_end': expect.any(Number), - 'http.request.worker_start': expect.any(Number), - 'http.request.fetch_start': expect.any(Number), - 'http.request.domain_lookup_start': expect.any(Number), - 'http.request.domain_lookup_end': expect.any(Number), - 'http.request.connect_start': expect.any(Number), - 'http.request.secure_connection_start': expect.any(Number), - 'http.request.connection_end': expect.any(Number), - 'http.request.request_start': expect.any(Number), - 'http.request.response_start': expect.any(Number), - 'http.request.response_end': expect.any(Number), - 'http.request.time_to_first_byte': expect.any(Number), - 'network.protocol.version': expect.any(String), + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + await page.goto(url); + + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + const requestSpans = spans + .filter(s => getSpanOp(s) === 'http.client') + .sort((a, b) => String(a.attributes['url.full']?.value).localeCompare(String(b.attributes['url.full']?.value))); + + expect(pageloadSpan).toBeDefined(); + expect(requestSpans).toHaveLength(3); + + requestSpans?.forEach((span, index) => + expect(span).toMatchObject({ + name: 'GET sentry-test-site.example', + parent_span_id: pageloadSpan?.span_id, + span_id: expect.stringMatching(/[a-f\d]{16}/), + start_timestamp: expect.any(Number), + end_timestamp: expect.any(Number), + trace_id: pageloadSpan?.trace_id, + status: 'ok', + attributes: expect.objectContaining({ + 'url.full': { type: 'string', value: `http://sentry-test-site.example/${index}` }, + 'http.request.redirect_start': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.redirect_end': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + 'http.request.worker_start': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + 'http.request.fetch_start': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + 'http.request.domain_lookup_start': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.domain_lookup_end': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.connect_start': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.secure_connection_start': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.connection_end': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.request_start': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.response_start': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'http.request.response_end': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + 'http.request.time_to_first_byte': { + type: expect.stringMatching(/^(integer|double)$/), + value: expect.any(Number), + }, + 'network.protocol.version': { type: 'string', value: expect.any(String) }, + }), }), - }), - ); -}); + ); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/init.js index bb6d4918d1bf..83076460599f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/init.js @@ -3,7 +3,6 @@ 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/linked-traces-streamed/consistent-sampling/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/init.js deleted file mode 100644 index 63afee65329a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/init.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - linkPreviousTrace: 'in-memory', - consistentTraceSampling: true, - }), - Sentry.spanStreamingIntegration(), - ], - tracePropagationTargets: ['sentry-test-external.io'], - tracesSampler: ctx => { - if (ctx.attributes && ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { - return 1; - } - return ctx.inheritOrSampleWith(0); - }, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/subject.js deleted file mode 100644 index de60904fab3a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -const btn1 = document.getElementById('btn1'); - -const btn2 = document.getElementById('btn2'); - -btn1.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); - }); -}); - -btn2.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { - await fetch('http://sentry-test-external.io'); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/template.html deleted file mode 100644 index f26a602c7c6f..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/template.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts deleted file mode 100644 index 3ef7ddefca6d..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { expect } from '@playwright/test'; -import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; -import { sentryTest } from '../../../../../../utils/fixtures'; -import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; - -sentryTest.describe('When `consistentTraceSampling` is `true`', () => { - sentryTest('continues sampling decision from initial pageload span', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const { pageloadSpan, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { - const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), - ); - await page.goto(url); - - const envelope = await pageloadEnvelopePromise; - const pageloadSampleRand = Number(envelope[0].trace?.sample_rand); - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(pageloadSpan.attributes['sentry.sample_rate']?.value).toBe(1); - expect(Number.isNaN(pageloadSampleRand)).toBe(false); - expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); - expect(pageloadSampleRand).toBeLessThanOrEqual(1); - - return { pageloadSpan, pageloadSampleRand }; - }); - - const customTraceSpan = await sentryTest.step('Custom trace', async () => { - const customEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), - ); - await page.locator('#btn1').click(); - const envelope = await customEnvelopePromise; - const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - - expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); - // although we "continue the trace" from pageload, this is actually a root span, - // so there must not be a parent span id - expect(span.parent_span_id).toBeUndefined(); - - expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); - - return span; - }); - - await sentryTest.step('Navigation', async () => { - const navigationEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), - ); - await page.goto(`${url}#foo`); - const envelope = await navigationEnvelopePromise; - const navSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; - - expect(navSpan.trace_id).not.toEqual(customTraceSpan.trace_id); - expect(navSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - - expect(navSpan.links).toEqual([ - { - trace_id: customTraceSpan.trace_id, - span_id: customTraceSpan.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - expect(navSpan.parent_span_id).toBeUndefined(); - - expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); - }); - }); - - sentryTest('Propagates continued sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const { pageloadSpan, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { - const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), - ); - await page.goto(url); - - const envelope = await pageloadEnvelopePromise; - const pageloadSampleRand = Number(envelope[0].trace?.sample_rand); - - expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); - expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); - expect(pageloadSampleRand).toBeLessThanOrEqual(1); - expect(Number.isNaN(pageloadSampleRand)).toBe(false); - - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(pageloadSpan.attributes['sentry.sample_rate']?.value).toBe(1); - - return { pageloadSpan, pageloadSampleRand }; - }); - - await sentryTest.step('Make fetch request', async () => { - const fetchEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), - ); - const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); - - await page.locator('#btn2').click(); - - const { baggage, sentryTrace } = await tracingHeadersPromise; - const fetchEnvelope = await fetchEnvelopePromise; - - const fetchTraceSampleRand = Number(fetchEnvelope[0].trace?.sample_rand); - const fetchTraceSpans = fetchEnvelope[1][0][1].items; - const fetchTraceSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'custom')!; - const httpClientSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'http.client'); - - expect(fetchTraceSampleRand).toBe(pageloadSampleRand); - - expect(fetchTraceSpan.attributes['sentry.sample_rate']?.value).toEqual( - pageloadSpan.attributes['sentry.sample_rate']?.value, - ); - expect(fetchTraceSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - - expect(sentryTrace).toBeDefined(); - expect(baggage).toBeDefined(); - - expect(extractTraceparentData(sentryTrace)).toEqual({ - traceId: fetchTraceSpan.trace_id, - parentSpanId: httpClientSpan?.span_id, - parentSampled: true, - }); - - expect(parseBaggageHeader(baggage)).toEqual({ - 'sentry-environment': 'production', - 'sentry-public_key': 'public', - 'sentry-sample_rand': `${pageloadSampleRand}`, - 'sentry-sample_rate': '1', - 'sentry-sampled': 'true', - 'sentry-trace_id': fetchTraceSpan.trace_id, - 'sentry-transaction': 'custom root span 2', - }); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/init.js deleted file mode 100644 index d570ac45144c..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/init.js +++ /dev/null @@ -1,19 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - linkPreviousTrace: 'in-memory', - consistentTraceSampling: true, - }), - Sentry.spanStreamingIntegration(), - ], - traceLifecycle: 'stream', - tracePropagationTargets: ['sentry-test-external.io'], - tracesSampleRate: 1, - debug: true, - sendClientReports: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/subject.js deleted file mode 100644 index de60904fab3a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -const btn1 = document.getElementById('btn1'); - -const btn2 = document.getElementById('btn2'); - -btn1.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); - }); -}); - -btn2.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { - await fetch('http://sentry-test-external.io'); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/template.html deleted file mode 100644 index 6347fa37fc00..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/template.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts deleted file mode 100644 index efd4ccce4e37..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { expect } from '@playwright/test'; -import type { ClientReport, SerializedStreamedSpan } from '@sentry/core'; -import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; -import { sentryTest } from '../../../../../../utils/fixtures'; -import { - envelopeRequestParser, - hidePage, - shouldSkipTracingTest, - waitForClientReportRequest, - waitForTracingHeadersOnUrl, -} from '../../../../../../utils/helpers'; -import { observeStreamedSpan } from '../../../../../../utils/spanUtils'; - -const metaTagSampleRand = 0.9; -const metaTagSampleRate = 0.2; -const metaTagTraceId = '12345678901234567890123456789012'; - -sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { - sentryTest( - 'Continues negative sampling decision from meta tag across all traces and downstream propagations', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansReceived: SerializedStreamedSpan[] = []; - observeStreamedSpan(page, span => { - spansReceived.push(span); - return false; - }); - - const clientReportPromise = waitForClientReportRequest(page); - - await sentryTest.step('Initial pageload', async () => { - await page.goto(url); - expect(spansReceived).toHaveLength(0); - }); - - await sentryTest.step('Custom instrumented button click', async () => { - await page.locator('#btn1').click(); - expect(spansReceived).toHaveLength(0); - }); - - await sentryTest.step('Navigation', async () => { - await page.goto(`${url}#foo`); - expect(spansReceived).toHaveLength(0); - }); - - await sentryTest.step('Make fetch request', async () => { - const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); - - await page.locator('#btn2').click(); - const { baggage, sentryTrace } = await tracingHeadersPromise; - - expect(sentryTrace).toBeDefined(); - expect(baggage).toBeDefined(); - - expect(extractTraceparentData(sentryTrace)).toEqual({ - traceId: expect.not.stringContaining(metaTagTraceId), - parentSpanId: expect.stringMatching(/^[\da-f]{16}$/), - parentSampled: false, - }); - - expect(parseBaggageHeader(baggage)).toEqual({ - 'sentry-environment': 'production', - 'sentry-public_key': 'public', - 'sentry-sample_rand': `${metaTagSampleRand}`, - 'sentry-sample_rate': `${metaTagSampleRate}`, - 'sentry-sampled': 'false', - 'sentry-trace_id': expect.not.stringContaining(metaTagTraceId), - 'sentry-transaction': 'custom root span 2', - }); - - expect(spansReceived).toHaveLength(0); - }); - - await sentryTest.step('Client report', async () => { - await hidePage(page); - const clientReport = envelopeRequestParser(await clientReportPromise); - expect(clientReport).toEqual({ - timestamp: expect.any(Number), - discarded_events: [ - { - category: 'span', - quantity: expect.any(Number), - reason: 'sample_rate', - }, - ], - }); - // exact number depends on performance observer emissions - expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(10); - }); - - expect(spansReceived).toHaveLength(0); - }, - ); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/init.js deleted file mode 100644 index 177fe4c4aeaf..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/init.js +++ /dev/null @@ -1,20 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - linkPreviousTrace: 'session-storage', - consistentTraceSampling: true, - }), - Sentry.spanStreamingIntegration(), - ], - tracePropagationTargets: ['sentry-test-external.io'], - tracesSampler: ({ inheritOrSampleWith }) => { - return inheritOrSampleWith(0); - }, - debug: true, - sendClientReports: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-1.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-1.html deleted file mode 100644 index 9a0719b7e505..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-1.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - -

Another Page

- Go To the next page - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-2.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-2.html deleted file mode 100644 index 27cd47bba7c1..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-2.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -

Another Page

- Go To the next page - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/subject.js deleted file mode 100644 index ec0264fa49ef..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -const btn1 = document.getElementById('btn1'); - -const btn2 = document.getElementById('btn2'); - -btn1?.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); - }); -}); - -btn2?.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { - await fetch('http://sentry-test-external.io'); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/template.html deleted file mode 100644 index eab1fecca6c4..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/template.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - Go To another page - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts deleted file mode 100644 index cd64596f9bfd..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { expect } from '@playwright/test'; -import type { ClientReport } from '@sentry/core'; -import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; -import { sentryTest } from '../../../../../../utils/fixtures'; -import { - envelopeRequestParser, - hidePage, - shouldSkipTracingTest, - waitForClientReportRequest, - waitForTracingHeadersOnUrl, -} from '../../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; - -const metaTagSampleRand = 0.9; -const metaTagSampleRate = 0.2; -const metaTagTraceIdIndex = '12345678901234567890123456789012'; -const metaTagTraceIdPage1 = 'a2345678901234567890123456789012'; - -sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { - sentryTest( - 'meta tag decision has precedence over sampling decision from previous trace in session storage', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const clientReportPromise = waitForClientReportRequest(page); - - await sentryTest.step('Initial pageload', async () => { - // negative sampling decision -> no pageload span - await page.goto(url); - }); - - await sentryTest.step('Make fetch request', async () => { - // The fetch requests starts a new trace on purpose. So we only want the - // sampling decision and rand to be the same as from the meta tag but not the trace id or DSC - const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); - - await page.locator('#btn2').click(); - - const { baggage, sentryTrace } = await tracingHeadersPromise; - - expect(sentryTrace).toBeDefined(); - expect(baggage).toBeDefined(); - - expect(extractTraceparentData(sentryTrace)).toEqual({ - traceId: expect.not.stringContaining(metaTagTraceIdIndex), - parentSpanId: expect.stringMatching(/^[\da-f]{16}$/), - parentSampled: false, - }); - - expect(parseBaggageHeader(baggage)).toEqual({ - 'sentry-environment': 'production', - 'sentry-public_key': 'public', - 'sentry-sample_rand': `${metaTagSampleRand}`, - 'sentry-sample_rate': `${metaTagSampleRate}`, - 'sentry-sampled': 'false', - 'sentry-trace_id': expect.not.stringContaining(metaTagTraceIdIndex), - 'sentry-transaction': 'custom root span 2', - }); - }); - - await sentryTest.step('Client report', async () => { - await hidePage(page); - - const clientReport = envelopeRequestParser(await clientReportPromise); - expect(clientReport).toEqual({ - timestamp: expect.any(Number), - discarded_events: [ - { - category: 'span', - quantity: expect.any(Number), - reason: 'sample_rate', - }, - ], - }); - // exact number depends on performance observer emissions - expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(3); - }); - - await sentryTest.step('Navigate to another page with meta tags', async () => { - const page1PageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload' && s.trace_id === metaTagTraceIdPage1), - ); - await page.locator('a').click(); - - const envelope = await page1PageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(Number(envelope[0].trace?.sample_rand)).toBe(0.12); - expect(Number(envelope[0].trace?.sample_rate)).toBe(0.2); - expect(pageloadSpan.trace_id).toEqual(metaTagTraceIdPage1); - }); - - await sentryTest.step('Navigate to another page without meta tags', async () => { - const page2PageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => - !!env[1][0][1].items.find( - s => - getSpanOp(s) === 'pageload' && s.trace_id !== metaTagTraceIdPage1 && s.trace_id !== metaTagTraceIdIndex, - ), - ); - await page.locator('a').click(); - - const envelope = await page2PageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(Number(envelope[0].trace?.sample_rand)).toBe(0.12); - expect(Number(envelope[0].trace?.sample_rate)).toBe(0.2); - expect(pageloadSpan.trace_id).not.toEqual(metaTagTraceIdPage1); - expect(pageloadSpan.trace_id).not.toEqual(metaTagTraceIdIndex); - }); - }, - ); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/init.js deleted file mode 100644 index a1ddc5465950..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/init.js +++ /dev/null @@ -1,18 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - linkPreviousTrace: 'in-memory', - consistentTraceSampling: true, - }), - Sentry.spanStreamingIntegration(), - ], - tracePropagationTargets: ['sentry-test-external.io'], - // only take into account sampling from meta tag; otherwise sample negatively - tracesSampleRate: 0, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/subject.js deleted file mode 100644 index de60904fab3a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -const btn1 = document.getElementById('btn1'); - -const btn2 = document.getElementById('btn2'); - -btn1.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); - }); -}); - -btn2.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { - await fetch('http://sentry-test-external.io'); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/template.html deleted file mode 100644 index 7ceca6fec2a3..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/template.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts deleted file mode 100644 index 2657bed50d61..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { expect } from '@playwright/test'; -import { - extractTraceparentData, - parseBaggageHeader, - SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE, -} from '@sentry/core'; -import { sentryTest } from '../../../../../../utils/fixtures'; -import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; - -const metaTagSampleRand = 0.051121; -const metaTagSampleRate = 0.2; - -sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { - sentryTest('Continues sampling decision across all traces from meta tag', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpan = await sentryTest.step('Initial pageload', async () => { - const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), - ); - - await page.goto(url); - - const envelope = await pageloadEnvelopePromise; - const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(Number(envelope[0].trace?.sample_rand)).toBe(metaTagSampleRand); - expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); - - // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(span.attributes['sentry.sample_rate']).toBeUndefined(); - expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); - - return span; - }); - - const customTraceSpan = await sentryTest.step('Custom trace', async () => { - const customEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), - ); - - await page.locator('#btn1').click(); - - const envelope = await customEnvelopePromise; - const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - - expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); - expect(span.parent_span_id).toBeUndefined(); - - expect(Number(envelope[0].trace?.sample_rand)).toBe(metaTagSampleRand); - expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); - expect(envelope[0].trace?.sampled).toBe('true'); - - // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(span.attributes['sentry.sample_rate']).toBeUndefined(); - - // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) - expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe(metaTagSampleRate); - - return span; - }); - - await sentryTest.step('Navigation', async () => { - const navigationEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), - ); - - await page.goto(`${url}#foo`); - - const envelope = await navigationEnvelopePromise; - const navSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; - - expect(navSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - expect(navSpan.trace_id).not.toEqual(customTraceSpan.trace_id); - - expect(navSpan.parent_span_id).toBeUndefined(); - - expect(Number(envelope[0].trace?.sample_rand)).toEqual(metaTagSampleRand); - expect(Number(envelope[0].trace?.sample_rate)).toEqual(metaTagSampleRate); - expect(envelope[0].trace?.sampled).toEqual('true'); - - // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(navSpan.attributes['sentry.sample_rate']).toBeUndefined(); - - // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) - expect(navSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe(metaTagSampleRate); - }); - }); - - sentryTest( - 'Propagates continued tag sampling decision to outgoing requests', - async ({ page, getLocalTestUrl }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpan = await sentryTest.step('Initial pageload', async () => { - const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), - ); - - await page.goto(url); - - const envelope = await pageloadEnvelopePromise; - const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(Number(envelope[0].trace?.sample_rand)).toBe(metaTagSampleRand); - expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); - - // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(span.attributes['sentry.sample_rate']).toBeUndefined(); - expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); - - return span; - }); - - await sentryTest.step('Make fetch request', async () => { - const fetchEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), - ); - const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); - - await page.locator('#btn2').click(); - - const { baggage, sentryTrace } = await tracingHeadersPromise; - const fetchEnvelope = await fetchEnvelopePromise; - - const fetchTraceSampleRand = Number(fetchEnvelope[0].trace?.sample_rand); - const fetchTraceSpans = fetchEnvelope[1][0][1].items; - const fetchTraceSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'custom')!; - const httpClientSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'http.client'); - - expect(fetchTraceSampleRand).toEqual(metaTagSampleRand); - - expect(fetchTraceSpan.attributes['sentry.sample_rate']).toBeUndefined(); - expect(fetchTraceSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe( - metaTagSampleRate, - ); - - expect(fetchTraceSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - - expect(sentryTrace).toBeDefined(); - expect(baggage).toBeDefined(); - - expect(extractTraceparentData(sentryTrace)).toEqual({ - traceId: fetchTraceSpan.trace_id, - parentSpanId: httpClientSpan?.span_id, - parentSampled: true, - }); - - expect(parseBaggageHeader(baggage)).toEqual({ - 'sentry-environment': 'production', - 'sentry-public_key': 'public', - 'sentry-sample_rand': `${metaTagSampleRand}`, - 'sentry-sample_rate': `${metaTagSampleRate}`, - 'sentry-sampled': 'true', - 'sentry-trace_id': fetchTraceSpan.trace_id, - 'sentry-transaction': 'custom root span 2', - }); - }); - }, - ); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/init.js deleted file mode 100644 index 623db0ecc028..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/init.js +++ /dev/null @@ -1,29 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - linkPreviousTrace: 'in-memory', - consistentTraceSampling: true, - enableInp: false, - }), - Sentry.spanStreamingIntegration(), - ], - tracePropagationTargets: ['sentry-test-external.io'], - tracesSampler: ctx => { - if (ctx.attributes && ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { - return 1; - } - if (ctx.name === 'custom root span 1') { - return 0; - } - if (ctx.name === 'custom root span 2') { - return 1; - } - return ctx.inheritOrSampleWith(0); - }, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/subject.js deleted file mode 100644 index de60904fab3a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -const btn1 = document.getElementById('btn1'); - -const btn2 = document.getElementById('btn2'); - -btn1.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); - }); -}); - -btn2.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { - await fetch('http://sentry-test-external.io'); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/template.html deleted file mode 100644 index f26a602c7c6f..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/template.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts deleted file mode 100644 index 37e3b204fe88..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { expect } from '@playwright/test'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import type { ClientReport } from '@sentry/core'; -import { sentryTest } from '../../../../../../utils/fixtures'; -import { - envelopeRequestParser, - hidePage, - shouldSkipTracingTest, - waitForClientReportRequest, -} from '../../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; - -/** - * This test demonstrates that: - * - explicit sampling decisions in `tracesSampler` has precedence over consistent sampling - * - despite consistentTraceSampling being activated, there are still a lot of cases where the trace chain can break - */ -sentryTest.describe('When `consistentTraceSampling` is `true`', () => { - sentryTest('explicit sampling decisions in `tracesSampler` have precedence', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const { pageloadSpan } = await sentryTest.step('Initial pageload', async () => { - const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), - ); - await page.goto(url); - - const envelope = await pageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - - expect(pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); - expect(Number(envelope[0].trace?.sample_rand)).toBeGreaterThanOrEqual(0); - - return { pageloadSpan }; - }); - - await sentryTest.step('Custom trace is sampled negatively (explicitly in tracesSampler)', async () => { - const clientReportPromise = waitForClientReportRequest(page); - - await page.locator('#btn1').click(); - - await page.waitForTimeout(500); - await hidePage(page); - - const clientReport = envelopeRequestParser(await clientReportPromise); - - expect(clientReport).toEqual({ - timestamp: expect.any(Number), - discarded_events: [ - { - category: 'span', - quantity: 1, - reason: 'sample_rate', - }, - ], - }); - }); - - await sentryTest.step('Subsequent navigation trace is also sampled negatively', async () => { - const clientReportPromise = waitForClientReportRequest(page); - - await page.goto(`${url}#foo`); - - await page.waitForTimeout(500); - - await hidePage(page); - - const clientReport = envelopeRequestParser(await clientReportPromise); - - expect(clientReport).toEqual({ - timestamp: expect.any(Number), - discarded_events: [ - { - category: 'span', - quantity: 1, - reason: 'sample_rate', - }, - ], - }); - }); - - const { customTrace2Span } = await sentryTest.step( - 'Custom trace 2 is sampled positively (explicitly in tracesSampler)', - async () => { - const customEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), - ); - - await page.locator('#btn2').click(); - - const envelope = await customEnvelopePromise; - const customTrace2Span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - - expect(customTrace2Span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); - expect(customTrace2Span.trace_id).not.toEqual(pageloadSpan.trace_id); - expect(customTrace2Span.parent_span_id).toBeUndefined(); - - expect(customTrace2Span.links).toEqual([ - { - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - sampled: false, - span_id: expect.stringMatching(/^[\da-f]{16}$/), - trace_id: expect.stringMatching(/^[\da-f]{32}$/), - }, - ]); - - return { customTrace2Span }; - }, - ); - - await sentryTest.step('Navigation trace is sampled positively (inherited from previous trace)', async () => { - const navigationEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => env[0].trace?.sampled === 'true' && !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), - ); - - await page.goto(`${url}#bar`); - - const envelope = await navigationEnvelopePromise; - const navigationSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; - - expect(navigationSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); - expect(navigationSpan.trace_id).not.toEqual(customTrace2Span.trace_id); - expect(navigationSpan.parent_span_id).toBeUndefined(); - - expect(navigationSpan.links).toEqual([ - { - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - sampled: true, - span_id: customTrace2Span.span_id, - trace_id: customTrace2Span.trace_id, - }, - ]); - }); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/subject.js deleted file mode 100644 index 2a929a7e5083..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/subject.js +++ /dev/null @@ -1,14 +0,0 @@ -const btn1 = document.getElementById('btn1'); -const btn2 = document.getElementById('btn2'); - -btn1.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); - }); -}); - -btn2.addEventListener('click', () => { - Sentry.startNewTrace(() => { - Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, () => {}); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/template.html deleted file mode 100644 index f26a602c7c6f..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/template.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts deleted file mode 100644 index 7bb7a5546910..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { expect } from '@playwright/test'; -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest('manually started custom traces are linked correctly in the chain', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpan = await sentryTest.step('Initial pageload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.goto(url); - return pageloadSpanPromise; - }); - - const customTraceSpan = await sentryTest.step('Custom trace', async () => { - const customSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'custom'); - await page.locator('#btn1').click(); - const span = await customSpanPromise; - - expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); - expect(span.links).toEqual([ - { - trace_id: pageloadSpan.trace_id, - span_id: pageloadSpan.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - return span; - }); - - await sentryTest.step('Navigation', async () => { - const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - await page.goto(`${url}#foo`); - const navSpan = await navigationSpanPromise; - - expect(navSpan.trace_id).not.toEqual(customTraceSpan.trace_id); - expect(navSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - - expect(navSpan.links).toEqual([ - { - trace_id: customTraceSpan.trace_id, - span_id: customTraceSpan.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts deleted file mode 100644 index 5fb19844a3ce..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/default/test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { expect } from '@playwright/test'; -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest("navigation spans link back to previous trace's root span", async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.goto(url); - const pageloadSpan = await pageloadSpanPromise; - - const navigation1SpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - await page.goto(`${url}#foo`); - const navigation1Span = await navigation1SpanPromise; - - const navigation2SpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - await page.goto(`${url}#bar`); - const navigation2Span = await navigation2SpanPromise; - - const pageloadTraceId = pageloadSpan.trace_id; - const navigation1TraceId = navigation1Span.trace_id; - const navigation2TraceId = navigation2Span.trace_id; - - expect(pageloadSpan.links).toBeUndefined(); - - expect(navigation1Span.links).toEqual([ - { - trace_id: pageloadTraceId, - span_id: pageloadSpan.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - expect(navigation1Span.attributes['sentry.previous_trace']).toEqual({ - type: 'string', - value: `${pageloadTraceId}-${pageloadSpan.span_id}-1`, - }); - - expect(navigation2Span.links).toEqual([ - { - trace_id: navigation1TraceId, - span_id: navigation1Span.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - expect(navigation2Span.attributes['sentry.previous_trace']).toEqual({ - type: 'string', - value: `${navigation1TraceId}-${navigation1Span.span_id}-1`, - }); - - expect(pageloadTraceId).not.toEqual(navigation1TraceId); - expect(navigation1TraceId).not.toEqual(navigation2TraceId); - expect(pageloadTraceId).not.toEqual(navigation2TraceId); -}); - -sentryTest("doesn't link between hard page reloads by default", async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - await sentryTest.step('First pageload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.goto(url); - const pageload1Span = await pageloadSpanPromise; - - expect(pageload1Span).toBeDefined(); - expect(pageload1Span.links).toBeUndefined(); - }); - - await sentryTest.step('Second pageload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.reload(); - const pageload2Span = await pageloadSpanPromise; - - expect(pageload2Span).toBeDefined(); - expect(pageload2Span.links).toBeUndefined(); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/init.js deleted file mode 100644 index 749560a5c459..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/init.js +++ /dev/null @@ -1,10 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], - tracesSampleRate: 1, - debug: true, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/init.js deleted file mode 100644 index 812e8cb92dc2..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/init.js +++ /dev/null @@ -1,10 +0,0 @@ -import * as Sentry from '@sentry/browser'; -import { interactionsIntegration } from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, - integrations: [Sentry.browserTracingIntegration(), interactionsIntegration(), Sentry.spanStreamingIntegration()], -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/template.html deleted file mode 100644 index 7f6845239468..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/template.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts deleted file mode 100644 index bfc974fc953a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { expect } from '@playwright/test'; -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -/* - This is quite peculiar behavior but it's a result of the route-based trace lifetime. - Once we shortened trace lifetime, this whole scenario will change as the interaction - spans will be their own trace. So most likely, we can replace this test with a new one - that covers the new default behavior. -*/ -sentryTest( - 'only the first root spans in the trace link back to the previous trace', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpan = await sentryTest.step('Initial pageload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.goto(url); - const span = await pageloadSpanPromise; - - expect(span).toBeDefined(); - expect(span.links).toBeUndefined(); - - return span; - }); - - await sentryTest.step('Click Before navigation', async () => { - const interactionSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.action.click'); - await page.click('#btn'); - const interactionSpan = await interactionSpanPromise; - - // sanity check: route-based trace lifetime means the trace_id should be the same - expect(interactionSpan.trace_id).toBe(pageloadSpan.trace_id); - - // no links yet as previous root span belonged to same trace - expect(interactionSpan.links).toBeUndefined(); - }); - - const navigationSpan = await sentryTest.step('Navigation', async () => { - const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - await page.goto(`${url}#foo`); - const span = await navigationSpanPromise; - - expect(getSpanOp(span)).toBe('navigation'); - expect(span.links).toEqual([ - { - trace_id: pageloadSpan.trace_id, - span_id: pageloadSpan.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - expect(span.trace_id).not.toEqual(span.links![0].trace_id); - return span; - }); - - await sentryTest.step('Click After navigation', async () => { - const interactionSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.action.click'); - await page.click('#btn'); - const interactionSpan = await interactionSpanPromise; - - // sanity check: route-based trace lifetime means the trace_id should be the same - expect(interactionSpan.trace_id).toBe(navigationSpan.trace_id); - - // since this is the second root span in the trace, it doesn't link back to the previous trace - expect(interactionSpan.links).toBeUndefined(); - }); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/template.html deleted file mode 100644 index 2221bd0fee1d..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/template.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts deleted file mode 100644 index 732547954091..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { expect } from '@playwright/test'; -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest( - "links back to previous trace's local root span if continued from meta tags", - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const metaTagTraceId = '12345678901234567890123456789012'; - - const pageloadSpan = await sentryTest.step('Initial pageload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.goto(url); - const span = await pageloadSpanPromise; - - // sanity check - expect(span.trace_id).toBe(metaTagTraceId); - expect(span.links).toBeUndefined(); - - return span; - }); - - const navigationSpan = await sentryTest.step('Navigation', async () => { - const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - await page.goto(`${url}#foo`); - return navigationSpanPromise; - }); - - expect(navigationSpan.links).toEqual([ - { - trace_id: metaTagTraceId, - span_id: pageloadSpan.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - expect(navigationSpan.trace_id).not.toEqual(metaTagTraceId); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/init.js deleted file mode 100644 index 778092cf026b..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - // We want to ignore redirects for this test - integrations: [Sentry.browserTracingIntegration({ detectRedirects: false }), Sentry.spanStreamingIntegration()], - tracesSampler: ctx => { - if (ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { - return 0; - } - return 1; - }, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts deleted file mode 100644 index 4bc05cb95737..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { expect } from '@playwright/test'; -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest('includes a span link to a previously negatively sampled span', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - await sentryTest.step('Initial pageload', async () => { - // No span envelope expected here because this pageload span is sampled negatively! - await page.goto(url); - }); - - await sentryTest.step('Navigation', async () => { - const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - await page.goto(`${url}#foo`); - const navigationSpan = await navigationSpanPromise; - - expect(getSpanOp(navigationSpan)).toBe('navigation'); - expect(navigationSpan.links).toEqual([ - { - trace_id: expect.stringMatching(/[a-f\d]{32}/), - span_id: expect.stringMatching(/[a-f\d]{16}/), - sampled: false, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - expect(navigationSpan.attributes['sentry.previous_trace']).toEqual({ - type: 'string', - value: expect.stringMatching(/[a-f\d]{32}-[a-f\d]{16}-0/), - }); - - expect(navigationSpan.trace_id).not.toEqual(navigationSpan.links![0].trace_id); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/init.js deleted file mode 100644 index e51af56c2a9d..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/init.js +++ /dev/null @@ -1,12 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ linkPreviousTrace: 'session-storage' }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts deleted file mode 100644 index 81d1d31e10e3..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { expect } from '@playwright/test'; -import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest('adds link between hard page reloads when opting into sessionStorage', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageload1Span = await sentryTest.step('First pageload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.goto(url); - const span = await pageloadSpanPromise; - expect(span).toBeDefined(); - expect(span.links).toBeUndefined(); - return span; - }); - - const pageload2Span = await sentryTest.step('Hard page reload', async () => { - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - await page.reload(); - return pageloadSpanPromise; - }); - - expect(pageload2Span.links).toEqual([ - { - trace_id: pageload1Span.trace_id, - span_id: pageload1Span.span_id, - sampled: true, - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, - }, - ]); - - expect(pageload1Span.trace_id).not.toEqual(pageload2Span.trace_id); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/init.js index 04a299421004..2497d6da6b84 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts index 4d13371bc693..3ef7ddefca6d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts @@ -1,136 +1,141 @@ import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; -import { extractTraceparentData, parseBaggageHeader, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; +import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; -import { - eventAndTraceHeaderRequestParser, - shouldSkipTracingTest, - waitForTracingHeadersOnUrl, - waitForTransactionRequest, -} from '../../../../../../utils/helpers'; +import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; sentryTest.describe('When `consistentTraceSampling` is `true`', () => { - sentryTest('Continues sampling decision from initial pageload', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest('continues sampling decision from initial pageload span', async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const { pageloadTraceContext, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => { - return evt.contexts?.trace?.op === 'pageload'; - }); + const { pageloadSpan, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { + const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), + ); await page.goto(url); - const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); - const pageloadSampleRand = Number(res[1]?.sample_rand); - const pageloadTraceContext = res[0].contexts?.trace; + const envelope = await pageloadEnvelopePromise; + const pageloadSampleRand = Number(envelope[0].trace?.sample_rand); + const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(pageloadSpan.attributes['sentry.sample_rate']?.value).toBe(1); + expect(Number.isNaN(pageloadSampleRand)).toBe(false); expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); + expect(pageloadSampleRand).toBeLessThanOrEqual(1); - return { pageloadTraceContext: res[0].contexts?.trace, pageloadSampleRand }; + return { pageloadSpan, pageloadSampleRand }; }); - const customTraceContext = await sentryTest.step('Custom trace', async () => { - const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); - await page.locator('#btn1').click(); - const [customTrace1Event, customTraceTraceHeader] = eventAndTraceHeaderRequestParser( - await customTrace1RequestPromise, + const customTraceSpan = await sentryTest.step('Custom trace', async () => { + const customEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), ); + await page.locator('#btn1').click(); + const envelope = await customEnvelopePromise; + const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - const customTraceContext = customTrace1Event.contexts?.trace; - - expect(customTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); // although we "continue the trace" from pageload, this is actually a root span, // so there must not be a parent span id - expect(customTraceContext?.parent_span_id).toBeUndefined(); + expect(span.parent_span_id).toBeUndefined(); - expect(pageloadSampleRand).toEqual(Number(customTraceTraceHeader?.sample_rand)); + expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); - return customTraceContext; + return span; }); await sentryTest.step('Navigation', async () => { - const navigation1RequestPromise = waitForTransactionRequest( + const navigationEnvelopePromise = waitForStreamedSpanEnvelope( page, - evt => evt.contexts?.trace?.op === 'navigation', + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), ); await page.goto(`${url}#foo`); - const [navigationEvent, navigationTraceHeader] = eventAndTraceHeaderRequestParser( - await navigation1RequestPromise, - ); - const navTraceContext = navigationEvent.contexts?.trace; + const envelope = await navigationEnvelopePromise; + const navSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; - expect(navTraceContext?.trace_id).not.toEqual(customTraceContext?.trace_id); - expect(navTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(navSpan.trace_id).not.toEqual(customTraceSpan.trace_id); + expect(navSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - expect(navTraceContext?.links).toEqual([ + expect(navSpan.links).toEqual([ { - trace_id: customTraceContext?.trace_id, - span_id: customTraceContext?.span_id, + trace_id: customTraceSpan.trace_id, + span_id: customTraceSpan.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - expect(navTraceContext?.parent_span_id).toBeUndefined(); + expect(navSpan.parent_span_id).toBeUndefined(); - expect(pageloadSampleRand).toEqual(Number(navigationTraceHeader?.sample_rand)); + expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); }); }); sentryTest('Propagates continued sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const { pageloadTraceContext, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const { pageloadSpan, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { + const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), + ); await page.goto(url); - const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); - const pageloadSampleRand = Number(res[1]?.sample_rand); + const envelope = await pageloadEnvelopePromise; + const pageloadSampleRand = Number(envelope[0].trace?.sample_rand); + expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); + expect(pageloadSampleRand).toBeLessThanOrEqual(1); + expect(Number.isNaN(pageloadSampleRand)).toBe(false); - const pageloadTraceContext = res[0].contexts?.trace; + const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(pageloadSpan.attributes['sentry.sample_rate']?.value).toBe(1); - return { pageloadTraceContext: pageloadTraceContext, pageloadSampleRand }; + return { pageloadSpan, pageloadSampleRand }; }); await sentryTest.step('Make fetch request', async () => { - const fetchTracePromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const fetchEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), + ); const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); await page.locator('#btn2').click(); const { baggage, sentryTrace } = await tracingHeadersPromise; + const fetchEnvelope = await fetchEnvelopePromise; - const [fetchTraceEvent, fetchTraceTraceHeader] = eventAndTraceHeaderRequestParser(await fetchTracePromise); - - const fetchTraceSampleRand = Number(fetchTraceTraceHeader?.sample_rand); - const fetchTraceTraceContext = fetchTraceEvent.contexts?.trace; - const httpClientSpan = fetchTraceEvent.spans?.find(span => span.op === 'http.client'); + const fetchTraceSampleRand = Number(fetchEnvelope[0].trace?.sample_rand); + const fetchTraceSpans = fetchEnvelope[1][0][1].items; + const fetchTraceSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'custom')!; + const httpClientSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'http.client'); - expect(fetchTraceSampleRand).toEqual(pageloadSampleRand); + expect(fetchTraceSampleRand).toBe(pageloadSampleRand); - expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toEqual( - pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE], + expect(fetchTraceSpan.attributes['sentry.sample_rate']?.value).toEqual( + pageloadSpan.attributes['sentry.sample_rate']?.value, ); - expect(fetchTraceTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(fetchTraceSpan.trace_id).not.toEqual(pageloadSpan.trace_id); expect(sentryTrace).toBeDefined(); expect(baggage).toBeDefined(); expect(extractTraceparentData(sentryTrace)).toEqual({ - traceId: fetchTraceTraceContext?.trace_id, + traceId: fetchTraceSpan.trace_id, parentSpanId: httpClientSpan?.span_id, parentSampled: true, }); @@ -141,7 +146,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { 'sentry-sample_rand': `${pageloadSampleRand}`, 'sentry-sample_rate': '1', 'sentry-sampled': 'true', - 'sentry-trace_id': fetchTraceTraceContext?.trace_id, + 'sentry-trace_id': fetchTraceSpan.trace_id, 'sentry-transaction': 'custom root span 2', }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/init.js index 05c4ec8db85a..4cb0d3389fc2 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts index c8faee2f5feb..efd4ccce4e37 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts @@ -1,15 +1,15 @@ import { expect } from '@playwright/test'; -import type { ClientReport } from '@sentry/core'; +import type { ClientReport, SerializedStreamedSpan } from '@sentry/core'; import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, - getMultipleSentryEnvelopeRequests, hidePage, shouldSkipTracingTest, waitForClientReportRequest, waitForTracingHeadersOnUrl, } from '../../../../../../utils/helpers'; +import { observeStreamedSpan } from '../../../../../../utils/spanUtils'; const metaTagSampleRand = 0.9; const metaTagSampleRate = 0.2; @@ -19,34 +19,31 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains sentryTest( 'Continues negative sampling decision from meta tag across all traces and downstream propagations', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - let txnsReceived = 0; - // @ts-expect-error - no need to return something valid here - getMultipleSentryEnvelopeRequests(page, 1, { envelopeType: 'transaction' }, () => { - ++txnsReceived; - return {}; + const spansReceived: SerializedStreamedSpan[] = []; + observeStreamedSpan(page, span => { + spansReceived.push(span); + return false; }); const clientReportPromise = waitForClientReportRequest(page); await sentryTest.step('Initial pageload', async () => { await page.goto(url); - expect(txnsReceived).toEqual(0); + expect(spansReceived).toHaveLength(0); }); await sentryTest.step('Custom instrumented button click', async () => { await page.locator('#btn1').click(); - expect(txnsReceived).toEqual(0); + expect(spansReceived).toHaveLength(0); }); await sentryTest.step('Navigation', async () => { await page.goto(`${url}#foo`); - expect(txnsReceived).toEqual(0); + expect(spansReceived).toHaveLength(0); }); await sentryTest.step('Make fetch request', async () => { @@ -73,6 +70,8 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains 'sentry-trace_id': expect.not.stringContaining(metaTagTraceId), 'sentry-transaction': 'custom root span 2', }); + + expect(spansReceived).toHaveLength(0); }); await sentryTest.step('Client report', async () => { @@ -82,19 +81,17 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains timestamp: expect.any(Number), discarded_events: [ { - category: 'transaction', - quantity: 4, + category: 'span', + quantity: expect.any(Number), reason: 'sample_rate', }, ], }); + // exact number depends on performance observer emissions + expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(10); }); - await sentryTest.step('Wait for transactions to be discarded', async () => { - // give it a little longer just in case a txn is pending to be sent - await page.waitForTimeout(1000); - expect(txnsReceived).toEqual(0); - }); + expect(spansReceived).toHaveLength(0); }, ); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/init.js index fbf897d47eab..c758d9875be1 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts index 3dab9594ba7c..cd64596f9bfd 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-precedence/test.ts @@ -4,13 +4,12 @@ import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, - eventAndTraceHeaderRequestParser, hidePage, shouldSkipTracingTest, waitForClientReportRequest, waitForTracingHeadersOnUrl, - waitForTransactionRequest, } from '../../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; const metaTagSampleRand = 0.9; const metaTagSampleRate = 0.2; @@ -21,16 +20,14 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains sentryTest( 'meta tag decision has precedence over sampling decision from previous trace in session storage', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); const clientReportPromise = waitForClientReportRequest(page); await sentryTest.step('Initial pageload', async () => { - // negative sampling decision -> no pageload txn + // negative sampling decision -> no pageload span await page.goto(url); }); @@ -71,37 +68,49 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains timestamp: expect.any(Number), discarded_events: [ { - category: 'transaction', - quantity: 2, + category: 'span', + quantity: expect.any(Number), reason: 'sample_rate', }, ], }); + // exact number depends on performance observer emissions + expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(3); }); await sentryTest.step('Navigate to another page with meta tags', async () => { - const page1Pageload = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const page1PageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload' && s.trace_id === metaTagTraceIdPage1), + ); await page.locator('a').click(); - const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await page1Pageload); - const pageloadTraceContext = pageloadEvent.contexts?.trace; + const envelope = await page1PageloadEnvelopePromise; + const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(Number(pageloadTraceHeader?.sample_rand)).toBe(0.12); - expect(Number(pageloadTraceHeader?.sample_rate)).toBe(0.2); - expect(pageloadTraceContext?.trace_id).toEqual(metaTagTraceIdPage1); + expect(Number(envelope[0].trace?.sample_rand)).toBe(0.12); + expect(Number(envelope[0].trace?.sample_rate)).toBe(0.2); + expect(pageloadSpan.trace_id).toEqual(metaTagTraceIdPage1); }); await sentryTest.step('Navigate to another page without meta tags', async () => { - const page2Pageload = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const page2PageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => + !!env[1][0][1].items.find( + s => + getSpanOp(s) === 'pageload' && s.trace_id !== metaTagTraceIdPage1 && s.trace_id !== metaTagTraceIdIndex, + ), + ); await page.locator('a').click(); - const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await page2Pageload); - const pageloadTraceContext = pageloadEvent.contexts?.trace; + const envelope = await page2PageloadEnvelopePromise; + const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(Number(pageloadTraceHeader?.sample_rand)).toBe(0.12); - expect(Number(pageloadTraceHeader?.sample_rate)).toBe(0.2); - expect(pageloadTraceContext?.trace_id).not.toEqual(metaTagTraceIdPage1); - expect(pageloadTraceContext?.trace_id).not.toEqual(metaTagTraceIdIndex); + expect(Number(envelope[0].trace?.sample_rand)).toBe(0.12); + expect(Number(envelope[0].trace?.sample_rate)).toBe(0.2); + expect(pageloadSpan.trace_id).not.toEqual(metaTagTraceIdPage1); + expect(pageloadSpan.trace_id).not.toEqual(metaTagTraceIdIndex); }); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/init.js index 61e2b2daa9ba..9aa63000ef2e 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts index ac2a865fb181..2657bed50d61 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts @@ -3,160 +3,155 @@ import { extractTraceparentData, parseBaggageHeader, SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE, - SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; -import { - eventAndTraceHeaderRequestParser, - shouldSkipTracingTest, - waitForTracingHeadersOnUrl, - waitForTransactionRequest, -} from '../../../../../../utils/helpers'; +import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; const metaTagSampleRand = 0.051121; const metaTagSampleRate = 0.2; sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { sentryTest('Continues sampling decision across all traces from meta tag', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpan = await sentryTest.step('Initial pageload', async () => { + const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), + ); await page.goto(url); - const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); - const pageloadTraceContext = pageloadEvent.contexts?.trace; + const envelope = await pageloadEnvelopePromise; + const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(Number(pageloadTraceHeader?.sample_rand)).toBe(metaTagSampleRand); - expect(Number(pageloadTraceHeader?.sample_rate)).toBe(metaTagSampleRate); + expect(Number(envelope[0].trace?.sample_rand)).toBe(metaTagSampleRand); + expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); + expect(span.attributes['sentry.sample_rate']).toBeUndefined(); + expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); - return pageloadTraceContext; + return span; }); - const customTraceContext = await sentryTest.step('Custom trace', async () => { - const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const customTraceSpan = await sentryTest.step('Custom trace', async () => { + const customEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), + ); await page.locator('#btn1').click(); - const [customTrace1Event, customTraceTraceHeader] = eventAndTraceHeaderRequestParser( - await customTrace1RequestPromise, - ); - - const customTraceContext = customTrace1Event.contexts?.trace; + const envelope = await customEnvelopePromise; + const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - expect(customTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); - expect(customTraceContext?.parent_span_id).toBeUndefined(); + expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); + expect(span.parent_span_id).toBeUndefined(); - expect(Number(customTraceTraceHeader?.sample_rand)).toBe(metaTagSampleRand); - expect(Number(customTraceTraceHeader?.sample_rate)).toBe(metaTagSampleRate); - expect(Boolean(customTraceTraceHeader?.sampled)).toBe(true); + expect(Number(envelope[0].trace?.sample_rand)).toBe(metaTagSampleRand); + expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); + expect(envelope[0].trace?.sampled).toBe('true'); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(customTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + expect(span.attributes['sentry.sample_rate']).toBeUndefined(); // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) - expect(customTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBe(metaTagSampleRate); + expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe(metaTagSampleRate); - return customTraceContext; + return span; }); await sentryTest.step('Navigation', async () => { - const navigation1RequestPromise = waitForTransactionRequest( + const navigationEnvelopePromise = waitForStreamedSpanEnvelope( page, - evt => evt.contexts?.trace?.op === 'navigation', + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), ); await page.goto(`${url}#foo`); - const [navigationEvent, navigationTraceHeader] = eventAndTraceHeaderRequestParser( - await navigation1RequestPromise, - ); - - const navigationTraceContext = navigationEvent.contexts?.trace; + const envelope = await navigationEnvelopePromise; + const navSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; - expect(navigationTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); - expect(navigationTraceContext?.trace_id).not.toEqual(customTraceContext?.trace_id); + expect(navSpan.trace_id).not.toEqual(pageloadSpan.trace_id); + expect(navSpan.trace_id).not.toEqual(customTraceSpan.trace_id); - expect(navigationTraceContext?.parent_span_id).toBeUndefined(); + expect(navSpan.parent_span_id).toBeUndefined(); - expect(Number(navigationTraceHeader?.sample_rand)).toEqual(metaTagSampleRand); - expect(Number(navigationTraceHeader?.sample_rate)).toEqual(metaTagSampleRate); - expect(Boolean(navigationTraceHeader?.sampled)).toEqual(true); + expect(Number(envelope[0].trace?.sample_rand)).toEqual(metaTagSampleRand); + expect(Number(envelope[0].trace?.sample_rate)).toEqual(metaTagSampleRate); + expect(envelope[0].trace?.sampled).toEqual('true'); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(navigationTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + expect(navSpan.attributes['sentry.sample_rate']).toBeUndefined(); // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) - expect(navigationTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBe( - metaTagSampleRate, - ); + expect(navSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe(metaTagSampleRate); }); }); sentryTest( 'Propagates continued tag sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpan = await sentryTest.step('Initial pageload', async () => { + const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), + ); await page.goto(url); - const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); - const pageloadTraceContext = pageloadEvent.contexts?.trace; + const envelope = await pageloadEnvelopePromise; + const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(Number(pageloadTraceHeader?.sample_rand)).toBe(metaTagSampleRand); - expect(Number(pageloadTraceHeader?.sample_rate)).toBe(metaTagSampleRate); + expect(Number(envelope[0].trace?.sample_rand)).toBe(metaTagSampleRand); + expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); + expect(span.attributes['sentry.sample_rate']).toBeUndefined(); + expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); - return pageloadTraceContext; + return span; }); await sentryTest.step('Make fetch request', async () => { - const fetchTracePromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const fetchEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), + ); const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); await page.locator('#btn2').click(); const { baggage, sentryTrace } = await tracingHeadersPromise; + const fetchEnvelope = await fetchEnvelopePromise; - const [fetchTraceEvent, fetchTraceTraceHeader] = eventAndTraceHeaderRequestParser(await fetchTracePromise); - - const fetchTraceSampleRand = Number(fetchTraceTraceHeader?.sample_rand); - const fetchTraceTraceContext = fetchTraceEvent.contexts?.trace; - const httpClientSpan = fetchTraceEvent.spans?.find(span => span.op === 'http.client'); + const fetchTraceSampleRand = Number(fetchEnvelope[0].trace?.sample_rand); + const fetchTraceSpans = fetchEnvelope[1][0][1].items; + const fetchTraceSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'custom')!; + const httpClientSpan = fetchTraceSpans.find(s => getSpanOp(s) === 'http.client'); expect(fetchTraceSampleRand).toEqual(metaTagSampleRand); - expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); - expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBe( + expect(fetchTraceSpan.attributes['sentry.sample_rate']).toBeUndefined(); + expect(fetchTraceSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe( metaTagSampleRate, ); - expect(fetchTraceTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(fetchTraceSpan.trace_id).not.toEqual(pageloadSpan.trace_id); expect(sentryTrace).toBeDefined(); expect(baggage).toBeDefined(); expect(extractTraceparentData(sentryTrace)).toEqual({ - traceId: fetchTraceTraceContext?.trace_id, + traceId: fetchTraceSpan.trace_id, parentSpanId: httpClientSpan?.span_id, parentSampled: true, }); @@ -167,7 +162,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains 'sentry-sample_rand': `${metaTagSampleRand}`, 'sentry-sample_rate': `${metaTagSampleRate}`, 'sentry-sampled': 'true', - 'sentry-trace_id': fetchTraceTraceContext?.trace_id, + 'sentry-trace_id': fetchTraceSpan.trace_id, 'sentry-transaction': 'custom root span 2', }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/init.js index e2efc43e50f2..e99cf357559a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts index 2bb196c898fd..858449cf0efa 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts @@ -1,15 +1,16 @@ +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import type { ClientReport } from '@sentry/core'; + import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, - eventAndTraceHeaderRequestParser, hidePage, shouldSkipTracingTest, waitForClientReportRequest, - waitForTransactionRequest, } from '../../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; /** * This test demonstrates that: @@ -18,26 +19,24 @@ import { */ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { sentryTest('explicit sampling decisions in `tracesSampler` have precedence', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const { pageloadTraceContext } = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => { - return evt.contexts?.trace?.op === 'pageload'; - }); + const { pageloadSpan } = await sentryTest.step('Initial pageload', async () => { + const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), + ); await page.goto(url); - const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); - const pageloadSampleRand = Number(res[1]?.sample_rand); - const pageloadTraceContext = res[0].contexts?.trace; + const envelope = await pageloadEnvelopePromise; + const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); - expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); + expect(pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); + expect(Number(envelope[0].trace?.sample_rand)).toBeGreaterThanOrEqual(0); - return { pageloadTraceContext: res[0].contexts?.trace, pageloadSampleRand }; + return { pageloadSpan }; }); await sentryTest.step('Custom trace is sampled negatively (explicitly in tracesSampler)', async () => { @@ -54,7 +53,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { timestamp: expect.any(Number), discarded_events: [ { - category: 'transaction', + category: 'span', quantity: 1, reason: 'sample_rate', }, @@ -77,7 +76,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { timestamp: expect.any(Number), discarded_events: [ { - category: 'transaction', + category: 'span', quantity: 1, reason: 'sample_rate', }, @@ -85,53 +84,67 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { }); }); - const { customTrace2Context } = await sentryTest.step( + const { customTrace2Span } = await sentryTest.step( 'Custom trace 2 is sampled positively (explicitly in tracesSampler)', async () => { - const customTrace2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const customEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'custom'), + ); await page.locator('#btn2').click(); - const [customTrace2Event] = eventAndTraceHeaderRequestParser(await customTrace2RequestPromise); - - const customTrace2Context = customTrace2Event.contexts?.trace; + const envelope = await customEnvelopePromise; + const customTrace2Span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - expect(customTrace2Context?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); - expect(customTrace2Context?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); - expect(customTrace2Context?.parent_span_id).toBeUndefined(); + expect(customTrace2Span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); + expect(customTrace2Span.trace_id).not.toEqual(pageloadSpan.trace_id); + expect(customTrace2Span.parent_span_id).toBeUndefined(); - expect(customTrace2Context?.links).toEqual([ + expect(customTrace2Span.links).toEqual([ { - attributes: { 'sentry.link.type': 'previous_trace' }, + attributes: { + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, + }, sampled: false, span_id: expect.stringMatching(/^[\da-f]{16}$/), trace_id: expect.stringMatching(/^[\da-f]{32}$/), }, ]); - return { customTrace2Context }; + return { customTrace2Span }; }, ); await sentryTest.step('Navigation trace is sampled positively (inherited from previous trace)', async () => { - const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + const navigationEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => env[0].trace?.sampled === 'true' && !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), + ); await page.goto(`${url}#bar`); - const [navigationEvent] = eventAndTraceHeaderRequestParser(await navigationRequestPromise); + const envelope = await navigationEnvelopePromise; + const navigationSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; - const navigationTraceContext = navigationEvent.contexts?.trace; + expect(navigationSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); + expect(navigationSpan.trace_id).not.toEqual(customTrace2Span.trace_id); + expect(navigationSpan.parent_span_id).toBeUndefined(); - expect(navigationTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); - expect(navigationTraceContext?.trace_id).not.toEqual(customTrace2Context?.trace_id); - expect(navigationTraceContext?.parent_span_id).toBeUndefined(); - - expect(navigationTraceContext?.links).toEqual([ + expect(navigationSpan.links).toEqual([ { - attributes: { 'sentry.link.type': 'previous_trace' }, + attributes: { + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, + }, sampled: true, - span_id: customTrace2Context?.span_id, - trace_id: customTrace2Context?.trace_id, + span_id: customTrace2Span.span_id, + trace_id: customTrace2Span.trace_id, }, ]); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/custom-trace/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/custom-trace/test.ts index c4637dc25018..bdbdd1c78a90 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/custom-trace/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/custom-trace/test.ts @@ -1,60 +1,62 @@ -import { expect } from '@playwright/test'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest('manually started custom traces are linked correctly in the chain', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpan = await sentryTest.step('Initial pageload', async () => { + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); - return pageloadRequest.contexts?.trace; + return pageloadSpanPromise; }); - const customTrace1Context = await sentryTest.step('Custom trace', async () => { - const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const customTraceSpan = await sentryTest.step('Custom trace', async () => { + const customSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'custom'); await page.locator('#btn1').click(); - const customTrace1Event = envelopeRequestParser(await customTrace1RequestPromise); - - const customTraceCtx = customTrace1Event.contexts?.trace; + const span = await customSpanPromise; - expect(customTraceCtx?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); - expect(customTraceCtx?.links).toEqual([ + expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); + expect(span.links).toEqual([ { - trace_id: pageloadTraceContext?.trace_id, - span_id: pageloadTraceContext?.span_id, + trace_id: pageloadSpan.trace_id, + span_id: pageloadSpan.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - return customTraceCtx; + return span; }); await sentryTest.step('Navigation', async () => { - const navigation1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(`${url}#foo`); - const navigationEvent = envelopeRequestParser(await navigation1RequestPromise); - const navTraceContext = navigationEvent.contexts?.trace; + const navSpan = await navigationSpanPromise; - expect(navTraceContext?.trace_id).not.toEqual(customTrace1Context?.trace_id); - expect(navTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(navSpan.trace_id).not.toEqual(customTraceSpan.trace_id); + expect(navSpan.trace_id).not.toEqual(pageloadSpan.trace_id); - expect(navTraceContext?.links).toEqual([ + expect(navSpan.links).toEqual([ { - trace_id: customTrace1Context?.trace_id, - span_id: customTrace1Context?.span_id, + trace_id: customTraceSpan.trace_id, + span_id: customTraceSpan.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/default/test.ts index 7399c2bb26c3..ca42ae5e32a0 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/default/test.ts @@ -1,70 +1,69 @@ -import { expect } from '@playwright/test'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest("navigation spans link back to previous trace's root span", async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); - await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); - return pageloadRequest.contexts?.trace; - }); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + await page.goto(url); + const pageloadSpan = await pageloadSpanPromise; - const navigation1TraceContext = await sentryTest.step('First navigation', async () => { - const navigation1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); - await page.goto(`${url}#foo`); - const navigation1Request = envelopeRequestParser(await navigation1RequestPromise); - return navigation1Request.contexts?.trace; - }); + const navigation1SpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); + await page.goto(`${url}#foo`); + const navigation1Span = await navigation1SpanPromise; - const navigation2TraceContext = await sentryTest.step('Second navigation', async () => { - const navigation2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); - await page.goto(`${url}#bar`); - const navigation2Request = envelopeRequestParser(await navigation2RequestPromise); - return navigation2Request.contexts?.trace; - }); + const navigation2SpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); + await page.goto(`${url}#bar`); + const navigation2Span = await navigation2SpanPromise; - const pageloadTraceId = pageloadTraceContext?.trace_id; - const navigation1TraceId = navigation1TraceContext?.trace_id; - const navigation2TraceId = navigation2TraceContext?.trace_id; + const pageloadTraceId = pageloadSpan.trace_id; + const navigation1TraceId = navigation1Span.trace_id; + const navigation2TraceId = navigation2Span.trace_id; - expect(pageloadTraceContext?.links).toBeUndefined(); + expect(pageloadSpan.links).toBeUndefined(); - expect(navigation1TraceContext?.links).toEqual([ + expect(navigation1Span.links).toEqual([ { trace_id: pageloadTraceId, - span_id: pageloadTraceContext?.span_id, + span_id: pageloadSpan.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - expect(navigation1TraceContext?.data).toMatchObject({ - 'sentry.previous_trace': `${pageloadTraceId}-${pageloadTraceContext?.span_id}-1`, + expect(navigation1Span.attributes['sentry.previous_trace']).toEqual({ + type: 'string', + value: `${pageloadTraceId}-${pageloadSpan.span_id}-1`, }); - expect(navigation2TraceContext?.links).toEqual([ + expect(navigation2Span.links).toEqual([ { trace_id: navigation1TraceId, - span_id: navigation1TraceContext?.span_id, + span_id: navigation1Span.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - expect(navigation2TraceContext?.data).toMatchObject({ - 'sentry.previous_trace': `${navigation1TraceId}-${navigation1TraceContext?.span_id}-1`, + expect(navigation2Span.attributes['sentry.previous_trace']).toEqual({ + type: 'string', + value: `${navigation1TraceId}-${navigation1Span.span_id}-1`, }); expect(pageloadTraceId).not.toEqual(navigation1TraceId); @@ -73,27 +72,25 @@ sentryTest("navigation spans link back to previous trace's root span", async ({ }); sentryTest("doesn't link between hard page reloads by default", async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); await sentryTest.step('First pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - const pageload1Event = envelopeRequestParser(await pageloadRequestPromise); + const pageload1Span = await pageloadSpanPromise; - expect(pageload1Event.contexts?.trace).toBeDefined(); - expect(pageload1Event.contexts?.trace?.links).toBeUndefined(); + expect(pageload1Span).toBeDefined(); + expect(pageload1Span.links).toBeUndefined(); }); await sentryTest.step('Second pageload', async () => { - const pageload2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.reload(); - const pageload2Event = envelopeRequestParser(await pageload2RequestPromise); + const pageload2Span = await pageloadSpanPromise; - expect(pageload2Event.contexts?.trace).toBeDefined(); - expect(pageload2Event.contexts?.trace?.links).toBeUndefined(); + expect(pageload2Span).toBeDefined(); + expect(pageload2Span.links).toBeUndefined(); }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/init.js index bdd43f5ac757..1ce4125ee422 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/init.js @@ -3,7 +3,6 @@ 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/linked-traces/interaction-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/init.js index 2c3e9264d882..96386e1d38d5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/init.js @@ -4,7 +4,6 @@ import { interactionsIntegration } from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1, integrations: [Sentry.browserTracingIntegration(), interactionsIntegration()], diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/test.ts index 24a1e2c59072..67e216567856 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/interaction-spans/test.ts @@ -1,7 +1,9 @@ -import { expect } from '@playwright/test'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; /* This is quite peculiar behavior but it's a result of the route-based trace lifetime. @@ -12,78 +14,67 @@ import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest sentryTest( 'only the first root spans in the trace link back to the previous trace', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpan = await sentryTest.step('Initial pageload', async () => { + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); + const span = await pageloadSpanPromise; - const pageloadEvent = envelopeRequestParser(await pageloadRequestPromise); - const traceContext = pageloadEvent.contexts?.trace; - - expect(traceContext).toBeDefined(); - expect(traceContext?.links).toBeUndefined(); + expect(span).toBeDefined(); + expect(span.links).toBeUndefined(); - return traceContext; + return span; }); await sentryTest.step('Click Before navigation', async () => { - const interactionRequestPromise = waitForTransactionRequest(page, evt => { - return evt.contexts?.trace?.op === 'ui.action.click'; - }); + const interactionSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.action.click'); await page.click('#btn'); - - const interactionEvent = envelopeRequestParser(await interactionRequestPromise); - const interactionTraceContext = interactionEvent.contexts?.trace; + const interactionSpan = await interactionSpanPromise; // sanity check: route-based trace lifetime means the trace_id should be the same - expect(interactionTraceContext?.trace_id).toBe(pageloadTraceContext?.trace_id); + expect(interactionSpan.trace_id).toBe(pageloadSpan.trace_id); // no links yet as previous root span belonged to same trace - expect(interactionTraceContext?.links).toBeUndefined(); + expect(interactionSpan.links).toBeUndefined(); }); - const navigationTraceContext = await sentryTest.step('Navigation', async () => { - const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + const navigationSpan = await sentryTest.step('Navigation', async () => { + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(`${url}#foo`); - const navigationEvent = envelopeRequestParser(await navigationRequestPromise); - - const traceContext = navigationEvent.contexts?.trace; + const span = await navigationSpanPromise; - expect(traceContext?.op).toBe('navigation'); - expect(traceContext?.links).toEqual([ + expect(getSpanOp(span)).toBe('navigation'); + expect(span.links).toEqual([ { - trace_id: pageloadTraceContext?.trace_id, - span_id: pageloadTraceContext?.span_id, + trace_id: pageloadSpan.trace_id, + span_id: pageloadSpan.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - expect(traceContext?.trace_id).not.toEqual(traceContext?.links![0].trace_id); - return traceContext; + expect(span.trace_id).not.toEqual(span.links![0].trace_id); + return span; }); await sentryTest.step('Click After navigation', async () => { - const interactionRequestPromise = waitForTransactionRequest(page, evt => { - return evt.contexts?.trace?.op === 'ui.action.click'; - }); + const interactionSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.action.click'); await page.click('#btn'); - const interactionEvent = envelopeRequestParser(await interactionRequestPromise); - - const interactionTraceContext = interactionEvent.contexts?.trace; + const interactionSpan = await interactionSpanPromise; // sanity check: route-based trace lifetime means the trace_id should be the same - expect(interactionTraceContext?.trace_id).toBe(navigationTraceContext?.trace_id); + expect(interactionSpan.trace_id).toBe(navigationSpan.trace_id); // since this is the second root span in the trace, it doesn't link back to the previous trace - expect(interactionTraceContext?.links).toBeUndefined(); + expect(interactionSpan.links).toBeUndefined(); }); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/meta/test.ts index a232a5a0f6e7..3f8688a0bf4d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/meta/test.ts @@ -1,54 +1,51 @@ -import { expect } from '@playwright/test'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "links back to previous trace's local root span if continued from meta tags", async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); const metaTagTraceId = '12345678901234567890123456789012'; - const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageloadSpan = await sentryTest.step('Initial pageload', async () => { + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); - - const traceContext = pageloadRequest.contexts?.trace; + const span = await pageloadSpanPromise; // sanity check - expect(traceContext?.trace_id).toBe(metaTagTraceId); - - expect(traceContext?.links).toBeUndefined(); + expect(span.trace_id).toBe(metaTagTraceId); + expect(span.links).toBeUndefined(); - return traceContext; + return span; }); - const navigationTraceContext = await sentryTest.step('Navigation', async () => { - const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + const navigationSpan = await sentryTest.step('Navigation', async () => { + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(`${url}#foo`); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); - return navigationRequest.contexts?.trace; + return navigationSpanPromise; }); - const navigationTraceId = navigationTraceContext?.trace_id; - - expect(navigationTraceContext?.links).toEqual([ + expect(navigationSpan.links).toEqual([ { trace_id: metaTagTraceId, - span_id: pageloadTraceContext?.span_id, + span_id: pageloadSpan.span_id, sampled: true, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - expect(navigationTraceId).not.toEqual(metaTagTraceId); + expect(navigationSpan.trace_id).not.toEqual(metaTagTraceId); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/init.js index 79ca4d68e8ba..a3b99e2e1dc3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', // We want to ignore redirects for this test integrations: [Sentry.browserTracingIntegration({ detectRedirects: false })], diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/test.ts index 894fe0d5929a..9fd3c0d2dd7d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/negatively-sampled/test.ts @@ -1,42 +1,45 @@ -import { expect } from '@playwright/test'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest('includes a span link to a previously negatively sampled span', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); await sentryTest.step('Initial pageload', async () => { - // No event to check for an event here because this pageload span is sampled negatively! + // No span envelope expected here because this pageload span is sampled negatively! await page.goto(url); }); await sentryTest.step('Navigation', async () => { - const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(`${url}#foo`); - const navigationEvent = envelopeRequestParser(await navigationRequestPromise); - const navigationTraceContext = navigationEvent.contexts?.trace; + const navigationSpan = await navigationSpanPromise; - expect(navigationTraceContext?.op).toBe('navigation'); - expect(navigationTraceContext?.links).toEqual([ + expect(getSpanOp(navigationSpan)).toBe('navigation'); + expect(navigationSpan.links).toEqual([ { trace_id: expect.stringMatching(/[a-f\d]{32}/), span_id: expect.stringMatching(/[a-f\d]{16}/), sampled: false, attributes: { - [SENTRY_LINK_TYPE]: 'previous_trace', + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, }, }, ]); - expect(navigationTraceContext?.data).toMatchObject({ - 'sentry.previous_trace': expect.stringMatching(/[a-f\d]{32}-[a-f\d]{16}-0/), + expect(navigationSpan.attributes['sentry.previous_trace']).toEqual({ + type: 'string', + value: expect.stringMatching(/[a-f\d]{32}-[a-f\d]{16}-0/), }); - expect(navigationTraceContext?.trace_id).not.toEqual(navigationTraceContext?.links![0].trace_id); + expect(navigationSpan.trace_id).not.toEqual(navigationSpan.links![0].trace_id); }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/init.js index 9b88e489ae07..fc053206d9ed 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [Sentry.browserTracingIntegration({ linkPreviousTrace: 'session-storage' })], tracesSampleRate: 1, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/test.ts index f7e0d288de5b..57121c395b53 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/session-storage/test.ts @@ -1,39 +1,43 @@ -import { expect } from '@playwright/test'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest('adds link between hard page reloads when opting into sessionStorage', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageload1TraceContext = await sentryTest.step('First pageload', async () => { - const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageload1Span = await sentryTest.step('First pageload', async () => { + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - const pageload1Event = envelopeRequestParser(await pageloadRequestPromise); - const pageload1TraceContext = pageload1Event.contexts?.trace; - expect(pageload1TraceContext).toBeDefined(); - expect(pageload1TraceContext?.links).toBeUndefined(); - return pageload1TraceContext; + const span = await pageloadSpanPromise; + expect(span).toBeDefined(); + expect(span.links).toBeUndefined(); + return span; }); - const pageload2Event = await sentryTest.step('Hard page reload', async () => { - const pageload2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + const pageload2Span = await sentryTest.step('Hard page reload', async () => { + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.reload(); - return envelopeRequestParser(await pageload2RequestPromise); + return pageloadSpanPromise; }); - expect(pageload2Event.contexts?.trace?.links).toEqual([ + expect(pageload2Span.links).toEqual([ { - trace_id: pageload1TraceContext?.trace_id, - span_id: pageload1TraceContext?.span_id, + trace_id: pageload1Span.trace_id, + span_id: pageload1Span.span_id, sampled: true, - attributes: { [SENTRY_LINK_TYPE]: 'previous_trace' }, + attributes: { + [SENTRY_LINK_TYPE]: { + type: 'string', + value: 'previous_trace', + }, + }, }, ]); - expect(pageload1TraceContext?.trace_id).not.toEqual(pageload2Event.contexts?.trace?.trace_id); + expect(pageload1Span.trace_id).not.toEqual(pageload2Span.trace_id); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/init.js deleted file mode 100644 index d3c861a8ae26..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/init.js +++ /dev/null @@ -1,17 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongTask: false, - enableLongAnimationFrame: true, - instrumentPageLoad: false, - enableInp: false, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/subject.js deleted file mode 100644 index b02ed6efa33b..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/subject.js +++ /dev/null @@ -1,18 +0,0 @@ -function getElapsed(startTime) { - const time = Date.now(); - return time - startTime; -} - -function handleClick() { - const startTime = Date.now(); - while (getElapsed(startTime) < 105) { - // - } - window.history.pushState({}, '', `#myHeading`); -} - -const button = document.getElementById('clickme'); - -console.log('button', button); - -button.addEventListener('click', handleClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/template.html deleted file mode 100644 index 6a6a89752f20..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/template.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - -

My Heading

- - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts deleted file mode 100644 index bfda616223b4..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest( - "doesn't capture long animation frame that starts before a navigation.", - async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const navigationSpansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'navigation')); - - await page.goto(url); - - await page.locator('#clickme').click(); - - const spans = await navigationSpansPromise; - - const loafSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - expect(loafSpans).toHaveLength(0); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/init.js index a8a7fc20c7f9..019eece018f6 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts index b7215249910f..703f02fcbab5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts @@ -1,30 +1,29 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( "doesn't capture long animation frame that starts before a navigation.", async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); + const allSpans = collectStreamedSpans(page); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - const navigationTransactionEventPromise = getFirstSentryEnvelopeRequest(page); + await page.goto(url); await page.locator('#clickme').click(); - const navigationTransactionEvent = await navigationTransactionEventPromise; - - expect(navigationTransactionEvent.contexts?.trace?.op).toBe('navigation'); - - const loafSpans = navigationTransactionEvent.spans?.filter(s => s.op?.startsWith('ui.long_animation_frame')); + const navigationRoot = await navigationSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + expect(getSpanOp(navigationRoot)).toBe('navigation'); + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === navigationRoot.span_id); - expect(loafSpans?.length).toEqual(0); + const loafSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); + expect(loafSpans).toHaveLength(0); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/assets/script.js deleted file mode 100644 index 195a094070be..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/assets/script.js +++ /dev/null @@ -1,12 +0,0 @@ -(() => { - const startTime = Date.now(); - - function getElapsed() { - const time = Date.now(); - return time - startTime; - } - - while (getElapsed() < 101) { - // - } -})(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/init.js deleted file mode 100644 index 3c004b952f80..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongTask: false, - enableLongAnimationFrame: false, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/template.html deleted file mode 100644 index 62aed26413f8..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -
Rendered Before Long Animation Frame
- - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts deleted file mode 100644 index e03474070bb0..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { Route } from '@playwright/test'; -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest( - 'does not capture long animation frame when flag is disabled.', - async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - await page.route('**/path/to/script.js', (route: Route) => - route.fulfill({ path: `${__dirname}/assets/script.js` }), - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - const spans = await spansPromise; - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui')); - - expect(uiSpans.length).toBe(0); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js index 250f2a72311b..0339c3fa3dbf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts index 3d4ca79877b1..7ea2ac55bb53 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts @@ -1,16 +1,14 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( - 'should not capture long animation frame when flag is disabled.', + 'does not capture long animation frame when flag is disabled.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` }), @@ -18,9 +16,15 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - expect(uiSpans?.length).toBe(0); + await page.goto(url); + + await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui')); + + expect(uiSpans.length).toBe(0); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/assets/script.js deleted file mode 100644 index 10552eeb5bd5..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/assets/script.js +++ /dev/null @@ -1,25 +0,0 @@ -function getElapsed(startTime) { - const time = Date.now(); - return time - startTime; -} - -function handleClick() { - const startTime = Date.now(); - while (getElapsed(startTime) < 105) { - // - } -} - -function start() { - const startTime = Date.now(); - while (getElapsed(startTime) < 105) { - // - } -} - -// trigger 2 long-animation-frame events -// one from the top-level and the other from an event-listener -start(); - -const button = document.getElementById('clickme'); -button.addEventListener('click', handleClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/init.js deleted file mode 100644 index 2be5f32985d7..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongTask: false, - enableLongAnimationFrame: true, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/template.html deleted file mode 100644 index c157aa80cb8d..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/template.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -
Rendered Before Long Animation Frame
- - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts deleted file mode 100644 index f885fc4c3bde..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; -import type { Route } from '@playwright/test'; -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest( - 'captures long animation frame span for top-level script.', - async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - await page.route('**/path/to/script.js', (route: Route) => - route.fulfill({ path: `${__dirname}/assets/script.js` }), - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - const spans = await spansPromise; - const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - - expect(uiSpans.length).toBeGreaterThanOrEqual(1); - - const topLevelUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'https://sentry-test-site.example/path/to/script.js', - )!; - - expect(topLevelUISpan).toEqual( - expect.objectContaining({ - name: 'Main UI thread blocked', - parent_span_id: pageloadSpan.span_id, - attributes: expect.objectContaining({ - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, - 'browser.script.source_char_position': expect.objectContaining({ value: 0 }), - 'browser.script.invoker': { - type: 'string', - value: 'https://sentry-test-site.example/path/to/script.js', - }, - 'browser.script.invoker_type': { type: 'string', value: 'classic-script' }, - [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, - }), - }), - ); - - const start = topLevelUISpan.start_timestamp ?? 0; - const end = topLevelUISpan.end_timestamp ?? 0; - const duration = end - start; - - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); - }, -); - -sentryTest('captures long animation frame span for event listener.', async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - // trigger long animation frame function - await page.getByRole('button').click(); - - const spans = await spansPromise; - const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - - expect(uiSpans.length).toBeGreaterThanOrEqual(2); - - const eventListenerUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'BUTTON#clickme.onclick', - )!; - - expect(eventListenerUISpan).toEqual( - expect.objectContaining({ - name: 'Main UI thread blocked', - parent_span_id: pageloadSpan.span_id, - attributes: expect.objectContaining({ - 'browser.script.invoker': { type: 'string', value: 'BUTTON#clickme.onclick' }, - 'browser.script.invoker_type': { type: 'string', value: 'event-listener' }, - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, - [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, - }), - }), - ); - - const start = eventListenerUISpan.start_timestamp ?? 0; - const end = eventListenerUISpan.end_timestamp ?? 0; - const duration = end - start; - - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js index cfa9cb5faa3f..f16f7af33b5c 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts index 937662e117a4..6c2fd0498ded 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts @@ -1,17 +1,15 @@ import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( - 'should capture long animation frame for top-level script.', + 'captures long animation frame span for top-level script.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` }), @@ -19,38 +17,43 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const promise = getFirstSentryEnvelopeRequest(page); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - await new Promise(resolve => setTimeout(resolve, 1000)); + await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - const eventData = await promise; + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')); + expect(uiSpans.length).toBeGreaterThanOrEqual(1); - expect(uiSpans?.length).toBeGreaterThanOrEqual(1); - - const topLevelUISpan = (uiSpans || []).find( - span => span.data?.['browser.script.invoker'] === 'https://sentry-test-site.example/path/to/script.js', + const topLevelUISpan = uiSpans.find( + s => s.attributes['browser.script.invoker']?.value === 'https://sentry-test-site.example/path/to/script.js', )!; + expect(topLevelUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_animation_frame', - description: 'Main UI thread blocked', - parent_span_id: eventData.contexts?.trace?.span_id, - data: { - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.source_char_position': 0, - 'browser.script.invoker': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.invoker_type': 'classic-script', - [SENTRY_OP]: 'ui.long_animation_frame', - [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', - }, + name: 'Main UI thread blocked', + parent_span_id: pageloadSpan.span_id, + attributes: expect.objectContaining({ + 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + 'browser.script.source_char_position': expect.objectContaining({ value: 0 }), + 'browser.script.invoker': { + type: 'string', + value: 'https://sentry-test-site.example/path/to/script.js', + }, + 'browser.script.invoker_type': { type: 'string', value: 'classic-script' }, + [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, + }), }), ); + const start = topLevelUISpan.start_timestamp ?? 0; - const end = topLevelUISpan.timestamp ?? 0; + const end = topLevelUISpan.end_timestamp ?? 0; const duration = end - start; expect(duration).toBeGreaterThanOrEqual(0.1); @@ -58,56 +61,51 @@ sentryTest( }, ); -sentryTest( - 'should capture long animation frame for event listener.', - async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } - - await page.route('**/path/to/script.js', (route: Route) => - route.fulfill({ path: `${__dirname}/assets/script.js` }), - ); +sentryTest('captures long animation frame span for event listener.', async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - const url = await getLocalTestUrl({ testDir: __dirname }); + await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - const promise = getFirstSentryEnvelopeRequest(page); + const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - // trigger long animation frame function - await page.getByRole('button').click(); + await page.goto(url); - await new Promise(resolve => setTimeout(resolve, 1000)); + // trigger long animation frame function + await page.getByRole('button').click(); - const eventData = await promise; + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')) || []; + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - expect(uiSpans.length).toBeGreaterThanOrEqual(2); + expect(uiSpans.length).toBeGreaterThanOrEqual(2); - const eventListenerUISpan = uiSpans.find(span => span.data['browser.script.invoker'] === 'BUTTON#clickme.onclick')!; + const eventListenerUISpan = uiSpans.find( + s => s.attributes['browser.script.invoker']?.value === 'BUTTON#clickme.onclick', + )!; - expect(eventListenerUISpan).toEqual( - expect.objectContaining({ - op: 'ui.long_animation_frame', - description: 'Main UI thread blocked', - parent_span_id: eventData.contexts?.trace?.span_id, - data: { - 'browser.script.invoker': 'BUTTON#clickme.onclick', - 'browser.script.invoker_type': 'event-listener', - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', - [SENTRY_OP]: 'ui.long_animation_frame', - [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', - }, + expect(eventListenerUISpan).toEqual( + expect.objectContaining({ + name: 'Main UI thread blocked', + parent_span_id: pageloadSpan.span_id, + attributes: expect.objectContaining({ + 'browser.script.invoker': { type: 'string', value: 'BUTTON#clickme.onclick' }, + 'browser.script.invoker_type': { type: 'string', value: 'event-listener' }, + 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, }), - ); - const start = eventListenerUISpan.start_timestamp ?? 0; - const end = eventListenerUISpan.timestamp ?? 0; - const duration = end - start; + }), + ); - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); - }, -); + const start = eventListenerUISpan.start_timestamp ?? 0; + const end = eventListenerUISpan.end_timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js index 37df798364a8..382115cbc8b9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts index 2c8f5427cc05..07d3a6cd313a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts @@ -1,8 +1,8 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest( 'should not capture long animation frame or long task when browser is non-chromium', @@ -18,8 +18,10 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + const spans = collectStreamedSpans(page); + await waitForStreamedSpanAndTraceHeaderOnUrl(page, url, span => getSpanOp(span) === 'pageload'); + await page.evaluate(() => (window as any).Sentry.flush()); + const uiSpans = spans?.filter(span => getSpanOp(span)?.startsWith('ui')); expect(uiSpans?.length).toBe(0); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/assets/script.js deleted file mode 100644 index 10552eeb5bd5..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/assets/script.js +++ /dev/null @@ -1,25 +0,0 @@ -function getElapsed(startTime) { - const time = Date.now(); - return time - startTime; -} - -function handleClick() { - const startTime = Date.now(); - while (getElapsed(startTime) < 105) { - // - } -} - -function start() { - const startTime = Date.now(); - while (getElapsed(startTime) < 105) { - // - } -} - -// trigger 2 long-animation-frame events -// one from the top-level and the other from an event-listener -start(); - -const button = document.getElementById('clickme'); -button.addEventListener('click', handleClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/init.js deleted file mode 100644 index b0dc0a8d7ff5..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongTask: true, - enableLongAnimationFrame: true, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/template.html deleted file mode 100644 index c157aa80cb8d..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/template.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -
Rendered Before Long Animation Frame
- - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts deleted file mode 100644 index 94ece2b74e6f..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; -import type { Route } from '@playwright/test'; -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest( - 'captures long animation frame span for top-level script.', - async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - // Long animation frame should take priority over long tasks - - await page.route('**/path/to/script.js', (route: Route) => - route.fulfill({ path: `${__dirname}/assets/script.js` }), - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - const spans = await spansPromise; - const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - - expect(uiSpans.length).toBeGreaterThanOrEqual(1); - - const topLevelUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'https://sentry-test-site.example/path/to/script.js', - )!; - - expect(topLevelUISpan).toEqual( - expect.objectContaining({ - name: 'Main UI thread blocked', - parent_span_id: pageloadSpan.span_id, - attributes: expect.objectContaining({ - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, - 'browser.script.source_char_position': expect.objectContaining({ value: 0 }), - 'browser.script.invoker': { - type: 'string', - value: 'https://sentry-test-site.example/path/to/script.js', - }, - 'browser.script.invoker_type': { type: 'string', value: 'classic-script' }, - [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, - }), - }), - ); - - const start = topLevelUISpan.start_timestamp ?? 0; - const end = topLevelUISpan.end_timestamp ?? 0; - const duration = end - start; - - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); - }, -); - -sentryTest('captures long animation frame span for event listener.', async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - // trigger long animation frame function - await page.getByRole('button').click(); - - const spans = await spansPromise; - const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - - expect(uiSpans.length).toBeGreaterThanOrEqual(2); - - const eventListenerUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'BUTTON#clickme.onclick', - )!; - - expect(eventListenerUISpan).toEqual( - expect.objectContaining({ - name: 'Main UI thread blocked', - parent_span_id: pageloadSpan.span_id, - attributes: expect.objectContaining({ - 'browser.script.invoker': { type: 'string', value: 'BUTTON#clickme.onclick' }, - 'browser.script.invoker_type': { type: 'string', value: 'event-listener' }, - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, - [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, - }), - }), - ); - - const start = eventListenerUISpan.start_timestamp ?? 0; - const end = eventListenerUISpan.end_timestamp ?? 0; - const duration = end - start; - - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js index 37df798364a8..382115cbc8b9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts index b571a7d6a306..e9f1ca3720e8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts @@ -1,17 +1,15 @@ import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( - 'should capture long animation frame for top-level script.', + 'captures long animation frame span for top-level script.', async ({ browserName, getLocalTestUrl, page }) => { // Long animation frames only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); // Long animation frame should take priority over long tasks @@ -21,38 +19,43 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const promise = getFirstSentryEnvelopeRequest(page); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - await new Promise(resolve => setTimeout(resolve, 1000)); + await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - const eventData = await promise; + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')); + expect(uiSpans.length).toBeGreaterThanOrEqual(1); - expect(uiSpans?.length).toBeGreaterThanOrEqual(1); - - const topLevelUISpan = (uiSpans || []).find( - span => span.data?.['browser.script.invoker'] === 'https://sentry-test-site.example/path/to/script.js', + const topLevelUISpan = uiSpans.find( + s => s.attributes['browser.script.invoker']?.value === 'https://sentry-test-site.example/path/to/script.js', )!; + expect(topLevelUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_animation_frame', - description: 'Main UI thread blocked', - parent_span_id: eventData.contexts?.trace?.span_id, - data: { - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.source_char_position': 0, - 'browser.script.invoker': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.invoker_type': 'classic-script', - [SENTRY_OP]: 'ui.long_animation_frame', - [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', - }, + name: 'Main UI thread blocked', + parent_span_id: pageloadSpan.span_id, + attributes: expect.objectContaining({ + 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + 'browser.script.source_char_position': expect.objectContaining({ value: 0 }), + 'browser.script.invoker': { + type: 'string', + value: 'https://sentry-test-site.example/path/to/script.js', + }, + 'browser.script.invoker_type': { type: 'string', value: 'classic-script' }, + [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, + }), }), ); + const start = topLevelUISpan.start_timestamp ?? 0; - const end = topLevelUISpan.timestamp ?? 0; + const end = topLevelUISpan.end_timestamp ?? 0; const duration = end - start; expect(duration).toBeGreaterThanOrEqual(0.1); @@ -60,56 +63,51 @@ sentryTest( }, ); -sentryTest( - 'should capture long animation frame for event listener.', - async ({ browserName, getLocalTestUrl, page }) => { - // Long animation frames only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } - - await page.route('**/path/to/script.js', (route: Route) => - route.fulfill({ path: `${__dirname}/assets/script.js` }), - ); +sentryTest('captures long animation frame span for event listener.', async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - const url = await getLocalTestUrl({ testDir: __dirname }); + await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - const promise = getFirstSentryEnvelopeRequest(page); + const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - // trigger long animation frame function - await page.getByRole('button').click(); + await page.goto(url); - await new Promise(resolve => setTimeout(resolve, 1000)); + // trigger long animation frame function + await page.getByRole('button').click(); - const eventData = await promise; + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')) || []; + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); - expect(uiSpans.length).toBeGreaterThanOrEqual(2); + expect(uiSpans.length).toBeGreaterThanOrEqual(2); - const eventListenerUISpan = uiSpans.find(span => span.data['browser.script.invoker'] === 'BUTTON#clickme.onclick')!; + const eventListenerUISpan = uiSpans.find( + s => s.attributes['browser.script.invoker']?.value === 'BUTTON#clickme.onclick', + )!; - expect(eventListenerUISpan).toEqual( - expect.objectContaining({ - op: 'ui.long_animation_frame', - description: 'Main UI thread blocked', - parent_span_id: eventData.contexts?.trace?.span_id, - data: { - 'browser.script.invoker': 'BUTTON#clickme.onclick', - 'browser.script.invoker_type': 'event-listener', - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', - [SENTRY_OP]: 'ui.long_animation_frame', - [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', - }, + expect(eventListenerUISpan).toEqual( + expect.objectContaining({ + name: 'Main UI thread blocked', + parent_span_id: pageloadSpan.span_id, + attributes: expect.objectContaining({ + 'browser.script.invoker': { type: 'string', value: 'BUTTON#clickme.onclick' }, + 'browser.script.invoker_type': { type: 'string', value: 'event-listener' }, + 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, }), - ); - const start = eventListenerUISpan.start_timestamp ?? 0; - const end = eventListenerUISpan.timestamp ?? 0; - const duration = end - start; + }), + ); - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); - }, -); + const start = eventListenerUISpan.start_timestamp ?? 0; + const end = eventListenerUISpan.end_timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/init.js deleted file mode 100644 index 9f36511d8ff5..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/init.js +++ /dev/null @@ -1,18 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongAnimationFrame: false, - instrumentPageLoad: false, - instrumentNavigation: true, - enableInp: false, - enableLongTask: true, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/subject.js deleted file mode 100644 index d814f8875715..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -const longTaskButton = document.getElementById('myButton'); - -longTaskButton?.addEventListener('click', () => { - const startTime = Date.now(); - - function getElapsed() { - const time = Date.now(); - return time - startTime; - } - - while (getElapsed() < 500) { - // - } - - // trigger a navigation in the same event loop tick - window.history.pushState({}, '', '#myHeading'); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/template.html deleted file mode 100644 index c2cb2a8129fe..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/template.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - -
Rendered Before Long Task
- - -

Heading

- - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts deleted file mode 100644 index 7e544f27a107..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest( - "doesn't capture long task spans starting before a navigation in the navigation transaction", - async ({ browserName, getLocalTestUrl, page }) => { - // Long tasks only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - const url = await getLocalTestUrl({ testDir: __dirname }); - - await page.route('**/path/to/script.js', route => route.fulfill({ path: `${__dirname}/assets/script.js` })); - - const navigationSpansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'navigation')); - - await page.goto(url); - - await page.locator('#myButton').click(); - - const spans = await navigationSpansPromise; - - const navigationSpan = spans.find(s => getSpanOp(s) === 'navigation'); - expect(navigationSpan).toBeDefined(); - - const longTaskSpans = spans.filter(s => getSpanOp(s) === 'ui.long_task'); - expect(longTaskSpans).toHaveLength(0); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/init.js index 63abc00dea38..ccf669361e37 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts index 5236717091f1..ffa5f54c4fd6 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts @@ -1,30 +1,32 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( - "doesn't capture long task spans starting before a navigation in the navigation transaction", + "doesn't capture long task spans starting before a navigation in the navigation span", async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); const url = await getLocalTestUrl({ testDir: __dirname }); await page.route('**/path/to/script.js', route => route.fulfill({ path: `${__dirname}/assets/script.js` })); - await page.goto(url); + const allSpans = collectStreamedSpans(page); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); - const navigationTransactionEventPromise = getFirstSentryEnvelopeRequest(page); + await page.goto(url); await page.locator('#myButton').click(); - const navigationTransactionEvent = await navigationTransactionEventPromise; + const navigationRoot = await navigationSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === navigationRoot.span_id); - expect(navigationTransactionEvent.contexts?.trace?.op).toBe('navigation'); + const navigationSpan = spans.find(s => getSpanOp(s) === 'navigation'); + expect(navigationSpan).toBeDefined(); - const longTaskSpans = navigationTransactionEvent?.spans?.filter(span => span.op === 'ui.long_task'); + const longTaskSpans = spans.filter(s => getSpanOp(s) === 'ui.long_task'); expect(longTaskSpans).toHaveLength(0); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/assets/script.js deleted file mode 100644 index 195a094070be..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/assets/script.js +++ /dev/null @@ -1,12 +0,0 @@ -(() => { - const startTime = Date.now(); - - function getElapsed() { - const time = Date.now(); - return time - startTime; - } - - while (getElapsed() < 101) { - // - } -})(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/init.js deleted file mode 100644 index 3c004b952f80..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongTask: false, - enableLongAnimationFrame: false, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/template.html deleted file mode 100644 index b03231da2c65..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -
Rendered Before Long Task
- - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts deleted file mode 100644 index da7f44f05df7..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Route } from '@playwright/test'; -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest("doesn't capture long task spans when flag is disabled.", async ({ browserName, getLocalTestUrl, page }) => { - // Long tasks only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - const spans = await spansPromise; - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui')); - - expect(uiSpans.length).toBe(0); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/init.js index 250f2a72311b..0339c3fa3dbf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/test.ts index bb32e21ee1d5..6ebf0c018e6f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled/test.ts @@ -1,21 +1,25 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; -sentryTest('should not capture long task when flag is disabled.', async ({ browserName, getLocalTestUrl, page }) => { +sentryTest("doesn't capture long task spans when flag is disabled.", async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - expect(uiSpans?.length).toBe(0); + await page.goto(url); + + await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui')); + + expect(uiSpans.length).toBe(0); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/assets/script.js deleted file mode 100644 index b61592e05943..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/assets/script.js +++ /dev/null @@ -1,12 +0,0 @@ -(() => { - const startTime = Date.now(); - - function getElapsed() { - const time = Date.now(); - return time - startTime; - } - - while (getElapsed() < 105) { - // - } -})(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/init.js deleted file mode 100644 index 977ff9c6425e..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/init.js +++ /dev/null @@ -1,14 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongAnimationFrame: false, - }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/template.html deleted file mode 100644 index b03231da2c65..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -
Rendered Before Long Task
- - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts deleted file mode 100644 index 1eeb65579b39..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Route } from '@playwright/test'; -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -sentryTest('captures long task.', async ({ browserName, getLocalTestUrl, page }) => { - // Long tasks only work on chrome - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); - - await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const spansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'pageload')); - - await page.goto(url); - - const spans = await spansPromise; - const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload')!; - - const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui')); - expect(uiSpans.length).toBeGreaterThan(0); - - const [firstUISpan] = uiSpans; - expect(firstUISpan).toEqual( - expect.objectContaining({ - name: 'Main UI thread blocked', - parent_span_id: pageloadSpan.span_id, - attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'ui.long_task' }, - }), - }), - ); - - const start = firstUISpan.start_timestamp ?? 0; - const end = firstUISpan.end_timestamp ?? 0; - const duration = end - start; - - expect(duration).toBeGreaterThanOrEqual(0.1); - expect(duration).toBeLessThanOrEqual(0.15); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/init.js index 73d20634b532..47e9fd0b92d8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts index e34b0d779f7e..8299abca7bb9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts @@ -1,34 +1,41 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; -sentryTest('should capture long task.', async ({ browserName, getLocalTestUrl, page }) => { +sentryTest('captures long task.', async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } + sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + const spans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - expect(uiSpans?.length).toBeGreaterThan(0); + await page.goto(url); - const [firstUISpan] = uiSpans || []; + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + + const uiSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui')); + expect(uiSpans.length).toBeGreaterThan(0); + + const [firstUISpan] = uiSpans; expect(firstUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_task', - description: 'Main UI thread blocked', - parent_span_id: eventData.contexts?.trace?.span_id, + name: 'Main UI thread blocked', + parent_span_id: pageloadSpan.span_id, + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'ui.long_task' }, + }), }), ); + const start = firstUISpan.start_timestamp ?? 0; - const end = firstUISpan.timestamp ?? 0; + const end = firstUISpan.end_timestamp ?? 0; const duration = end - start; expect(duration).toBeGreaterThanOrEqual(0.1); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js index a7797f5dce06..723707736f2e 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts index e34b0d779f7e..b078111ff353 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts @@ -1,8 +1,8 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest('should capture long task.', async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome @@ -14,21 +14,27 @@ sentryTest('should capture long task.', async ({ browserName, getLocalTestUrl, p const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + const spans = collectStreamedSpans(page); + const [pageloadSpan] = await waitForStreamedSpanAndTraceHeaderOnUrl( + page, + url, + span => getSpanOp(span) === 'pageload', + ); + await page.evaluate(() => (window as any).Sentry.flush()); + const uiSpans = spans.filter(span => getSpanOp(span)?.startsWith('ui')); - expect(uiSpans?.length).toBeGreaterThan(0); + expect(uiSpans.length).toBeGreaterThan(0); - const [firstUISpan] = uiSpans || []; + const [firstUISpan] = uiSpans; expect(firstUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_task', - description: 'Main UI thread blocked', - parent_span_id: eventData.contexts?.trace?.span_id, + attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'ui.long_task' } }), + name: 'Main UI thread blocked', + parent_span_id: pageloadSpan.span_id, }), ); const start = firstUISpan.start_timestamp ?? 0; - const end = firstUISpan.timestamp ?? 0; + const end = firstUISpan.end_timestamp ?? 0; const duration = end - start; expect(duration).toBeGreaterThanOrEqual(0.1); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/init.js index 52fc529d095a..d4c7810ef518 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/init.js @@ -3,7 +3,6 @@ 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/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts index 1d7a14a46055..6786d28edbc5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts @@ -1,43 +1,30 @@ import { expect } from '@playwright/test'; -import type { Event, EventEnvelopeHeaders } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - envelopeHeaderRequestParser, - getFirstSentryEnvelopeRequest, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; - -sentryTest('should create a pageload transaction based on `sentry-trace` ', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; +sentryTest('creates a pageload span based on `sentry-trace` ', async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.contexts?.trace).toMatchObject({ - op: 'pageload', + const spans = collectStreamedSpans(page); + const [pageload] = await waitForStreamedSpanAndTraceHeaderOnUrl(page, url); + await page.evaluate(() => (window as any).Sentry.flush()); + expect(getSpanOp(pageload)).toBe('pageload'); + expect(pageload).toMatchObject({ + is_segment: true, parent_span_id: '1121201211212012', trace_id: '12312012123120121231201212312012', }); - - expect(eventData.spans?.length).toBeGreaterThan(0); + expect(spans.filter(span => !span.is_segment)).not.toHaveLength(0); }); sentryTest( - 'should pick up `baggage` tag, propagate the content in transaction and not add own data', + 'propagates `baggage` in the span envelope without adding own data', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - - const envHeader = await getFirstSentryEnvelopeRequest(page, url, envelopeHeaderRequestParser); - - expect(envHeader.trace).toBeDefined(); - expect(envHeader.trace).toEqual({ + const [, trace] = await waitForStreamedSpanAndTraceHeaderOnUrl(page, url); + expect(trace).toEqual({ release: '2.1.12', sample_rate: '0.3232', trace_id: '123', @@ -47,47 +34,31 @@ sentryTest( }, ); -sentryTest( - "should create a navigation that's not influenced by `sentry-trace` ", - async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadRequest = await getFirstSentryEnvelopeRequest(page, url); - const navigationRequest = await getFirstSentryEnvelopeRequest(page, `${url}#foo`); - - expect(pageloadRequest.contexts?.trace).toMatchObject({ - op: 'pageload', - parent_span_id: '1121201211212012', - trace_id: '12312012123120121231201212312012', - }); - - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); - expect(navigationRequest.contexts?.trace?.trace_id).toBeDefined(); - expect(navigationRequest.contexts?.trace?.trace_id).not.toBe(pageloadRequest.contexts?.trace?.trace_id); - - const pageloadSpans = pageloadRequest.spans; - const navigationSpans = navigationRequest.spans; - - const pageloadSpanId = pageloadRequest.contexts?.trace?.span_id; - const navigationSpanId = navigationRequest.contexts?.trace?.span_id; - - expect(pageloadSpanId).toBeDefined(); - expect(navigationSpanId).toBeDefined(); - - pageloadSpans?.forEach(span => - expect(span).toMatchObject({ - parent_span_id: pageloadSpanId, - }), - ); - - navigationSpans?.forEach(span => - expect(span).toMatchObject({ - parent_span_id: navigationSpanId, - }), - ); - }, -); +sentryTest("creates a navigation that's not influenced by `sentry-trace` ", async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); + const url = await getLocalTestUrl({ testDir: __dirname }); + const spans = collectStreamedSpans(page); + const [pageload] = await waitForStreamedSpanAndTraceHeaderOnUrl(page, url); + const [navigation] = await waitForStreamedSpanAndTraceHeaderOnUrl( + page, + `${url}#foo`, + span => getSpanOp(span) === 'navigation', + ); + await page.evaluate(() => (window as any).Sentry.flush()); + + expect(getSpanOp(pageload)).toBe('pageload'); + expect(pageload).toMatchObject({ + parent_span_id: '1121201211212012', + trace_id: '12312012123120121231201212312012', + }); + expect(getSpanOp(navigation)).toBe('navigation'); + expect(navigation.trace_id).toBeDefined(); + expect(navigation.trace_id).not.toBe(pageload.trace_id); + expect(pageload.span_id).toBeDefined(); + expect(navigation.span_id).toBeDefined(); + for (const root of [pageload, navigation]) { + spans + .filter(span => !span.is_segment && span.attributes['sentry.segment.id']?.value === root.span_id) + .forEach(span => expect(span.parent_span_id).toBe(root.span_id)); + } +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/init.js index f1e482cc6950..fcce12c3dca2 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts index 829710ce5011..7451fe7510d3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts @@ -2,68 +2,62 @@ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/co import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; - -sentryTest( - 'should create a navigation transaction that aborts an ongoing pageload', - async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( - page, - event => event.contexts?.trace?.op === 'navigation', - ); - - await page.goto(url); - - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); - - expect(pageloadRequest.contexts?.trace?.op).toBe('pageload'); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); - - expect(navigationRequest.transaction_info?.source).toEqual('url'); - - const pageloadTraceId = pageloadRequest.contexts?.trace?.trace_id; - const navigationTraceId = navigationRequest.contexts?.trace?.trace_id; - - expect(pageloadTraceId).toBeDefined(); - expect(navigationTraceId).toBeDefined(); - expect(pageloadTraceId).not.toEqual(navigationTraceId); - - expect(pageloadRequest.transaction).toEqual('/index.html'); - expect(navigationRequest.transaction).toEqual('/sub-page'); - - expect(pageloadRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'cancelled', - }); - expect(navigationRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.navigation.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', - }); - expect(pageloadRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), - }, - url: 'http://sentry-test.io/index.html', - }); - expect(navigationRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), - }, - url: 'http://sentry-test.io/sub-page', - }); - }, -); +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; + +sentryTest('should create a navigation span that aborts an ongoing pageload', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); + + await page.goto(url); + + const pageloadSpan = await pageloadSpanPromise; + const navigationSpan = await navigationSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); + + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(getSpanOp(navigationSpan)).toBe('navigation'); + + expect(navigationSpan.attributes['sentry.segment.name.source']?.value).toEqual('url'); + + const pageloadTraceId = pageloadSpan.trace_id; + const navigationTraceId = navigationSpan.trace_id; + + expect(pageloadTraceId).toBeDefined(); + expect(navigationTraceId).toBeDefined(); + expect(pageloadTraceId).not.toEqual(navigationTraceId); + + expect(pageloadSpan.name).toEqual('Pageload'); + expect(pageloadSpan.attributes['url.path']?.value).toEqual('/index.html'); + expect(navigationSpan.name).toEqual('Navigation'); + expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); + + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, + ['sentry.idle_span_finish_reason']: { type: 'string', value: 'cancelled' }, + }); + expect(navigationSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.navigation.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'navigation' }, + ['sentry.idle_span_finish_reason']: { type: 'string', value: 'idleTimeout' }, + }); + expect(pageloadSpan.attributes).toMatchObject({ + 'user_agent.original': { type: 'string', value: expect.any(String) }, + 'url.full': { type: 'string', value: 'http://sentry-test.io/index.html' }, + }); + expect(navigationSpan.attributes).toMatchObject({ + 'user_agent.original': { type: 'string', value: expect.any(String) }, + 'url.full': { type: 'string', value: 'http://sentry-test.io/sub-page' }, + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/init.js index b538f322b360..484a12682a2d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/multiple-redirects/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts index caa2b6afb328..daeb9d1a9498 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts @@ -1,7 +1,8 @@ import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; import { SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, @@ -17,66 +18,72 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/next-page', + span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/next-page', ); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); + const pageloadSpan = await pageloadSpanPromise; + const navigationSpan = await navigationSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); - expect(pageloadRequest.contexts?.trace?.op).toBe('pageload'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); - expect(pageloadRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'cancelled', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, + ['sentry.idle_span_finish_reason']: { type: 'string', value: 'cancelled' }, }); - expect(pageloadRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), - }, - url: 'http://sentry-test.io/index.html', + expect(pageloadSpan.attributes).toMatchObject({ + 'user_agent.original': { type: 'string', value: expect.any(String) }, + 'url.full': { type: 'string', value: 'http://sentry-test.io/index.html' }, }); - const spans = pageloadRequest.spans || []; + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); - const redirectSpans = spans.filter(span => span.op === 'navigation.redirect'); + const redirectSpans = spans.filter(span => getSpanOp(span) === 'navigation.redirect'); expect(redirectSpans).toHaveLength(3); redirectSpans.forEach(redirectSpan => { - expect(redirectSpan?.timestamp).toEqual(redirectSpan?.start_timestamp); - expect(redirectSpan).toEqual({ - data: { - 'sentry.op': 'navigation.redirect', - 'sentry.origin': 'auto.navigation.browser', - [URL_FULL]: expect.any(String), - [URL_PATH]: expect.any(String), + expect(redirectSpan?.end_timestamp).toEqual(redirectSpan?.start_timestamp); + expect(redirectSpan).toMatchObject({ + attributes: { + 'sentry.op': { type: 'string', value: 'navigation.redirect' }, + 'sentry.origin': { type: 'string', value: 'auto.navigation.browser' }, + 'sentry.segment.id': { type: 'string', value: pageloadSpan.span_id }, + 'sentry.segment.name': { type: 'string', value: 'Pageload' }, + [URL_FULL]: { type: 'string', value: expect.any(String) }, + [URL_PATH]: { type: 'string', value: expect.any(String) }, }, - description: expect.stringContaining('/sub-page-redirect-'), - op: 'navigation.redirect', - origin: 'auto.navigation.browser', + name: 'Navigation', + is_segment: false, status: 'ok', - parent_span_id: pageloadRequest.contexts!.trace!.span_id, + parent_span_id: pageloadSpan.span_id, span_id: expect.any(String), start_timestamp: expect.any(Number), - timestamp: expect.any(Number), + end_timestamp: expect.any(Number), trace_id: expect.any(String), }); }); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); - expect(navigationRequest.transaction).toEqual('/next-page'); + expect(getSpanOp(navigationSpan)).toBe('navigation'); + expect(navigationSpan.name).toEqual('Navigation'); + expect(navigationSpan.attributes['url.path']?.value).toEqual('/next-page'); // 2 subsequent redirects belonging to the navigation root span - expect(navigationRequest.spans?.filter(span => span.op === 'navigation.redirect')).toHaveLength(2); + expect( + allSpans + .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id) + .filter(span => getSpanOp(span) === 'navigation.redirect'), + ).toHaveLength(2); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/init.js index b85590114a76..c3e90ce95430 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/navigation-navigation-click/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts index a88987209bcd..f8f93d67b9c6 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'creates navigation root span if click happened within 1.5s of the last navigation', @@ -9,39 +10,47 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page', + span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page', ); - const navigation2RequestPromise = waitForTransactionRequest( + const navigation2SpanPromise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page-2', + span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page-2', ); await page.goto(url); - await pageloadRequestPromise; + await pageloadSpanPromise; // Now trigger navigation (since no span is active), and then a redirect in the navigation, with await page.click('#btn1'); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); - const navigation2Request = envelopeRequestParser(await navigation2RequestPromise); + const navigationSpan = await navigationSpanPromise; + const navigation2Span = await navigation2SpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); - expect(navigationRequest.transaction).toEqual('/sub-page'); + expect(getSpanOp(navigationSpan)).toBe('navigation'); + expect(navigationSpan.name).toEqual('Navigation'); + expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); - const spans = (navigationRequest.spans || []).filter(s => s.op === 'navigation.redirect'); + const spans = allSpans + .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id) + .filter(s => getSpanOp(s) === 'navigation.redirect'); expect(spans).toHaveLength(0); - expect(navigation2Request.contexts?.trace?.op).toBe('navigation'); - expect(navigation2Request.transaction).toEqual('/sub-page-2'); + expect(getSpanOp(navigation2Span)).toBe('navigation'); + expect(navigation2Span.name).toEqual('Navigation'); + expect(navigation2Span.attributes['url.path']?.value).toEqual('/sub-page-2'); - const spans2 = (navigation2Request.spans || []).filter(s => s.op === 'navigation.redirect'); + const spans2 = allSpans + .filter(span => span.attributes['sentry.segment.id']?.value === navigation2Span.span_id) + .filter(s => getSpanOp(s) === 'navigation.redirect'); expect(spans2).toHaveLength(0); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/init.js index b53ef315b481..f9d8db46d3d9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/navigation-navigation-keypress/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts index fe152feda462..a6e89c4d5dea 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'creates a navigation root span if a keypress happened within the last 1.5s', @@ -9,22 +10,23 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page', + span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page', ); - const navigationRequest2Promise = waitForTransactionRequest( + const navigationSpan2Promise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page-2', + span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page-2', ); await page.goto(url); - await pageloadRequestPromise; + await pageloadSpanPromise; await page.focus('#btn1'); await page.keyboard.press('Enter'); @@ -34,21 +36,29 @@ sentryTest( await page.focus('#btn2'); await page.keyboard.press('Enter'); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); - const navigationRequest2 = envelopeRequestParser(await navigationRequest2Promise); + const navigationSpan = await navigationSpanPromise; + const navigationSpan2 = await navigationSpan2Promise; + await page.evaluate(() => (window as any).Sentry.flush()); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); - expect(navigationRequest.transaction).toEqual('/sub-page'); + expect(getSpanOp(navigationSpan)).toBe('navigation'); + expect(navigationSpan.name).toEqual('Navigation'); + expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); - const redirectSpans = navigationRequest.spans?.filter(span => span.op === 'navigation.redirect') || []; + const redirectSpans = allSpans + .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id) + .filter(span => getSpanOp(span) === 'navigation.redirect'); expect(redirectSpans).toHaveLength(1); - expect(redirectSpans[0].description).toEqual('/sub-page-redirect'); + expect(redirectSpans[0].name).toBe('Navigation'); + expect(redirectSpans[0].attributes['url.path']?.value).toBe('/sub-page-redirect'); - expect(navigationRequest2.contexts?.trace?.op).toBe('navigation'); - expect(navigationRequest2.transaction).toEqual('/sub-page-2'); + expect(getSpanOp(navigationSpan2)).toBe('navigation'); + expect(navigationSpan2.name).toEqual('Navigation'); + expect(navigationSpan2.attributes['url.path']?.value).toEqual('/sub-page-2'); - const redirectSpans2 = navigationRequest2.spans?.filter(span => span.op === 'navigation.redirect') || []; + const redirectSpans2 = allSpans + .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan2.span_id) + .filter(span => getSpanOp(span) === 'navigation.redirect'); expect(redirectSpans2).toHaveLength(0); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/init.js index 1545e68dd89c..5cf45a1f0b06 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/navigation-redirect/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts index 71fe35c8bcc9..65491e7d879e 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'creates a navigation root span and redirect child span if no click happened within the last 1.5s', @@ -9,32 +10,35 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( - page, - event => event.contexts?.trace?.op === 'navigation', - ); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(url); - await pageloadRequestPromise; + await pageloadSpanPromise; // Now trigger navigation (since no span is active), and then a redirect in the navigation, with await page.click('#btn1'); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); + const navigationSpan = await navigationSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); - expect(navigationRequest.transaction).toEqual('/sub-page'); + expect(getSpanOp(navigationSpan)).toBe('navigation'); + expect(navigationSpan.name).toEqual('Navigation'); + expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); - const spans = navigationRequest.spans || []; + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id); expect(spans).toContainEqual( expect.objectContaining({ - op: 'navigation.redirect', - description: '/sub-page-redirect', + name: 'Navigation', + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'navigation.redirect' }, + 'url.path': { type: 'string', value: '/sub-page-redirect' }, + }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/init.js index bfefd3b54534..331024032a6f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts index a25cc01f086f..d7d24d392ca7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if `detectRedirects` is set to false", @@ -9,25 +10,24 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( - page, - event => event.contexts?.trace?.op === 'navigation', - ); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); // Ensure a navigation span is sent, too - await navigationRequestPromise; + await navigationSpanPromise; - const spans = pageloadRequest.spans || []; + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); expect(spans).not.toContainEqual( expect.objectContaining({ - op: 'navigation.redirect', + attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/init.js index bdd43f5ac757..1ce4125ee422 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/pageload-navigation-click/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts index 771b07afaba6..d9d46d5e428a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if a click happened before navigation", @@ -9,25 +10,24 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( - page, - event => event.contexts?.trace?.op === 'navigation', - ); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); // Ensure a navigation span is sent, too - await navigationRequestPromise; + await navigationSpanPromise; - const pageloadTxnSpans = pageloadRequest.spans || []; + const pageloadSpans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); - expect(pageloadTxnSpans).not.toContainEqual( + expect(pageloadSpans).not.toContainEqual( expect.objectContaining({ - op: 'navigation.redirect', + attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/init.js index bdd43f5ac757..1ce4125ee422 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/pageload-navigation-keypress/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts index e77e08936466..fd191b3e6996 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if a keypress happened before navigation", @@ -9,27 +10,26 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( - page, - event => event.contexts?.trace?.op === 'navigation', - ); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(url); await page.focus('#btn1'); await page.keyboard.press('Enter'); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); // Ensure a navigation span is sent, too - await navigationRequestPromise; + await navigationSpanPromise; - const spans = pageloadRequest.spans || []; + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); expect(spans).not.toContainEqual( expect.objectContaining({ - op: 'navigation.redirect', + attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/init.js index 4aecc7f5ef40..f317a550e2a7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/pageload-navigation-time/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts index 6b5925f52cd1..be0d1ce732da 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts @@ -1,6 +1,7 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if redirect happened more than 1.5s after pageload", @@ -9,25 +10,24 @@ sentryTest( sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( - page, - event => event.contexts?.trace?.op === 'navigation', - ); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'navigation'); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); // Ensure a navigation span is sent, too - await navigationRequestPromise; + await navigationSpanPromise; - const spans = pageloadRequest.spans || []; + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); expect(spans).not.toContainEqual( expect.objectContaining({ - op: 'navigation.redirect', + attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/init.js index bb91be7762dc..cba0015b22c8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/init.js @@ -3,7 +3,6 @@ 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/navigation-redirect/pageload-redirect/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts index fc126da536f3..9f54683d08fc 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts @@ -1,7 +1,8 @@ import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; import { SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, @@ -15,56 +16,57 @@ sentryTest('creates a pageload root span with navigation.redirect childspan', as sentryTest.skip(); } + const allSpans = collectStreamedSpans(page); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); await page.goto(url); - const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + const pageloadSpan = await pageloadSpanPromise; + await page.evaluate(() => (window as any).Sentry.flush()); - expect(pageloadRequest.contexts?.trace?.op).toBe('pageload'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); - expect(pageloadRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, + ['sentry.idle_span_finish_reason']: { type: 'string', value: 'idleTimeout' }, }); - expect(pageloadRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), - }, - url: 'http://sentry-test.io/index.html', + expect(pageloadSpan.attributes).toMatchObject({ + 'user_agent.original': { type: 'string', value: expect.any(String) }, + 'url.full': { type: 'string', value: 'http://sentry-test.io/index.html' }, }); - const spans = pageloadRequest.spans || []; + const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); expect(spans).toContainEqual( expect.objectContaining({ - op: 'navigation.redirect', + attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), }), ); - const redirectSpan = spans.find(span => span.op === 'navigation.redirect'); - expect(redirectSpan?.timestamp).toEqual(redirectSpan?.start_timestamp); - expect(redirectSpan).toEqual({ - data: { - 'sentry.op': 'navigation.redirect', - 'sentry.origin': 'auto.navigation.browser', - [URL_FULL]: 'http://sentry-test.io/sub-page', - [URL_PATH]: '/sub-page', + const redirectSpan = spans.find(span => getSpanOp(span) === 'navigation.redirect'); + expect(redirectSpan?.end_timestamp).toEqual(redirectSpan?.start_timestamp); + expect(redirectSpan).toMatchObject({ + attributes: { + 'sentry.op': { type: 'string', value: 'navigation.redirect' }, + 'sentry.origin': { type: 'string', value: 'auto.navigation.browser' }, + 'sentry.segment.id': { type: 'string', value: pageloadSpan.span_id }, + 'sentry.segment.name': { type: 'string', value: 'Pageload' }, + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page' }, + [URL_PATH]: { type: 'string', value: '/sub-page' }, }, - description: '/sub-page', - op: 'navigation.redirect', - origin: 'auto.navigation.browser', + name: 'Navigation', + is_segment: false, status: 'ok', - parent_span_id: pageloadRequest.contexts!.trace!.span_id, + parent_span_id: pageloadSpan.span_id, span_id: expect.any(String), start_timestamp: expect.any(Number), - timestamp: expect.any(Number), + end_timestamp: expect.any(Number), trace_id: expect.any(String), }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts deleted file mode 100644 index 62fcd88c337f..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-streamed/test.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { expect } from '@playwright/test'; -import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; -import { - SENTRY_SEGMENT_NAME_SOURCE, - SENTRY_TRACE_LIFECYCLE, - URL_FULL, - URL_PATH, - USER_AGENT_ORIGINAL, - SENTRY_ENVIRONMENT, - SENTRY_SDK_INTEGRATIONS, - SENTRY_OP, - SENTRY_ORIGIN, -} from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { - getSpanOp, - getSpansFromEnvelope, - waitForStreamedSpan, - waitForStreamedSpanEnvelope, -} from '../../../../utils/spanUtils'; - -sentryTest('starts a streamed navigation span on page navigation', async ({ browserName, getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - const navigationSpanEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!getSpansFromEnvelope(env).find(s => getSpanOp(s) === 'navigation'), - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const pageloadSpan = await pageloadSpanPromise; - - // simulate navigation - page.goto(`${url}#foo`); - - const navigationSpanEnvelope = await navigationSpanEnvelopePromise; - - const navigationSpanEnvelopeHeader = navigationSpanEnvelope[0]; - const navigationSpanEnvelopeItem = navigationSpanEnvelope[1]; - const navigationSpans = navigationSpanEnvelopeItem[0][1].items; - const navigationSpan = navigationSpans.find(s => getSpanOp(s) === 'navigation')!; - - expect(navigationSpanEnvelopeHeader).toEqual({ - sent_at: expect.any(String), - trace: { - trace_id: expect.stringMatching(/^[\da-f]{32}$/), - environment: 'production', - public_key: 'public', - sample_rand: expect.any(String), - sample_rate: '1', - sampled: 'true', - }, - sdk: { - name: 'sentry.javascript.browser', - version: SDK_VERSION, - }, - }); - - const numericSampleRand = parseFloat(navigationSpanEnvelopeHeader.trace!.sample_rand!); - expect(Number.isNaN(numericSampleRand)).toBe(false); - - const pageloadTraceId = pageloadSpan.trace_id; - const navigationTraceId = navigationSpan.trace_id; - - expect(pageloadTraceId).toBeDefined(); - expect(navigationTraceId).toBeDefined(); - expect(pageloadTraceId).not.toEqual(navigationTraceId); - - expect(pageloadSpan.name).toEqual('Pageload'); - - expect(navigationSpan).toEqual({ - attributes: { - 'sentry.is_localhost': { value: false, type: 'boolean' }, - [SENTRY_TRACE_LIFECYCLE]: { - type: 'string', - value: 'stream', - }, - 'culture.calendar': { - type: 'string', - value: expect.any(String), - }, - 'culture.locale': { - type: 'string', - value: expect.any(String), - }, - 'culture.timezone': { - type: 'string', - value: expect.any(String), - }, - [USER_AGENT_ORIGINAL]: { - type: 'string', - value: expect.any(String), - }, - [URL_FULL]: { - type: 'string', - value: expect.any(String), - }, - [URL_PATH]: { - type: 'string', - value: expect.any(String), - }, - 'device.processor_count': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - ...(browserName !== 'webkit' && { - 'network.connection.effective_type': { - type: 'string', - value: expect.any(String), - }, - 'network.connection.rtt': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - }), - 'sentry.idle_span_finish_reason': { - type: 'string', - value: 'idleTimeout', - }, - [SENTRY_OP]: { - type: 'string', - value: 'navigation', - }, - [SENTRY_ORIGIN]: { - type: 'string', - value: 'auto.navigation.browser', - }, - 'sentry.previous_trace': { - type: 'string', - value: `${pageloadTraceId}-${pageloadSpan.span_id}-1`, - }, - 'sentry.sample_rate': { - type: 'integer', - value: 1, - }, - 'sentry.sdk.name': { - type: 'string', - value: 'sentry.javascript.browser', - }, - 'sentry.sdk.version': { - type: 'string', - value: SDK_VERSION, - }, - [SENTRY_SDK_INTEGRATIONS]: { - type: 'array', - value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), - }, - 'sentry.segment.id': { - type: 'string', - value: navigationSpan.span_id, - }, - 'sentry.segment.name': { - type: 'string', - value: 'Navigation', - }, - 'sentry.segment.name.source': { - type: 'string', - value: 'url', - }, - [SENTRY_ENVIRONMENT]: { - type: 'string', - value: 'production', - }, - }, - end_timestamp: expect.any(Number), - is_segment: true, - links: [ - { - attributes: { - 'sentry.link.type': { - type: 'string', - value: 'previous_trace', - }, - }, - sampled: true, - span_id: pageloadSpan.span_id, - trace_id: pageloadTraceId, - }, - ], - // The raw URL stays in `url.path`/`url.full`: with span streaming, a navigation span name is - // low cardinality and falls back to 'Navigation' when there is no parameterized route. - name: 'Navigation', - span_id: navigationSpan.span_id, - start_timestamp: expect.any(Number), - status: 'ok', - trace_id: navigationTraceId, - }); -}); - -sentryTest('handles pushState with full URL', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - const navigationSpan1Promise = waitForStreamedSpan( - page, - // Matched on `url.path` rather than the span name, which is low cardinality. - span => getSpanOp(span) === 'navigation' && span.attributes?.[URL_PATH]?.value === '/sub-page', - ); - const navigationSpan2Promise = waitForStreamedSpan( - page, - span => getSpanOp(span) === 'navigation' && span.attributes?.[URL_PATH]?.value === '/sub-page-2', - ); - - await page.goto(url); - await pageloadSpanPromise; - - await page.evaluate("window.history.pushState({}, '', `${window.location.origin}/sub-page`);"); - - const navigationSpan1 = await navigationSpan1Promise; - - expect(navigationSpan1.name).toEqual('Navigation'); - - expect(navigationSpan1.attributes).toMatchObject({ - [URL_PATH]: { - type: 'string', - value: '/sub-page', - }, - [SENTRY_ORIGIN]: { - type: 'string', - value: 'auto.navigation.browser', - }, - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { - type: 'integer', - value: 1, - }, - [SENTRY_SEGMENT_NAME_SOURCE]: { - type: 'string', - value: 'url', - }, - [SENTRY_OP]: { - type: 'string', - value: 'navigation', - }, - }); - - await page.evaluate("window.history.pushState({}, '', `${window.location.origin}/sub-page-2`);"); - - const navigationSpan2 = await navigationSpan2Promise; - - expect(navigationSpan2.name).toEqual('Navigation'); - - expect(navigationSpan2.attributes).toMatchObject({ - [URL_PATH]: { - type: 'string', - value: '/sub-page-2', - }, - [SENTRY_ORIGIN]: { - type: 'string', - value: 'auto.navigation.browser', - }, - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { - type: 'integer', - value: 1, - }, - [SENTRY_SEGMENT_NAME_SOURCE]: { - type: 'string', - value: 'url', - }, - [SENTRY_OP]: { - type: 'string', - value: 'navigation', - }, - ['sentry.idle_span_finish_reason']: { - type: 'string', - value: 'idleTimeout', - }, - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/init.js similarity index 60% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/init.js index 749560a5c459..83076460599f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], + integrations: [Sentry.browserTracingIntegration()], tracesSampleRate: 1, - debug: true, }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts index ab8a3931e132..bff9dc54a9ce 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts @@ -1,150 +1,297 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; +import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; +import { + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_TRACE_LIFECYCLE, + URL_FULL, + URL_PATH, + USER_AGENT_ORIGINAL, + SENTRY_ENVIRONMENT, + SENTRY_SDK_INTEGRATIONS, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { sentryTest } from '../../../../utils/fixtures'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; import { - envelopeRequestParser, - getFirstSentryEnvelopeRequest, - shouldSkipTracingTest, - waitForTransactionRequest, -} from '../../../../utils/helpers'; - -sentryTest('should create a navigation transaction on page navigation', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } + collectStreamedSpans, + getSpanOp, + getSpansFromEnvelope, + waitForStreamedSpan, + waitForStreamedSpanEnvelope, +} from '../../../../utils/spanUtils'; - const url = await getLocalTestUrl({ testDir: __dirname }); +sentryTest('starts a streamed navigation span on page navigation', async ({ browserName, getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); - const pageloadRequest = await getFirstSentryEnvelopeRequest(page, url); - const navigationRequest = await getFirstSentryEnvelopeRequest(page, `${url}#foo`); + const collectedSpans = collectStreamedSpans(page); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpanEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!getSpansFromEnvelope(env).find(s => getSpanOp(s) === 'navigation'), + ); - expect(pageloadRequest.contexts?.trace?.op).toBe('pageload'); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); - expect(navigationRequest.transaction_info?.source).toEqual('url'); + const pageloadSpan = await pageloadSpanPromise; - const pageloadTraceId = pageloadRequest.contexts?.trace?.trace_id; - const navigationTraceId = navigationRequest.contexts?.trace?.trace_id; + await page.goto(`${url}#foo`); - expect(pageloadTraceId).toBeDefined(); - expect(navigationTraceId).toBeDefined(); - expect(pageloadTraceId).not.toEqual(navigationTraceId); + const navigationSpanEnvelope = await navigationSpanEnvelopePromise; - expect(pageloadRequest.transaction).toEqual('/index.html'); - // Fragment is not in transaction name - expect(navigationRequest.transaction).toEqual('/index.html'); + const navigationSpanEnvelopeHeader = navigationSpanEnvelope[0]; + const navigationSpanEnvelopeItem = navigationSpanEnvelope[1]; + const navigationSpans = navigationSpanEnvelopeItem[0][1].items; + const navigationSpan = navigationSpans.find(s => getSpanOp(s) === 'navigation')!; - expect(pageloadRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'idleTimeout', - }); - expect(navigationRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.navigation.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', - }); - expect(pageloadRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), + expect(navigationSpanEnvelopeHeader).toEqual({ + sent_at: expect.any(String), + trace: { + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + environment: 'production', + public_key: 'public', + sample_rand: expect.any(String), + sample_rate: '1', + sampled: 'true', }, - url: 'http://sentry-test.io/index.html', - }); - expect(navigationRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), + sdk: { + name: 'sentry.javascript.browser', + version: SDK_VERSION, }, - url: 'http://sentry-test.io/index.html#foo', }); - const pageloadSpans = pageloadRequest.spans; - const navigationSpans = navigationRequest.spans; + const numericSampleRand = parseFloat(navigationSpanEnvelopeHeader.trace!.sample_rand!); + expect(Number.isNaN(numericSampleRand)).toBe(false); - const pageloadSpanId = pageloadRequest.contexts?.trace?.span_id; - const navigationSpanId = navigationRequest.contexts?.trace?.span_id; + const pageloadTraceId = pageloadSpan.trace_id; + const navigationTraceId = navigationSpan.trace_id; - expect(pageloadSpanId).toBeDefined(); - expect(navigationSpanId).toBeDefined(); - - pageloadSpans?.forEach(span => - expect(span).toMatchObject({ - parent_span_id: pageloadSpanId, - }), - ); + expect(pageloadTraceId).toBeDefined(); + expect(navigationTraceId).toBeDefined(); + expect(pageloadTraceId).not.toEqual(navigationTraceId); - navigationSpans?.forEach(span => - expect(span).toMatchObject({ - parent_span_id: navigationSpanId, - }), - ); + expect(pageloadSpan.name).toEqual('Pageload'); + expect(pageloadSpan.attributes).toMatchObject({ + [URL_PATH]: { type: 'string', value: '/index.html' }, + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/index.html' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout' }, + }); + expect(pageloadSpan.span_id).not.toBe(navigationSpan.span_id); + await page.evaluate(() => (window as any).Sentry.flush()); + for (const root of [pageloadSpan, navigationSpan]) { + collectedSpans + .filter(span => !span.is_segment && span.attributes['sentry.segment.id']?.value === root.span_id) + .forEach(span => expect(span.parent_span_id).toBe(root.span_id)); + } - expect(pageloadSpanId).not.toEqual(navigationSpanId); + expect(navigationSpan).toEqual({ + attributes: { + 'sentry.is_localhost': { value: false, type: 'boolean' }, + [SENTRY_TRACE_LIFECYCLE]: { + type: 'string', + value: 'stream', + }, + 'culture.calendar': { + type: 'string', + value: expect.any(String), + }, + 'culture.locale': { + type: 'string', + value: expect.any(String), + }, + 'culture.timezone': { + type: 'string', + value: expect.any(String), + }, + [USER_AGENT_ORIGINAL]: { + type: 'string', + value: expect.any(String), + }, + [URL_FULL]: { + type: 'string', + value: 'http://sentry-test.io/index.html#foo', + }, + [URL_PATH]: { + type: 'string', + value: '/index.html', + }, + 'device.processor_count': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + ...(browserName !== 'webkit' && { + 'network.connection.effective_type': { + type: 'string', + value: expect.any(String), + }, + 'network.connection.rtt': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + }), + 'sentry.idle_span_finish_reason': { + type: 'string', + value: 'idleTimeout', + }, + [SENTRY_OP]: { + type: 'string', + value: 'navigation', + }, + [SENTRY_ORIGIN]: { + type: 'string', + value: 'auto.navigation.browser', + }, + 'sentry.previous_trace': { + type: 'string', + value: `${pageloadTraceId}-${pageloadSpan.span_id}-1`, + }, + 'sentry.sample_rate': { + type: 'integer', + value: 1, + }, + 'sentry.sdk.name': { + type: 'string', + value: 'sentry.javascript.browser', + }, + 'sentry.sdk.version': { + type: 'string', + value: SDK_VERSION, + }, + [SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), + }, + 'sentry.segment.id': { + type: 'string', + value: navigationSpan.span_id, + }, + 'sentry.segment.name': { + type: 'string', + value: 'Navigation', + }, + 'sentry.segment.name.source': { + type: 'string', + value: 'url', + }, + [SENTRY_ENVIRONMENT]: { + type: 'string', + value: 'production', + }, + }, + end_timestamp: expect.any(Number), + is_segment: true, + links: [ + { + attributes: { + 'sentry.link.type': { + type: 'string', + value: 'previous_trace', + }, + }, + sampled: true, + span_id: pageloadSpan.span_id, + trace_id: pageloadTraceId, + }, + ], + name: 'Navigation', + span_id: navigationSpan.span_id, + start_timestamp: expect.any(Number), + status: 'ok', + trace_id: navigationTraceId, + }); }); -// -sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } +sentryTest('handles pushState with full URL', async ({ getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const navigationRequestPromise = waitForTransactionRequest( + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const navigationSpan1Promise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page', + span => getSpanOp(span) === 'navigation' && span.attributes?.[URL_PATH]?.value === '/sub-page', ); - const navigationRequestPromise2 = waitForTransactionRequest( + const navigationSpan2Promise = waitForStreamedSpan( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page-2', + span => getSpanOp(span) === 'navigation' && span.attributes?.[URL_PATH]?.value === '/sub-page-2', ); await page.goto(url); - await pageloadRequestPromise; + await pageloadSpanPromise; await page.evaluate("window.history.pushState({}, '', `${window.location.origin}/sub-page`);"); - const navigationRequest = envelopeRequestParser(await navigationRequestPromise); + const navigationSpan1 = await navigationSpan1Promise; - expect(navigationRequest.transaction).toEqual('/sub-page'); - - expect(navigationRequest.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.navigation.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + expect(navigationSpan1.name).toEqual('Navigation'); + expect(navigationSpan1.attributes).toMatchObject({ + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page' }, + [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, + 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout' }, }); - expect(navigationRequest.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), + + expect(navigationSpan1.attributes).toMatchObject({ + [URL_PATH]: { + type: 'string', + value: '/sub-page', + }, + [SENTRY_ORIGIN]: { + type: 'string', + value: 'auto.navigation.browser', + }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { + type: 'integer', + value: 1, + }, + [SENTRY_SEGMENT_NAME_SOURCE]: { + type: 'string', + value: 'url', + }, + [SENTRY_OP]: { + type: 'string', + value: 'navigation', }, - url: 'http://sentry-test.io/sub-page', }); await page.evaluate("window.history.pushState({}, '', `${window.location.origin}/sub-page-2`);"); - const navigationRequest2 = envelopeRequestParser(await navigationRequestPromise2); - - expect(navigationRequest2.transaction).toEqual('/sub-page-2'); + const navigationSpan2 = await navigationSpan2Promise; - expect(navigationRequest2.contexts?.trace?.data).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.navigation.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + expect(navigationSpan2.name).toEqual('Navigation'); + expect(navigationSpan2.attributes).toMatchObject({ + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page-2' }, + [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, + 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout' }, }); - expect(navigationRequest2.request).toEqual({ - headers: { - 'User-Agent': expect.any(String), + + expect(navigationSpan2.attributes).toMatchObject({ + [URL_PATH]: { + type: 'string', + value: '/sub-page-2', + }, + [SENTRY_ORIGIN]: { + type: 'string', + value: 'auto.navigation.browser', + }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { + type: 'integer', + value: 1, + }, + [SENTRY_SEGMENT_NAME_SOURCE]: { + type: 'string', + value: 'url', + }, + [SENTRY_OP]: { + type: 'string', + value: 'navigation', + }, + ['sentry.idle_span_finish_reason']: { + type: 'string', + value: 'idleTimeout', }, - url: 'http://sentry-test.io/sub-page-2', }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/init.js index 25e1637ea608..207473572df5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts index 03bfc13814af..df7333b449c6 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest('should call onRequestSpanEnd hook', async ({ browserName, getLocalTestUrl, page }) => { const supportedBrowsers = ['chromium', 'firefox']; @@ -35,26 +35,26 @@ sentryTest('should call onRequestSpanEnd hook', async ({ browserName, getLocalTe const url = await getLocalTestUrl({ testDir: __dirname }); - const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); + const spans = collectStreamedSpans(page); + await waitForStreamedSpanAndTraceHeaderOnUrl(page, url, span => getSpanOp(span) === 'pageload'); + await page.evaluate(() => (window as any).Sentry.flush()); - const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers - - expect(tracingEvent.spans).toContainEqual( + expect(spans).toContainEqual( expect.objectContaining({ - op: 'http.client', - data: expect.objectContaining({ - type: 'xhr', - 'hook.called.response-type': 'xhr', + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'http.client' }, + type: { type: 'string', value: 'xhr' }, + 'hook.called.response-type': { type: 'string', value: 'xhr' }, }), }), ); - expect(tracingEvent.spans).toContainEqual( + expect(spans).toContainEqual( expect.objectContaining({ - op: 'http.client', - data: expect.objectContaining({ - type: 'fetch', - 'hook.called.response-type': 'fetch', + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'http.client' }, + type: { type: 'string', value: 'fetch' }, + 'hook.called.response-type': { type: 'string', value: 'fetch' }, }), }), ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/init.js index a24444de55b1..680dfd7304df 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts index d1d90a698a77..99841247a3be 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts @@ -1,7 +1,7 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest('should call onRequestSpanStart hook', async ({ browserName, getLocalTestUrl, page }) => { const supportedBrowsers = ['chromium', 'firefox']; @@ -27,24 +27,24 @@ sentryTest('should call onRequestSpanStart hook', async ({ browserName, getLocal const url = await getLocalTestUrl({ testDir: __dirname }); - const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); + const spans = collectStreamedSpans(page); + await waitForStreamedSpanAndTraceHeaderOnUrl(page, url, span => getSpanOp(span) === 'pageload'); + await page.evaluate(() => (window as any).Sentry.flush()); - const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers - - expect(tracingEvent.spans).toContainEqual( + expect(spans).toContainEqual( expect.objectContaining({ - op: 'http.client', - data: expect.objectContaining({ - 'hook.called.headers': 'xhr', + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'http.client' }, + 'hook.called.headers': { type: 'string', value: 'xhr' }, }), }), ); - expect(tracingEvent.spans).toContainEqual( + expect(spans).toContainEqual( expect.objectContaining({ - op: 'http.client', - data: expect.objectContaining({ - 'hook.called.headers': 'fetch', + attributes: expect.objectContaining({ + 'sentry.op': { type: 'string', value: 'http.client' }, + 'hook.called.headers': { type: 'string', value: 'fetch' }, }), }), ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/init.js deleted file mode 100644 index bd3b6ed17872..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/init.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; -window._testBaseTimestamp = performance.timeOrigin / 1000; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], - traceLifecycle: 'stream', - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts deleted file mode 100644 index 717011ffb9c3..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/test.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { expect } from '@playwright/test'; -import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; -import { - BROWSER_NAVIGATION_TYPE, - SENTRY_SEGMENT_NAME_SOURCE, - SENTRY_SEGMENT_ID, - SENTRY_SEGMENT_NAME, - SENTRY_SDK_NAME, - SENTRY_SDK_VERSION, - SENTRY_TRACE_LIFECYCLE, - URL_FULL, - URL_PATH, - USER_AGENT_ORIGINAL, - SENTRY_ENVIRONMENT, - SENTRY_SDK_INTEGRATIONS, - SENTRY_OP, - SENTRY_ORIGIN, -} from '@sentry/conventions/attributes'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, getSpansFromEnvelope, waitForStreamedSpanEnvelope } from '../../../../utils/spanUtils'; - -sentryTest( - 'creates a pageload streamed span envelope with url as pageload span name source', - async ({ browserName, getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const spanEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => !!getSpansFromEnvelope(env).find(s => getSpanOp(s) === 'pageload'), - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - const spanEnvelope = await spanEnvelopePromise; - const envelopeHeader = spanEnvelope[0]; - const envelopeItem = spanEnvelope[1]; - const spans = envelopeItem[0][1].items; - const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload'); - - const timeOrigin = await page.evaluate('window._testBaseTimestamp'); - - expect(envelopeHeader).toEqual({ - sdk: { - name: 'sentry.javascript.browser', - version: SDK_VERSION, - }, - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rand: expect.any(String), - sample_rate: '1', - sampled: 'true', - trace_id: expect.stringMatching(/^[\da-f]{32}$/), - }, - }); - - const numericSampleRand = parseFloat(envelopeHeader.trace!.sample_rand!); - const traceId = envelopeHeader.trace!.trace_id; - - expect(Number.isNaN(numericSampleRand)).toBe(false); - - expect(envelopeItem[0][0].item_count).toBeGreaterThan(1); - - expect(pageloadSpan?.start_timestamp).toBeCloseTo(timeOrigin, 1); - - expect(pageloadSpan).toEqual({ - attributes: { - 'sentry.is_localhost': { value: false, type: 'boolean' }, - 'culture.calendar': { - type: 'string', - value: expect.any(String), - }, - 'culture.locale': { - type: 'string', - value: expect.any(String), - }, - 'culture.timezone': { - type: 'string', - value: expect.any(String), - }, - [USER_AGENT_ORIGINAL]: { - type: 'string', - value: expect.any(String), - }, - [URL_FULL]: { - type: 'string', - value: expect.any(String), - }, - [URL_PATH]: { - type: 'string', - value: expect.any(String), - }, - // formerly known as 'hardwareConcurrency' - 'device.processor_count': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - 'browser.performance.navigation.activation_start': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - 'browser.performance.time_origin': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - 'browser.web_vital.ttfb.request_time': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - ...(browserName !== 'webkit' && { - // formerly known as 'effectiveConnectionType' - 'network.connection.effective_type': { - type: 'string', - value: expect.any(String), - }, - 'network.connection.rtt': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - 'browser.web_vital.ttfb.value': { - type: expect.stringMatching(/^(integer)|(double)$/), - value: expect.any(Number), - }, - [BROWSER_NAVIGATION_TYPE]: { - type: 'string', - value: 'navigate', - }, - }), - 'sentry.idle_span_finish_reason': { - type: 'string', - value: 'idleTimeout', - }, - [SENTRY_OP]: { - type: 'string', - value: 'pageload', - }, - [SENTRY_ORIGIN]: { - type: 'string', - value: 'auto.pageload.browser', - }, - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { - type: 'integer', - value: 1, - }, - [SENTRY_SDK_NAME]: { - type: 'string', - value: 'sentry.javascript.browser', - }, - [SENTRY_SDK_VERSION]: { - type: 'string', - value: SDK_VERSION, - }, - [SENTRY_SDK_INTEGRATIONS]: { - type: 'array', - value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), - }, - [SENTRY_SEGMENT_ID]: { - type: 'string', - value: pageloadSpan?.span_id, - }, - [SENTRY_SEGMENT_NAME]: { - type: 'string', - value: 'Pageload', - }, - [SENTRY_SEGMENT_NAME_SOURCE]: { - type: 'string', - value: 'url', - }, - [SENTRY_ENVIRONMENT]: { - type: 'string', - value: 'production', - }, - [SENTRY_TRACE_LIFECYCLE]: { - type: 'string', - value: 'stream', - }, - }, - end_timestamp: expect.any(Number), - is_segment: true, - // The raw URL stays in `url.path`/`url.full`: with span streaming, a pageload span name is - // low cardinality and falls back to 'Pageload' when there is no parameterized route. - name: 'Pageload', - span_id: expect.stringMatching(/^[\da-f]{16}$/), - start_timestamp: expect.any(Number), - status: 'ok', - trace_id: traceId, - }); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/init.js index b74e6633eadb..1f0b64911a75 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; 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-update-txn-name/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts index 0776668fb9e8..b3ce273fc0e4 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts @@ -1,12 +1,13 @@ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import { type Event, SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest( - 'sets the source to custom when updating the transaction name with `span.updateName`', + 'sets the source to custom when updating the span name with `span.updateName`', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); @@ -14,25 +15,31 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); + const spans = collectStreamedSpans(page); + const [pageloadSpan] = await waitForStreamedSpanAndTraceHeaderOnUrl( + page, + url, + span => getSpanOp(span) === 'pageload', + ); + await page.evaluate(() => (window as any).Sentry.flush()); - const traceContextData = eventData.contexts?.trace?.data; + const attributes = pageloadSpan.attributes; - expect(traceContextData).toBeDefined(); + expect(attributes).toBeDefined(); - expect(eventData.transaction).toBe('new name'); + expect(pageloadSpan.name).toBe('new name'); - expect(traceContextData).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'custom', - [SENTRY_OP]: 'pageload', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'custom' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, }); - expect(traceContextData![SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); + expect(attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - expect(eventData.spans?.length).toBeGreaterThan(0); - expect(eventData.transaction_info?.source).toEqual('custom'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(spans.filter(span => !span.is_segment).length).toBeGreaterThan(0); + expect(pageloadSpan.attributes['sentry.segment.name.source']?.value).toEqual('custom'); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/init.js index b74e6633eadb..1f0b64911a75 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; 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-updateSpanName/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts index ab1b1ed20c46..8c71cebf5a93 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts @@ -1,12 +1,13 @@ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import { type Event, SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest( - 'sets the source to custom when updating the transaction name with Sentry.updateSpanName', + 'sets the source to custom when updating the span name with Sentry.updateSpanName', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); @@ -14,25 +15,31 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); + const spans = collectStreamedSpans(page); + const [pageloadSpan] = await waitForStreamedSpanAndTraceHeaderOnUrl( + page, + url, + span => getSpanOp(span) === 'pageload', + ); + await page.evaluate(() => (window as any).Sentry.flush()); - const traceContextData = eventData.contexts?.trace?.data; + const attributes = pageloadSpan.attributes; - expect(traceContextData).toBeDefined(); + expect(attributes).toBeDefined(); - expect(traceContextData).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'custom', - [SENTRY_OP]: 'pageload', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'custom' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, }); - expect(traceContextData![SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); + expect(attributes[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toEqual({ type: 'string', value: 'new name' }); - expect(eventData.transaction).toBe('new name'); + expect(pageloadSpan.name).toBe('new name'); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - expect(eventData.spans?.length).toBeGreaterThan(0); - expect(eventData.transaction_info?.source).toEqual('custom'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(spans.filter(span => !span.is_segment).length).toBeGreaterThan(0); + expect(pageloadSpan.attributes['sentry.segment.name.source']?.value).toEqual('custom'); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/init.js index b74e6633eadb..1f0b64911a75 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; 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/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts index 8e7c3a4b2480..0b523ee5a8fa 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts @@ -1,35 +1,196 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import type { Event } from '@sentry/core'; +import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; +import { + BROWSER_NAVIGATION_TYPE, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_SEGMENT_ID, + SENTRY_SEGMENT_NAME, + SENTRY_SDK_NAME, + SENTRY_SDK_VERSION, + SENTRY_TRACE_LIFECYCLE, + URL_FULL, + URL_PATH, + USER_AGENT_ORIGINAL, + SENTRY_ENVIRONMENT, + SENTRY_SDK_INTEGRATIONS, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { + collectStreamedSpans, + getSpanOp, + getSpansFromEnvelope, + waitForStreamedSpanEnvelope, +} from '../../../../utils/spanUtils'; -sentryTest('creates a pageload transaction with url as source', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } +sentryTest( + 'creates a pageload streamed span envelope with url as pageload span name source', + async ({ browserName, getLocalTestUrl, page }) => { + sentryTest.skip(shouldSkipTracingTest()); - const url = await getLocalTestUrl({ testDir: __dirname }); + const collectedSpans = collectStreamedSpans(page); + const spanEnvelopePromise = waitForStreamedSpanEnvelope( + page, + env => !!getSpansFromEnvelope(env).find(s => getSpanOp(s) === 'pageload'), + ); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const timeOrigin = await page.evaluate('window._testBaseTimestamp'); + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); - const { start_timestamp: startTimestamp } = eventData; + const spanEnvelope = await spanEnvelopePromise; + const envelopeHeader = spanEnvelope[0]; + const envelopeItem = spanEnvelope[1]; + const spans = envelopeItem[0][1].items; + const pageloadSpan = spans.find(s => getSpanOp(s) === 'pageload'); - const traceContextData = eventData.contexts?.trace?.data; + const timeOrigin = await page.evaluate('window._testBaseTimestamp'); - expect(startTimestamp).toBeCloseTo(timeOrigin, 1); + expect(envelopeHeader).toEqual({ + sdk: { + name: 'sentry.javascript.browser', + version: SDK_VERSION, + }, + sent_at: expect.any(String), + trace: { + environment: 'production', + public_key: 'public', + sample_rand: expect.any(String), + sample_rate: '1', + sampled: 'true', + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + }, + }); - expect(traceContextData).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'idleTimeout', - }); + const numericSampleRand = parseFloat(envelopeHeader.trace!.sample_rand!); + const traceId = envelopeHeader.trace!.trace_id; - expect(eventData.contexts?.trace?.op).toBe('pageload'); - expect(eventData.spans?.length).toBeGreaterThan(0); - expect(eventData.transaction_info?.source).toEqual('url'); -}); + expect(Number.isNaN(numericSampleRand)).toBe(false); + + expect(envelopeItem[0][0].item_count).toBe(spans.length); + await page.evaluate(() => (window as any).Sentry.flush()); + expect(collectedSpans.filter(span => !span.is_segment)).not.toHaveLength(0); + + expect(pageloadSpan?.start_timestamp).toBeCloseTo(timeOrigin, 1); + + expect(pageloadSpan).toEqual({ + attributes: { + 'sentry.is_localhost': { value: false, type: 'boolean' }, + 'culture.calendar': { + type: 'string', + value: expect.any(String), + }, + 'culture.locale': { + type: 'string', + value: expect.any(String), + }, + 'culture.timezone': { + type: 'string', + value: expect.any(String), + }, + [USER_AGENT_ORIGINAL]: { + type: 'string', + value: expect.any(String), + }, + [URL_FULL]: { + type: 'string', + value: 'http://sentry-test.io/index.html', + }, + [URL_PATH]: { + type: 'string', + value: '/index.html', + }, + 'device.processor_count': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + 'browser.performance.navigation.activation_start': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + 'browser.performance.time_origin': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + 'browser.web_vital.ttfb.request_time': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + ...(browserName !== 'webkit' && { + 'network.connection.effective_type': { + type: 'string', + value: expect.any(String), + }, + 'network.connection.rtt': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + 'browser.web_vital.ttfb.value': { + type: expect.stringMatching(/^(integer)|(double)$/), + value: expect.any(Number), + }, + [BROWSER_NAVIGATION_TYPE]: { + type: 'string', + value: 'navigate', + }, + }), + 'sentry.idle_span_finish_reason': { + type: 'string', + value: 'idleTimeout', + }, + [SENTRY_OP]: { + type: 'string', + value: 'pageload', + }, + [SENTRY_ORIGIN]: { + type: 'string', + value: 'auto.pageload.browser', + }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { + type: 'integer', + value: 1, + }, + [SENTRY_SDK_NAME]: { + type: 'string', + value: 'sentry.javascript.browser', + }, + [SENTRY_SDK_VERSION]: { + type: 'string', + value: SDK_VERSION, + }, + [SENTRY_SDK_INTEGRATIONS]: { + type: 'array', + value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), + }, + [SENTRY_SEGMENT_ID]: { + type: 'string', + value: pageloadSpan?.span_id, + }, + [SENTRY_SEGMENT_NAME]: { + type: 'string', + value: 'Pageload', + }, + [SENTRY_SEGMENT_NAME_SOURCE]: { + type: 'string', + value: 'url', + }, + [SENTRY_ENVIRONMENT]: { + type: 'string', + value: 'production', + }, + [SENTRY_TRACE_LIFECYCLE]: { + type: 'string', + value: 'stream', + }, + }, + end_timestamp: expect.any(Number), + is_segment: true, + name: 'Pageload', + span_id: expect.stringMatching(/^[\da-f]{16}$/), + start_timestamp: expect.any(Number), + status: 'ok', + trace_id: traceId, + }); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/init.js index ba739b4b8cc5..2c5a44a7f76d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/init.js @@ -6,7 +6,6 @@ window._testBaseTimestamp = performance.timeOrigin / 1000; setTimeout(() => { window._testTimeoutTimestamp = (performance.timeOrigin + performance.now()) / 1000; 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/pageloadDelayed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts index 0d608a782314..40aa61ddb618 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts @@ -1,25 +1,31 @@ import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; -sentryTest('should create a pageload transaction when initialized delayed', async ({ getLocalTestUrl, page }) => { +sentryTest('should create a pageload span when initialized delayed', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); } const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); + const spans = collectStreamedSpans(page); + const [pageloadSpan] = await waitForStreamedSpanAndTraceHeaderOnUrl( + page, + url, + span => getSpanOp(span) === 'pageload', + ); + await page.evaluate(() => (window as any).Sentry.flush()); const timeOrigin = await page.evaluate('window._testBaseTimestamp'); const timeoutTimestamp = await page.evaluate('window._testTimeoutTimestamp'); - const { start_timestamp: startTimestamp } = eventData; + const { start_timestamp: startTimestamp } = pageloadSpan; expect(startTimestamp).toBeCloseTo(timeOrigin, 1); expect(startTimestamp).toBeLessThan(timeoutTimestamp); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - expect(eventData.spans?.length).toBeGreaterThan(0); - expect(eventData.transaction_info?.source).toEqual('url'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(spans.filter(span => !span.is_segment).length).toBeGreaterThan(0); + expect(pageloadSpan.attributes['sentry.segment.name.source']?.value).toEqual('url'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/init.js index b2820c740c2b..229372e215c7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts index 16f5a2f7f539..daca73d5f5d7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts @@ -1,28 +1,32 @@ import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { - envelopeRequestParser, - shouldSkipTracingTest, - waitForTransactionRequestOnUrl, -} from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; -// This tests asserts that the pageload span will finish itself after the child span timeout if it -// has a child span without adding any additional ones or finishing any of them finishing. All of the child spans that -// are still running should have the status "cancelled". sentryTest('should send a pageload span terminated via child span timeout', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { sentryTest.skip(); } const url = await getLocalTestUrl({ testDir: __dirname }); - const req = await waitForTransactionRequestOnUrl(page, url); + const spans = collectStreamedSpans(page); + const [pageloadSpan] = await waitForStreamedSpanAndTraceHeaderOnUrl( + page, + url, + span => getSpanOp(span) === 'pageload', + ); + await page.evaluate(() => (window as any).Sentry.flush()); - const eventData = envelopeRequestParser(req); - - expect(eventData.contexts?.trace?.op).toBe('pageload'); - expect(eventData.contexts?.trace?.data?.['sentry.idle_span_discarded_spans']).toBeUndefined(); - expect(eventData.spans?.length).toBeGreaterThanOrEqual(1); - const testSpan = eventData.spans?.find(span => span.description === 'pageload-child-span'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(pageloadSpan.attributes['sentry.idle_span_discarded_spans']).toBeUndefined(); + expect(spans.length).toBeGreaterThanOrEqual(1); + const testSpan = spans.find(span => span.name === 'pageload-child-span'); expect(testSpan).toBeDefined(); - expect(testSpan?.status).toBe('cancelled'); + expect(testSpan?.status).toBe('ok'); + expect(pageloadSpan.attributes['sentry.idle_span_finish_reason']?.value).toBe('heartbeatFailed'); + expect(testSpan).toMatchObject({ + parent_span_id: pageloadSpan.span_id, + trace_id: pageloadSpan.trace_id, + end_timestamp: pageloadSpan.end_timestamp, + }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/init.js deleted file mode 100644 index ded3ca204b6b..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/init.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; -window._testBaseTimestamp = performance.timeOrigin / 1000; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true }), Sentry.spanStreamingIntegration()], - tracesSampleRate: 1, - debug: true, -}); - -setTimeout(() => { - Sentry.reportPageLoaded(); -}, 2500); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts deleted file mode 100644 index 3c4a37bc9de3..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - SENTRY_IDLE_SPAN_FINISH_REASON, - SENTRY_SEGMENT_NAME_SOURCE, - SENTRY_OP, - SENTRY_ORIGIN, -} from '@sentry/conventions/attributes'; -import { expect } from '@playwright/test'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest( - 'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - - await page.goto(url); - - const pageloadSpan = await pageloadSpanPromise; - - const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; - - expect(pageloadSpan.attributes).toMatchObject({ - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), - [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, - [SENTRY_OP]: { type: 'string', value: 'pageload' }, - [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'reportPageLoaded' }, - }); - - // We wait for 2.5 seconds before calling Sentry.reportPageLoaded(). The span starts at navigation start, - // but the timeout only starts once the bundle has executed, so allow generous upper headroom for slow CI. - expect(spanDurationSeconds).toBeGreaterThan(2); - expect(spanDurationSeconds).toBeLessThan(4); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/init.js deleted file mode 100644 index b1c19f779713..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/init.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; -window._testBaseTimestamp = performance.timeOrigin / 1000; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ enableReportPageLoaded: true, finalTimeout: 3000 }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, - debug: true, -}); - -// not calling Sentry.reportPageLoaded() on purpose! diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts deleted file mode 100644 index 8ea2ea1ca842..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; -import { expect } from '@playwright/test'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest( - 'final timeout cancels the pageload span even if `enableReportPageLoaded` is true', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - - await page.goto(url); - - const pageloadSpan = await pageloadSpanPromise; - - const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; - - expect(pageloadSpan.attributes).toMatchObject({ - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), - [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, - [SENTRY_OP]: { type: 'string', value: 'pageload' }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'finalTimeout' }, - }); - - // We wait for 3 seconds before calling Sentry.reportPageLoaded() - // the margins are to account for timing weirdness in CI to avoid flakes - expect(spanDurationSeconds).toBeGreaterThan(2.5); - expect(spanDurationSeconds).toBeLessThan(3.5); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/init.js deleted file mode 100644 index ac42880742a3..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/init.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; -window._testBaseTimestamp = performance.timeOrigin / 1000; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ enableReportPageLoaded: true, instrumentNavigation: false }), - Sentry.spanStreamingIntegration(), - ], - tracesSampleRate: 1, - debug: true, -}); - -setTimeout(() => { - Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(), { name: 'custom_navigation' }); -}, 1000); - -setTimeout(() => { - Sentry.reportPageLoaded(); -}, 2500); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts deleted file mode 100644 index db6f6737ccd6..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; -import { expect } from '@playwright/test'; -import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; -import { sentryTest } from '../../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; - -sentryTest( - 'starting a navigation span cancels the pageload span even if `enableReportPageLoaded` is true', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - - await page.goto(url); - - const pageloadSpan = await pageloadSpanPromise; - - const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; - - expect(pageloadSpan.attributes).toMatchObject({ - [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), - [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, - [SENTRY_OP]: { type: 'string', value: 'pageload' }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'cancelled' }, - }); - - // ending span after 1s but adding a margin of 0.5s to account for timing weirdness in CI to avoid flakes - expect(spanDurationSeconds).toBeLessThan(1.5); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/init.js index 054a40d79664..39a212ae5fa5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true })], tracesSampleRate: 1, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/test.ts index bca0d6052b95..52fb62fbec10 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/default/test.ts @@ -1,33 +1,37 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; + import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + await page.goto(url); - const eventData = envelopeRequestParser(await pageloadEventPromise); + const pageloadSpan = await pageloadSpanPromise; - const traceContextData = eventData.contexts?.trace?.data; - const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!; + const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; - expect(traceContextData).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'reportPageLoaded', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'reportPageLoaded' }, }); // We wait for 2.5 seconds before calling Sentry.reportPageLoaded(). The span starts at navigation start, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/init.js index 2f633a9c6f1e..7ec015be44dd 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, finalTimeout: 3000 })], tracesSampleRate: 1, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts index ae6ffdec21b3..8ea2ea1ca842 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts @@ -2,32 +2,30 @@ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/co import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'final timeout cancels the pageload span even if `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + await page.goto(url); - const eventData = envelopeRequestParser(await pageloadEventPromise); + const pageloadSpan = await pageloadSpanPromise; - const traceContextData = eventData.contexts?.trace?.data; - const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!; + const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; - expect(traceContextData).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'finalTimeout', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, + 'sentry.idle_span_finish_reason': { type: 'string', value: 'finalTimeout' }, }); // We wait for 3 seconds before calling Sentry.reportPageLoaded() diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/init.js index 3e937c873954..65e9938f7985 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/init.js @@ -4,7 +4,6 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, instrumentNavigation: false })], tracesSampleRate: 1, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts index 9e947592a576..db6f6737ccd6 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts @@ -2,32 +2,30 @@ import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/co import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'starting a navigation span cancels the pageload span even if `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + await page.goto(url); - const eventData = envelopeRequestParser(await pageloadEventPromise); + const pageloadSpan = await pageloadSpanPromise; - const traceContextData = eventData.contexts?.trace?.data; - const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!; + const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; - expect(traceContextData).toMatchObject({ - [SENTRY_ORIGIN]: 'auto.pageload.browser', - [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, - [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'cancelled', + expect(pageloadSpan.attributes).toMatchObject({ + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, + [SENTRY_OP]: { type: 'string', value: 'pageload' }, + 'sentry.idle_span_finish_reason': { type: 'string', value: 'cancelled' }, }); // ending span after 1s but adding a margin of 0.5s to account for timing weirdness in CI to avoid flakes diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/init.js index 0eb1efc7a5a4..3a4c2cb1cdf4 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/test.ts index 4bc9621f8395..282625e0d8b7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/resource-spans-ignored/test.ts @@ -1,8 +1,8 @@ import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest('should allow specific types of resource spans to be ignored.', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { @@ -13,8 +13,10 @@ sentryTest('should allow specific types of resource spans to be ignored.', async const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); - const allSpans = eventData.spans?.filter(({ op }) => op?.startsWith('resource.script')); + const spans = collectStreamedSpans(page); + await waitForStreamedSpanAndTraceHeaderOnUrl(page, url, span => getSpanOp(span) === 'pageload'); + await page.evaluate(() => (window as any).Sentry.flush()); + const allSpans = spans?.filter(span => getSpanOp(span)?.startsWith('resource.script')); expect(allSpans?.length).toBe(0); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/init.js deleted file mode 100644 index 53029a40b668..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/init.js +++ /dev/null @@ -1,17 +0,0 @@ -import * as Sentry from '@sentry/browser'; -import { interactionsIntegration } from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - enableLongTask: false, - }), - interactionsIntegration(), - Sentry.spanStreamingIntegration(), - Sentry.spotlightBrowserIntegration(), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/subject.js deleted file mode 100644 index cae57f7a9167..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/subject.js +++ /dev/null @@ -1,12 +0,0 @@ -// Block the main thread for 70ms so the PerformanceObserver registers -// a click event entry, which triggers `ui.interaction.click` child spans. -const simulateSlowClick = e => { - const startTime = Date.now(); - while (Date.now() - startTime < 70) { - // - } - e.target.classList.add('clicked'); -}; - -document.querySelector('[data-test-id=spotlight-button]').addEventListener('click', simulateSlowClick); -document.querySelector('[data-test-id=regular-button]').addEventListener('click', simulateSlowClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/template.html deleted file mode 100644 index 9348e00e7db7..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/template.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - -
- -
- - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/test.ts deleted file mode 100644 index 0a646701fde8..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipCdnBundleTest, shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, observeStreamedSpan, waitForStreamedSpan, waitForStreamedSpans } from '../../../../utils/spanUtils'; - -// Interaction spans are named after the component, so the element is on `browser.web_vital.inp.target`. -// The click handler adds the `clicked` class before the event timing entry is recorded. -const SPOTLIGHT_BUTTON = 'body > div#sentry-spotlight > button.clicked'; -const REGULAR_BUTTON = 'body > button.clicked'; - -sentryTest( - 'filters ui.interaction.click spans for spotlight elements via ignoreSpans in streaming mode', - async ({ getLocalTestUrl, page }) => { - // spotlightBrowserIntegration is not available in CDN bundles - if (shouldSkipTracingTest() || shouldSkipCdnBundleTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - - // Set up an observer that fails if a spotlight interaction span is ever sent - let sawSpotlightInteractionSpan = false; - await observeStreamedSpan(page, span => { - if ( - getSpanOp(span) === 'ui.interaction.click' && - span.attributes['browser.web_vital.inp.target']?.value === SPOTLIGHT_BUTTON - ) { - sawSpotlightInteractionSpan = true; - return true; - } - return false; - }); - - await page.goto(url); - - // Wait for pageload to finish before clicking - await waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - - // Click on the spotlight element — its ui.interaction.click child should be filtered - await page.locator('[data-test-id=spotlight-button]').click(); - await page.locator('.clicked[data-test-id=spotlight-button]').isVisible(); - - // Wait for the spotlight click's segment span to arrive - await waitForStreamedSpans(page, spans => - spans.some(span => span.is_segment && getSpanOp(span) === 'ui.action.click'), - ); - - // Click on the regular button — its ui.interaction.click child should be kept - const regularInteractionSpansPromise = waitForStreamedSpans(page, spans => - spans.some(span => span.attributes['browser.web_vital.inp.target']?.value === REGULAR_BUTTON), - ); - - await page.locator('[data-test-id=regular-button]').click(); - await page.locator('.clicked[data-test-id=regular-button]').isVisible(); - - const regularSpans = await regularInteractionSpansPromise; - const regularInteractionSpan = regularSpans.find( - span => span.attributes['browser.web_vital.inp.target']?.value === REGULAR_BUTTON, - ); - expect(getSpanOp(regularInteractionSpan!)).toBe('ui.interaction.click'); - - // Verify no spotlight interaction span was ever sent - expect(sawSpotlightInteractionSpan).toBe(false); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/init.js index f2b876b3bf7d..652e289ca5f7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/init.js @@ -4,7 +4,6 @@ import { interactionsIntegration } from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts index d0480062c10a..7ee7d37aafc1 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts @@ -1,57 +1,49 @@ import { expect } from '@playwright/test'; -import type { TransactionEvent } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - envelopeRequestParser, - shouldSkipCdnBundleTest, - shouldSkipTracingTest, - waitForTransactionRequest, -} from '../../../../utils/helpers'; - -sentryTest( - 'filters ui.interaction.click spans for spotlight elements via ignoreSpans', - async ({ getLocalTestUrl, page }) => { - // spotlightBrowserIntegration is not available in CDN bundles - if (shouldSkipTracingTest() || shouldSkipCdnBundleTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - // Wait for the pageload transaction to complete - await waitForTransactionRequest(page); - - // Click on the spotlight element — interaction span should be filtered - const spotlightTxnPromise = waitForTransactionRequest(page, txn => txn.contexts?.trace?.op === 'ui.action.click'); - await page.locator('[data-test-id=spotlight-button]').click(); - await page.locator('.clicked[data-test-id=spotlight-button]').isVisible(); - const spotlightTransaction = envelopeRequestParser(await spotlightTxnPromise); - - expect(spotlightTransaction.contexts?.trace?.op).toBe('ui.action.click'); - - const spotlightInteractionSpans = spotlightTransaction.spans?.filter(span => span.op === 'ui.interaction.click'); - expect(spotlightInteractionSpans).toHaveLength(0); - - // Let the first idle span fully settle before clicking again - await page.waitForTimeout(1000); - - // Click on the regular button — wait specifically for a transaction that contains - // a ui.interaction.click child span, since the PerformanceObserver may deliver - // the event entry asynchronously - const regularTxnPromise = waitForTransactionRequest( - page, - txn => - txn.contexts?.trace?.op === 'ui.action.click' && - (txn.spans?.some(span => span.op === 'ui.interaction.click') ?? false), - ); - await page.locator('[data-test-id=regular-button]').click(); - await page.locator('.clicked[data-test-id=regular-button]').isVisible(); - const regularTransaction = envelopeRequestParser(await regularTxnPromise); - - const regularInteractionSpans = regularTransaction.spans?.filter(span => span.op === 'ui.interaction.click'); - expect(regularInteractionSpans?.length).toBeGreaterThanOrEqual(1); - expect(regularInteractionSpans![0]!.description).toContain('button'); - expect(regularInteractionSpans![0]!.description).not.toContain('#sentry-spotlight'); - }, -); +import { shouldSkipCdnBundleTest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; + +// The click handler adds the class before the event timing entry records the target. +const SPOTLIGHT_BUTTON = 'body > div#sentry-spotlight > button.clicked'; +const REGULAR_BUTTON = 'body > button.clicked'; + +sentryTest('filters spotlight interaction spans via ignoreSpans', async ({ getLocalTestUrl, page }) => { + // spotlightBrowserIntegration is not available in CDN bundles. + sentryTest.skip(shouldSkipTracingTest() || shouldSkipCdnBundleTest()); + const url = await getLocalTestUrl({ testDir: __dirname }); + const spans = collectStreamedSpans(page); + const pageloadPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + await page.goto(url); + await pageloadPromise; + + const spotlightPromise = waitForStreamedSpan(page, span => span.is_segment && getSpanOp(span) === 'ui.action.click'); + await page.locator('[data-test-id=spotlight-button]').click(); + await expect(page.locator('.clicked[data-test-id=spotlight-button]')).toBeVisible(); + const spotlight = await spotlightPromise; + expect(getSpanOp(spotlight)).toBe('ui.action.click'); + + const regularPromise = waitForStreamedSpan( + page, + span => span.is_segment && getSpanOp(span) === 'ui.action.click' && span.span_id !== spotlight.span_id, + ); + const regularInteractionPromise = waitForStreamedSpan( + page, + span => span.attributes['browser.web_vital.inp.target']?.value === REGULAR_BUTTON, + ); + await page.locator('[data-test-id=regular-button]').click(); + await expect(page.locator('.clicked[data-test-id=regular-button]')).toBeVisible(); + const [regular, interaction] = await Promise.all([regularPromise, regularInteractionPromise]); + await page.evaluate(() => (window as any).Sentry.flush()); + + expect(getSpanOp(interaction)).toBe('ui.interaction.click'); + expect(interaction.parent_span_id).toBe(regular.span_id); + expect(interaction.trace_id).toBe(regular.trace_id); + expect(interaction.attributes['browser.web_vital.inp.target']?.value).not.toContain('#sentry-spotlight'); + expect( + spans.filter( + span => + getSpanOp(span) === 'ui.interaction.click' && + span.attributes['browser.web_vital.inp.target']?.value === SPOTLIGHT_BUTTON, + ), + ).toHaveLength(0); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/customTargets/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/customTargets/init.js index 65cfbab96ee2..b2280b70e307 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/customTargets/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/customTargets/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [Sentry.browserTracingIntegration()], tracePropagationTargets: ['http://sentry-test-site.example'], diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsMatch/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsMatch/init.js index bb6d4918d1bf..83076460599f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsMatch/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsMatch/init.js @@ -3,7 +3,6 @@ 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/tracePropagationTargets/defaultTargetsNoMatch/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsNoMatch/init.js index 238d85509b25..d7b0a648a2de 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsNoMatch/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/tracePropagationTargets/defaultTargetsNoMatch/init.js @@ -3,7 +3,6 @@ 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/twp-errors-meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors-meta/init.js index 17b2b0a13c6b..c026daa1eed9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors-meta/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors-meta/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: integrations => { integrations.push(Sentry.browserTracingIntegration()); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors/init.js index 17b2b0a13c6b..c026daa1eed9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/twp-errors/init.js @@ -3,7 +3,6 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ - traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: integrations => { integrations.push(Sentry.browserTracingIntegration()); From 6cd2ac9f264a5d965793d29693da4843c84f26d3 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 7 Oct 2026 13:23:11 +0200 Subject: [PATCH 2/7] test(browser): retain static browser tracing counterparts Co-Authored-By: GPT-6 --- .../backgroundtab-pageload-static/init.js | 10 + .../backgroundtab-pageload-static/subject.js | 8 + .../template.html | 9 + .../backgroundtab-pageload-static/test.ts | 20 ++ .../http-timings-static/init.js | 16 ++ .../http-timings-static/subject.js | 3 + .../http-timings-static/test.ts | 59 ++++++ .../consistent-sampling/default/init.js | 22 +++ .../consistent-sampling/default/subject.js | 17 ++ .../consistent-sampling/default/template.html | 8 + .../consistent-sampling/default/test.ts | 149 +++++++++++++++ .../consistent-sampling/meta-negative/init.js | 18 ++ .../meta-negative/subject.js | 17 ++ .../meta-negative/template.html | 13 ++ .../consistent-sampling/meta-negative/test.ts | 100 ++++++++++ .../meta-precedence/init.js | 20 ++ .../meta-precedence/page-1.html | 15 ++ .../meta-precedence/page-2.html | 10 + .../meta-precedence/subject.js | 17 ++ .../meta-precedence/template.html | 14 ++ .../meta-precedence/test.ts | 108 +++++++++++ .../consistent-sampling/meta/init.js | 18 ++ .../consistent-sampling/meta/subject.js | 17 ++ .../consistent-sampling/meta/template.html | 13 ++ .../consistent-sampling/meta/test.ts | 176 ++++++++++++++++++ .../tracesSampler-precedence/init.js | 29 +++ .../tracesSampler-precedence/subject.js | 17 ++ .../tracesSampler-precedence/template.html | 8 + .../tracesSampler-precedence/test.ts | 139 ++++++++++++++ .../linked-traces-static/custom-trace/init.js | 11 ++ .../custom-trace/subject.js | 14 ++ .../custom-trace/template.html | 8 + .../linked-traces-static/custom-trace/test.ts | 62 ++++++ .../linked-traces-static/default/init.js | 11 ++ .../linked-traces-static/default/test.ts | 99 ++++++++++ .../interaction-spans/init.js | 11 ++ .../interaction-spans/template.html | 7 + .../interaction-spans/test.ts | 89 +++++++++ .../linked-traces-static/meta/init.js | 11 ++ .../linked-traces-static/meta/template.html | 11 ++ .../linked-traces-static/meta/test.ts | 54 ++++++ .../negatively-sampled/init.js | 16 ++ .../negatively-sampled/test.ts | 42 +++++ .../session-storage/init.js | 10 + .../session-storage/test.ts | 39 ++++ .../init.js | 17 ++ .../subject.js | 18 ++ .../template.html | 11 ++ .../test.ts | 30 +++ .../assets/script.js | 12 ++ .../init.js | 15 ++ .../template.html | 10 + .../test.ts | 26 +++ .../assets/script.js | 25 +++ .../init.js | 15 ++ .../template.html | 11 ++ .../test.ts | 113 +++++++++++ .../assets/script.js | 25 +++ .../init.js | 15 ++ .../template.html | 11 ++ .../test.ts | 115 ++++++++++++ .../init.js | 18 ++ .../subject.js | 17 ++ .../template.html | 12 ++ .../test.ts | 30 +++ .../assets/script.js | 12 ++ .../long-tasks-disabled-static/init.js | 15 ++ .../long-tasks-disabled-static/template.html | 10 + .../long-tasks-disabled-static/test.ts | 21 +++ .../assets/script.js | 12 ++ .../long-tasks-enabled-static/init.js | 14 ++ .../long-tasks-enabled-static/template.html | 10 + .../long-tasks-enabled-static/test.ts | 36 ++++ .../navigation-static/init.js | 10 + .../navigation-static/test.ts | 150 +++++++++++++++ .../pageload-static/init.js | 11 ++ .../pageload-static/test.ts | 35 ++++ .../reportPageLoaded-static/default/init.js | 16 ++ .../reportPageLoaded-static/default/test.ts | 38 ++++ .../finalTimeout/init.js | 14 ++ .../finalTimeout/test.ts | 38 ++++ .../navigation/init.js | 20 ++ .../navigation/test.ts | 36 ++++ .../init.js | 17 ++ .../subject.js | 12 ++ .../template.html | 12 ++ .../test.ts | 57 ++++++ 87 files changed, 2707 insertions(+) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-1.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-2.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/assets/script.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/assets/script.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/assets/script.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/assets/script.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/assets/script.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/template.html create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/init.js new file mode 100644 index 000000000000..bb6d4918d1bf --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-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/backgroundtab-pageload-static/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/subject.js new file mode 100644 index 000000000000..b657f38ac009 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/subject.js @@ -0,0 +1,8 @@ +document.getElementById('go-background').addEventListener('click', () => { + setTimeout(() => { + Object.defineProperty(document, 'hidden', { value: true, writable: true }); + const ev = document.createEvent('Event'); + ev.initEvent('visibilitychange'); + document.dispatchEvent(ev); + }, 250); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/template.html new file mode 100644 index 000000000000..8083ddc80694 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts new file mode 100644 index 000000000000..80ea1f5fa09c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts @@ -0,0 +1,20 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest('should finish pageload transaction when the page goes background', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await page.locator('#go-background').click(); + + const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); + + expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); + expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); + expect(pageloadTransaction.contexts?.trace?.data?.['sentry.cancellation_reason']).toBe('document.hidden'); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/init.js new file mode 100644 index 000000000000..5e521633ad12 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/init.js @@ -0,0 +1,16 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + _experiments: { + enableHTTPTimings: true, + }, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/subject.js new file mode 100644 index 000000000000..e19cc07e28f5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/subject.js @@ -0,0 +1,3 @@ +fetch('http://sentry-test-site.example/0').then( + fetch('http://sentry-test-site.example/1').then(fetch('http://sentry-test-site.example/2')), +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts new file mode 100644 index 000000000000..6894d7407349 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts @@ -0,0 +1,59 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest('creates fetch spans with http timing', async ({ browserName, getLocalTestUrl, page }) => { + const supportedBrowsers = ['chromium', 'firefox']; + + if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { + sentryTest.skip(); + } + await page.route('http://sentry-test-site.example/*', async route => { + const request = route.request(); + const postData = await request.postDataJSON(); + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(Object.assign({ id: 1 }, postData)), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); + const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers + + const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); + + expect(requestSpans).toHaveLength(3); + + await page.pause(); + requestSpans?.forEach((span, index) => + expect(span).toMatchObject({ + description: `GET http://sentry-test-site.example/${index}`, + parent_span_id: tracingEvent.contexts?.trace?.span_id, + span_id: expect.stringMatching(/[a-f\d]{16}/), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: tracingEvent.contexts?.trace?.trace_id, + data: expect.objectContaining({ + 'http.request.redirect_start': expect.any(Number), + 'http.request.redirect_end': expect.any(Number), + 'http.request.worker_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.connect_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + 'http.request.time_to_first_byte': expect.any(Number), + 'network.protocol.version': expect.any(String), + }), + }), + ); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/init.js new file mode 100644 index 000000000000..04a299421004 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/init.js @@ -0,0 +1,22 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + linkPreviousTrace: 'in-memory', + consistentTraceSampling: true, + }), + ], + tracePropagationTargets: ['sentry-test-external.io'], + tracesSampler: ctx => { + if (ctx.attributes && ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { + return 1; + } + return ctx.inheritOrSampleWith(0); + }, + debug: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/subject.js new file mode 100644 index 000000000000..de60904fab3a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/subject.js @@ -0,0 +1,17 @@ +const btn1 = document.getElementById('btn1'); + +const btn2 = document.getElementById('btn2'); + +btn1.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); + }); +}); + +btn2.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { + await fetch('http://sentry-test-external.io'); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/template.html new file mode 100644 index 000000000000..f26a602c7c6f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/template.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/test.ts new file mode 100644 index 000000000000..4d13371bc693 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/test.ts @@ -0,0 +1,149 @@ +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { extractTraceparentData, parseBaggageHeader, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; +import { sentryTest } from '../../../../../../utils/fixtures'; +import { + eventAndTraceHeaderRequestParser, + shouldSkipTracingTest, + waitForTracingHeadersOnUrl, + waitForTransactionRequest, +} from '../../../../../../utils/helpers'; + +sentryTest.describe('When `consistentTraceSampling` is `true`', () => { + sentryTest('Continues sampling decision from initial pageload', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const { pageloadTraceContext, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => { + return evt.contexts?.trace?.op === 'pageload'; + }); + await page.goto(url); + + const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); + const pageloadSampleRand = Number(res[1]?.sample_rand); + const pageloadTraceContext = res[0].contexts?.trace; + + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); + + return { pageloadTraceContext: res[0].contexts?.trace, pageloadSampleRand }; + }); + + const customTraceContext = await sentryTest.step('Custom trace', async () => { + const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + await page.locator('#btn1').click(); + const [customTrace1Event, customTraceTraceHeader] = eventAndTraceHeaderRequestParser( + await customTrace1RequestPromise, + ); + + const customTraceContext = customTrace1Event.contexts?.trace; + + expect(customTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + // although we "continue the trace" from pageload, this is actually a root span, + // so there must not be a parent span id + expect(customTraceContext?.parent_span_id).toBeUndefined(); + + expect(pageloadSampleRand).toEqual(Number(customTraceTraceHeader?.sample_rand)); + + return customTraceContext; + }); + + await sentryTest.step('Navigation', async () => { + const navigation1RequestPromise = waitForTransactionRequest( + page, + evt => evt.contexts?.trace?.op === 'navigation', + ); + await page.goto(`${url}#foo`); + const [navigationEvent, navigationTraceHeader] = eventAndTraceHeaderRequestParser( + await navigation1RequestPromise, + ); + const navTraceContext = navigationEvent.contexts?.trace; + + expect(navTraceContext?.trace_id).not.toEqual(customTraceContext?.trace_id); + expect(navTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + + expect(navTraceContext?.links).toEqual([ + { + trace_id: customTraceContext?.trace_id, + span_id: customTraceContext?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + expect(navTraceContext?.parent_span_id).toBeUndefined(); + + expect(pageloadSampleRand).toEqual(Number(navigationTraceHeader?.sample_rand)); + }); + }); + + sentryTest('Propagates continued sampling decision to outgoing requests', async ({ page, getLocalTestUrl }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const { pageloadTraceContext, pageloadSampleRand } = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + + const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); + const pageloadSampleRand = Number(res[1]?.sample_rand); + + expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); + + const pageloadTraceContext = res[0].contexts?.trace; + + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + + return { pageloadTraceContext: pageloadTraceContext, pageloadSampleRand }; + }); + + await sentryTest.step('Make fetch request', async () => { + const fetchTracePromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); + + await page.locator('#btn2').click(); + + const { baggage, sentryTrace } = await tracingHeadersPromise; + + const [fetchTraceEvent, fetchTraceTraceHeader] = eventAndTraceHeaderRequestParser(await fetchTracePromise); + + const fetchTraceSampleRand = Number(fetchTraceTraceHeader?.sample_rand); + const fetchTraceTraceContext = fetchTraceEvent.contexts?.trace; + const httpClientSpan = fetchTraceEvent.spans?.find(span => span.op === 'http.client'); + + expect(fetchTraceSampleRand).toEqual(pageloadSampleRand); + + expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toEqual( + pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE], + ); + expect(fetchTraceTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + + expect(sentryTrace).toBeDefined(); + expect(baggage).toBeDefined(); + + expect(extractTraceparentData(sentryTrace)).toEqual({ + traceId: fetchTraceTraceContext?.trace_id, + parentSpanId: httpClientSpan?.span_id, + parentSampled: true, + }); + + expect(parseBaggageHeader(baggage)).toEqual({ + 'sentry-environment': 'production', + 'sentry-public_key': 'public', + 'sentry-sample_rand': `${pageloadSampleRand}`, + 'sentry-sample_rate': '1', + 'sentry-sampled': 'true', + 'sentry-trace_id': fetchTraceTraceContext?.trace_id, + 'sentry-transaction': 'custom root span 2', + }); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/init.js new file mode 100644 index 000000000000..05c4ec8db85a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + linkPreviousTrace: 'in-memory', + consistentTraceSampling: true, + }), + ], + tracePropagationTargets: ['sentry-test-external.io'], + tracesSampleRate: 1, + debug: true, + sendClientReports: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/subject.js new file mode 100644 index 000000000000..de60904fab3a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/subject.js @@ -0,0 +1,17 @@ +const btn1 = document.getElementById('btn1'); + +const btn2 = document.getElementById('btn2'); + +btn1.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); + }); +}); + +btn2.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { + await fetch('http://sentry-test-external.io'); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/template.html new file mode 100644 index 000000000000..6347fa37fc00 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/template.html @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts new file mode 100644 index 000000000000..c8faee2f5feb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts @@ -0,0 +1,100 @@ +import { expect } from '@playwright/test'; +import type { ClientReport } from '@sentry/core'; +import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; +import { sentryTest } from '../../../../../../utils/fixtures'; +import { + envelopeRequestParser, + getMultipleSentryEnvelopeRequests, + hidePage, + shouldSkipTracingTest, + waitForClientReportRequest, + waitForTracingHeadersOnUrl, +} from '../../../../../../utils/helpers'; + +const metaTagSampleRand = 0.9; +const metaTagSampleRate = 0.2; +const metaTagTraceId = '12345678901234567890123456789012'; + +sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { + sentryTest( + 'Continues negative sampling decision from meta tag across all traces and downstream propagations', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + let txnsReceived = 0; + // @ts-expect-error - no need to return something valid here + getMultipleSentryEnvelopeRequests(page, 1, { envelopeType: 'transaction' }, () => { + ++txnsReceived; + return {}; + }); + + const clientReportPromise = waitForClientReportRequest(page); + + await sentryTest.step('Initial pageload', async () => { + await page.goto(url); + expect(txnsReceived).toEqual(0); + }); + + await sentryTest.step('Custom instrumented button click', async () => { + await page.locator('#btn1').click(); + expect(txnsReceived).toEqual(0); + }); + + await sentryTest.step('Navigation', async () => { + await page.goto(`${url}#foo`); + expect(txnsReceived).toEqual(0); + }); + + await sentryTest.step('Make fetch request', async () => { + const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); + + await page.locator('#btn2').click(); + const { baggage, sentryTrace } = await tracingHeadersPromise; + + expect(sentryTrace).toBeDefined(); + expect(baggage).toBeDefined(); + + expect(extractTraceparentData(sentryTrace)).toEqual({ + traceId: expect.not.stringContaining(metaTagTraceId), + parentSpanId: expect.stringMatching(/^[\da-f]{16}$/), + parentSampled: false, + }); + + expect(parseBaggageHeader(baggage)).toEqual({ + 'sentry-environment': 'production', + 'sentry-public_key': 'public', + 'sentry-sample_rand': `${metaTagSampleRand}`, + 'sentry-sample_rate': `${metaTagSampleRate}`, + 'sentry-sampled': 'false', + 'sentry-trace_id': expect.not.stringContaining(metaTagTraceId), + 'sentry-transaction': 'custom root span 2', + }); + }); + + await sentryTest.step('Client report', async () => { + await hidePage(page); + const clientReport = envelopeRequestParser(await clientReportPromise); + expect(clientReport).toEqual({ + timestamp: expect.any(Number), + discarded_events: [ + { + category: 'transaction', + quantity: 4, + reason: 'sample_rate', + }, + ], + }); + }); + + await sentryTest.step('Wait for transactions to be discarded', async () => { + // give it a little longer just in case a txn is pending to be sent + await page.waitForTimeout(1000); + expect(txnsReceived).toEqual(0); + }); + }, + ); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/init.js new file mode 100644 index 000000000000..fbf897d47eab --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/init.js @@ -0,0 +1,20 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + linkPreviousTrace: 'session-storage', + consistentTraceSampling: true, + }), + ], + tracePropagationTargets: ['sentry-test-external.io'], + tracesSampler: ({ inheritOrSampleWith }) => { + return inheritOrSampleWith(0); + }, + debug: true, + sendClientReports: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-1.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-1.html new file mode 100644 index 000000000000..9a0719b7e505 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-1.html @@ -0,0 +1,15 @@ + + + + + + + + +

Another Page

+ Go To the next page + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-2.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-2.html new file mode 100644 index 000000000000..27cd47bba7c1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-2.html @@ -0,0 +1,10 @@ + + + + + + +

Another Page

+ Go To the next page + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/subject.js new file mode 100644 index 000000000000..ec0264fa49ef --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/subject.js @@ -0,0 +1,17 @@ +const btn1 = document.getElementById('btn1'); + +const btn2 = document.getElementById('btn2'); + +btn1?.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); + }); +}); + +btn2?.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { + await fetch('http://sentry-test-external.io'); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/template.html new file mode 100644 index 000000000000..eab1fecca6c4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/template.html @@ -0,0 +1,14 @@ + + + + + + + + Go To another page + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/test.ts new file mode 100644 index 000000000000..3dab9594ba7c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/test.ts @@ -0,0 +1,108 @@ +import { expect } from '@playwright/test'; +import type { ClientReport } from '@sentry/core'; +import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; +import { sentryTest } from '../../../../../../utils/fixtures'; +import { + envelopeRequestParser, + eventAndTraceHeaderRequestParser, + hidePage, + shouldSkipTracingTest, + waitForClientReportRequest, + waitForTracingHeadersOnUrl, + waitForTransactionRequest, +} from '../../../../../../utils/helpers'; + +const metaTagSampleRand = 0.9; +const metaTagSampleRate = 0.2; +const metaTagTraceIdIndex = '12345678901234567890123456789012'; +const metaTagTraceIdPage1 = 'a2345678901234567890123456789012'; + +sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { + sentryTest( + 'meta tag decision has precedence over sampling decision from previous trace in session storage', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const clientReportPromise = waitForClientReportRequest(page); + + await sentryTest.step('Initial pageload', async () => { + // negative sampling decision -> no pageload txn + await page.goto(url); + }); + + await sentryTest.step('Make fetch request', async () => { + // The fetch requests starts a new trace on purpose. So we only want the + // sampling decision and rand to be the same as from the meta tag but not the trace id or DSC + const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); + + await page.locator('#btn2').click(); + + const { baggage, sentryTrace } = await tracingHeadersPromise; + + expect(sentryTrace).toBeDefined(); + expect(baggage).toBeDefined(); + + expect(extractTraceparentData(sentryTrace)).toEqual({ + traceId: expect.not.stringContaining(metaTagTraceIdIndex), + parentSpanId: expect.stringMatching(/^[\da-f]{16}$/), + parentSampled: false, + }); + + expect(parseBaggageHeader(baggage)).toEqual({ + 'sentry-environment': 'production', + 'sentry-public_key': 'public', + 'sentry-sample_rand': `${metaTagSampleRand}`, + 'sentry-sample_rate': `${metaTagSampleRate}`, + 'sentry-sampled': 'false', + 'sentry-trace_id': expect.not.stringContaining(metaTagTraceIdIndex), + 'sentry-transaction': 'custom root span 2', + }); + }); + + await sentryTest.step('Client report', async () => { + await hidePage(page); + + const clientReport = envelopeRequestParser(await clientReportPromise); + expect(clientReport).toEqual({ + timestamp: expect.any(Number), + discarded_events: [ + { + category: 'transaction', + quantity: 2, + reason: 'sample_rate', + }, + ], + }); + }); + + await sentryTest.step('Navigate to another page with meta tags', async () => { + const page1Pageload = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.locator('a').click(); + + const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await page1Pageload); + const pageloadTraceContext = pageloadEvent.contexts?.trace; + + expect(Number(pageloadTraceHeader?.sample_rand)).toBe(0.12); + expect(Number(pageloadTraceHeader?.sample_rate)).toBe(0.2); + expect(pageloadTraceContext?.trace_id).toEqual(metaTagTraceIdPage1); + }); + + await sentryTest.step('Navigate to another page without meta tags', async () => { + const page2Pageload = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.locator('a').click(); + + const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await page2Pageload); + const pageloadTraceContext = pageloadEvent.contexts?.trace; + + expect(Number(pageloadTraceHeader?.sample_rand)).toBe(0.12); + expect(Number(pageloadTraceHeader?.sample_rate)).toBe(0.2); + expect(pageloadTraceContext?.trace_id).not.toEqual(metaTagTraceIdPage1); + expect(pageloadTraceContext?.trace_id).not.toEqual(metaTagTraceIdIndex); + }); + }, + ); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/init.js new file mode 100644 index 000000000000..61e2b2daa9ba --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + linkPreviousTrace: 'in-memory', + consistentTraceSampling: true, + }), + ], + tracePropagationTargets: ['sentry-test-external.io'], + // only take into account sampling from meta tag; otherwise sample negatively + tracesSampleRate: 0, + debug: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/subject.js new file mode 100644 index 000000000000..de60904fab3a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/subject.js @@ -0,0 +1,17 @@ +const btn1 = document.getElementById('btn1'); + +const btn2 = document.getElementById('btn2'); + +btn1.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); + }); +}); + +btn2.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { + await fetch('http://sentry-test-external.io'); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/template.html new file mode 100644 index 000000000000..7ceca6fec2a3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/template.html @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/test.ts new file mode 100644 index 000000000000..ac2a865fb181 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/test.ts @@ -0,0 +1,176 @@ +import { expect } from '@playwright/test'; +import { + extractTraceparentData, + parseBaggageHeader, + SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, +} from '@sentry/core'; +import { sentryTest } from '../../../../../../utils/fixtures'; +import { + eventAndTraceHeaderRequestParser, + shouldSkipTracingTest, + waitForTracingHeadersOnUrl, + waitForTransactionRequest, +} from '../../../../../../utils/helpers'; + +const metaTagSampleRand = 0.051121; +const metaTagSampleRate = 0.2; + +sentryTest.describe('When `consistentTraceSampling` is `true` and page contains tags', () => { + sentryTest('Continues sampling decision across all traces from meta tag', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + + await page.goto(url); + + const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); + const pageloadTraceContext = pageloadEvent.contexts?.trace; + + expect(Number(pageloadTraceHeader?.sample_rand)).toBe(metaTagSampleRand); + expect(Number(pageloadTraceHeader?.sample_rate)).toBe(metaTagSampleRate); + + // since the local sample rate was not applied, the sample rate attribute shouldn't be set + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); + + return pageloadTraceContext; + }); + + const customTraceContext = await sentryTest.step('Custom trace', async () => { + const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + + await page.locator('#btn1').click(); + + const [customTrace1Event, customTraceTraceHeader] = eventAndTraceHeaderRequestParser( + await customTrace1RequestPromise, + ); + + const customTraceContext = customTrace1Event.contexts?.trace; + + expect(customTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(customTraceContext?.parent_span_id).toBeUndefined(); + + expect(Number(customTraceTraceHeader?.sample_rand)).toBe(metaTagSampleRand); + expect(Number(customTraceTraceHeader?.sample_rate)).toBe(metaTagSampleRate); + expect(Boolean(customTraceTraceHeader?.sampled)).toBe(true); + + // since the local sample rate was not applied, the sample rate attribute shouldn't be set + expect(customTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + + // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) + expect(customTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBe(metaTagSampleRate); + + return customTraceContext; + }); + + await sentryTest.step('Navigation', async () => { + const navigation1RequestPromise = waitForTransactionRequest( + page, + evt => evt.contexts?.trace?.op === 'navigation', + ); + + await page.goto(`${url}#foo`); + + const [navigationEvent, navigationTraceHeader] = eventAndTraceHeaderRequestParser( + await navigation1RequestPromise, + ); + + const navigationTraceContext = navigationEvent.contexts?.trace; + + expect(navigationTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(navigationTraceContext?.trace_id).not.toEqual(customTraceContext?.trace_id); + + expect(navigationTraceContext?.parent_span_id).toBeUndefined(); + + expect(Number(navigationTraceHeader?.sample_rand)).toEqual(metaTagSampleRand); + expect(Number(navigationTraceHeader?.sample_rate)).toEqual(metaTagSampleRate); + expect(Boolean(navigationTraceHeader?.sampled)).toEqual(true); + + // since the local sample rate was not applied, the sample rate attribute shouldn't be set + expect(navigationTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + + // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) + expect(navigationTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBe( + metaTagSampleRate, + ); + }); + }); + + sentryTest( + 'Propagates continued tag sampling decision to outgoing requests', + async ({ page, getLocalTestUrl }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + + await page.goto(url); + + const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); + const pageloadTraceContext = pageloadEvent.contexts?.trace; + + expect(Number(pageloadTraceHeader?.sample_rand)).toBe(metaTagSampleRand); + expect(Number(pageloadTraceHeader?.sample_rate)).toBe(metaTagSampleRate); + + // since the local sample rate was not applied, the sample rate attribute shouldn't be set + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); + + return pageloadTraceContext; + }); + + await sentryTest.step('Make fetch request', async () => { + const fetchTracePromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + const tracingHeadersPromise = waitForTracingHeadersOnUrl(page, 'http://sentry-test-external.io'); + + await page.locator('#btn2').click(); + + const { baggage, sentryTrace } = await tracingHeadersPromise; + + const [fetchTraceEvent, fetchTraceTraceHeader] = eventAndTraceHeaderRequestParser(await fetchTracePromise); + + const fetchTraceSampleRand = Number(fetchTraceTraceHeader?.sample_rand); + const fetchTraceTraceContext = fetchTraceEvent.contexts?.trace; + const httpClientSpan = fetchTraceEvent.spans?.find(span => span.op === 'http.client'); + + expect(fetchTraceSampleRand).toEqual(metaTagSampleRand); + + expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); + expect(fetchTraceTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBe( + metaTagSampleRate, + ); + + expect(fetchTraceTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + + expect(sentryTrace).toBeDefined(); + expect(baggage).toBeDefined(); + + expect(extractTraceparentData(sentryTrace)).toEqual({ + traceId: fetchTraceTraceContext?.trace_id, + parentSpanId: httpClientSpan?.span_id, + parentSampled: true, + }); + + expect(parseBaggageHeader(baggage)).toEqual({ + 'sentry-environment': 'production', + 'sentry-public_key': 'public', + 'sentry-sample_rand': `${metaTagSampleRand}`, + 'sentry-sample_rate': `${metaTagSampleRate}`, + 'sentry-sampled': 'true', + 'sentry-trace_id': fetchTraceTraceContext?.trace_id, + 'sentry-transaction': 'custom root span 2', + }); + }); + }, + ); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/init.js new file mode 100644 index 000000000000..e2efc43e50f2 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/init.js @@ -0,0 +1,29 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + linkPreviousTrace: 'in-memory', + consistentTraceSampling: true, + enableInp: false, + }), + ], + tracePropagationTargets: ['sentry-test-external.io'], + tracesSampler: ctx => { + if (ctx.attributes && ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { + return 1; + } + if (ctx.name === 'custom root span 1') { + return 0; + } + if (ctx.name === 'custom root span 2') { + return 1; + } + return ctx.inheritOrSampleWith(0); + }, + debug: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/subject.js new file mode 100644 index 000000000000..de60904fab3a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/subject.js @@ -0,0 +1,17 @@ +const btn1 = document.getElementById('btn1'); + +const btn2 = document.getElementById('btn2'); + +btn1.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); + }); +}); + +btn2.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, async () => { + await fetch('http://sentry-test-external.io'); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/template.html new file mode 100644 index 000000000000..f26a602c7c6f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/template.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts new file mode 100644 index 000000000000..2bb196c898fd --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts @@ -0,0 +1,139 @@ +import { expect } from '@playwright/test'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; +import type { ClientReport } from '@sentry/core'; +import { sentryTest } from '../../../../../../utils/fixtures'; +import { + envelopeRequestParser, + eventAndTraceHeaderRequestParser, + hidePage, + shouldSkipTracingTest, + waitForClientReportRequest, + waitForTransactionRequest, +} from '../../../../../../utils/helpers'; + +/** + * This test demonstrates that: + * - explicit sampling decisions in `tracesSampler` has precedence over consistent sampling + * - despite consistentTraceSampling being activated, there are still a lot of cases where the trace chain can break + */ +sentryTest.describe('When `consistentTraceSampling` is `true`', () => { + sentryTest('explicit sampling decisions in `tracesSampler` have precedence', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const { pageloadTraceContext } = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => { + return evt.contexts?.trace?.op === 'pageload'; + }); + await page.goto(url); + + const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); + const pageloadSampleRand = Number(res[1]?.sample_rand); + const pageloadTraceContext = res[0].contexts?.trace; + + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); + + return { pageloadTraceContext: res[0].contexts?.trace, pageloadSampleRand }; + }); + + await sentryTest.step('Custom trace is sampled negatively (explicitly in tracesSampler)', async () => { + const clientReportPromise = waitForClientReportRequest(page); + + await page.locator('#btn1').click(); + + await page.waitForTimeout(500); + await hidePage(page); + + const clientReport = envelopeRequestParser(await clientReportPromise); + + expect(clientReport).toEqual({ + timestamp: expect.any(Number), + discarded_events: [ + { + category: 'transaction', + quantity: 1, + reason: 'sample_rate', + }, + ], + }); + }); + + await sentryTest.step('Subsequent navigation trace is also sampled negatively', async () => { + const clientReportPromise = waitForClientReportRequest(page); + + await page.goto(`${url}#foo`); + + await page.waitForTimeout(500); + + await hidePage(page); + + const clientReport = envelopeRequestParser(await clientReportPromise); + + expect(clientReport).toEqual({ + timestamp: expect.any(Number), + discarded_events: [ + { + category: 'transaction', + quantity: 1, + reason: 'sample_rate', + }, + ], + }); + }); + + const { customTrace2Context } = await sentryTest.step( + 'Custom trace 2 is sampled positively (explicitly in tracesSampler)', + async () => { + const customTrace2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + + await page.locator('#btn2').click(); + + const [customTrace2Event] = eventAndTraceHeaderRequestParser(await customTrace2RequestPromise); + + const customTrace2Context = customTrace2Event.contexts?.trace; + + expect(customTrace2Context?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(customTrace2Context?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(customTrace2Context?.parent_span_id).toBeUndefined(); + + expect(customTrace2Context?.links).toEqual([ + { + attributes: { 'sentry.link.type': 'previous_trace' }, + sampled: false, + span_id: expect.stringMatching(/^[\da-f]{16}$/), + trace_id: expect.stringMatching(/^[\da-f]{32}$/), + }, + ]); + + return { customTrace2Context }; + }, + ); + + await sentryTest.step('Navigation trace is sampled positively (inherited from previous trace)', async () => { + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + + await page.goto(`${url}#bar`); + + const [navigationEvent] = eventAndTraceHeaderRequestParser(await navigationRequestPromise); + + const navigationTraceContext = navigationEvent.contexts?.trace; + + expect(navigationTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(navigationTraceContext?.trace_id).not.toEqual(customTrace2Context?.trace_id); + expect(navigationTraceContext?.parent_span_id).toBeUndefined(); + + expect(navigationTraceContext?.links).toEqual([ + { + attributes: { 'sentry.link.type': 'previous_trace' }, + sampled: true, + span_id: customTrace2Context?.span_id, + trace_id: customTrace2Context?.trace_id, + }, + ]); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/init.js new file mode 100644 index 000000000000..bdd43f5ac757 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/init.js @@ -0,0 +1,11 @@ +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, + debug: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/subject.js new file mode 100644 index 000000000000..2a929a7e5083 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/subject.js @@ -0,0 +1,14 @@ +const btn1 = document.getElementById('btn1'); +const btn2 = document.getElementById('btn2'); + +btn1.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 1', op: 'custom' }, () => {}); + }); +}); + +btn2.addEventListener('click', () => { + Sentry.startNewTrace(() => { + Sentry.startSpan({ name: 'custom root span 2', op: 'custom' }, () => {}); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/template.html new file mode 100644 index 000000000000..f26a602c7c6f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/template.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/test.ts new file mode 100644 index 000000000000..c4637dc25018 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/test.ts @@ -0,0 +1,62 @@ +import { expect } from '@playwright/test'; +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest('manually started custom traces are linked correctly in the chain', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + return pageloadRequest.contexts?.trace; + }); + + const customTrace1Context = await sentryTest.step('Custom trace', async () => { + const customTrace1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); + await page.locator('#btn1').click(); + const customTrace1Event = envelopeRequestParser(await customTrace1RequestPromise); + + const customTraceCtx = customTrace1Event.contexts?.trace; + + expect(customTraceCtx?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + expect(customTraceCtx?.links).toEqual([ + { + trace_id: pageloadTraceContext?.trace_id, + span_id: pageloadTraceContext?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + + return customTraceCtx; + }); + + await sentryTest.step('Navigation', async () => { + const navigation1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + await page.goto(`${url}#foo`); + const navigationEvent = envelopeRequestParser(await navigation1RequestPromise); + const navTraceContext = navigationEvent.contexts?.trace; + + expect(navTraceContext?.trace_id).not.toEqual(customTrace1Context?.trace_id); + expect(navTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + + expect(navTraceContext?.links).toEqual([ + { + trace_id: customTrace1Context?.trace_id, + span_id: customTrace1Context?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/init.js new file mode 100644 index 000000000000..bdd43f5ac757 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/init.js @@ -0,0 +1,11 @@ +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, + debug: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/test.ts new file mode 100644 index 000000000000..7399c2bb26c3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/test.ts @@ -0,0 +1,99 @@ +import { expect } from '@playwright/test'; +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest("navigation spans link back to previous trace's root span", async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + return pageloadRequest.contexts?.trace; + }); + + const navigation1TraceContext = await sentryTest.step('First navigation', async () => { + const navigation1RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + await page.goto(`${url}#foo`); + const navigation1Request = envelopeRequestParser(await navigation1RequestPromise); + return navigation1Request.contexts?.trace; + }); + + const navigation2TraceContext = await sentryTest.step('Second navigation', async () => { + const navigation2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + await page.goto(`${url}#bar`); + const navigation2Request = envelopeRequestParser(await navigation2RequestPromise); + return navigation2Request.contexts?.trace; + }); + + const pageloadTraceId = pageloadTraceContext?.trace_id; + const navigation1TraceId = navigation1TraceContext?.trace_id; + const navigation2TraceId = navigation2TraceContext?.trace_id; + + expect(pageloadTraceContext?.links).toBeUndefined(); + + expect(navigation1TraceContext?.links).toEqual([ + { + trace_id: pageloadTraceId, + span_id: pageloadTraceContext?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + + expect(navigation1TraceContext?.data).toMatchObject({ + 'sentry.previous_trace': `${pageloadTraceId}-${pageloadTraceContext?.span_id}-1`, + }); + + expect(navigation2TraceContext?.links).toEqual([ + { + trace_id: navigation1TraceId, + span_id: navigation1TraceContext?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + + expect(navigation2TraceContext?.data).toMatchObject({ + 'sentry.previous_trace': `${navigation1TraceId}-${navigation1TraceContext?.span_id}-1`, + }); + + expect(pageloadTraceId).not.toEqual(navigation1TraceId); + expect(navigation1TraceId).not.toEqual(navigation2TraceId); + expect(pageloadTraceId).not.toEqual(navigation2TraceId); +}); + +sentryTest("doesn't link between hard page reloads by default", async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await sentryTest.step('First pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + const pageload1Event = envelopeRequestParser(await pageloadRequestPromise); + + expect(pageload1Event.contexts?.trace).toBeDefined(); + expect(pageload1Event.contexts?.trace?.links).toBeUndefined(); + }); + + await sentryTest.step('Second pageload', async () => { + const pageload2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.reload(); + const pageload2Event = envelopeRequestParser(await pageload2RequestPromise); + + expect(pageload2Event.contexts?.trace).toBeDefined(); + expect(pageload2Event.contexts?.trace?.links).toBeUndefined(); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/init.js new file mode 100644 index 000000000000..2c3e9264d882 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/init.js @@ -0,0 +1,11 @@ +import * as Sentry from '@sentry/browser'; +import { interactionsIntegration } from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + tracesSampleRate: 1, + integrations: [Sentry.browserTracingIntegration(), interactionsIntegration()], +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/template.html new file mode 100644 index 000000000000..7f6845239468 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/template.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/test.ts new file mode 100644 index 000000000000..24a1e2c59072 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/test.ts @@ -0,0 +1,89 @@ +import { expect } from '@playwright/test'; +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +/* + This is quite peculiar behavior but it's a result of the route-based trace lifetime. + Once we shortened trace lifetime, this whole scenario will change as the interaction + spans will be their own trace. So most likely, we can replace this test with a new one + that covers the new default behavior. +*/ +sentryTest( + 'only the first root spans in the trace link back to the previous trace', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + + const pageloadEvent = envelopeRequestParser(await pageloadRequestPromise); + const traceContext = pageloadEvent.contexts?.trace; + + expect(traceContext).toBeDefined(); + expect(traceContext?.links).toBeUndefined(); + + return traceContext; + }); + + await sentryTest.step('Click Before navigation', async () => { + const interactionRequestPromise = waitForTransactionRequest(page, evt => { + return evt.contexts?.trace?.op === 'ui.action.click'; + }); + await page.click('#btn'); + + const interactionEvent = envelopeRequestParser(await interactionRequestPromise); + const interactionTraceContext = interactionEvent.contexts?.trace; + + // sanity check: route-based trace lifetime means the trace_id should be the same + expect(interactionTraceContext?.trace_id).toBe(pageloadTraceContext?.trace_id); + + // no links yet as previous root span belonged to same trace + expect(interactionTraceContext?.links).toBeUndefined(); + }); + + const navigationTraceContext = await sentryTest.step('Navigation', async () => { + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + await page.goto(`${url}#foo`); + const navigationEvent = envelopeRequestParser(await navigationRequestPromise); + + const traceContext = navigationEvent.contexts?.trace; + + expect(traceContext?.op).toBe('navigation'); + expect(traceContext?.links).toEqual([ + { + trace_id: pageloadTraceContext?.trace_id, + span_id: pageloadTraceContext?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + + expect(traceContext?.trace_id).not.toEqual(traceContext?.links![0].trace_id); + return traceContext; + }); + + await sentryTest.step('Click After navigation', async () => { + const interactionRequestPromise = waitForTransactionRequest(page, evt => { + return evt.contexts?.trace?.op === 'ui.action.click'; + }); + await page.click('#btn'); + const interactionEvent = envelopeRequestParser(await interactionRequestPromise); + + const interactionTraceContext = interactionEvent.contexts?.trace; + + // sanity check: route-based trace lifetime means the trace_id should be the same + expect(interactionTraceContext?.trace_id).toBe(navigationTraceContext?.trace_id); + + // since this is the second root span in the trace, it doesn't link back to the previous trace + expect(interactionTraceContext?.links).toBeUndefined(); + }); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/init.js new file mode 100644 index 000000000000..bdd43f5ac757 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/init.js @@ -0,0 +1,11 @@ +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, + debug: true, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/template.html new file mode 100644 index 000000000000..2221bd0fee1d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/template.html @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/test.ts new file mode 100644 index 000000000000..a232a5a0f6e7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/test.ts @@ -0,0 +1,54 @@ +import { expect } from '@playwright/test'; +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest( + "links back to previous trace's local root span if continued from meta tags", + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const metaTagTraceId = '12345678901234567890123456789012'; + + const pageloadTraceContext = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + + const traceContext = pageloadRequest.contexts?.trace; + + // sanity check + expect(traceContext?.trace_id).toBe(metaTagTraceId); + + expect(traceContext?.links).toBeUndefined(); + + return traceContext; + }); + + const navigationTraceContext = await sentryTest.step('Navigation', async () => { + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + await page.goto(`${url}#foo`); + const navigationRequest = envelopeRequestParser(await navigationRequestPromise); + return navigationRequest.contexts?.trace; + }); + + const navigationTraceId = navigationTraceContext?.trace_id; + + expect(navigationTraceContext?.links).toEqual([ + { + trace_id: metaTagTraceId, + span_id: pageloadTraceContext?.span_id, + sampled: true, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + + expect(navigationTraceId).not.toEqual(metaTagTraceId); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/init.js new file mode 100644 index 000000000000..79ca4d68e8ba --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/init.js @@ -0,0 +1,16 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + // We want to ignore redirects for this test + integrations: [Sentry.browserTracingIntegration({ detectRedirects: false })], + tracesSampler: ctx => { + if (ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { + return 0; + } + return 1; + }, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/test.ts new file mode 100644 index 000000000000..894fe0d5929a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/test.ts @@ -0,0 +1,42 @@ +import { expect } from '@playwright/test'; +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest('includes a span link to a previously negatively sampled span', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await sentryTest.step('Initial pageload', async () => { + // No event to check for an event here because this pageload span is sampled negatively! + await page.goto(url); + }); + + await sentryTest.step('Navigation', async () => { + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + await page.goto(`${url}#foo`); + const navigationEvent = envelopeRequestParser(await navigationRequestPromise); + const navigationTraceContext = navigationEvent.contexts?.trace; + + expect(navigationTraceContext?.op).toBe('navigation'); + expect(navigationTraceContext?.links).toEqual([ + { + trace_id: expect.stringMatching(/[a-f\d]{32}/), + span_id: expect.stringMatching(/[a-f\d]{16}/), + sampled: false, + attributes: { + [SENTRY_LINK_TYPE]: 'previous_trace', + }, + }, + ]); + + expect(navigationTraceContext?.data).toMatchObject({ + 'sentry.previous_trace': expect.stringMatching(/[a-f\d]{32}-[a-f\d]{16}-0/), + }); + + expect(navigationTraceContext?.trace_id).not.toEqual(navigationTraceContext?.links![0].trace_id); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/init.js new file mode 100644 index 000000000000..9b88e489ae07 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/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({ linkPreviousTrace: 'session-storage' })], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/test.ts new file mode 100644 index 000000000000..f7e0d288de5b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/session-storage/test.ts @@ -0,0 +1,39 @@ +import { expect } from '@playwright/test'; +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest('adds link between hard page reloads when opting into sessionStorage', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageload1TraceContext = await sentryTest.step('First pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.goto(url); + const pageload1Event = envelopeRequestParser(await pageloadRequestPromise); + const pageload1TraceContext = pageload1Event.contexts?.trace; + expect(pageload1TraceContext).toBeDefined(); + expect(pageload1TraceContext?.links).toBeUndefined(); + return pageload1TraceContext; + }); + + const pageload2Event = await sentryTest.step('Hard page reload', async () => { + const pageload2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); + await page.reload(); + return envelopeRequestParser(await pageload2RequestPromise); + }); + + expect(pageload2Event.contexts?.trace?.links).toEqual([ + { + trace_id: pageload1TraceContext?.trace_id, + span_id: pageload1TraceContext?.span_id, + sampled: true, + attributes: { [SENTRY_LINK_TYPE]: 'previous_trace' }, + }, + ]); + + expect(pageload1TraceContext?.trace_id).not.toEqual(pageload2Event.contexts?.trace?.trace_id); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/init.js new file mode 100644 index 000000000000..a8a7fc20c7f9 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongTask: false, + enableLongAnimationFrame: true, + instrumentPageLoad: false, + enableInp: false, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/subject.js new file mode 100644 index 000000000000..b02ed6efa33b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/subject.js @@ -0,0 +1,18 @@ +function getElapsed(startTime) { + const time = Date.now(); + return time - startTime; +} + +function handleClick() { + const startTime = Date.now(); + while (getElapsed(startTime) < 105) { + // + } + window.history.pushState({}, '', `#myHeading`); +} + +const button = document.getElementById('clickme'); + +console.log('button', button); + +button.addEventListener('click', handleClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/template.html new file mode 100644 index 000000000000..6a6a89752f20 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/template.html @@ -0,0 +1,11 @@ + + + + + + + + +

My Heading

+ + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts new file mode 100644 index 000000000000..b7215249910f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts @@ -0,0 +1,30 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest( + "doesn't capture long animation frame that starts before a navigation.", + async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + const navigationTransactionEventPromise = getFirstSentryEnvelopeRequest(page); + + await page.locator('#clickme').click(); + + const navigationTransactionEvent = await navigationTransactionEventPromise; + + expect(navigationTransactionEvent.contexts?.trace?.op).toBe('navigation'); + + const loafSpans = navigationTransactionEvent.spans?.filter(s => s.op?.startsWith('ui.long_animation_frame')); + + expect(loafSpans?.length).toEqual(0); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/assets/script.js new file mode 100644 index 000000000000..195a094070be --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/assets/script.js @@ -0,0 +1,12 @@ +(() => { + const startTime = Date.now(); + + function getElapsed() { + const time = Date.now(); + return time - startTime; + } + + while (getElapsed() < 101) { + // + } +})(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/init.js new file mode 100644 index 000000000000..250f2a72311b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/init.js @@ -0,0 +1,15 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongTask: false, + enableLongAnimationFrame: false, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/template.html new file mode 100644 index 000000000000..62aed26413f8 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/template.html @@ -0,0 +1,10 @@ + + + + + + +
Rendered Before Long Animation Frame
+ + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/test.ts new file mode 100644 index 000000000000..3d4ca79877b1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/test.ts @@ -0,0 +1,26 @@ +import type { Route } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest( + 'should not capture long animation frame when flag is disabled.', + async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => + route.fulfill({ path: `${__dirname}/assets/script.js` }), + ); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + + expect(uiSpans?.length).toBe(0); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/assets/script.js new file mode 100644 index 000000000000..10552eeb5bd5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/assets/script.js @@ -0,0 +1,25 @@ +function getElapsed(startTime) { + const time = Date.now(); + return time - startTime; +} + +function handleClick() { + const startTime = Date.now(); + while (getElapsed(startTime) < 105) { + // + } +} + +function start() { + const startTime = Date.now(); + while (getElapsed(startTime) < 105) { + // + } +} + +// trigger 2 long-animation-frame events +// one from the top-level and the other from an event-listener +start(); + +const button = document.getElementById('clickme'); +button.addEventListener('click', handleClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/init.js new file mode 100644 index 000000000000..cfa9cb5faa3f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/init.js @@ -0,0 +1,15 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongTask: false, + enableLongAnimationFrame: true, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/template.html new file mode 100644 index 000000000000..c157aa80cb8d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/template.html @@ -0,0 +1,11 @@ + + + + + + +
Rendered Before Long Animation Frame
+ + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts new file mode 100644 index 000000000000..937662e117a4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts @@ -0,0 +1,113 @@ +import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import type { Route } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest( + 'should capture long animation frame for top-level script.', + async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => + route.fulfill({ path: `${__dirname}/assets/script.js` }), + ); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + const eventData = await promise; + + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')); + + expect(uiSpans?.length).toBeGreaterThanOrEqual(1); + + const topLevelUISpan = (uiSpans || []).find( + span => span.data?.['browser.script.invoker'] === 'https://sentry-test-site.example/path/to/script.js', + )!; + expect(topLevelUISpan).toEqual( + expect.objectContaining({ + op: 'ui.long_animation_frame', + description: 'Main UI thread blocked', + parent_span_id: eventData.contexts?.trace?.span_id, + data: { + 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', + 'browser.script.source_char_position': 0, + 'browser.script.invoker': 'https://sentry-test-site.example/path/to/script.js', + 'browser.script.invoker_type': 'classic-script', + [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', + }, + }), + ); + const start = topLevelUISpan.start_timestamp ?? 0; + const end = topLevelUISpan.timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); + }, +); + +sentryTest( + 'should capture long animation frame for event listener.', + async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => + route.fulfill({ path: `${__dirname}/assets/script.js` }), + ); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + // trigger long animation frame function + await page.getByRole('button').click(); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + const eventData = await promise; + + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')) || []; + + expect(uiSpans.length).toBeGreaterThanOrEqual(2); + + const eventListenerUISpan = uiSpans.find(span => span.data['browser.script.invoker'] === 'BUTTON#clickme.onclick')!; + + expect(eventListenerUISpan).toEqual( + expect.objectContaining({ + op: 'ui.long_animation_frame', + description: 'Main UI thread blocked', + parent_span_id: eventData.contexts?.trace?.span_id, + data: { + 'browser.script.invoker': 'BUTTON#clickme.onclick', + 'browser.script.invoker_type': 'event-listener', + 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', + [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', + }, + }), + ); + const start = eventListenerUISpan.start_timestamp ?? 0; + const end = eventListenerUISpan.timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/assets/script.js new file mode 100644 index 000000000000..10552eeb5bd5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/assets/script.js @@ -0,0 +1,25 @@ +function getElapsed(startTime) { + const time = Date.now(); + return time - startTime; +} + +function handleClick() { + const startTime = Date.now(); + while (getElapsed(startTime) < 105) { + // + } +} + +function start() { + const startTime = Date.now(); + while (getElapsed(startTime) < 105) { + // + } +} + +// trigger 2 long-animation-frame events +// one from the top-level and the other from an event-listener +start(); + +const button = document.getElementById('clickme'); +button.addEventListener('click', handleClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/init.js new file mode 100644 index 000000000000..37df798364a8 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/init.js @@ -0,0 +1,15 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongTask: true, + enableLongAnimationFrame: true, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/template.html new file mode 100644 index 000000000000..c157aa80cb8d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/template.html @@ -0,0 +1,11 @@ + + + + + + +
Rendered Before Long Animation Frame
+ + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts new file mode 100644 index 000000000000..b571a7d6a306 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts @@ -0,0 +1,115 @@ +import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import type { Route } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest( + 'should capture long animation frame for top-level script.', + async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + // Long animation frame should take priority over long tasks + + await page.route('**/path/to/script.js', (route: Route) => + route.fulfill({ path: `${__dirname}/assets/script.js` }), + ); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + const eventData = await promise; + + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')); + + expect(uiSpans?.length).toBeGreaterThanOrEqual(1); + + const topLevelUISpan = (uiSpans || []).find( + span => span.data?.['browser.script.invoker'] === 'https://sentry-test-site.example/path/to/script.js', + )!; + expect(topLevelUISpan).toEqual( + expect.objectContaining({ + op: 'ui.long_animation_frame', + description: 'Main UI thread blocked', + parent_span_id: eventData.contexts?.trace?.span_id, + data: { + 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', + 'browser.script.source_char_position': 0, + 'browser.script.invoker': 'https://sentry-test-site.example/path/to/script.js', + 'browser.script.invoker_type': 'classic-script', + [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', + }, + }), + ); + const start = topLevelUISpan.start_timestamp ?? 0; + const end = topLevelUISpan.timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); + }, +); + +sentryTest( + 'should capture long animation frame for event listener.', + async ({ browserName, getLocalTestUrl, page }) => { + // Long animation frames only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => + route.fulfill({ path: `${__dirname}/assets/script.js` }), + ); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const promise = getFirstSentryEnvelopeRequest(page); + + await page.goto(url); + + // trigger long animation frame function + await page.getByRole('button').click(); + + await new Promise(resolve => setTimeout(resolve, 1000)); + + const eventData = await promise; + + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')) || []; + + expect(uiSpans.length).toBeGreaterThanOrEqual(2); + + const eventListenerUISpan = uiSpans.find(span => span.data['browser.script.invoker'] === 'BUTTON#clickme.onclick')!; + + expect(eventListenerUISpan).toEqual( + expect.objectContaining({ + op: 'ui.long_animation_frame', + description: 'Main UI thread blocked', + parent_span_id: eventData.contexts?.trace?.span_id, + data: { + 'browser.script.invoker': 'BUTTON#clickme.onclick', + 'browser.script.invoker_type': 'event-listener', + 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', + [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', + }, + }), + ); + const start = eventListenerUISpan.start_timestamp ?? 0; + const end = eventListenerUISpan.timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/init.js new file mode 100644 index 000000000000..63abc00dea38 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongAnimationFrame: false, + instrumentPageLoad: false, + instrumentNavigation: true, + enableInp: false, + enableLongTask: true, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/subject.js new file mode 100644 index 000000000000..d814f8875715 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/subject.js @@ -0,0 +1,17 @@ +const longTaskButton = document.getElementById('myButton'); + +longTaskButton?.addEventListener('click', () => { + const startTime = Date.now(); + + function getElapsed() { + const time = Date.now(); + return time - startTime; + } + + while (getElapsed() < 500) { + // + } + + // trigger a navigation in the same event loop tick + window.history.pushState({}, '', '#myHeading'); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/template.html new file mode 100644 index 000000000000..c2cb2a8129fe --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/template.html @@ -0,0 +1,12 @@ + + + + + + +
Rendered Before Long Task
+ + +

Heading

+ + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/test.ts new file mode 100644 index 000000000000..5236717091f1 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/test.ts @@ -0,0 +1,30 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest( + "doesn't capture long task spans starting before a navigation in the navigation transaction", + async ({ browserName, getLocalTestUrl, page }) => { + // Long tasks only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('**/path/to/script.js', route => route.fulfill({ path: `${__dirname}/assets/script.js` })); + + await page.goto(url); + + const navigationTransactionEventPromise = getFirstSentryEnvelopeRequest(page); + + await page.locator('#myButton').click(); + + const navigationTransactionEvent = await navigationTransactionEventPromise; + + expect(navigationTransactionEvent.contexts?.trace?.op).toBe('navigation'); + + const longTaskSpans = navigationTransactionEvent?.spans?.filter(span => span.op === 'ui.long_task'); + expect(longTaskSpans).toHaveLength(0); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/assets/script.js new file mode 100644 index 000000000000..195a094070be --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/assets/script.js @@ -0,0 +1,12 @@ +(() => { + const startTime = Date.now(); + + function getElapsed() { + const time = Date.now(); + return time - startTime; + } + + while (getElapsed() < 101) { + // + } +})(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/init.js new file mode 100644 index 000000000000..250f2a72311b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/init.js @@ -0,0 +1,15 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongTask: false, + enableLongAnimationFrame: false, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/template.html new file mode 100644 index 000000000000..b03231da2c65 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/template.html @@ -0,0 +1,10 @@ + + + + + + +
Rendered Before Long Task
+ + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/test.ts new file mode 100644 index 000000000000..bb32e21ee1d5 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/test.ts @@ -0,0 +1,21 @@ +import type { Route } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest('should not capture long task when flag is disabled.', async ({ browserName, getLocalTestUrl, page }) => { + // Long tasks only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + + expect(uiSpans?.length).toBe(0); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/assets/script.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/assets/script.js new file mode 100644 index 000000000000..b61592e05943 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/assets/script.js @@ -0,0 +1,12 @@ +(() => { + const startTime = Date.now(); + + function getElapsed() { + const time = Date.now(); + return time - startTime; + } + + while (getElapsed() < 105) { + // + } +})(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/init.js new file mode 100644 index 000000000000..73d20634b532 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/init.js @@ -0,0 +1,14 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongAnimationFrame: false, + }), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/template.html new file mode 100644 index 000000000000..b03231da2c65 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/template.html @@ -0,0 +1,10 @@ + + + + + + +
Rendered Before Long Task
+ + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/test.ts new file mode 100644 index 000000000000..e34b0d779f7e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/test.ts @@ -0,0 +1,36 @@ +import type { Route } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest('should capture long task.', async ({ browserName, getLocalTestUrl, page }) => { + // Long tasks only work on chrome + if (shouldSkipTracingTest() || browserName !== 'chromium') { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); + + expect(uiSpans?.length).toBeGreaterThan(0); + + const [firstUISpan] = uiSpans || []; + expect(firstUISpan).toEqual( + expect.objectContaining({ + op: 'ui.long_task', + description: 'Main UI thread blocked', + parent_span_id: eventData.contexts?.trace?.span_id, + }), + ); + const start = firstUISpan.start_timestamp ?? 0; + const end = firstUISpan.timestamp ?? 0; + const duration = end - start; + + expect(duration).toBeGreaterThanOrEqual(0.1); + expect(duration).toBeLessThanOrEqual(0.15); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/init.js new file mode 100644 index 000000000000..bb6d4918d1bf --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-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/navigation-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts new file mode 100644 index 000000000000..ab8a3931e132 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts @@ -0,0 +1,150 @@ +import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { + envelopeRequestParser, + getFirstSentryEnvelopeRequest, + shouldSkipTracingTest, + waitForTransactionRequest, +} from '../../../../utils/helpers'; + +sentryTest('should create a navigation transaction on page navigation', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadRequest = await getFirstSentryEnvelopeRequest(page, url); + const navigationRequest = await getFirstSentryEnvelopeRequest(page, `${url}#foo`); + + expect(pageloadRequest.contexts?.trace?.op).toBe('pageload'); + expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); + + expect(navigationRequest.transaction_info?.source).toEqual('url'); + + const pageloadTraceId = pageloadRequest.contexts?.trace?.trace_id; + const navigationTraceId = navigationRequest.contexts?.trace?.trace_id; + + expect(pageloadTraceId).toBeDefined(); + expect(navigationTraceId).toBeDefined(); + expect(pageloadTraceId).not.toEqual(navigationTraceId); + + expect(pageloadRequest.transaction).toEqual('/index.html'); + // Fragment is not in transaction name + expect(navigationRequest.transaction).toEqual('/index.html'); + + expect(pageloadRequest.contexts?.trace?.data).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.pageload.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'pageload', + ['sentry.idle_span_finish_reason']: 'idleTimeout', + }); + expect(navigationRequest.contexts?.trace?.data).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.navigation.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'navigation', + ['sentry.idle_span_finish_reason']: 'idleTimeout', + }); + expect(pageloadRequest.request).toEqual({ + headers: { + 'User-Agent': expect.any(String), + }, + url: 'http://sentry-test.io/index.html', + }); + expect(navigationRequest.request).toEqual({ + headers: { + 'User-Agent': expect.any(String), + }, + url: 'http://sentry-test.io/index.html#foo', + }); + + const pageloadSpans = pageloadRequest.spans; + const navigationSpans = navigationRequest.spans; + + const pageloadSpanId = pageloadRequest.contexts?.trace?.span_id; + const navigationSpanId = navigationRequest.contexts?.trace?.span_id; + + expect(pageloadSpanId).toBeDefined(); + expect(navigationSpanId).toBeDefined(); + + pageloadSpans?.forEach(span => + expect(span).toMatchObject({ + parent_span_id: pageloadSpanId, + }), + ); + + navigationSpans?.forEach(span => + expect(span).toMatchObject({ + parent_span_id: navigationSpanId, + }), + ); + + expect(pageloadSpanId).not.toEqual(navigationSpanId); +}); + +// +sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + const navigationRequestPromise = waitForTransactionRequest( + page, + event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page', + ); + const navigationRequestPromise2 = waitForTransactionRequest( + page, + event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page-2', + ); + + await page.goto(url); + await pageloadRequestPromise; + + await page.evaluate("window.history.pushState({}, '', `${window.location.origin}/sub-page`);"); + + const navigationRequest = envelopeRequestParser(await navigationRequestPromise); + + expect(navigationRequest.transaction).toEqual('/sub-page'); + + expect(navigationRequest.contexts?.trace?.data).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.navigation.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'navigation', + ['sentry.idle_span_finish_reason']: 'idleTimeout', + }); + expect(navigationRequest.request).toEqual({ + headers: { + 'User-Agent': expect.any(String), + }, + url: 'http://sentry-test.io/sub-page', + }); + + await page.evaluate("window.history.pushState({}, '', `${window.location.origin}/sub-page-2`);"); + + const navigationRequest2 = envelopeRequestParser(await navigationRequestPromise2); + + expect(navigationRequest2.transaction).toEqual('/sub-page-2'); + + expect(navigationRequest2.contexts?.trace?.data).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.navigation.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'navigation', + ['sentry.idle_span_finish_reason']: 'idleTimeout', + }); + expect(navigationRequest2.request).toEqual({ + headers: { + 'User-Agent': expect.any(String), + }, + url: 'http://sentry-test.io/sub-page-2', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/init.js new file mode 100644 index 000000000000..b74e6633eadb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/init.js @@ -0,0 +1,11 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window._testBaseTimestamp = performance.timeOrigin / 1000; + +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-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts new file mode 100644 index 000000000000..8e7c3a4b2480 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts @@ -0,0 +1,35 @@ +import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest('creates a pageload transaction with url as source', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + const timeOrigin = await page.evaluate('window._testBaseTimestamp'); + + const { start_timestamp: startTimestamp } = eventData; + + const traceContextData = eventData.contexts?.trace?.data; + + expect(startTimestamp).toBeCloseTo(timeOrigin, 1); + + expect(traceContextData).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.pageload.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'pageload', + ['sentry.idle_span_finish_reason']: 'idleTimeout', + }); + + expect(eventData.contexts?.trace?.op).toBe('pageload'); + expect(eventData.spans?.length).toBeGreaterThan(0); + expect(eventData.transaction_info?.source).toEqual('url'); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/init.js new file mode 100644 index 000000000000..054a40d79664 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/init.js @@ -0,0 +1,16 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window._testBaseTimestamp = performance.timeOrigin / 1000; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true })], + tracesSampleRate: 1, + debug: true, +}); + +setTimeout(() => { + Sentry.reportPageLoaded(); +}, 2500); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts new file mode 100644 index 000000000000..bca0d6052b95 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts @@ -0,0 +1,38 @@ +import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest( + 'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + const eventData = envelopeRequestParser(await pageloadEventPromise); + + const traceContextData = eventData.contexts?.trace?.data; + const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!; + + expect(traceContextData).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.pageload.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'pageload', + ['sentry.idle_span_finish_reason']: 'reportPageLoaded', + }); + + // We wait for 2.5 seconds before calling Sentry.reportPageLoaded(). The span starts at navigation start, + // but the timeout only starts once the bundle has executed, so allow generous upper headroom for slow CI. + expect(spanDurationSeconds).toBeGreaterThan(2); + expect(spanDurationSeconds).toBeLessThan(4); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/init.js new file mode 100644 index 000000000000..2f633a9c6f1e --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/init.js @@ -0,0 +1,14 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window._testBaseTimestamp = performance.timeOrigin / 1000; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, finalTimeout: 3000 })], + tracesSampleRate: 1, + debug: true, +}); + +// not calling Sentry.reportPageLoaded() on purpose! diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts new file mode 100644 index 000000000000..ae6ffdec21b3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts @@ -0,0 +1,38 @@ +import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest( + 'final timeout cancels the pageload span even if `enableReportPageLoaded` is true', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + const eventData = envelopeRequestParser(await pageloadEventPromise); + + const traceContextData = eventData.contexts?.trace?.data; + const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!; + + expect(traceContextData).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.pageload.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'pageload', + ['sentry.idle_span_finish_reason']: 'finalTimeout', + }); + + // We wait for 3 seconds before calling Sentry.reportPageLoaded() + // the margins are to account for timing weirdness in CI to avoid flakes + expect(spanDurationSeconds).toBeGreaterThan(2.5); + expect(spanDurationSeconds).toBeLessThan(3.5); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js new file mode 100644 index 000000000000..3e937c873954 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js @@ -0,0 +1,20 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window._testBaseTimestamp = performance.timeOrigin / 1000; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, instrumentNavigation: false })], + tracesSampleRate: 1, + debug: true, +}); + +setTimeout(() => { + Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(), { name: 'custom_navigation' }); +}, 1000); + +setTimeout(() => { + Sentry.reportPageLoaded(); +}, 2500); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts new file mode 100644 index 000000000000..9e947592a576 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts @@ -0,0 +1,36 @@ +import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; +import { sentryTest } from '../../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; + +sentryTest( + 'starting a navigation span cancels the pageload span even if `enableReportPageLoaded` is true', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + const eventData = envelopeRequestParser(await pageloadEventPromise); + + const traceContextData = eventData.contexts?.trace?.data; + const spanDurationSeconds = eventData.timestamp! - eventData.start_timestamp!; + + expect(traceContextData).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.pageload.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'url', + [SENTRY_OP]: 'pageload', + ['sentry.idle_span_finish_reason']: 'cancelled', + }); + + // ending span after 1s but adding a margin of 0.5s to account for timing weirdness in CI to avoid flakes + expect(spanDurationSeconds).toBeLessThan(1.5); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/init.js new file mode 100644 index 000000000000..f2b876b3bf7d --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; +import { interactionsIntegration } from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + traceLifecycle: 'static', + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [ + Sentry.browserTracingIntegration({ + enableLongTask: false, + }), + interactionsIntegration(), + Sentry.spotlightBrowserIntegration(), + ], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/subject.js new file mode 100644 index 000000000000..cae57f7a9167 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/subject.js @@ -0,0 +1,12 @@ +// Block the main thread for 70ms so the PerformanceObserver registers +// a click event entry, which triggers `ui.interaction.click` child spans. +const simulateSlowClick = e => { + const startTime = Date.now(); + while (Date.now() - startTime < 70) { + // + } + e.target.classList.add('clicked'); +}; + +document.querySelector('[data-test-id=spotlight-button]').addEventListener('click', simulateSlowClick); +document.querySelector('[data-test-id=regular-button]').addEventListener('click', simulateSlowClick); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/template.html new file mode 100644 index 000000000000..9348e00e7db7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/template.html @@ -0,0 +1,12 @@ + + + + + + +
+ +
+ + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts new file mode 100644 index 000000000000..d0480062c10a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts @@ -0,0 +1,57 @@ +import { expect } from '@playwright/test'; +import type { TransactionEvent } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { + envelopeRequestParser, + shouldSkipCdnBundleTest, + shouldSkipTracingTest, + waitForTransactionRequest, +} from '../../../../utils/helpers'; + +sentryTest( + 'filters ui.interaction.click spans for spotlight elements via ignoreSpans', + async ({ getLocalTestUrl, page }) => { + // spotlightBrowserIntegration is not available in CDN bundles + if (shouldSkipTracingTest() || shouldSkipCdnBundleTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + await page.goto(url); + + // Wait for the pageload transaction to complete + await waitForTransactionRequest(page); + + // Click on the spotlight element — interaction span should be filtered + const spotlightTxnPromise = waitForTransactionRequest(page, txn => txn.contexts?.trace?.op === 'ui.action.click'); + await page.locator('[data-test-id=spotlight-button]').click(); + await page.locator('.clicked[data-test-id=spotlight-button]').isVisible(); + const spotlightTransaction = envelopeRequestParser(await spotlightTxnPromise); + + expect(spotlightTransaction.contexts?.trace?.op).toBe('ui.action.click'); + + const spotlightInteractionSpans = spotlightTransaction.spans?.filter(span => span.op === 'ui.interaction.click'); + expect(spotlightInteractionSpans).toHaveLength(0); + + // Let the first idle span fully settle before clicking again + await page.waitForTimeout(1000); + + // Click on the regular button — wait specifically for a transaction that contains + // a ui.interaction.click child span, since the PerformanceObserver may deliver + // the event entry asynchronously + const regularTxnPromise = waitForTransactionRequest( + page, + txn => + txn.contexts?.trace?.op === 'ui.action.click' && + (txn.spans?.some(span => span.op === 'ui.interaction.click') ?? false), + ); + await page.locator('[data-test-id=regular-button]').click(); + await page.locator('.clicked[data-test-id=regular-button]').isVisible(); + const regularTransaction = envelopeRequestParser(await regularTxnPromise); + + const regularInteractionSpans = regularTransaction.spans?.filter(span => span.op === 'ui.interaction.click'); + expect(regularInteractionSpans?.length).toBeGreaterThanOrEqual(1); + expect(regularInteractionSpans![0]!.description).toContain('button'); + expect(regularInteractionSpans![0]!.description).not.toContain('#sentry-spotlight'); + }, +); From 04597070f8b5b9ad6854d57fb5a4e76fb35a642d Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 7 Oct 2026 14:57:14 +0200 Subject: [PATCH 3/7] test(browser): remove HTTP timings debug pause Co-Authored-By: GPT-6 --- .../browserTracingIntegration/http-timings-static/test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts index 6894d7407349..6c109b66783a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts @@ -29,7 +29,6 @@ sentryTest('creates fetch spans with http timing', async ({ browserName, getLoca expect(requestSpans).toHaveLength(3); - await page.pause(); requestSpans?.forEach((span, index) => expect(span).toMatchObject({ description: `GET http://sentry-test-site.example/${index}`, From 071e9063930a4493623da0440f1693d3f5a5e51e Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 7 Oct 2026 15:25:31 +0200 Subject: [PATCH 4/7] test(browser): use attribute constants in browser tracing suites Co-Authored-By: GPT-6 --- .../backgroundtab-custom/test.ts | 5 +- .../backgroundtab-pageload-static/test.ts | 3 +- .../backgroundtab-pageload/test.ts | 3 +- .../browserTracingIntegration/error/test.ts | 3 +- .../http-timings-static/test.ts | 44 ++++++++++----- .../http-timings/test.ts | 49 +++++++++++------ .../tracesSampler-precedence/test.ts | 5 +- .../consistent-sampling/default/test.ts | 10 ++-- .../consistent-sampling/meta/test.ts | 11 ++-- .../test.ts | 3 +- .../test.ts | 27 ++++++---- .../long-animation-frame-enabled/test.ts | 27 ++++++---- .../test.ts | 27 ++++++---- .../test.ts | 27 ++++++---- .../long-tasks-before-navigation/test.ts | 3 +- .../long-tasks-enabled/test.ts | 3 +- .../long-tasks-no-animation-frame/test.ts | 3 +- .../browserTracingIntegration/meta/test.ts | 3 +- .../navigation-aborting-pageload/test.ts | 28 ++++++---- .../multiple-redirects/test.ts | 26 +++++---- .../navigation-navigation-click/test.ts | 13 ++--- .../navigation-navigation-keypress/test.ts | 15 +++--- .../navigation-redirect/test.ts | 9 ++-- .../navigation-redirect/opt-out/test.ts | 5 +- .../pageload-navigation-click/test.ts | 5 +- .../pageload-navigation-keypress/test.ts | 5 +- .../pageload-navigation-time/test.ts | 5 +- .../pageload-redirect/test.ts | 22 ++++---- .../navigation-static/test.ts | 15 ++++-- .../navigation/test.ts | 53 ++++++++++++------- .../on-request-span-end/test.ts | 5 +- .../on-request-span-start/test.ts | 5 +- .../pageload-static/test.ts | 9 +++- .../pageload-update-txn-name/test.ts | 2 +- .../pageload-updateSpanName/test.ts | 2 +- .../pageload/test.ts | 36 ++++++++----- .../pageloadDelayed/test.ts | 3 +- .../pageloadWithChildSpanTimeout/test.ts | 3 +- .../reportPageLoaded-static/default/test.ts | 9 +++- .../finalTimeout/test.ts | 9 +++- .../navigation/test.ts | 9 +++- .../reportPageLoaded/finalTimeout/test.ts | 9 +++- .../reportPageLoaded/navigation/test.ts | 9 +++- .../spotlight-interaction-filter/test.ts | 7 +-- 44 files changed, 367 insertions(+), 207 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts index ba3af4ced459..944ed474f19e 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-custom/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_STATUS_MESSAGE, SENTRY_CANCELLATION_REASON } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import type { StreamedSpanJSON } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -32,6 +33,6 @@ sentryTest('should finish a custom span when the page goes background', async ({ expect(name_after).toBe(name_before); // a cancelled span is reported as `ok`, with the raw status kept as an attribute expect(spanJsonAfter.status).toBe('ok'); - expect(attributes_after['sentry.status.message']).toBeUndefined(); - expect(attributes_after['sentry.cancellation_reason']).toBe('document.hidden'); + expect(attributes_after[SENTRY_STATUS_MESSAGE]).toBeUndefined(); + expect(attributes_after[SENTRY_CANCELLATION_REASON]).toBe('document.hidden'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts index 80ea1f5fa09c..f788c8e372e6 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_CANCELLATION_REASON } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -16,5 +17,5 @@ sentryTest('should finish pageload transaction when the page goes background', a expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); - expect(pageloadTransaction.contexts?.trace?.data?.['sentry.cancellation_reason']).toBe('document.hidden'); + expect(pageloadTransaction.contexts?.trace?.data?.[SENTRY_CANCELLATION_REASON]).toBe('document.hidden'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts index 5915b17d78eb..f23e41c923bf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_CANCELLATION_REASON } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -14,5 +15,5 @@ sentryTest('finishes streamed pageload span when the page goes background', asyn expect(getSpanOp(pageloadSpan)).toBe('pageload'); expect(pageloadSpan.status).toBe('ok'); - expect(pageloadSpan.attributes['sentry.cancellation_reason']?.value).toBe('document.hidden'); + expect(pageloadSpan.attributes[SENTRY_CANCELLATION_REASON]?.value).toBe('document.hidden'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts index 8af1a0f80935..275aa4e7a194 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts @@ -1,3 +1,4 @@ +import { URL_PATH } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -28,6 +29,6 @@ sentryTest( expect(error.exception?.values?.[0]).toBeDefined(); expect(pageload.name).toBe('Pageload'); expect(error.transaction).toBe('/index.html'); - expect(error.transaction).toBe(pageload.attributes['url.path']?.value); + expect(error.transaction).toBe(pageload.attributes[URL_PATH]?.value); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts index 6c109b66783a..a6f1d71c1a18 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts @@ -1,3 +1,19 @@ +import { + HTTP_REQUEST_REDIRECT_START, + HTTP_REQUEST_REDIRECT_END, + HTTP_REQUEST_WORKER_START, + HTTP_REQUEST_FETCH_START, + HTTP_REQUEST_DOMAIN_LOOKUP_START, + HTTP_REQUEST_DOMAIN_LOOKUP_END, + HTTP_REQUEST_CONNECT_START, + HTTP_REQUEST_SECURE_CONNECTION_START, + HTTP_REQUEST_CONNECTION_END, + HTTP_REQUEST_REQUEST_START, + HTTP_REQUEST_RESPONSE_START, + HTTP_REQUEST_RESPONSE_END, + HTTP_REQUEST_TIME_TO_FIRST_BYTE, + NETWORK_PROTOCOL_VERSION, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -38,20 +54,20 @@ sentryTest('creates fetch spans with http timing', async ({ browserName, getLoca timestamp: expect.any(Number), trace_id: tracingEvent.contexts?.trace?.trace_id, data: expect.objectContaining({ - 'http.request.redirect_start': expect.any(Number), - 'http.request.redirect_end': expect.any(Number), - 'http.request.worker_start': expect.any(Number), - 'http.request.fetch_start': expect.any(Number), - 'http.request.domain_lookup_start': expect.any(Number), - 'http.request.domain_lookup_end': expect.any(Number), - 'http.request.connect_start': expect.any(Number), - 'http.request.secure_connection_start': expect.any(Number), - 'http.request.connection_end': expect.any(Number), - 'http.request.request_start': expect.any(Number), - 'http.request.response_start': expect.any(Number), - 'http.request.response_end': expect.any(Number), - 'http.request.time_to_first_byte': expect.any(Number), - 'network.protocol.version': expect.any(String), + [HTTP_REQUEST_REDIRECT_START]: expect.any(Number), + [HTTP_REQUEST_REDIRECT_END]: expect.any(Number), + [HTTP_REQUEST_WORKER_START]: expect.any(Number), + [HTTP_REQUEST_FETCH_START]: expect.any(Number), + [HTTP_REQUEST_DOMAIN_LOOKUP_START]: expect.any(Number), + [HTTP_REQUEST_DOMAIN_LOOKUP_END]: expect.any(Number), + [HTTP_REQUEST_CONNECT_START]: expect.any(Number), + [HTTP_REQUEST_SECURE_CONNECTION_START]: expect.any(Number), + [HTTP_REQUEST_CONNECTION_END]: expect.any(Number), + [HTTP_REQUEST_REQUEST_START]: expect.any(Number), + [HTTP_REQUEST_RESPONSE_START]: expect.any(Number), + [HTTP_REQUEST_RESPONSE_END]: expect.any(Number), + [HTTP_REQUEST_TIME_TO_FIRST_BYTE]: expect.any(Number), + [NETWORK_PROTOCOL_VERSION]: expect.any(String), }), }), ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts index 162aebbd4e01..c6c30314ef18 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts @@ -1,3 +1,20 @@ +import { + URL_FULL, + HTTP_REQUEST_REDIRECT_START, + HTTP_REQUEST_REDIRECT_END, + HTTP_REQUEST_WORKER_START, + HTTP_REQUEST_FETCH_START, + HTTP_REQUEST_DOMAIN_LOOKUP_START, + HTTP_REQUEST_DOMAIN_LOOKUP_END, + HTTP_REQUEST_CONNECT_START, + HTTP_REQUEST_SECURE_CONNECTION_START, + HTTP_REQUEST_CONNECTION_END, + HTTP_REQUEST_REQUEST_START, + HTTP_REQUEST_RESPONSE_START, + HTTP_REQUEST_RESPONSE_END, + HTTP_REQUEST_TIME_TO_FIRST_BYTE, + NETWORK_PROTOCOL_VERSION, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -31,7 +48,7 @@ sentryTest( await page.evaluate(() => (window as any).Sentry.flush()); const requestSpans = spans .filter(s => getSpanOp(s) === 'http.client') - .sort((a, b) => String(a.attributes['url.full']?.value).localeCompare(String(b.attributes['url.full']?.value))); + .sort((a, b) => String(a.attributes[URL_FULL]?.value).localeCompare(String(b.attributes[URL_FULL]?.value))); expect(pageloadSpan).toBeDefined(); expect(requestSpans).toHaveLength(3); @@ -46,48 +63,48 @@ sentryTest( trace_id: pageloadSpan?.trace_id, status: 'ok', attributes: expect.objectContaining({ - 'url.full': { type: 'string', value: `http://sentry-test-site.example/${index}` }, - 'http.request.redirect_start': { + [URL_FULL]: { type: 'string', value: `http://sentry-test-site.example/${index}` }, + [HTTP_REQUEST_REDIRECT_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.redirect_end': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, - 'http.request.worker_start': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, - 'http.request.fetch_start': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, - 'http.request.domain_lookup_start': { + [HTTP_REQUEST_REDIRECT_END]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + [HTTP_REQUEST_WORKER_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + [HTTP_REQUEST_FETCH_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + [HTTP_REQUEST_DOMAIN_LOOKUP_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.domain_lookup_end': { + [HTTP_REQUEST_DOMAIN_LOOKUP_END]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.connect_start': { + [HTTP_REQUEST_CONNECT_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.secure_connection_start': { + [HTTP_REQUEST_SECURE_CONNECTION_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.connection_end': { + [HTTP_REQUEST_CONNECTION_END]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.request_start': { + [HTTP_REQUEST_REQUEST_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.response_start': { + [HTTP_REQUEST_RESPONSE_START]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'http.request.response_end': { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, - 'http.request.time_to_first_byte': { + [HTTP_REQUEST_RESPONSE_END]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number) }, + [HTTP_REQUEST_TIME_TO_FIRST_BYTE]: { type: expect.stringMatching(/^(integer|double)$/), value: expect.any(Number), }, - 'network.protocol.version': { type: 'string', value: expect.any(String) }, + [NETWORK_PROTOCOL_VERSION]: { type: 'string', value: expect.any(String) }, }), }), ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts index 2bb196c898fd..e63005bc538c 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import type { ClientReport } from '@sentry/core'; @@ -102,7 +103,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { expect(customTrace2Context?.links).toEqual([ { - attributes: { 'sentry.link.type': 'previous_trace' }, + attributes: { [SENTRY_LINK_TYPE]: 'previous_trace' }, sampled: false, span_id: expect.stringMatching(/^[\da-f]{16}$/), trace_id: expect.stringMatching(/^[\da-f]{32}$/), @@ -128,7 +129,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { expect(navigationTraceContext?.links).toEqual([ { - attributes: { 'sentry.link.type': 'previous_trace' }, + attributes: { [SENTRY_LINK_TYPE]: 'previous_trace' }, sampled: true, span_id: customTrace2Context?.span_id, trace_id: customTrace2Context?.trace_id, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts index 3ef7ddefca6d..226d4c55ebbb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts @@ -1,6 +1,6 @@ import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; -import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, extractTraceparentData, parseBaggageHeader } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { shouldSkipTracingTest, waitForTracingHeadersOnUrl } from '../../../../../../utils/helpers'; import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/spanUtils'; @@ -22,7 +22,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { const pageloadSampleRand = Number(envelope[0].trace?.sample_rand); const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(pageloadSpan.attributes['sentry.sample_rate']?.value).toBe(1); + expect(pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); expect(Number.isNaN(pageloadSampleRand)).toBe(false); expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); expect(pageloadSampleRand).toBeLessThanOrEqual(1); @@ -102,7 +102,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; - expect(pageloadSpan.attributes['sentry.sample_rate']?.value).toBe(1); + expect(pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); return { pageloadSpan, pageloadSampleRand }; }); @@ -126,8 +126,8 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { expect(fetchTraceSampleRand).toBe(pageloadSampleRand); - expect(fetchTraceSpan.attributes['sentry.sample_rate']?.value).toEqual( - pageloadSpan.attributes['sentry.sample_rate']?.value, + expect(fetchTraceSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toEqual( + pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value, ); expect(fetchTraceSpan.trace_id).not.toEqual(pageloadSpan.trace_id); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts index 2657bed50d61..bc2eb2118a09 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta/test.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; import { + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, extractTraceparentData, parseBaggageHeader, SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE, @@ -32,7 +33,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(span.attributes['sentry.sample_rate']).toBeUndefined(); + expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); return span; @@ -57,7 +58,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains expect(envelope[0].trace?.sampled).toBe('true'); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(span.attributes['sentry.sample_rate']).toBeUndefined(); + expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe(metaTagSampleRate); @@ -86,7 +87,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains expect(envelope[0].trace?.sampled).toEqual('true'); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(navSpan.attributes['sentry.sample_rate']).toBeUndefined(); + expect(navSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); // but we need to set this attribute to still be able to correctly add the sample rate to the DSC (checked above in trace header) expect(navSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe(metaTagSampleRate); @@ -115,7 +116,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains expect(Number(envelope[0].trace?.sample_rate)).toBe(metaTagSampleRate); // since the local sample rate was not applied, the sample rate attribute shouldn't be set - expect(span.attributes['sentry.sample_rate']).toBeUndefined(); + expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); expect(span.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]).toBeUndefined(); return span; @@ -140,7 +141,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains expect(fetchTraceSampleRand).toEqual(metaTagSampleRand); - expect(fetchTraceSpan.attributes['sentry.sample_rate']).toBeUndefined(); + expect(fetchTraceSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBeUndefined(); expect(fetchTraceSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_PREVIOUS_TRACE_SAMPLE_RATE]?.value).toBe( metaTagSampleRate, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts index 703f02fcbab5..031da81f9d78 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -21,7 +22,7 @@ sentryTest( const navigationRoot = await navigationSpanPromise; await page.evaluate(() => (window as any).Sentry.flush()); expect(getSpanOp(navigationRoot)).toBe('navigation'); - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === navigationRoot.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationRoot.span_id); const loafSpans = spans.filter(s => getSpanOp(s)?.startsWith('ui.long_animation_frame')); expect(loafSpans).toHaveLength(0); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts index 937662e117a4..351dffff733f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts @@ -1,4 +1,11 @@ -import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + BROWSER_SCRIPT_INVOKER, + CODE_FILE_PATH, + BROWSER_SCRIPT_SOURCE_CHAR_POSITION, + BROWSER_SCRIPT_INVOKER_TYPE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; @@ -32,7 +39,7 @@ sentryTest( expect(uiSpans?.length).toBeGreaterThanOrEqual(1); const topLevelUISpan = (uiSpans || []).find( - span => span.data?.['browser.script.invoker'] === 'https://sentry-test-site.example/path/to/script.js', + span => span.data?.[BROWSER_SCRIPT_INVOKER] === 'https://sentry-test-site.example/path/to/script.js', )!; expect(topLevelUISpan).toEqual( expect.objectContaining({ @@ -40,10 +47,10 @@ sentryTest( description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.source_char_position': 0, - 'browser.script.invoker': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.invoker_type': 'classic-script', + [CODE_FILE_PATH]: 'https://sentry-test-site.example/path/to/script.js', + [BROWSER_SCRIPT_SOURCE_CHAR_POSITION]: 0, + [BROWSER_SCRIPT_INVOKER]: 'https://sentry-test-site.example/path/to/script.js', + [BROWSER_SCRIPT_INVOKER_TYPE]: 'classic-script', [SENTRY_OP]: 'ui.long_animation_frame', [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, @@ -87,7 +94,7 @@ sentryTest( expect(uiSpans.length).toBeGreaterThanOrEqual(2); - const eventListenerUISpan = uiSpans.find(span => span.data['browser.script.invoker'] === 'BUTTON#clickme.onclick')!; + const eventListenerUISpan = uiSpans.find(span => span.data[BROWSER_SCRIPT_INVOKER] === 'BUTTON#clickme.onclick')!; expect(eventListenerUISpan).toEqual( expect.objectContaining({ @@ -95,9 +102,9 @@ sentryTest( description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { - 'browser.script.invoker': 'BUTTON#clickme.onclick', - 'browser.script.invoker_type': 'event-listener', - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', + [BROWSER_SCRIPT_INVOKER]: 'BUTTON#clickme.onclick', + [BROWSER_SCRIPT_INVOKER_TYPE]: 'event-listener', + [CODE_FILE_PATH]: 'https://sentry-test-site.example/path/to/script.js', [SENTRY_OP]: 'ui.long_animation_frame', [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts index 6c2fd0498ded..8a1cc8026aaa 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts @@ -1,4 +1,11 @@ -import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + BROWSER_SCRIPT_INVOKER, + CODE_FILE_PATH, + BROWSER_SCRIPT_SOURCE_CHAR_POSITION, + BROWSER_SCRIPT_INVOKER_TYPE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; @@ -31,7 +38,7 @@ sentryTest( expect(uiSpans.length).toBeGreaterThanOrEqual(1); const topLevelUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'https://sentry-test-site.example/path/to/script.js', + s => s.attributes[BROWSER_SCRIPT_INVOKER]?.value === 'https://sentry-test-site.example/path/to/script.js', )!; expect(topLevelUISpan).toEqual( @@ -39,13 +46,13 @@ sentryTest( name: 'Main UI thread blocked', parent_span_id: pageloadSpan.span_id, attributes: expect.objectContaining({ - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, - 'browser.script.source_char_position': expect.objectContaining({ value: 0 }), - 'browser.script.invoker': { + [CODE_FILE_PATH]: { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + [BROWSER_SCRIPT_SOURCE_CHAR_POSITION]: expect.objectContaining({ value: 0 }), + [BROWSER_SCRIPT_INVOKER]: { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js', }, - 'browser.script.invoker_type': { type: 'string', value: 'classic-script' }, + [BROWSER_SCRIPT_INVOKER_TYPE]: { type: 'string', value: 'classic-script' }, [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, }), @@ -85,7 +92,7 @@ sentryTest('captures long animation frame span for event listener.', async ({ br expect(uiSpans.length).toBeGreaterThanOrEqual(2); const eventListenerUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'BUTTON#clickme.onclick', + s => s.attributes[BROWSER_SCRIPT_INVOKER]?.value === 'BUTTON#clickme.onclick', )!; expect(eventListenerUISpan).toEqual( @@ -93,9 +100,9 @@ sentryTest('captures long animation frame span for event listener.', async ({ br name: 'Main UI thread blocked', parent_span_id: pageloadSpan.span_id, attributes: expect.objectContaining({ - 'browser.script.invoker': { type: 'string', value: 'BUTTON#clickme.onclick' }, - 'browser.script.invoker_type': { type: 'string', value: 'event-listener' }, - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + [BROWSER_SCRIPT_INVOKER]: { type: 'string', value: 'BUTTON#clickme.onclick' }, + [BROWSER_SCRIPT_INVOKER_TYPE]: { type: 'string', value: 'event-listener' }, + [CODE_FILE_PATH]: { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts index b571a7d6a306..17d5c9363eac 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts @@ -1,4 +1,11 @@ -import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + BROWSER_SCRIPT_INVOKER, + CODE_FILE_PATH, + BROWSER_SCRIPT_SOURCE_CHAR_POSITION, + BROWSER_SCRIPT_INVOKER_TYPE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; @@ -34,7 +41,7 @@ sentryTest( expect(uiSpans?.length).toBeGreaterThanOrEqual(1); const topLevelUISpan = (uiSpans || []).find( - span => span.data?.['browser.script.invoker'] === 'https://sentry-test-site.example/path/to/script.js', + span => span.data?.[BROWSER_SCRIPT_INVOKER] === 'https://sentry-test-site.example/path/to/script.js', )!; expect(topLevelUISpan).toEqual( expect.objectContaining({ @@ -42,10 +49,10 @@ sentryTest( description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.source_char_position': 0, - 'browser.script.invoker': 'https://sentry-test-site.example/path/to/script.js', - 'browser.script.invoker_type': 'classic-script', + [CODE_FILE_PATH]: 'https://sentry-test-site.example/path/to/script.js', + [BROWSER_SCRIPT_SOURCE_CHAR_POSITION]: 0, + [BROWSER_SCRIPT_INVOKER]: 'https://sentry-test-site.example/path/to/script.js', + [BROWSER_SCRIPT_INVOKER_TYPE]: 'classic-script', [SENTRY_OP]: 'ui.long_animation_frame', [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, @@ -89,7 +96,7 @@ sentryTest( expect(uiSpans.length).toBeGreaterThanOrEqual(2); - const eventListenerUISpan = uiSpans.find(span => span.data['browser.script.invoker'] === 'BUTTON#clickme.onclick')!; + const eventListenerUISpan = uiSpans.find(span => span.data[BROWSER_SCRIPT_INVOKER] === 'BUTTON#clickme.onclick')!; expect(eventListenerUISpan).toEqual( expect.objectContaining({ @@ -97,9 +104,9 @@ sentryTest( description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { - 'browser.script.invoker': 'BUTTON#clickme.onclick', - 'browser.script.invoker_type': 'event-listener', - 'code.file.path': 'https://sentry-test-site.example/path/to/script.js', + [BROWSER_SCRIPT_INVOKER]: 'BUTTON#clickme.onclick', + [BROWSER_SCRIPT_INVOKER_TYPE]: 'event-listener', + [CODE_FILE_PATH]: 'https://sentry-test-site.example/path/to/script.js', [SENTRY_OP]: 'ui.long_animation_frame', [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts index e9f1ca3720e8..192956b477d9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts @@ -1,4 +1,11 @@ -import { SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + BROWSER_SCRIPT_INVOKER, + CODE_FILE_PATH, + BROWSER_SCRIPT_SOURCE_CHAR_POSITION, + BROWSER_SCRIPT_INVOKER_TYPE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; @@ -33,7 +40,7 @@ sentryTest( expect(uiSpans.length).toBeGreaterThanOrEqual(1); const topLevelUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'https://sentry-test-site.example/path/to/script.js', + s => s.attributes[BROWSER_SCRIPT_INVOKER]?.value === 'https://sentry-test-site.example/path/to/script.js', )!; expect(topLevelUISpan).toEqual( @@ -41,13 +48,13 @@ sentryTest( name: 'Main UI thread blocked', parent_span_id: pageloadSpan.span_id, attributes: expect.objectContaining({ - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, - 'browser.script.source_char_position': expect.objectContaining({ value: 0 }), - 'browser.script.invoker': { + [CODE_FILE_PATH]: { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + [BROWSER_SCRIPT_SOURCE_CHAR_POSITION]: expect.objectContaining({ value: 0 }), + [BROWSER_SCRIPT_INVOKER]: { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js', }, - 'browser.script.invoker_type': { type: 'string', value: 'classic-script' }, + [BROWSER_SCRIPT_INVOKER_TYPE]: { type: 'string', value: 'classic-script' }, [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, }), @@ -87,7 +94,7 @@ sentryTest('captures long animation frame span for event listener.', async ({ br expect(uiSpans.length).toBeGreaterThanOrEqual(2); const eventListenerUISpan = uiSpans.find( - s => s.attributes['browser.script.invoker']?.value === 'BUTTON#clickme.onclick', + s => s.attributes[BROWSER_SCRIPT_INVOKER]?.value === 'BUTTON#clickme.onclick', )!; expect(eventListenerUISpan).toEqual( @@ -95,9 +102,9 @@ sentryTest('captures long animation frame span for event listener.', async ({ br name: 'Main UI thread blocked', parent_span_id: pageloadSpan.span_id, attributes: expect.objectContaining({ - 'browser.script.invoker': { type: 'string', value: 'BUTTON#clickme.onclick' }, - 'browser.script.invoker_type': { type: 'string', value: 'event-listener' }, - 'code.file.path': { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, + [BROWSER_SCRIPT_INVOKER]: { type: 'string', value: 'BUTTON#clickme.onclick' }, + [BROWSER_SCRIPT_INVOKER_TYPE]: { type: 'string', value: 'event-listener' }, + [CODE_FILE_PATH]: { type: 'string', value: 'https://sentry-test-site.example/path/to/script.js' }, [SENTRY_OP]: { type: 'string', value: 'ui.long_animation_frame' }, [SENTRY_ORIGIN]: { type: 'string', value: 'auto.ui.browser.metrics' }, }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts index ffa5f54c4fd6..fb458438ed43 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -21,7 +22,7 @@ sentryTest( const navigationRoot = await navigationSpanPromise; await page.evaluate(() => (window as any).Sentry.flush()); - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === navigationRoot.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationRoot.span_id); const navigationSpan = spans.find(s => getSpanOp(s) === 'navigation'); expect(navigationSpan).toBeDefined(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts index 8299abca7bb9..add1a8130425 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_OP } from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; @@ -29,7 +30,7 @@ sentryTest('captures long task.', async ({ browserName, getLocalTestUrl, page }) name: 'Main UI thread blocked', parent_span_id: pageloadSpan.span_id, attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'ui.long_task' }, + [SENTRY_OP]: { type: 'string', value: 'ui.long_task' }, }), }), ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts index b078111ff353..707a336d3df1 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-no-animation-frame/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_OP } from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; @@ -28,7 +29,7 @@ sentryTest('should capture long task.', async ({ browserName, getLocalTestUrl, p const [firstUISpan] = uiSpans; expect(firstUISpan).toEqual( expect.objectContaining({ - attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'ui.long_task' } }), + attributes: expect.objectContaining({ [SENTRY_OP]: { type: 'string', value: 'ui.long_task' } }), name: 'Main UI thread blocked', parent_span_id: pageloadSpan.span_id, }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts index 6786d28edbc5..86b7d100bb37 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/meta/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -58,7 +59,7 @@ sentryTest("creates a navigation that's not influenced by `sentry-trace` " expect(navigation.span_id).toBeDefined(); for (const root of [pageload, navigation]) { spans - .filter(span => !span.is_segment && span.attributes['sentry.segment.id']?.value === root.span_id) + .filter(span => !span.is_segment && span.attributes[SENTRY_SEGMENT_ID]?.value === root.span_id) .forEach(span => expect(span.parent_span_id).toBe(root.span_id)); } }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts index 7451fe7510d3..c62d4a2a7b3f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-aborting-pageload/test.ts @@ -1,4 +1,12 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + URL_PATH, + SENTRY_IDLE_SPAN_FINISH_REASON, + USER_AGENT_ORIGINAL, + URL_FULL, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -24,7 +32,7 @@ sentryTest('should create a navigation span that aborts an ongoing pageload', as expect(getSpanOp(pageloadSpan)).toBe('pageload'); expect(getSpanOp(navigationSpan)).toBe('navigation'); - expect(navigationSpan.attributes['sentry.segment.name.source']?.value).toEqual('url'); + expect(navigationSpan.attributes[SENTRY_SEGMENT_NAME_SOURCE]?.value).toEqual('url'); const pageloadTraceId = pageloadSpan.trace_id; const navigationTraceId = navigationSpan.trace_id; @@ -34,30 +42,30 @@ sentryTest('should create a navigation span that aborts an ongoing pageload', as expect(pageloadTraceId).not.toEqual(navigationTraceId); expect(pageloadSpan.name).toEqual('Pageload'); - expect(pageloadSpan.attributes['url.path']?.value).toEqual('/index.html'); + expect(pageloadSpan.attributes[URL_PATH]?.value).toEqual('/index.html'); expect(navigationSpan.name).toEqual('Navigation'); - expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); + expect(navigationSpan.attributes[URL_PATH]?.value).toEqual('/sub-page'); expect(pageloadSpan.attributes).toMatchObject({ [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, [SENTRY_OP]: { type: 'string', value: 'pageload' }, - ['sentry.idle_span_finish_reason']: { type: 'string', value: 'cancelled' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'cancelled' }, }); expect(navigationSpan.attributes).toMatchObject({ [SENTRY_ORIGIN]: { type: 'string', value: 'auto.navigation.browser' }, [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, [SENTRY_OP]: { type: 'string', value: 'navigation' }, - ['sentry.idle_span_finish_reason']: { type: 'string', value: 'idleTimeout' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout' }, }); expect(pageloadSpan.attributes).toMatchObject({ - 'user_agent.original': { type: 'string', value: expect.any(String) }, - 'url.full': { type: 'string', value: 'http://sentry-test.io/index.html' }, + [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/index.html' }, }); expect(navigationSpan.attributes).toMatchObject({ - 'user_agent.original': { type: 'string', value: expect.any(String) }, - 'url.full': { type: 'string', value: 'http://sentry-test.io/sub-page' }, + [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page' }, }); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts index daeb9d1a9498..75f306ef5078 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/multiple-redirects/test.ts @@ -4,6 +4,10 @@ import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; import { + SENTRY_IDLE_SPAN_FINISH_REASON, + USER_AGENT_ORIGINAL, + SENTRY_SEGMENT_ID, + SENTRY_SEGMENT_NAME, SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, URL_PATH, @@ -24,7 +28,7 @@ sentryTest( const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); const navigationSpanPromise = waitForStreamedSpan( page, - span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/next-page', + span => getSpanOp(span) === 'navigation' && span.attributes[URL_PATH]?.value === '/next-page', ); await page.goto(url); @@ -40,15 +44,15 @@ sentryTest( [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, [SENTRY_OP]: { type: 'string', value: 'pageload' }, - ['sentry.idle_span_finish_reason']: { type: 'string', value: 'cancelled' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'cancelled' }, }); expect(pageloadSpan.attributes).toMatchObject({ - 'user_agent.original': { type: 'string', value: expect.any(String) }, - 'url.full': { type: 'string', value: 'http://sentry-test.io/index.html' }, + [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/index.html' }, }); - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === pageloadSpan.span_id); const redirectSpans = spans.filter(span => getSpanOp(span) === 'navigation.redirect'); expect(redirectSpans).toHaveLength(3); @@ -57,10 +61,10 @@ sentryTest( expect(redirectSpan?.end_timestamp).toEqual(redirectSpan?.start_timestamp); expect(redirectSpan).toMatchObject({ attributes: { - 'sentry.op': { type: 'string', value: 'navigation.redirect' }, - 'sentry.origin': { type: 'string', value: 'auto.navigation.browser' }, - 'sentry.segment.id': { type: 'string', value: pageloadSpan.span_id }, - 'sentry.segment.name': { type: 'string', value: 'Pageload' }, + [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.navigation.browser' }, + [SENTRY_SEGMENT_ID]: { type: 'string', value: pageloadSpan.span_id }, + [SENTRY_SEGMENT_NAME]: { type: 'string', value: 'Pageload' }, [URL_FULL]: { type: 'string', value: expect.any(String) }, [URL_PATH]: { type: 'string', value: expect.any(String) }, }, @@ -77,12 +81,12 @@ sentryTest( expect(getSpanOp(navigationSpan)).toBe('navigation'); expect(navigationSpan.name).toEqual('Navigation'); - expect(navigationSpan.attributes['url.path']?.value).toEqual('/next-page'); + expect(navigationSpan.attributes[URL_PATH]?.value).toEqual('/next-page'); // 2 subsequent redirects belonging to the navigation root span expect( allSpans - .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id) + .filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationSpan.span_id) .filter(span => getSpanOp(span) === 'navigation.redirect'), ).toHaveLength(2); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts index f8f93d67b9c6..93e77f615917 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-click/test.ts @@ -1,3 +1,4 @@ +import { URL_PATH, SENTRY_SEGMENT_ID } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -16,11 +17,11 @@ sentryTest( const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); const navigationSpanPromise = waitForStreamedSpan( page, - span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page', + span => getSpanOp(span) === 'navigation' && span.attributes[URL_PATH]?.value === '/sub-page', ); const navigation2SpanPromise = waitForStreamedSpan( page, - span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page-2', + span => getSpanOp(span) === 'navigation' && span.attributes[URL_PATH]?.value === '/sub-page-2', ); await page.goto(url); @@ -36,20 +37,20 @@ sentryTest( expect(getSpanOp(navigationSpan)).toBe('navigation'); expect(navigationSpan.name).toEqual('Navigation'); - expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); + expect(navigationSpan.attributes[URL_PATH]?.value).toEqual('/sub-page'); const spans = allSpans - .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id) + .filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationSpan.span_id) .filter(s => getSpanOp(s) === 'navigation.redirect'); expect(spans).toHaveLength(0); expect(getSpanOp(navigation2Span)).toBe('navigation'); expect(navigation2Span.name).toEqual('Navigation'); - expect(navigation2Span.attributes['url.path']?.value).toEqual('/sub-page-2'); + expect(navigation2Span.attributes[URL_PATH]?.value).toEqual('/sub-page-2'); const spans2 = allSpans - .filter(span => span.attributes['sentry.segment.id']?.value === navigation2Span.span_id) + .filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigation2Span.span_id) .filter(s => getSpanOp(s) === 'navigation.redirect'); expect(spans2).toHaveLength(0); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts index a6e89c4d5dea..fe01834e77cb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-navigation-keypress/test.ts @@ -1,3 +1,4 @@ +import { URL_PATH, SENTRY_SEGMENT_ID } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -16,12 +17,12 @@ sentryTest( const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); const navigationSpanPromise = waitForStreamedSpan( page, - span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page', + span => getSpanOp(span) === 'navigation' && span.attributes[URL_PATH]?.value === '/sub-page', ); const navigationSpan2Promise = waitForStreamedSpan( page, - span => getSpanOp(span) === 'navigation' && span.attributes['url.path']?.value === '/sub-page-2', + span => getSpanOp(span) === 'navigation' && span.attributes[URL_PATH]?.value === '/sub-page-2', ); await page.goto(url); @@ -42,22 +43,22 @@ sentryTest( expect(getSpanOp(navigationSpan)).toBe('navigation'); expect(navigationSpan.name).toEqual('Navigation'); - expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); + expect(navigationSpan.attributes[URL_PATH]?.value).toEqual('/sub-page'); const redirectSpans = allSpans - .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id) + .filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationSpan.span_id) .filter(span => getSpanOp(span) === 'navigation.redirect'); expect(redirectSpans).toHaveLength(1); expect(redirectSpans[0].name).toBe('Navigation'); - expect(redirectSpans[0].attributes['url.path']?.value).toBe('/sub-page-redirect'); + expect(redirectSpans[0].attributes[URL_PATH]?.value).toBe('/sub-page-redirect'); expect(getSpanOp(navigationSpan2)).toBe('navigation'); expect(navigationSpan2.name).toEqual('Navigation'); - expect(navigationSpan2.attributes['url.path']?.value).toEqual('/sub-page-2'); + expect(navigationSpan2.attributes[URL_PATH]?.value).toEqual('/sub-page-2'); const redirectSpans2 = allSpans - .filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan2.span_id) + .filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationSpan2.span_id) .filter(span => getSpanOp(span) === 'navigation.redirect'); expect(redirectSpans2).toHaveLength(0); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts index 65491e7d879e..bab505425ba5 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/navigation-redirect/test.ts @@ -1,3 +1,4 @@ +import { URL_PATH, SENTRY_SEGMENT_ID, SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -28,16 +29,16 @@ sentryTest( expect(getSpanOp(navigationSpan)).toBe('navigation'); expect(navigationSpan.name).toEqual('Navigation'); - expect(navigationSpan.attributes['url.path']?.value).toEqual('/sub-page'); + expect(navigationSpan.attributes[URL_PATH]?.value).toEqual('/sub-page'); - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === navigationSpan.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === navigationSpan.span_id); expect(spans).toContainEqual( expect.objectContaining({ name: 'Navigation', attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'navigation.redirect' }, - 'url.path': { type: 'string', value: '/sub-page-redirect' }, + [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' }, + [URL_PATH]: { type: 'string', value: '/sub-page-redirect' }, }), }), ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts index d7d24d392ca7..ba2c70c2b924 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/opt-out/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -23,11 +24,11 @@ sentryTest( // Ensure a navigation span is sent, too await navigationSpanPromise; - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === pageloadSpan.span_id); expect(spans).not.toContainEqual( expect.objectContaining({ - attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), + attributes: expect.objectContaining({ [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts index d9d46d5e428a..a7e18331a79d 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-click/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -23,11 +24,11 @@ sentryTest( // Ensure a navigation span is sent, too await navigationSpanPromise; - const pageloadSpans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); + const pageloadSpans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === pageloadSpan.span_id); expect(pageloadSpans).not.toContainEqual( expect.objectContaining({ - attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), + attributes: expect.objectContaining({ [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts index fd191b3e6996..e82f4f565293 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-keypress/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -25,11 +26,11 @@ sentryTest( // Ensure a navigation span is sent, too await navigationSpanPromise; - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === pageloadSpan.span_id); expect(spans).not.toContainEqual( expect.objectContaining({ - attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), + attributes: expect.objectContaining({ [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts index be0d1ce732da..535bc77adaae 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-navigation-time/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; @@ -23,11 +24,11 @@ sentryTest( // Ensure a navigation span is sent, too await navigationSpanPromise; - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === pageloadSpan.span_id); expect(spans).not.toContainEqual( expect.objectContaining({ - attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), + attributes: expect.objectContaining({ [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' } }), }), ); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts index 9f54683d08fc..866e09aa6551 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-redirect/pageload-redirect/test.ts @@ -4,6 +4,10 @@ import { sentryTest } from '../../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../../utils/helpers'; import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; import { + SENTRY_IDLE_SPAN_FINISH_REASON, + USER_AGENT_ORIGINAL, + SENTRY_SEGMENT_ID, + SENTRY_SEGMENT_NAME, SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, URL_PATH, @@ -33,19 +37,19 @@ sentryTest('creates a pageload root span with navigation.redirect childspan', as [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, [SENTRY_OP]: { type: 'string', value: 'pageload' }, - ['sentry.idle_span_finish_reason']: { type: 'string', value: 'idleTimeout' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout' }, }); expect(pageloadSpan.attributes).toMatchObject({ - 'user_agent.original': { type: 'string', value: expect.any(String) }, - 'url.full': { type: 'string', value: 'http://sentry-test.io/index.html' }, + [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, + [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/index.html' }, }); - const spans = allSpans.filter(span => span.attributes['sentry.segment.id']?.value === pageloadSpan.span_id); + const spans = allSpans.filter(span => span.attributes[SENTRY_SEGMENT_ID]?.value === pageloadSpan.span_id); expect(spans).toContainEqual( expect.objectContaining({ - attributes: expect.objectContaining({ 'sentry.op': { type: 'string', value: 'navigation.redirect' } }), + attributes: expect.objectContaining({ [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' } }), }), ); @@ -53,10 +57,10 @@ sentryTest('creates a pageload root span with navigation.redirect childspan', as expect(redirectSpan?.end_timestamp).toEqual(redirectSpan?.start_timestamp); expect(redirectSpan).toMatchObject({ attributes: { - 'sentry.op': { type: 'string', value: 'navigation.redirect' }, - 'sentry.origin': { type: 'string', value: 'auto.navigation.browser' }, - 'sentry.segment.id': { type: 'string', value: pageloadSpan.span_id }, - 'sentry.segment.name': { type: 'string', value: 'Pageload' }, + [SENTRY_OP]: { type: 'string', value: 'navigation.redirect' }, + [SENTRY_ORIGIN]: { type: 'string', value: 'auto.navigation.browser' }, + [SENTRY_SEGMENT_ID]: { type: 'string', value: pageloadSpan.span_id }, + [SENTRY_SEGMENT_NAME]: { type: 'string', value: 'Pageload' }, [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page' }, [URL_PATH]: { type: 'string', value: '/sub-page' }, }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts index ab8a3931e132..ca3ba87cda15 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; @@ -41,14 +46,14 @@ sentryTest('should create a navigation transaction on page navigation', async ({ [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(navigationRequest.contexts?.trace?.data).toMatchObject({ [SENTRY_ORIGIN]: 'auto.navigation.browser', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(pageloadRequest.request).toEqual({ headers: { @@ -119,7 +124,7 @@ sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, pa [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(navigationRequest.request).toEqual({ headers: { @@ -139,7 +144,7 @@ sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, pa [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'navigation', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(navigationRequest2.request).toEqual({ headers: { diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts index bff9dc54a9ce..84eaf5b4943a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts @@ -1,6 +1,19 @@ import { expect } from '@playwright/test'; import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_ID, + SENTRY_IS_LOCALHOST, + CULTURE_CALENDAR, + CULTURE_LOCALE, + CULTURE_TIMEZONE, + DEVICE_PROCESSOR_COUNT, + NETWORK_CONNECTION_EFFECTIVE_TYPE, + NETWORK_CONNECTION_RTT, + SENTRY_SDK_NAME, + SENTRY_SDK_VERSION, + SENTRY_SEGMENT_NAME, + SENTRY_LINK_TYPE, SENTRY_SEGMENT_NAME_SOURCE, SENTRY_TRACE_LIFECYCLE, URL_FULL, @@ -78,32 +91,32 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow [SENTRY_ORIGIN]: { type: 'string', value: 'auto.pageload.browser' }, [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1 }, [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout' }, }); expect(pageloadSpan.span_id).not.toBe(navigationSpan.span_id); await page.evaluate(() => (window as any).Sentry.flush()); for (const root of [pageloadSpan, navigationSpan]) { collectedSpans - .filter(span => !span.is_segment && span.attributes['sentry.segment.id']?.value === root.span_id) + .filter(span => !span.is_segment && span.attributes[SENTRY_SEGMENT_ID]?.value === root.span_id) .forEach(span => expect(span.parent_span_id).toBe(root.span_id)); } expect(navigationSpan).toEqual({ attributes: { - 'sentry.is_localhost': { value: false, type: 'boolean' }, + [SENTRY_IS_LOCALHOST]: { value: false, type: 'boolean' }, [SENTRY_TRACE_LIFECYCLE]: { type: 'string', value: 'stream', }, - 'culture.calendar': { + [CULTURE_CALENDAR]: { type: 'string', value: expect.any(String), }, - 'culture.locale': { + [CULTURE_LOCALE]: { type: 'string', value: expect.any(String), }, - 'culture.timezone': { + [CULTURE_TIMEZONE]: { type: 'string', value: expect.any(String), }, @@ -119,21 +132,21 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow type: 'string', value: '/index.html', }, - 'device.processor_count': { + [DEVICE_PROCESSOR_COUNT]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, ...(browserName !== 'webkit' && { - 'network.connection.effective_type': { + [NETWORK_CONNECTION_EFFECTIVE_TYPE]: { type: 'string', value: expect.any(String), }, - 'network.connection.rtt': { + [NETWORK_CONNECTION_RTT]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, }), - 'sentry.idle_span_finish_reason': { + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout', }, @@ -149,15 +162,15 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow type: 'string', value: `${pageloadTraceId}-${pageloadSpan.span_id}-1`, }, - 'sentry.sample_rate': { + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: { type: 'integer', value: 1, }, - 'sentry.sdk.name': { + [SENTRY_SDK_NAME]: { type: 'string', value: 'sentry.javascript.browser', }, - 'sentry.sdk.version': { + [SENTRY_SDK_VERSION]: { type: 'string', value: SDK_VERSION, }, @@ -165,15 +178,15 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow type: 'array', value: expect.arrayContaining(['BrowserTracing', 'SpanStreaming']), }, - 'sentry.segment.id': { + [SENTRY_SEGMENT_ID]: { type: 'string', value: navigationSpan.span_id, }, - 'sentry.segment.name': { + [SENTRY_SEGMENT_NAME]: { type: 'string', value: 'Navigation', }, - 'sentry.segment.name.source': { + [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url', }, @@ -187,7 +200,7 @@ sentryTest('starts a streamed navigation span on page navigation', async ({ brow links: [ { attributes: { - 'sentry.link.type': { + [SENTRY_LINK_TYPE]: { type: 'string', value: 'previous_trace', }, @@ -231,7 +244,7 @@ sentryTest('handles pushState with full URL', async ({ getLocalTestUrl, page }) expect(navigationSpan1.attributes).toMatchObject({ [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page' }, [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout' }, }); expect(navigationSpan1.attributes).toMatchObject({ @@ -265,7 +278,7 @@ sentryTest('handles pushState with full URL', async ({ getLocalTestUrl, page }) expect(navigationSpan2.attributes).toMatchObject({ [URL_FULL]: { type: 'string', value: 'http://sentry-test.io/sub-page-2' }, [USER_AGENT_ORIGINAL]: { type: 'string', value: expect.any(String) }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'idleTimeout' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout' }, }); expect(navigationSpan2.attributes).toMatchObject({ @@ -289,7 +302,7 @@ sentryTest('handles pushState with full URL', async ({ getLocalTestUrl, page }) type: 'string', value: 'navigation', }, - ['sentry.idle_span_finish_reason']: { + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout', }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts index df7333b449c6..8fd42411f5a9 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-end/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -42,7 +43,7 @@ sentryTest('should call onRequestSpanEnd hook', async ({ browserName, getLocalTe expect(spans).toContainEqual( expect.objectContaining({ attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'http.client' }, + [SENTRY_OP]: { type: 'string', value: 'http.client' }, type: { type: 'string', value: 'xhr' }, 'hook.called.response-type': { type: 'string', value: 'xhr' }, }), @@ -52,7 +53,7 @@ sentryTest('should call onRequestSpanEnd hook', async ({ browserName, getLocalTe expect(spans).toContainEqual( expect.objectContaining({ attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'http.client' }, + [SENTRY_OP]: { type: 'string', value: 'http.client' }, type: { type: 'string', value: 'fetch' }, 'hook.called.response-type': { type: 'string', value: 'fetch' }, }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts index 99841247a3be..c21fa92e97a3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/on-request-span-start/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_OP } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -34,7 +35,7 @@ sentryTest('should call onRequestSpanStart hook', async ({ browserName, getLocal expect(spans).toContainEqual( expect.objectContaining({ attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'http.client' }, + [SENTRY_OP]: { type: 'string', value: 'http.client' }, 'hook.called.headers': { type: 'string', value: 'xhr' }, }), }), @@ -43,7 +44,7 @@ sentryTest('should call onRequestSpanStart hook', async ({ browserName, getLocal expect(spans).toContainEqual( expect.objectContaining({ attributes: expect.objectContaining({ - 'sentry.op': { type: 'string', value: 'http.client' }, + [SENTRY_OP]: { type: 'string', value: 'http.client' }, 'hook.called.headers': { type: 'string', value: 'fetch' }, }), }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts index 8e7c3a4b2480..bb474e4bfe60 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import type { Event } from '@sentry/core'; @@ -26,7 +31,7 @@ sentryTest('creates a pageload transaction with url as source', async ({ getLoca [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'idleTimeout', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(eventData.contexts?.trace?.op).toBe('pageload'); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts index b3ce273fc0e4..232565e32f9f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-update-txn-name/test.ts @@ -40,6 +40,6 @@ sentryTest( expect(getSpanOp(pageloadSpan)).toBe('pageload'); expect(spans.filter(span => !span.is_segment).length).toBeGreaterThan(0); - expect(pageloadSpan.attributes['sentry.segment.name.source']?.value).toEqual('custom'); + expect(pageloadSpan.attributes[SENTRY_SEGMENT_NAME_SOURCE]?.value).toEqual('custom'); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts index 8c71cebf5a93..0ee7a634717f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName/test.ts @@ -40,6 +40,6 @@ sentryTest( expect(getSpanOp(pageloadSpan)).toBe('pageload'); expect(spans.filter(span => !span.is_segment).length).toBeGreaterThan(0); - expect(pageloadSpan.attributes['sentry.segment.name.source']?.value).toEqual('custom'); + expect(pageloadSpan.attributes[SENTRY_SEGMENT_NAME_SOURCE]?.value).toEqual('custom'); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts index 0b523ee5a8fa..83e6991b114c 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload/test.ts @@ -1,6 +1,18 @@ import { expect } from '@playwright/test'; import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { + SENTRY_IS_LOCALHOST, + CULTURE_CALENDAR, + CULTURE_LOCALE, + CULTURE_TIMEZONE, + DEVICE_PROCESSOR_COUNT, + BROWSER_PERFORMANCE_NAVIGATION_ACTIVATION_START, + BROWSER_PERFORMANCE_TIME_ORIGIN, + BROWSER_WEB_VITAL_TTFB_REQUEST_TIME, + NETWORK_CONNECTION_EFFECTIVE_TYPE, + NETWORK_CONNECTION_RTT, + BROWSER_WEB_VITAL_TTFB_VALUE, + SENTRY_IDLE_SPAN_FINISH_REASON, BROWSER_NAVIGATION_TYPE, SENTRY_SEGMENT_NAME_SOURCE, SENTRY_SEGMENT_ID, @@ -76,16 +88,16 @@ sentryTest( expect(pageloadSpan).toEqual({ attributes: { - 'sentry.is_localhost': { value: false, type: 'boolean' }, - 'culture.calendar': { + [SENTRY_IS_LOCALHOST]: { value: false, type: 'boolean' }, + [CULTURE_CALENDAR]: { type: 'string', value: expect.any(String), }, - 'culture.locale': { + [CULTURE_LOCALE]: { type: 'string', value: expect.any(String), }, - 'culture.timezone': { + [CULTURE_TIMEZONE]: { type: 'string', value: expect.any(String), }, @@ -101,32 +113,32 @@ sentryTest( type: 'string', value: '/index.html', }, - 'device.processor_count': { + [DEVICE_PROCESSOR_COUNT]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'browser.performance.navigation.activation_start': { + [BROWSER_PERFORMANCE_NAVIGATION_ACTIVATION_START]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'browser.performance.time_origin': { + [BROWSER_PERFORMANCE_TIME_ORIGIN]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'browser.web_vital.ttfb.request_time': { + [BROWSER_WEB_VITAL_TTFB_REQUEST_TIME]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, ...(browserName !== 'webkit' && { - 'network.connection.effective_type': { + [NETWORK_CONNECTION_EFFECTIVE_TYPE]: { type: 'string', value: expect.any(String), }, - 'network.connection.rtt': { + [NETWORK_CONNECTION_RTT]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, - 'browser.web_vital.ttfb.value': { + [BROWSER_WEB_VITAL_TTFB_VALUE]: { type: expect.stringMatching(/^(integer)|(double)$/), value: expect.any(Number), }, @@ -135,7 +147,7 @@ sentryTest( value: 'navigate', }, }), - 'sentry.idle_span_finish_reason': { + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'idleTimeout', }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts index 40aa61ddb618..2b09200481b7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadDelayed/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_SEGMENT_NAME_SOURCE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -27,5 +28,5 @@ sentryTest('should create a pageload span when initialized delayed', async ({ ge expect(getSpanOp(pageloadSpan)).toBe('pageload'); expect(spans.filter(span => !span.is_segment).length).toBeGreaterThan(0); - expect(pageloadSpan.attributes['sentry.segment.name.source']?.value).toEqual('url'); + expect(pageloadSpan.attributes[SENTRY_SEGMENT_NAME_SOURCE]?.value).toEqual('url'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts index daca73d5f5d7..63d6106d1e40 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts @@ -1,3 +1,4 @@ +import { SENTRY_IDLE_SPAN_FINISH_REASON } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -23,7 +24,7 @@ sentryTest('should send a pageload span terminated via child span timeout', asyn const testSpan = spans.find(span => span.name === 'pageload-child-span'); expect(testSpan).toBeDefined(); expect(testSpan?.status).toBe('ok'); - expect(pageloadSpan.attributes['sentry.idle_span_finish_reason']?.value).toBe('heartbeatFailed'); + expect(pageloadSpan.attributes[SENTRY_IDLE_SPAN_FINISH_REASON]?.value).toBe('heartbeatFailed'); expect(testSpan).toMatchObject({ parent_span_id: pageloadSpan.span_id, trace_id: pageloadSpan.trace_id, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts index bca0d6052b95..a20d10297c4b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; @@ -27,7 +32,7 @@ sentryTest( [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'reportPageLoaded', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'reportPageLoaded', }); // We wait for 2.5 seconds before calling Sentry.reportPageLoaded(). The span starts at navigation start, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts index ae6ffdec21b3..8244b6f83c7a 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; @@ -27,7 +32,7 @@ sentryTest( [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'finalTimeout', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'finalTimeout', }); // We wait for 3 seconds before calling Sentry.reportPageLoaded() diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts index 9e947592a576..376c35d25a7f 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; @@ -27,7 +32,7 @@ sentryTest( [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', [SENTRY_OP]: 'pageload', - ['sentry.idle_span_finish_reason']: 'cancelled', + [SENTRY_IDLE_SPAN_FINISH_REASON]: 'cancelled', }); // ending span after 1s but adding a margin of 0.5s to account for timing weirdness in CI to avoid flakes diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts index 8ea2ea1ca842..aaef5b809ed7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/finalTimeout/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; @@ -25,7 +30,7 @@ sentryTest( [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, [SENTRY_OP]: { type: 'string', value: 'pageload' }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'finalTimeout' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'finalTimeout' }, }); // We wait for 3 seconds before calling Sentry.reportPageLoaded() diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts index db6f6737ccd6..b67276f0bcd0 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded/navigation/test.ts @@ -1,4 +1,9 @@ -import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { + SENTRY_IDLE_SPAN_FINISH_REASON, + SENTRY_SEGMENT_NAME_SOURCE, + SENTRY_OP, + SENTRY_ORIGIN, +} from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import { sentryTest } from '../../../../../utils/fixtures'; @@ -25,7 +30,7 @@ sentryTest( [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: expect.objectContaining({ value: 1 }), [SENTRY_SEGMENT_NAME_SOURCE]: { type: 'string', value: 'url' }, [SENTRY_OP]: { type: 'string', value: 'pageload' }, - 'sentry.idle_span_finish_reason': { type: 'string', value: 'cancelled' }, + [SENTRY_IDLE_SPAN_FINISH_REASON]: { type: 'string', value: 'cancelled' }, }); // ending span after 1s but adding a margin of 0.5s to account for timing weirdness in CI to avoid flakes diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts index 7ee7d37aafc1..26d8b21cc6b3 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter/test.ts @@ -1,3 +1,4 @@ +import { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; import { shouldSkipCdnBundleTest, shouldSkipTracingTest } from '../../../../utils/helpers'; @@ -28,7 +29,7 @@ sentryTest('filters spotlight interaction spans via ignoreSpans', async ({ getLo ); const regularInteractionPromise = waitForStreamedSpan( page, - span => span.attributes['browser.web_vital.inp.target']?.value === REGULAR_BUTTON, + span => span.attributes[BROWSER_WEB_VITAL_INP_TARGET]?.value === REGULAR_BUTTON, ); await page.locator('[data-test-id=regular-button]').click(); await expect(page.locator('.clicked[data-test-id=regular-button]')).toBeVisible(); @@ -38,12 +39,12 @@ sentryTest('filters spotlight interaction spans via ignoreSpans', async ({ getLo expect(getSpanOp(interaction)).toBe('ui.interaction.click'); expect(interaction.parent_span_id).toBe(regular.span_id); expect(interaction.trace_id).toBe(regular.trace_id); - expect(interaction.attributes['browser.web_vital.inp.target']?.value).not.toContain('#sentry-spotlight'); + expect(interaction.attributes[BROWSER_WEB_VITAL_INP_TARGET]?.value).not.toContain('#sentry-spotlight'); expect( spans.filter( span => getSpanOp(span) === 'ui.interaction.click' && - span.attributes['browser.web_vital.inp.target']?.value === SPOTLIGHT_BUTTON, + span.attributes[BROWSER_WEB_VITAL_INP_TARGET]?.value === SPOTLIGHT_BUTTON, ), ).toHaveLength(0); }); From 7dab78342b3725696858ecf2c18209d329a7386d Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 7 Oct 2026 15:37:41 +0200 Subject: [PATCH 5/7] test(browser): synchronize tracing tests with telemetry Co-Authored-By: GPT-6 --- .../backgroundtab-pageload-static/test.ts | 10 ++++--- .../http-timings-static/test.ts | 11 ++++---- .../consistent-sampling/meta-negative/test.ts | 22 +++++++++------ .../tracesSampler-precedence/test.ts | 8 ++---- .../tracesSampler-precedence/test.ts | 12 ++++---- .../test.ts | 28 ++++++++----------- .../test.ts | 28 ++++++++----------- .../test.ts | 21 ++++++-------- 8 files changed, 66 insertions(+), 74 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts index f788c8e372e6..626c7cbdcdbf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts @@ -1,8 +1,8 @@ +import { PAGELOAD } from '@sentry/conventions/op'; import { SENTRY_CANCELLATION_REASON } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest('should finish pageload transaction when the page goes background', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { @@ -10,12 +10,14 @@ sentryTest('should finish pageload transaction when the page goes background', a } const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + await page.goto(url); await page.locator('#go-background').click(); - const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); + const pageloadTransaction = envelopeRequestParser(await pageloadPromise); - expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); + expect(pageloadTransaction.contexts?.trace?.op).toBe(PAGELOAD); expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); expect(pageloadTransaction.contexts?.trace?.data?.[SENTRY_CANCELLATION_REASON]).toBe('document.hidden'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts index a6f1d71c1a18..bc64d2630c9b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, HTTP_CLIENT } from '@sentry/conventions/op'; import { HTTP_REQUEST_REDIRECT_START, HTTP_REQUEST_REDIRECT_END, @@ -15,9 +16,8 @@ import { NETWORK_PROTOCOL_VERSION, } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest('creates fetch spans with http timing', async ({ browserName, getLocalTestUrl, page }) => { const supportedBrowsers = ['chromium', 'firefox']; @@ -38,10 +38,11 @@ sentryTest('creates fetch spans with http timing', async ({ browserName, getLoca const url = await getLocalTestUrl({ testDir: __dirname }); - const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url, timeout: 10000 }); - const tracingEvent = envelopes[envelopes.length - 1]; // last envelope contains tracing data on all browsers + const pageloadPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + await page.goto(url); + const tracingEvent = envelopeRequestParser(await pageloadPromise); - const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); + const requestSpans = tracingEvent.spans?.filter(({ op }) => op === HTTP_CLIENT); expect(requestSpans).toHaveLength(3); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts index c8faee2f5feb..c4eae38fbd89 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts @@ -4,7 +4,8 @@ import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, - getMultipleSentryEnvelopeRequests, + envelopeUrlRegex, + getEnvelopeType, hidePage, shouldSkipTracingTest, waitForClientReportRequest, @@ -26,13 +27,17 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains const url = await getLocalTestUrl({ testDir: __dirname }); let txnsReceived = 0; - // @ts-expect-error - no need to return something valid here - getMultipleSentryEnvelopeRequests(page, 1, { envelopeType: 'transaction' }, () => { - ++txnsReceived; - return {}; + page.on('request', request => { + if (envelopeUrlRegex.test(request.url()) && getEnvelopeType(request) === 'transaction') { + ++txnsReceived; + } }); - const clientReportPromise = waitForClientReportRequest(page); + const clientReportPromise = waitForClientReportRequest(page, report => + report.discarded_events.some( + event => event.category === 'transaction' && event.reason === 'sample_rate' && event.quantity === 4, + ), + ); await sentryTest.step('Initial pageload', async () => { await page.goto(url); @@ -90,9 +95,8 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains }); }); - await sentryTest.step('Wait for transactions to be discarded', async () => { - // give it a little longer just in case a txn is pending to be sent - await page.waitForTimeout(1000); + await sentryTest.step('No transactions sent after processing completes', async () => { + expect(await page.evaluate(() => (window as any).Sentry.flush())).toBe(true); expect(txnsReceived).toEqual(0); }); }, diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts index e63005bc538c..a61043dfc109 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, NAVIGATION } from '@sentry/conventions/op'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; @@ -27,7 +28,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { const { pageloadTraceContext } = await sentryTest.step('Initial pageload', async () => { const pageloadRequestPromise = waitForTransactionRequest(page, evt => { - return evt.contexts?.trace?.op === 'pageload'; + return evt.contexts?.trace?.op === PAGELOAD; }); await page.goto(url); @@ -46,7 +47,6 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { await page.locator('#btn1').click(); - await page.waitForTimeout(500); await hidePage(page); const clientReport = envelopeRequestParser(await clientReportPromise); @@ -68,8 +68,6 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { await page.goto(`${url}#foo`); - await page.waitForTimeout(500); - await hidePage(page); const clientReport = envelopeRequestParser(await clientReportPromise); @@ -115,7 +113,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { ); await sentryTest.step('Navigation trace is sampled positively (inherited from previous trace)', async () => { - const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'navigation'); + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === NAVIGATION); await page.goto(`${url}#bar`); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts index 858449cf0efa..90e4a2c10287 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, NAVIGATION } from '@sentry/conventions/op'; import { SENTRY_LINK_TYPE } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; @@ -26,12 +27,12 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { const { pageloadSpan } = await sentryTest.step('Initial pageload', async () => { const pageloadEnvelopePromise = waitForStreamedSpanEnvelope( page, - env => !!env[1][0][1].items.find(s => getSpanOp(s) === 'pageload'), + env => !!env[1][0][1].items.find(s => getSpanOp(s) === PAGELOAD), ); await page.goto(url); const envelope = await pageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; + const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === PAGELOAD)!; expect(pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); expect(Number(envelope[0].trace?.sample_rand)).toBeGreaterThanOrEqual(0); @@ -44,7 +45,6 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { await page.locator('#btn1').click(); - await page.waitForTimeout(500); await hidePage(page); const clientReport = envelopeRequestParser(await clientReportPromise); @@ -66,8 +66,6 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { await page.goto(`${url}#foo`); - await page.waitForTimeout(500); - await hidePage(page); const clientReport = envelopeRequestParser(await clientReportPromise); @@ -122,13 +120,13 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { await sentryTest.step('Navigation trace is sampled positively (inherited from previous trace)', async () => { const navigationEnvelopePromise = waitForStreamedSpanEnvelope( page, - env => env[0].trace?.sampled === 'true' && !!env[1][0][1].items.find(s => getSpanOp(s) === 'navigation'), + env => env[0].trace?.sampled === 'true' && !!env[1][0][1].items.find(s => getSpanOp(s) === NAVIGATION), ); await page.goto(`${url}#bar`); const envelope = await navigationEnvelopePromise; - const navigationSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; + const navigationSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === NAVIGATION)!; expect(navigationSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); expect(navigationSpan.trace_id).not.toEqual(customTrace2Span.trace_id); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts index 351dffff733f..0a3f97f6a786 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, UI_LONG_ANIMATION_FRAME } from '@sentry/conventions/op'; import { BROWSER_SCRIPT_INVOKER, CODE_FILE_PATH, @@ -8,9 +9,8 @@ import { } from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest( 'should capture long animation frame for top-level script.', @@ -26,15 +26,13 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const promise = getFirstSentryEnvelopeRequest(page); + const promise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); await page.goto(url); - await new Promise(resolve => setTimeout(resolve, 1000)); + const eventData = envelopeRequestParser(await promise); - const eventData = await promise; - - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')); + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith(UI_LONG_ANIMATION_FRAME)); expect(uiSpans?.length).toBeGreaterThanOrEqual(1); @@ -43,7 +41,7 @@ sentryTest( )!; expect(topLevelUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_animation_frame', + op: UI_LONG_ANIMATION_FRAME, description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { @@ -51,7 +49,7 @@ sentryTest( [BROWSER_SCRIPT_SOURCE_CHAR_POSITION]: 0, [BROWSER_SCRIPT_INVOKER]: 'https://sentry-test-site.example/path/to/script.js', [BROWSER_SCRIPT_INVOKER_TYPE]: 'classic-script', - [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_OP]: UI_LONG_ANIMATION_FRAME, [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, }), @@ -79,18 +77,16 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const promise = getFirstSentryEnvelopeRequest(page); + const promise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); await page.goto(url); // trigger long animation frame function await page.getByRole('button').click(); - await new Promise(resolve => setTimeout(resolve, 1000)); - - const eventData = await promise; + const eventData = envelopeRequestParser(await promise); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')) || []; + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith(UI_LONG_ANIMATION_FRAME)) || []; expect(uiSpans.length).toBeGreaterThanOrEqual(2); @@ -98,14 +94,14 @@ sentryTest( expect(eventListenerUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_animation_frame', + op: UI_LONG_ANIMATION_FRAME, description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { [BROWSER_SCRIPT_INVOKER]: 'BUTTON#clickme.onclick', [BROWSER_SCRIPT_INVOKER_TYPE]: 'event-listener', [CODE_FILE_PATH]: 'https://sentry-test-site.example/path/to/script.js', - [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_OP]: UI_LONG_ANIMATION_FRAME, [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts index 17d5c9363eac..fb252c80fa30 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, UI_LONG_ANIMATION_FRAME } from '@sentry/conventions/op'; import { BROWSER_SCRIPT_INVOKER, CODE_FILE_PATH, @@ -8,9 +9,8 @@ import { } from '@sentry/conventions/attributes'; import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; -import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest( 'should capture long animation frame for top-level script.', @@ -28,15 +28,13 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const promise = getFirstSentryEnvelopeRequest(page); + const promise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); await page.goto(url); - await new Promise(resolve => setTimeout(resolve, 1000)); + const eventData = envelopeRequestParser(await promise); - const eventData = await promise; - - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')); + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith(UI_LONG_ANIMATION_FRAME)); expect(uiSpans?.length).toBeGreaterThanOrEqual(1); @@ -45,7 +43,7 @@ sentryTest( )!; expect(topLevelUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_animation_frame', + op: UI_LONG_ANIMATION_FRAME, description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { @@ -53,7 +51,7 @@ sentryTest( [BROWSER_SCRIPT_SOURCE_CHAR_POSITION]: 0, [BROWSER_SCRIPT_INVOKER]: 'https://sentry-test-site.example/path/to/script.js', [BROWSER_SCRIPT_INVOKER_TYPE]: 'classic-script', - [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_OP]: UI_LONG_ANIMATION_FRAME, [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, }), @@ -81,18 +79,16 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - const promise = getFirstSentryEnvelopeRequest(page); + const promise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); await page.goto(url); // trigger long animation frame function await page.getByRole('button').click(); - await new Promise(resolve => setTimeout(resolve, 1000)); - - const eventData = await promise; + const eventData = envelopeRequestParser(await promise); - const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long_animation_frame')) || []; + const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith(UI_LONG_ANIMATION_FRAME)) || []; expect(uiSpans.length).toBeGreaterThanOrEqual(2); @@ -100,14 +96,14 @@ sentryTest( expect(eventListenerUISpan).toEqual( expect.objectContaining({ - op: 'ui.long_animation_frame', + op: UI_LONG_ANIMATION_FRAME, description: 'Main UI thread blocked', parent_span_id: eventData.contexts?.trace?.span_id, data: { [BROWSER_SCRIPT_INVOKER]: 'BUTTON#clickme.onclick', [BROWSER_SCRIPT_INVOKER_TYPE]: 'event-listener', [CODE_FILE_PATH]: 'https://sentry-test-site.example/path/to/script.js', - [SENTRY_OP]: 'ui.long_animation_frame', + [SENTRY_OP]: UI_LONG_ANIMATION_FRAME, [SENTRY_ORIGIN]: 'auto.ui.browser.metrics', }, }), diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts index d0480062c10a..ae6640054caa 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, UI_ACTION_CLICK, UI_INTERACTION_CLICK } from '@sentry/conventions/op'; import { expect } from '@playwright/test'; import type { TransactionEvent } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; @@ -17,39 +18,35 @@ sentryTest( } const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadPromise = waitForTransactionRequest(page, txn => txn.contexts?.trace?.op === PAGELOAD); await page.goto(url); - - // Wait for the pageload transaction to complete - await waitForTransactionRequest(page); + await pageloadPromise; // Click on the spotlight element — interaction span should be filtered - const spotlightTxnPromise = waitForTransactionRequest(page, txn => txn.contexts?.trace?.op === 'ui.action.click'); + const spotlightTxnPromise = waitForTransactionRequest(page, txn => txn.contexts?.trace?.op === UI_ACTION_CLICK); await page.locator('[data-test-id=spotlight-button]').click(); await page.locator('.clicked[data-test-id=spotlight-button]').isVisible(); const spotlightTransaction = envelopeRequestParser(await spotlightTxnPromise); - expect(spotlightTransaction.contexts?.trace?.op).toBe('ui.action.click'); + expect(spotlightTransaction.contexts?.trace?.op).toBe(UI_ACTION_CLICK); - const spotlightInteractionSpans = spotlightTransaction.spans?.filter(span => span.op === 'ui.interaction.click'); + const spotlightInteractionSpans = spotlightTransaction.spans?.filter(span => span.op === UI_INTERACTION_CLICK); expect(spotlightInteractionSpans).toHaveLength(0); - // Let the first idle span fully settle before clicking again - await page.waitForTimeout(1000); - // Click on the regular button — wait specifically for a transaction that contains // a ui.interaction.click child span, since the PerformanceObserver may deliver // the event entry asynchronously const regularTxnPromise = waitForTransactionRequest( page, txn => - txn.contexts?.trace?.op === 'ui.action.click' && - (txn.spans?.some(span => span.op === 'ui.interaction.click') ?? false), + txn.contexts?.trace?.op === UI_ACTION_CLICK && + (txn.spans?.some(span => span.op === UI_INTERACTION_CLICK) ?? false), ); await page.locator('[data-test-id=regular-button]').click(); await page.locator('.clicked[data-test-id=regular-button]').isVisible(); const regularTransaction = envelopeRequestParser(await regularTxnPromise); - const regularInteractionSpans = regularTransaction.spans?.filter(span => span.op === 'ui.interaction.click'); + const regularInteractionSpans = regularTransaction.spans?.filter(span => span.op === UI_INTERACTION_CLICK); expect(regularInteractionSpans?.length).toBeGreaterThanOrEqual(1); expect(regularInteractionSpans![0]!.description).toContain('button'); expect(regularInteractionSpans![0]!.description).not.toContain('#sentry-spotlight'); From 7bb14c72b3fbb6c39b82f9b533da488db1d8b9b0 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 9 Oct 2026 09:43:50 +0200 Subject: [PATCH 6/7] code review --- .../consistent-sampling/meta-negative/test.ts | 1 + .../tracesSampler-precedence/test.ts | 6 ++- .../pageload-updateSpanName-static/init.js | 11 ++++++ .../pageload-updateSpanName-static/subject.js | 4 ++ .../pageload-updateSpanName-static/test.ts | 38 +++++++++++++++++++ .../pageloadWithChildSpanTimeout/test.ts | 2 +- 6 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts index efd4ccce4e37..34401725079c 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/meta-negative/test.ts @@ -91,6 +91,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(10); }); + await page.evaluate(() => (window as any).Sentry.flush()); expect(spansReceived).toHaveLength(0); }, ); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts index 90e4a2c10287..801f0a51d3bf 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/tracesSampler-precedence/test.ts @@ -54,11 +54,12 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { discarded_events: [ { category: 'span', - quantity: 1, + quantity: expect.any(Number), reason: 'sample_rate', }, ], }); + expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(1); }); await sentryTest.step('Subsequent navigation trace is also sampled negatively', async () => { @@ -75,11 +76,12 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { discarded_events: [ { category: 'span', - quantity: 1, + quantity: expect.any(Number), reason: 'sample_rate', }, ], }); + expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(1); }); const { customTrace2Span } = await sentryTest.step( diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/init.js new file mode 100644 index 000000000000..b74e6633eadb --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/init.js @@ -0,0 +1,11 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window._testBaseTimestamp = performance.timeOrigin / 1000; + +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-updateSpanName-static/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/subject.js new file mode 100644 index 000000000000..7f0ad0fea340 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/subject.js @@ -0,0 +1,4 @@ +const activeSpan = Sentry.getActiveSpan(); +const rootSpan = activeSpan && Sentry.getRootSpan(activeSpan); + +Sentry.updateSpanName(rootSpan, 'new name'); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/test.ts new file mode 100644 index 000000000000..ab1b1ed20c46 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-updateSpanName-static/test.ts @@ -0,0 +1,38 @@ +import { SENTRY_SEGMENT_NAME_SOURCE, SENTRY_OP, SENTRY_ORIGIN } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; +import { type Event, SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; + +sentryTest( + 'sets the source to custom when updating the transaction name with Sentry.updateSpanName', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const eventData = await getFirstSentryEnvelopeRequest(page, url); + + const traceContextData = eventData.contexts?.trace?.data; + + expect(traceContextData).toBeDefined(); + + expect(traceContextData).toMatchObject({ + [SENTRY_ORIGIN]: 'auto.pageload.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, + [SENTRY_SEGMENT_NAME_SOURCE]: 'custom', + [SENTRY_OP]: 'pageload', + }); + + expect(traceContextData![SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); + + expect(eventData.transaction).toBe('new name'); + + expect(eventData.contexts?.trace?.op).toBe('pageload'); + expect(eventData.spans?.length).toBeGreaterThan(0); + expect(eventData.transaction_info?.source).toEqual('custom'); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts index 63d6106d1e40..daf8048d6622 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageloadWithChildSpanTimeout/test.ts @@ -20,7 +20,7 @@ sentryTest('should send a pageload span terminated via child span timeout', asyn expect(getSpanOp(pageloadSpan)).toBe('pageload'); expect(pageloadSpan.attributes['sentry.idle_span_discarded_spans']).toBeUndefined(); - expect(spans.length).toBeGreaterThanOrEqual(1); + expect(spans.length).toBeGreaterThanOrEqual(2); const testSpan = spans.find(span => span.name === 'pageload-child-span'); expect(testSpan).toBeDefined(); expect(testSpan?.status).toBe('ok'); From 595871f5f2a86686b82b2c100d53745f9b5f1135 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Fri, 9 Oct 2026 11:41:45 +0200 Subject: [PATCH 7/7] test(browser): address browser tracing bot review feedback Co-Authored-By: OpenAI ChatGPT --- .../backgroundtab-pageload/test.ts | 5 +-- .../browserTracingIntegration/error/test.ts | 5 +-- .../http-timings/test.ts | 5 +-- .../consistent-sampling/default/test.ts | 1 - .../test.ts | 12 ++++--- .../navigation-static/test.ts | 35 ++++++++++--------- .../pageload-static/test.ts | 11 +++--- 7 files changed, 42 insertions(+), 32 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts index f23e41c923bf..10380d5b0e1b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD } from '@sentry/conventions/op'; import { SENTRY_CANCELLATION_REASON } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; @@ -7,13 +8,13 @@ import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest('finishes streamed pageload span when the page goes background', async ({ getLocalTestUrl, page }) => { sentryTest.skip(shouldSkipTracingTest()); const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === PAGELOAD); await page.goto(url); await page.locator('#go-background').click(); const pageloadSpan = await pageloadSpanPromise; - expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(getSpanOp(pageloadSpan)).toBe(PAGELOAD); expect(pageloadSpan.status).toBe('ok'); expect(pageloadSpan.attributes[SENTRY_CANCELLATION_REASON]?.value).toBe('document.hidden'); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts index 275aa4e7a194..07a56ecdc184 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/error/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD } from '@sentry/conventions/op'; import { URL_PATH } from '@sentry/conventions/attributes'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; @@ -17,13 +18,13 @@ sentryTest( sentryTest.skip(shouldSkipTracingTest() || browserName === 'webkit'); const url = await getLocalTestUrl({ testDir: __dirname }); const spans = collectStreamedSpans(page); - const pageloadPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const pageloadPromise = waitForStreamedSpan(page, span => getSpanOp(span) === PAGELOAD); const errorPromise = waitForErrorRequest(page); await page.goto(url); await runScriptInSandbox(page, { content: "throw new Error('Error during pageload');" }); const [pageload, errorRequest] = await Promise.all([pageloadPromise, errorPromise]); await page.evaluate(() => (window as any).Sentry.flush()); - expect(getSpanOp(pageload)).toBe('pageload'); + expect(getSpanOp(pageload)).toBe(PAGELOAD); expect(spans.filter(span => !span.is_segment)).not.toHaveLength(0); const error = envelopeRequestParser(errorRequest); expect(error.exception?.values?.[0]).toBeDefined(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts index c6c30314ef18..bb80bf6b8efb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, HTTP_CLIENT } from '@sentry/conventions/op'; import { URL_FULL, HTTP_REQUEST_REDIRECT_START, @@ -41,13 +42,13 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); const spans = collectStreamedSpans(page); - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === PAGELOAD); await page.goto(url); const pageloadSpan = await pageloadSpanPromise; await page.evaluate(() => (window as any).Sentry.flush()); const requestSpans = spans - .filter(s => getSpanOp(s) === 'http.client') + .filter(s => getSpanOp(s) === HTTP_CLIENT) .sort((a, b) => String(a.attributes[URL_FULL]?.value).localeCompare(String(b.attributes[URL_FULL]?.value))); expect(pageloadSpan).toBeDefined(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts index 226d4c55ebbb..28c0b0ea1796 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces/consistent-sampling/default/test.ts @@ -95,7 +95,6 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { const envelope = await pageloadEnvelopePromise; const pageloadSampleRand = Number(envelope[0].trace?.sample_rand); - expect(Number(envelope[0].trace?.sample_rand)).toBe(pageloadSampleRand); expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); expect(pageloadSampleRand).toBeLessThanOrEqual(1); expect(Number.isNaN(pageloadSampleRand)).toBe(false); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts index b7215249910f..ca072e14b342 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts @@ -1,7 +1,8 @@ +import { NAVIGATION } from '@sentry/conventions/op'; import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest( "doesn't capture long animation frame that starts before a navigation.", @@ -15,13 +16,16 @@ sentryTest( await page.goto(url); - const navigationTransactionEventPromise = getFirstSentryEnvelopeRequest(page); + const navigationTransactionEventPromise = waitForTransactionRequest( + page, + event => event.contexts?.trace?.op === NAVIGATION, + ); await page.locator('#clickme').click(); - const navigationTransactionEvent = await navigationTransactionEventPromise; + const navigationTransactionEvent = envelopeRequestParser(await navigationTransactionEventPromise); - expect(navigationTransactionEvent.contexts?.trace?.op).toBe('navigation'); + expect(navigationTransactionEvent.contexts?.trace?.op).toBe(NAVIGATION); const loafSpans = navigationTransactionEvent.spans?.filter(s => s.op?.startsWith('ui.long_animation_frame')); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts index ca3ba87cda15..bf3324b9809b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD, NAVIGATION } from '@sentry/conventions/op'; import { SENTRY_IDLE_SPAN_FINISH_REASON, SENTRY_SEGMENT_NAME_SOURCE, @@ -8,12 +9,7 @@ import { expect } from '@playwright/test'; import type { Event } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - envelopeRequestParser, - getFirstSentryEnvelopeRequest, - shouldSkipTracingTest, - waitForTransactionRequest, -} from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest('should create a navigation transaction on page navigation', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { @@ -22,11 +18,16 @@ sentryTest('should create a navigation transaction on page navigation', async ({ const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequest = await getFirstSentryEnvelopeRequest(page, url); - const navigationRequest = await getFirstSentryEnvelopeRequest(page, `${url}#foo`); + const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + await page.goto(url); + const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise); + + const navigationRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === NAVIGATION); + await page.goto(`${url}#foo`); + const navigationRequest = envelopeRequestParser(await navigationRequestPromise); - expect(pageloadRequest.contexts?.trace?.op).toBe('pageload'); - expect(navigationRequest.contexts?.trace?.op).toBe('navigation'); + expect(pageloadRequest.contexts?.trace?.op).toBe(PAGELOAD); + expect(navigationRequest.contexts?.trace?.op).toBe(NAVIGATION); expect(navigationRequest.transaction_info?.source).toEqual('url'); @@ -45,14 +46,14 @@ sentryTest('should create a navigation transaction on page navigation', async ({ [SENTRY_ORIGIN]: 'auto.pageload.browser', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', + [SENTRY_OP]: PAGELOAD, [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(navigationRequest.contexts?.trace?.data).toMatchObject({ [SENTRY_ORIGIN]: 'auto.navigation.browser', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', + [SENTRY_OP]: NAVIGATION, [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(pageloadRequest.request).toEqual({ @@ -100,14 +101,14 @@ sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, pa const url = await getLocalTestUrl({ testDir: __dirname }); - const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); + const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); const navigationRequestPromise = waitForTransactionRequest( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page', + event => event.contexts?.trace?.op === NAVIGATION && event.transaction === '/sub-page', ); const navigationRequestPromise2 = waitForTransactionRequest( page, - event => event.contexts?.trace?.op === 'navigation' && event.transaction === '/sub-page-2', + event => event.contexts?.trace?.op === NAVIGATION && event.transaction === '/sub-page-2', ); await page.goto(url); @@ -123,7 +124,7 @@ sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, pa [SENTRY_ORIGIN]: 'auto.navigation.browser', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', + [SENTRY_OP]: NAVIGATION, [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(navigationRequest.request).toEqual({ @@ -143,7 +144,7 @@ sentryTest('should handle pushState with full URL', async ({ getLocalTestUrl, pa [SENTRY_ORIGIN]: 'auto.navigation.browser', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'navigation', + [SENTRY_OP]: NAVIGATION, [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); expect(navigationRequest2.request).toEqual({ diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts index bb474e4bfe60..2f89771057f7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts @@ -1,3 +1,4 @@ +import { PAGELOAD } from '@sentry/conventions/op'; import { SENTRY_IDLE_SPAN_FINISH_REASON, SENTRY_SEGMENT_NAME_SOURCE, @@ -8,7 +9,7 @@ import { expect } from '@playwright/test'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/browser'; import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; sentryTest('creates a pageload transaction with url as source', async ({ getLocalTestUrl, page }) => { if (shouldSkipTracingTest()) { @@ -17,7 +18,9 @@ sentryTest('creates a pageload transaction with url as source', async ({ getLoca const url = await getLocalTestUrl({ testDir: __dirname }); - const eventData = await getFirstSentryEnvelopeRequest(page, url); + const pageloadRequestPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + await page.goto(url); + const eventData = envelopeRequestParser(await pageloadRequestPromise); const timeOrigin = await page.evaluate('window._testBaseTimestamp'); const { start_timestamp: startTimestamp } = eventData; @@ -30,11 +33,11 @@ sentryTest('creates a pageload transaction with url as source', async ({ getLoca [SENTRY_ORIGIN]: 'auto.pageload.browser', [SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]: 1, [SENTRY_SEGMENT_NAME_SOURCE]: 'url', - [SENTRY_OP]: 'pageload', + [SENTRY_OP]: PAGELOAD, [SENTRY_IDLE_SPAN_FINISH_REASON]: 'idleTimeout', }); - expect(eventData.contexts?.trace?.op).toBe('pageload'); + expect(eventData.contexts?.trace?.op).toBe(PAGELOAD); expect(eventData.spans?.length).toBeGreaterThan(0); expect(eventData.transaction_info?.source).toEqual('url'); });