Skip to content

test(browser): port requests and web vitals to span streaming - #24886

Merged
msonnb merged 7 commits into
ms/browser-tests-browser-tracingfrom
ms/browser-tests-requests-web-vitals
Oct 9, 2026
Merged

msonnb merged 7 commits into
ms/browser-tests-browser-tracingfrom
ms/browser-tests-requests-web-vitals

Conversation

@msonnb

@msonnb msonnb commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Exercise request instrumentation, web vitals, interactions, and user timing with default span streaming. Retain the existing transaction counterparts for fetch, XHR, resource timing, and TTFB as explicitly pinned *-static suites so the migration preserves coverage of both lifecycles. Check RTT as a span attribute on pageload and navigation.

Fixes #24143

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.8 kB - -
@sentry/browser - with treeshaking flags 27.92 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.84 kB - -
@sentry/browser (incl. Tracing) 51.85 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 51.87 kB - -
@sentry/browser (incl. Tracing, Profiling) 54.8 kB - -
@sentry/browser (incl. Tracing, Replay) 91.58 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 80.45 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 96.29 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 109.27 kB - -
@sentry/browser (incl. Feedback) 47.32 kB - -
@sentry/browser (incl. sendFeedback) 34.84 kB - -
@sentry/browser (incl. FeedbackAsync) 39.95 kB - -
@sentry/browser (incl. Metrics) 30.82 kB - -
@sentry/browser (incl. Logs) 31.11 kB - -
@sentry/browser (incl. Metrics & Logs) 31.75 kB - -
@sentry/react 31.63 kB - -
@sentry/react (incl. Tracing) 54.17 kB - -
@sentry/vue 37.84 kB - -
@sentry/vue (incl. Tracing) 54.78 kB - -
@sentry/svelte 29.83 kB - -
@sentry/remix (Remix 3 client bundle) 56.83 kB - -
CDN Bundle 31.53 kB - -
CDN Bundle (incl. Tracing) 52.34 kB - -
CDN Bundle (incl. Logs, Metrics) 33.72 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 54.3 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 74.65 kB - -
CDN Bundle (incl. Tracing, Replay) 90.01 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.96 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 96.17 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.15 kB - -
CDN Bundle - uncompressed 92.92 kB - -
CDN Bundle (incl. Tracing) - uncompressed 155.46 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.46 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 161.41 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.5 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 275.63 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 281.57 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 289.33 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 295.26 kB - -
@sentry/nextjs (client) 56.53 kB - -
@sentry/sveltekit (client) 52.23 kB - -
@sentry/core/server 40.86 kB - -
@sentry/core/browser 13.71 kB - -
@sentry/node 150.99 kB +0.01% +8 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 83.59 kB - -
@sentry/node - without tracing 94.01 kB +0.01% +4 B 🔺
@sentry/node - without channel injection 129.16 kB +0.02% +13 B 🔺
@sentry/aws-serverless 102.17 kB +0.01% +6 B 🔺
@sentry/cloudflare (withSentry) - minified 209.81 kB - -
@sentry/cloudflare (withSentry) 520.23 kB - -
@sentry/nextjs/cloudflare (withSentry) - minified 227.48 kB - -

View base workflow run

@msonnb
msonnb changed the base branch from ms/browser-tests-trace-semantics to ms/browser-tests-browser-tracing October 5, 2026 07:28
@msonnb
msonnb added this pull request to stack #24891 October 5, 2026 07:28
@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from a8d6d9c to 83fab7a Compare October 5, 2026 07:41
@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch 2 times, most recently from 5a08efb to dc00794 Compare October 5, 2026 13:23
@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch 2 times, most recently from b188ce7 to a064fc9 Compare October 7, 2026 11:27
@msonnb
msonnb marked this pull request as ready for review October 7, 2026 12:33
@msonnb
msonnb requested a review from a team as a code owner October 7, 2026 12:33
@msonnb
msonnb requested review from Lms24 and logaretm and removed request for a team October 7, 2026 12:33

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from a064fc9 to 3d541db Compare October 7, 2026 12:50

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from 3d541db to 0fa7920 Compare October 7, 2026 13:02

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from 0fa7920 to 136bc99 Compare October 7, 2026 13:12

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from 136bc99 to 4c8dc91 Compare October 7, 2026 13:25

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from 4c8dc91 to 52e9455 Compare October 7, 2026 13:37

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@Lms24 Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: I'd prefer keeping one transaction test that asserts on the respective web vital being added as a measurement to the pageload span.

