Skip to content

Commit 3454549

Browse files
committed
fix(browser): Ensure standalone CLS and LCP spans have traceId of pageload span
1 parent 5524f60 commit 3454549

File tree

4 files changed

+24
-12
lines changed
  • dev-packages/browser-integration-tests/suites/tracing/metrics
    • web-vitals-cls-standalone-spans
    • web-vitals-lcp-standalone-spans
  • packages/browser-utils/src/metrics

4 files changed

+24
-12
lines changed

dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,10 @@ sentryTest(
348348
sentryTest('sends CLS of the initial page when soft-navigating to a new page', async ({ getLocalTestUrl, page }) => {
349349
const url = await getLocalTestUrl({ testDir: __dirname });
350350

351-
const eventData = await getFirstSentryEnvelopeRequest<SentryEvent>(page, url);
351+
const pageloadEventData = await getFirstSentryEnvelopeRequest<SentryEvent>(page, url);
352352

353-
expect(eventData.type).toBe('transaction');
354-
expect(eventData.contexts?.trace?.op).toBe('pageload');
353+
expect(pageloadEventData.type).toBe('transaction');
354+
expect(pageloadEventData.contexts?.trace?.op).toBe('pageload');
355355

356356
const spanEnvelopePromise = getMultipleSentryEnvelopeRequests<SpanEnvelope>(
357357
page,
@@ -364,12 +364,16 @@ sentryTest('sends CLS of the initial page when soft-navigating to a new page', a
364364

365365
await page.goto(`${url}#soft-navigation`);
366366

367+
const pageloadTraceId = pageloadEventData.contexts?.trace?.trace_id;
368+
expect(pageloadTraceId).toMatch(/[a-f0-9]{32}/);
369+
367370
const spanEnvelope = (await spanEnvelopePromise)[0];
368371
const spanEnvelopeItem = spanEnvelope[1][0][1];
369372
// Flakey value dependent on timings -> we check for a range
370373
expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0.05);
371374
expect(spanEnvelopeItem.measurements?.cls?.value).toBeLessThan(0.15);
372-
expect(spanEnvelopeItem.data?.['sentry.pageload.span_id']).toMatch(/[a-f0-9]{16}/);
375+
expect(spanEnvelopeItem.data?.['sentry.pageload.span_id']).toBe(pageloadEventData.contexts?.trace?.span_id);
376+
expect(spanEnvelopeItem.trace_id).toEqual(pageloadTraceId);
373377
});
374378

375379
sentryTest("doesn't send further CLS after the first navigation", async ({ getLocalTestUrl, page }) => {

dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-lcp-standalone-spans/test.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { expect } from '@playwright/test';
33
import type { Event as SentryEvent, EventEnvelope, SpanEnvelope } from '@sentry/core';
44
import { sentryTest } from '../../../../utils/fixtures';
55
import {
6+
envelopeRequestParser,
67
getFirstSentryEnvelopeRequest,
78
getMultipleSentryEnvelopeRequests,
89
properFullEnvelopeRequestParser,
910
shouldSkipTracingTest,
11+
waitForTransactionRequest,
1012
} from '../../../../utils/helpers';
1113

1214
sentryTest.beforeEach(async ({ browserName, page }) => {
@@ -31,6 +33,8 @@ sentryTest('captures LCP vital as a standalone span', async ({ getLocalTestUrl,
3133
properFullEnvelopeRequestParser,
3234
);
3335

36+
const pageloadEnvelopePromise = waitForTransactionRequest(page, e => e.contexts?.trace?.op === 'pageload');
37+
3438
page.route('**', route => route.continue());
3539
page.route('**/my/image.png', async (route: Route) => {
3640
return route.fulfill({
@@ -47,10 +51,14 @@ sentryTest('captures LCP vital as a standalone span', async ({ getLocalTestUrl,
4751
await hidePage(page);
4852

4953
const spanEnvelope = (await spanEnvelopePromise)[0];
54+
const pageloadTransactionEvent = envelopeRequestParser(await pageloadEnvelopePromise);
5055

5156
const spanEnvelopeHeaders = spanEnvelope[0];
5257
const spanEnvelopeItem = spanEnvelope[1][0][1];
5358

59+
const pageloadTraceId = pageloadTransactionEvent.contexts?.trace?.trace_id;
60+
expect(pageloadTraceId).toMatch(/[a-f0-9]{32}/);
61+
5462
expect(spanEnvelopeItem).toEqual({
5563
data: {
5664
'sentry.exclusive_time': 0,
@@ -80,7 +88,7 @@ sentryTest('captures LCP vital as a standalone span', async ({ getLocalTestUrl,
8088
segment_id: expect.stringMatching(/[a-f0-9]{16}/),
8189
start_timestamp: expect.any(Number),
8290
timestamp: spanEnvelopeItem.start_timestamp, // LCP is a point-in-time metric
83-
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
91+
trace_id: pageloadTraceId,
8492
});
8593

8694
// LCP value should be greater than 0
@@ -95,7 +103,6 @@ sentryTest('captures LCP vital as a standalone span', async ({ getLocalTestUrl,
95103
sampled: 'true',
96104
trace_id: spanEnvelopeItem.trace_id,
97105
sample_rand: expect.any(String),
98-
// no transaction, because span source is URL
99106
},
100107
});
101108
});
@@ -152,10 +159,10 @@ sentryTest('sends LCP of the initial page when soft-navigating to a new page', a
152159

153160
const url = await getLocalTestUrl({ testDir: __dirname });
154161

155-
const eventData = await getFirstSentryEnvelopeRequest<SentryEvent>(page, url);
162+
const pageloadEventData = await getFirstSentryEnvelopeRequest<SentryEvent>(page, url);
156163

157-
expect(eventData.type).toBe('transaction');
158-
expect(eventData.contexts?.trace?.op).toBe('pageload');
164+
expect(pageloadEventData.type).toBe('transaction');
165+
expect(pageloadEventData.contexts?.trace?.op).toBe('pageload');
159166

160167
const spanEnvelopePromise = getMultipleSentryEnvelopeRequests<SpanEnvelope>(
161168
page,
@@ -173,7 +180,8 @@ sentryTest('sends LCP of the initial page when soft-navigating to a new page', a
173180
const spanEnvelopeItem = spanEnvelope[1][0][1];
174181

175182
expect(spanEnvelopeItem.measurements?.lcp?.value).toBeGreaterThan(0);
176-
expect(spanEnvelopeItem.data?.['sentry.pageload.span_id']).toMatch(/[a-f0-9]{16}/);
183+
expect(spanEnvelopeItem.data?.['sentry.pageload.span_id']).toBe(pageloadEventData.contexts?.trace?.span_id);
184+
expect(spanEnvelopeItem.trace_id).toBe(pageloadEventData.contexts?.trace?.trace_id);
177185
});
178186

179187
sentryTest("doesn't send further LCP after the first navigation", async ({ getLocalTestUrl, page }) => {

packages/browser-utils/src/metrics/cls.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function trackClsAsStandaloneSpan(): void {
7272
return;
7373
}
7474

75-
const unsubscribeStartNavigation = client.on('startNavigationSpan', (_, options) => {
75+
const unsubscribeStartNavigation = client.on('beforeStartNavigationSpan', (_, options) => {
7676
// we only want to collect LCP if we actually navigate. Redirects should be ignored.
7777
if (!options?.isRedirect) {
7878
_collectClsOnce();

packages/browser-utils/src/metrics/lcp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function trackLcpAsStandaloneSpan(): void {
7272
return;
7373
}
7474

75-
const unsubscribeStartNavigation = client.on('startNavigationSpan', (_, options) => {
75+
const unsubscribeStartNavigation = client.on('beforeStartNavigationSpan', (_, options) => {
7676
// we only want to collect LCP if we actually navigate. Redirects should be ignored.
7777
if (!options?.isRedirect) {
7878
_collectLcpOnce();

0 commit comments

Comments
 (0)