From 8fb2e6d2f9aaed38532571b98d998ebf01dbeaf1 Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Thu, 23 Jul 2026 09:12:26 -0400 Subject: [PATCH 1/3] feat(browser): Always stream INP and remove standalone v1 spans INP is now always emitted as a v2 web vital span. With span streaming enabled it rides the streaming pipeline; with streaming disabled it overrides the static trace lifecycle for itself and still streams (sent directly via `captureSpan` + `createStreamedSpanEnvelope`, since it is a single span per interaction), because it reports late and would otherwise be dropped. INP was the last user of the standalone v1 span mechanism, so that is removed entirely: the `experimental.standalone` option, the `_isStandaloneSpan` handling and self-send path in `SentrySpan`, the `createSpanEnvelope` wire format, and the `SpanEnvelope`/`SpanItem` types and their exports from `@sentry/core` and `@sentry/types`. Also dedupes the v2 `SpanContainerItem` construction shared by `createStreamedSpanEnvelope` and gen_ai span extraction into `createSpanContainerItem`. --- MIGRATION.md | 1 + .../standalone-mixed-transaction/init.js | 8 - .../standalone-mixed-transaction/subject.js | 4 - .../standalone-mixed-transaction/test.ts | 132 ------------ .../startSpan/standalone-sdk-disabled/init.js | 18 -- .../standalone-sdk-disabled/subject.js | 3 - .../startSpan/standalone-sdk-disabled/test.ts | 21 -- .../public-api/startSpan/standalone/init.js | 8 - .../startSpan/standalone/subject.js | 1 - .../public-api/startSpan/standalone/test.ts | 59 ------ .../standalone-without-baggage/init.js | 10 - .../standalone-without-baggage/subject.js | 3 - .../standalone-without-baggage/template.html | 8 - .../standalone-without-baggage/test.ts | 48 ----- .../metrics/web-vitals-inp-late/test.ts | 101 +++------ .../metrics/web-vitals-inp-navigate/test.ts | 138 ++----------- .../web-vitals-inp-parametrized-late/test.ts | 77 +------ .../web-vitals-inp-parametrized/test.ts | 75 +------ .../tracing/metrics/web-vitals-inp/test.ts | 158 +++++--------- .../react-17/tests/transactions.test.ts | 38 +--- .../react-router-6/tests/transactions.test.ts | 38 +--- .../tests/transactions.test.ts | 38 +--- .../tests/transactions.test.ts | 38 +--- packages/browser-utils/src/index.ts | 1 - .../src/metrics/browserMetrics.ts | 2 +- packages/browser-utils/src/metrics/inp.ts | 111 +--------- packages/browser-utils/src/metrics/utils.ts | 94 +-------- .../src/metrics/webVitalSpans.ts | 39 ++-- .../browser-utils/test/metrics/inpt.test.ts | 147 -------------- .../browser-utils/test/metrics/utils.test.ts | 81 -------- .../test/metrics/webVitalSpans.test.ts | 71 ++++++- .../browser/src/integrations/webVitals.ts | 12 +- .../test/integrations/webVitals.test.ts | 9 +- packages/core/src/envelope.ts | 79 +------ packages/core/src/shared-exports.ts | 5 +- packages/core/src/tracing/idleSpan.ts | 8 +- packages/core/src/tracing/index.ts | 1 + packages/core/src/tracing/sentrySpan.ts | 84 +------- packages/core/src/tracing/spans/envelope.ts | 18 +- .../src/tracing/spans/extractGenAiSpans.ts | 15 +- packages/core/src/tracing/trace.ts | 2 - packages/core/src/types/envelope.ts | 10 +- packages/core/src/types/span.ts | 9 - packages/core/src/types/startSpanOptions.ts | 18 -- packages/core/src/utils/envelope.ts | 13 -- .../test/lib/envelope-safe-timestamp.test.ts | 8 +- packages/core/test/lib/envelope.test.ts | 192 +----------------- .../core/test/lib/tracing/idleSpan.test.ts | 22 -- packages/core/test/lib/tracing/trace.test.ts | 35 ---- packages/core/test/lib/utils/envelope.test.ts | 66 ------ packages/types/src/index.ts | 6 - 51 files changed, 295 insertions(+), 1888 deletions(-) delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/test.ts delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/init.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/subject.js delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/template.html delete mode 100644 dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/test.ts delete mode 100644 packages/browser-utils/test/metrics/inpt.test.ts delete mode 100644 packages/browser-utils/test/metrics/utils.test.ts diff --git a/MIGRATION.md b/MIGRATION.md index e2d20c5f5118..fc09b95318e6 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -336,6 +336,7 @@ Sentry.init({ ### `@sentry/browser` - The experimental `_experiments.enableStandaloneClsSpans` and `_experiments.enableStandaloneLcpSpans` options were removed from both `browserTracingIntegration` and `webVitalsIntegration`. CLS and LCP are no longer configurable: they are recorded as measurements on the pageload span, unless span streaming is enabled (`traceLifecycle: 'stream'`), in which case they are sent as dedicated spans. +- INP is now always sent as a web vital span (streamed when span streaming is enabled, standalone otherwise) that carries its value as a `browser.web_vital.inp.value` attribute. Previously, with span streaming disabled, INP was sent as a standalone span that carried its value as a span measurement. ### `@sentry/node` / Server-side SDKs diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/init.js b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/init.js deleted file mode 100644 index f568455a877c..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/init.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1.0, -}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/subject.js b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/subject.js deleted file mode 100644 index e504cc75b843..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/subject.js +++ /dev/null @@ -1,4 +0,0 @@ -Sentry.startSpan({ name: 'outer' }, () => { - Sentry.startSpan({ name: 'inner' }, () => {}); - Sentry.startSpan({ name: 'standalone', experimental: { standalone: true } }, () => {}); -}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/test.ts b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/test.ts deleted file mode 100644 index e5d8285bbdb0..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-mixed-transaction/test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { expect } from '@playwright/test'; -import type { Envelope, EventEnvelope, SpanEnvelope, TransactionEvent } from '@sentry/core'; -import { sentryTest } from '../../../../utils/fixtures'; -import { - getMultipleSentryEnvelopeRequests, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; - -sentryTest( - 'sends a transaction and a span envelope if a standalone span is created as a child of an ongoing span tree', - async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - const envelopes = await getMultipleSentryEnvelopeRequests( - page, - 2, - { url, envelopeType: ['transaction', 'span'] }, - properFullEnvelopeRequestParser, - ); - - const spanEnvelope = envelopes.find(envelope => envelope[1][0][0].type === 'span') as SpanEnvelope; - const transactionEnvelope = envelopes.find(envelope => envelope[1][0][0].type === 'transaction') as EventEnvelope; - - const spanEnvelopeHeader = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - - const transactionEnvelopeHeader = transactionEnvelope[0]; - const transactionEnvelopeItem = transactionEnvelope[1][0][1] as TransactionEvent; - - const traceId = transactionEnvelopeHeader.trace!.trace_id!; - const parentSpanId = transactionEnvelopeItem.contexts?.trace?.span_id; - - expect(traceId).toMatch(/[a-f\d]{32}/); - expect(parentSpanId).toMatch(/[a-f\d]{16}/); - - expect(spanEnvelopeHeader).toEqual({ - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: traceId, - transaction: 'outer', - sample_rand: expect.any(String), - }, - }); - - expect(transactionEnvelopeHeader).toEqual({ - event_id: expect.any(String), - sdk: { - name: 'sentry.javascript.browser', - version: expect.any(String), - }, - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: traceId, - transaction: 'outer', - sample_rand: expect.any(String), - }, - }); - - expect(spanEnvelopeItem).toEqual({ - data: { - 'sentry.origin': 'manual', - }, - description: 'standalone', - segment_id: transactionEnvelopeItem.contexts?.trace?.span_id, - parent_span_id: parentSpanId, - origin: 'manual', - status: 'ok', - span_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: traceId, - }); - - expect(transactionEnvelopeItem).toEqual({ - contexts: expect.objectContaining({ - trace: { - data: { - 'sentry.origin': 'manual', - 'sentry.sample_rate': 1, - 'sentry.source': 'custom', - }, - origin: 'manual', - status: 'ok', - span_id: parentSpanId, - trace_id: traceId, - }, - }), - environment: 'production', - event_id: expect.any(String), - platform: 'javascript', - request: { - headers: expect.any(Object), - url: expect.any(String), - }, - sdk: expect.any(Object), - spans: [ - { - data: { - 'sentry.origin': 'manual', - }, - description: 'inner', - origin: 'manual', - status: 'ok', - parent_span_id: parentSpanId, - span_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: traceId, - }, - ], - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - transaction: 'outer', - transaction_info: { - source: 'custom', - }, - type: 'transaction', - }); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/init.js b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/init.js deleted file mode 100644 index b1e11c77c2c0..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/init.js +++ /dev/null @@ -1,18 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -window.fetchCallCount = 0; -window.spanEnded = false; - -const originalWindowFetch = window.fetch; -window.fetch = (...args) => { - window.fetchCallCount++; - return originalWindowFetch(...args); -}; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1.0, - enabled: false, -}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/subject.js b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/subject.js deleted file mode 100644 index 07d058f2db97..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/subject.js +++ /dev/null @@ -1,3 +0,0 @@ -Sentry.startSpan({ name: 'standalone_segment_span', experimental: { standalone: true } }, () => {}); - -window.spanEnded = true; diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/test.ts b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/test.ts deleted file mode 100644 index 0bbb6b7f32f9..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone-sdk-disabled/test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; - -sentryTest("doesn't send a standalone span envelope if SDK is disabled", async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - // @ts-expect-error this exists in the test init/subject - await page.waitForFunction(() => !!window.spanEnded); - await page.waitForTimeout(2000); - - // @ts-expect-error this exists in the test init - const fetchCallCount = await page.evaluate(() => window.fetchCallCount); - // We expect no fetch calls because the SDK is disabled - expect(fetchCallCount).toBe(0); -}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/init.js b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/init.js deleted file mode 100644 index f568455a877c..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/init.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1.0, -}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/subject.js b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/subject.js deleted file mode 100644 index 4ce33ad3b8c4..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/subject.js +++ /dev/null @@ -1 +0,0 @@ -Sentry.startSpan({ name: 'standalone_segment_span', experimental: { standalone: true } }, () => {}); diff --git a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/test.ts b/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/test.ts deleted file mode 100644 index 6f1509d64bf6..000000000000 --- a/dev-packages/browser-integration-tests/suites/public-api/startSpan/standalone/test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { expect } from '@playwright/test'; -import type { SpanEnvelope } from '@sentry/core'; -import { sentryTest } from '../../../../utils/fixtures'; -import { - getFirstSentryEnvelopeRequest, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; - -sentryTest('sends a segment span envelope', async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } - - const url = await getLocalTestUrl({ testDir: __dirname }); - const spanEnvelope = await getFirstSentryEnvelopeRequest(page, url, properFullEnvelopeRequestParser); - - const headers = spanEnvelope[0]; - const item = spanEnvelope[1][0]; - - const itemHeader = item[0]; - const spanJson = item[1]; - - const traceId = spanJson.trace_id; - - expect(headers).toEqual({ - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: traceId, - transaction: 'standalone_segment_span', - sample_rand: expect.any(String), - }, - }); - - expect(itemHeader).toEqual({ - type: 'span', - }); - - expect(spanJson).toEqual({ - data: { - 'sentry.origin': 'manual', - 'sentry.sample_rate': 1, - 'sentry.source': 'custom', - }, - description: 'standalone_segment_span', - origin: 'manual', - status: 'ok', - span_id: expect.stringMatching(/^[\da-f]{16}$/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: expect.stringMatching(/^[\da-f]{32}$/), - is_segment: true, - segment_id: spanJson.span_id, - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/init.js deleted file mode 100644 index e2dd69ede824..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/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()], - tracePropagationTargets: ['sentry-test-external.io'], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/subject.js deleted file mode 100644 index 179c0203e14c..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/subject.js +++ /dev/null @@ -1,3 +0,0 @@ -Sentry.startSpan({ name: 'standalone_span', experimental: { standalone: true } }, () => { - fetch('http://sentry-test-external.io'); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/template.html deleted file mode 100644 index f98e1c83e643..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/template.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/test.ts deleted file mode 100644 index 2700c8aa5077..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/standalone-without-baggage/test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { expect } from '@playwright/test'; -import type { SpanEnvelope } from '@sentry/core'; -import { sentryTest } from '../../../../utils/fixtures'; -import { - getMultipleSentryEnvelopeRequests, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; - -const TRACE_ID = '12345678901234567890123456789012'; -const OUTGOING_REQUEST_URL = 'http://sentry-test-external.io'; - -sentryTest( - 'omits the trace envelope header when a standalone span continues a trace without baggage', - async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.route(OUTGOING_REQUEST_URL, route => route.fulfill({ status: 200, body: 'ok' })); - const outgoingRequestPromise = page.waitForRequest(OUTGOING_REQUEST_URL); - - const [spanEnvelope] = await getMultipleSentryEnvelopeRequests( - page, - 1, - { url, envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - const outgoingRequest = await outgoingRequestPromise; - - expect(spanEnvelope[0]).toEqual({ - sent_at: expect.any(String), - }); - - // To be clear: This is _expected_ behavior, not a bug. - // SDKs must assume that an incoming `sentry-trace` but no `baggage` meta tag means that the - // trace was started from an SDK that's not yet compatible with the DSC or baggage propagation. - // The test demonstrates that the SDK as expected continues the trace but does not send a `trace` - // header, nor a baggage header. - expect(spanEnvelope[0].trace).toBeUndefined(); - - expect(spanEnvelope[1]).toHaveLength(1); - expect(spanEnvelope[1][0][1].trace_id).toBe(TRACE_ID); - - const outgoingRequestHeaders = outgoingRequest.headers(); - expect(outgoingRequestHeaders['sentry-trace']).toMatch(new RegExp(`^${TRACE_ID}-[\\da-f]{16}-1$`)); - expect(outgoingRequestHeaders['baggage']).toBeUndefined(); - }, -); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts index 745f757ce5fa..803de24237bc 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/test.ts @@ -1,90 +1,37 @@ import { expect } from '@playwright/test'; -import type { Event as SentryEvent, SpanEnvelope } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - getFirstSentryEnvelopeRequest, - getMultipleSentryEnvelopeRequests, - hidePage, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; +import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; -sentryTest('should capture an INP click event span after pageload', async ({ browserName, getLocalTestUrl, page }) => { - const supportedBrowsers = ['chromium']; +sentryTest( + 'captures an INP click as a streamed span after pageload', + async ({ browserName, getLocalTestUrl, page }) => { + const supportedBrowsers = ['chromium']; - if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { - sentryTest.skip(); - } + if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { + sentryTest.skip(); + } - const url = await getLocalTestUrl({ testDir: __dirname }); + const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - await getFirstSentryEnvelopeRequest(page); // wait for page load + const inpSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.interaction.click'); - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); + await page.goto(url); - await page.locator('[data-test-id=normal-button]').click(); - await page.locator('.clicked[data-test-id=normal-button]').isVisible(); + await page.locator('[data-test-id=normal-button]').click(); + await page.locator('.clicked[data-test-id=normal-button]').isVisible(); - await page.waitForTimeout(500); + await page.waitForTimeout(500); - // Page hide to trigger INP - await hidePage(page); + // Page hide to trigger INP + await hidePage(page); - // Get the INP span envelope - const spanEnvelope = (await spanEnvelopePromise)[0]; + const inpSpan = await inpSpanPromise; - const spanEnvelopeHeaders = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; + expect(inpSpan.attributes['sentry.op']).toEqual({ type: 'string', value: 'ui.interaction.click' }); + expect(inpSpan.name).toBe('body > NormalButton'); - const traceId = spanEnvelopeHeaders.trace!.trace_id; - expect(traceId).toMatch(/[a-f\d]{32}/); - - expect(spanEnvelopeHeaders).toEqual({ - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: traceId, - sample_rand: expect.any(String), - }, - }); - - const inpValue = spanEnvelopeItem.measurements?.inp.value; - expect(inpValue).toBeGreaterThan(0); - - expect(spanEnvelopeItem).toEqual({ - data: { - 'sentry.exclusive_time': inpValue, - 'sentry.op': 'ui.interaction.click', - 'sentry.origin': 'auto.http.browser.inp', - 'sentry.source': 'custom', - transaction: 'test-url', - 'user_agent.original': expect.stringContaining('Chrome'), - }, - measurements: { - inp: { - unit: 'millisecond', - value: inpValue, - }, - }, - description: 'body > NormalButton', - exclusive_time: inpValue, - op: 'ui.interaction.click', - origin: 'auto.http.browser.inp', - status: 'ok', - is_segment: true, - segment_id: spanEnvelopeItem.span_id, - span_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - trace_id: traceId, - }); -}); + const inpValue = inpSpan.attributes['browser.web_vital.inp.value']?.value as number; + expect(inpValue).toBeGreaterThan(0); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/test.ts index df80d55b1d6c..fc38f14c99f8 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/test.ts @@ -1,18 +1,12 @@ import { expect } from '@playwright/test'; -import type { Event as SentryEvent, SpanEnvelope } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - getFirstSentryEnvelopeRequest, - getMultipleSentryEnvelopeRequests, - hidePage, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; +import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; const supportedBrowsers = ['chromium']; sentryTest( - 'should capture INP with correct target name when navigation keeps DOM element', + 'captures INP with correct target name when navigation keeps DOM element', async ({ browserName, getLocalTestUrl, page }) => { if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { sentryTest.skip(); @@ -20,15 +14,9 @@ sentryTest( const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - await getFirstSentryEnvelopeRequest(page); // wait for page load + const inpSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.interaction.click'); - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); + await page.goto(url); // Simulating route change (keeping