Comment on lines -18 to -23
// A plain (non-streamed) `beforeSendSpan` operates on the v1 `SpanJSON`. INP is sent as a v2 span,
// so this verifies the static callback still runs and its changes are carried into the v2 span.
beforeSendSpan: Sentry.withStaticSpan(span => {
if (span.op === 'ui.interaction.click') {
span.description = 'scrubbed';
span.data['custom.attribute'] = 'from-before-send-span';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: I think we should keep this test as "static", since the main objective here is asserting that the INP span goes through beforeSendSpan in the v1 format when span streaming is disabled.

So once we remove transactions, this test can be dropped

Comment on lines +12 to +22
'records connection RTT on pageload and navigation spans',
async ({ getLocalTestUrl, page, browserName }) => {
sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium');
const url = await getLocalTestUrl({ testDir: __dirname });
await page.goto(url);

const pageloadRequest = envelopeRequestParser(await pageloadRequestPromise) as Event;

const navigationRequestPromise = waitForTransactionRequest(
page,
event => event.contexts?.trace?.op === 'navigation',
);
await page.goto(`${url}#foo`);

const navigationRequest = envelopeRequestParser(await navigationRequestPromise) as Event;

expect(pageloadRequest.contexts?.trace?.op).toBe('pageload');
expect(navigationRequest.contexts?.trace?.op).toBe('navigation');
const [pageload] = await waitForStreamedSpanAndTraceHeaderOnUrl(page, url);
const [navigation] = await waitForStreamedSpanAndTraceHeaderOnUrl(page, `${url}#foo`);

expect(pageloadRequest.measurements?.['connection.rtt']?.value).toBeDefined();
expect(navigationRequest.measurements?.['connection.rtt']).toBeUndefined();
expect(pageload.attributes[NETWORK_CONNECTION_RTT]).toEqual({ type: 'integer', value: 0 });
expect(navigation.attributes[NETWORK_CONNECTION_RTT]).toEqual(pageload.attributes[NETWORK_CONNECTION_RTT]);
expect(navigation.attributes[BROWSER_WEB_VITAL_FCP_VALUE]).toBeUndefined();
expect(navigation.attributes[BROWSER_WEB_VITAL_TTFB_VALUE]).toBeUndefined();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm actually not sure if sending connction.rtt on navigation spans is an SDK bug or expected behaviour now? probably worth looking into. if it's a bug, I'm also fine with merging the test as-is and fixing it in a follow-up.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (isMeasurementValue(connection.rtt)) {
if (spanStreamingEnabled) {
span.setAttribute(NETWORK_CONNECTION_RTT, connection.rtt);
} else if (spanToJSON(span).attributes[SENTRY_OP] === 'pageload') {
// Measurements are only recorded on the pageload span, matching the historical
// behavior where `connection.rtt` was only flushed for pageload transactions.
setMeasurement('connection.rtt', connection.rtt, 'millisecond');
}
}

so from this it looks like this is expected now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, then let's roll with it 😅

expect(streamSpan.end_timestamp).toBeGreaterThan(streamSpan.start_timestamp);
expect(streamSpan.parent_span_id).toBe(requestSpan.parent_span_id);
expect(streamSpan.trace_id).toBe(requestSpan.trace_id);
expect(streamSpan.end_timestamp).toBeGreaterThanOrEqual(streamSpan.start_timestamp);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: any reason this assertion got weaker?

Suggested change
expect(streamSpan.end_timestamp).toBeGreaterThanOrEqual(streamSpan.start_timestamp);
expect(streamSpan.end_timestamp).toBeGreaterThan(streamSpan.start_timestamp);

});

expect(requestSpan?.data).not.toHaveProperty('url.fragment');
expect(requestSpan?.attributes).not.toHaveProperty('url.fragment');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m: TIL that this check actually asserts that attributes.url.fragment doesn't exist, instead of attributes['url.fragment']. I had no idea 😬

Turns out the correct syntax here is

Suggested change
expect(requestSpan?.attributes).not.toHaveProperty('url.fragment');
expect(requestSpan?.attributes).not.toHaveProperty(['url.fragment']);

Would be amazing if you could go over our tests and check for this pattern. I'm pretty sure we use this more often. But of course as a follow up!

EDIT: Looks like this is "just" a playwright thing. Vitest asserts on the whole key.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oof, good catch. only one more nuxt test was affected, fixed it here: 811c410#diff-6ef19271481ee7f31290d28e413d8ff6213b9251009cc4f01c140bfddf3eb80d

@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from 0949ba8 to 811c410 Compare October 9, 2026 08:49

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

msonnb and others added 6 commits October 9, 2026 11:41
Co-Authored-By: GPT-6 <codex@openai.com>
Co-Authored-By: GPT-6 <codex@openai.com>
Co-Authored-By: OpenAI ChatGPT <codex@openai.com>
Co-Authored-By: OpenAI ChatGPT <codex@openai.com>
@msonnb
msonnb force-pushed the ms/browser-tests-requests-web-vitals branch from 2beb4b0 to a063aeb Compare October 9, 2026 09:42

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a063aeb. Configure here.

Co-Authored-By: OpenAI ChatGPT <codex@openai.com>
Comment on lines +605 to +609
if (!interactionSelector) {
// Older interactions may no longer be buffered. A requested click must be observed,
// otherwise hiding would silently lose the INP value the caller is testing.
fallback = setTimeout(done, 1000);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The hidePage function can hang indefinitely when an interactionSelector is provided, as there's no fallback timeout if the expected performance entry isn't found.
Severity: MEDIUM

Suggested Fix

Add a fallback timeout within the page.evaluate call, for example by racing the existing promise with a setTimeout. This will ensure the test fails with a clear error message instead of hanging when the performance entry is not found.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: dev-packages/browser-integration-tests/utils/helpers.ts#L605-L609

Potential issue: When `hidePage` is called with an `interactionSelector`, it waits for a
corresponding 'click' `PerformanceEventTiming` entry. However, if this entry is not
found (e.g., due to buffer eviction on a loaded CI machine), the Promise within
`page.evaluate()` never resolves. Because `page.evaluate()` lacks a built-in timeout,
the test will hang until the global test timeout is reached (e.g., 30 seconds),
resulting in a generic and unhelpful "Test timeout exceeded" error instead of a clear
failure message. This makes debugging flaky tests difficult.

@msonnb
msonnb merged commit 061f62c into develop Oct 9, 2026
120 of 122 checks passed
@msonnb
msonnb deleted the ms/browser-tests-requests-web-vitals branch October 9, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser-integration-tests: port request and web vitals suites to span streaming

3 participants