diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/init.js deleted file mode 100644 index dce8cd2508fd..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/init.js +++ /dev/null @@ -1,16 +0,0 @@ -import * as Sentry from '@sentry/browser'; - -window.Sentry = Sentry; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ - idleTimeout: 5000, - _experiments: { - enableStandaloneClsSpans: true, - }, - }), - ], - tracesSampleRate: 1, -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/subject.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/subject.js deleted file mode 100644 index ed1b9b790bb9..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -import { simulateCLS } from '../../../../utils/web-vitals/cls.ts'; - -// Simulate Layout shift right at the beginning of the page load, depending on the URL hash -// don't run if expected CLS is NaN -const expectedCLS = Number(location.hash.slice(1)); -if (expectedCLS && expectedCLS >= 0) { - simulateCLS(expectedCLS).then(() => window.dispatchEvent(new Event('cls-done'))); -} - -// Simulate layout shift whenever the trigger-cls event is dispatched -// Cannot trigger cia a button click because expected layout shift after -// an interaction doesn't contribute to CLS. -window.addEventListener('trigger-cls', () => { - simulateCLS(0.1).then(() => { - window.dispatchEvent(new Event('cls-done')); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/template.html b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/template.html deleted file mode 100644 index 10e2e22f7d6a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -
-

Some content

- - diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/test.ts deleted file mode 100644 index fd4b3b8fa06b..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-standalone-spans/test.ts +++ /dev/null @@ -1,516 +0,0 @@ -import type { Page } from '@playwright/test'; -import { expect } from '@playwright/test'; -import type { Event as SentryEvent, EventEnvelope, SpanEnvelope } from '@sentry/core'; -import { sentryTest } from '../../../../utils/fixtures'; -import { - getFirstSentryEnvelopeRequest, - getMultipleSentryEnvelopeRequests, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; - -sentryTest.beforeEach(async ({ browserName, page }) => { - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } - - await page.setViewportSize({ width: 800, height: 1200 }); -}); - -function waitForLayoutShift(page: Page): Promise { - return page.evaluate(() => { - return new Promise(resolve => { - window.addEventListener('cls-done', () => resolve()); - }); - }); -} - -function triggerAndWaitForLayoutShift(page: Page): Promise { - return page.evaluate(() => { - window.dispatchEvent(new CustomEvent('trigger-cls')); - return new Promise(resolve => { - window.addEventListener('cls-done', () => resolve()); - }); - }); -} - -function hidePage(page: Page): Promise { - return page.evaluate(() => { - window.dispatchEvent(new Event('pagehide')); - }); -} - -sentryTest('captures a "GOOD" CLS vital with its source as a standalone span', async ({ getLocalTestUrl, page }) => { - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(`${url}#0.05`); - - await waitForLayoutShift(page); - - await hidePage(page); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - - const spanEnvelopeHeaders = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - - expect(spanEnvelopeItem).toEqual({ - data: { - 'sentry.exclusive_time': 0, - 'sentry.op': 'ui.webvital.cls', - 'sentry.origin': 'auto.http.browser.cls', - 'sentry.report_event': 'pagehide', - transaction: expect.stringContaining('index.html'), - 'user_agent.original': expect.stringContaining('Chrome'), - 'sentry.pageload.span_id': expect.stringMatching(/[a-f\d]{16}/), - 'cls.source.1': expect.stringContaining('body > div#content > p'), - }, - description: expect.stringContaining('body > div#content > p'), - exclusive_time: 0, - measurements: { - cls: { - unit: '', - value: expect.any(Number), // better check below, - }, - }, - op: 'ui.webvital.cls', - origin: 'auto.http.browser.cls', - parent_span_id: expect.stringMatching(/[a-f\d]{16}/), - span_id: expect.stringMatching(/[a-f\d]{16}/), - segment_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: spanEnvelopeItem.start_timestamp, - trace_id: expect.stringMatching(/[a-f\d]{32}/), - }); - - // Flakey value dependent on timings -> we check for a range - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0.03); - expect(spanEnvelopeItem.measurements?.cls?.value).toBeLessThan(0.07); - - expect(spanEnvelopeHeaders).toEqual({ - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: spanEnvelopeItem.trace_id, - sample_rand: expect.any(String), - // no transaction, because span source is URL - }, - }); -}); - -sentryTest('captures a "MEH" CLS vital with its source as a standalone span', async ({ getLocalTestUrl, page }) => { - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(`${url}#0.21`); - - await waitForLayoutShift(page); - - // Page hide to trigger CLS emission - await page.evaluate(() => { - window.dispatchEvent(new Event('pagehide')); - }); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - - const spanEnvelopeHeaders = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - - expect(spanEnvelopeItem).toEqual({ - data: { - 'sentry.exclusive_time': 0, - 'sentry.op': 'ui.webvital.cls', - 'sentry.origin': 'auto.http.browser.cls', - 'sentry.report_event': 'pagehide', - transaction: expect.stringContaining('index.html'), - 'user_agent.original': expect.stringContaining('Chrome'), - 'sentry.pageload.span_id': expect.stringMatching(/[a-f\d]{16}/), - 'cls.source.1': expect.stringContaining('body > div#content > p'), - }, - description: expect.stringContaining('body > div#content > p'), - exclusive_time: 0, - measurements: { - cls: { - unit: '', - value: expect.any(Number), // better check below, - }, - }, - op: 'ui.webvital.cls', - origin: 'auto.http.browser.cls', - parent_span_id: expect.stringMatching(/[a-f\d]{16}/), - span_id: expect.stringMatching(/[a-f\d]{16}/), - segment_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: spanEnvelopeItem.start_timestamp, - trace_id: expect.stringMatching(/[a-f\d]{32}/), - }); - - // Flakey value dependent on timings -> we check for a range - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0.18); - expect(spanEnvelopeItem.measurements?.cls?.value).toBeLessThan(0.23); - - expect(spanEnvelopeHeaders).toEqual({ - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: spanEnvelopeItem.trace_id, - sample_rand: expect.any(String), - // no transaction, because span source is URL - }, - }); -}); - -sentryTest('captures a "POOR" CLS vital with its source as a standalone span.', async ({ getLocalTestUrl, page }) => { - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(`${url}#0.35`); - - await waitForLayoutShift(page); - - // Page hide to trigger CLS emission - await hidePage(page); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - - const spanEnvelopeHeaders = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - - expect(spanEnvelopeItem).toEqual({ - data: { - 'sentry.exclusive_time': 0, - 'sentry.op': 'ui.webvital.cls', - 'sentry.origin': 'auto.http.browser.cls', - 'sentry.report_event': 'pagehide', - transaction: expect.stringContaining('index.html'), - 'user_agent.original': expect.stringContaining('Chrome'), - 'sentry.pageload.span_id': expect.stringMatching(/[a-f\d]{16}/), - 'cls.source.1': expect.stringContaining('body > div#content > p'), - }, - description: expect.stringContaining('body > div#content > p'), - exclusive_time: 0, - measurements: { - cls: { - unit: '', - value: expect.any(Number), // better check below, - }, - }, - op: 'ui.webvital.cls', - origin: 'auto.http.browser.cls', - parent_span_id: expect.stringMatching(/[a-f\d]{16}/), - span_id: expect.stringMatching(/[a-f\d]{16}/), - segment_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: spanEnvelopeItem.start_timestamp, - trace_id: expect.stringMatching(/[a-f\d]{32}/), - }); - - // Flakey value dependent on timings -> we check for a range - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0.33); - expect(spanEnvelopeItem.measurements?.cls?.value).toBeLessThan(0.38); - - expect(spanEnvelopeHeaders).toEqual({ - sent_at: expect.any(String), - trace: { - environment: 'production', - public_key: 'public', - sample_rate: '1', - sampled: 'true', - trace_id: spanEnvelopeItem.trace_id, - sample_rand: expect.any(String), - // no transaction, because span source is URL - }, - }); -}); - -sentryTest( - 'captures a 0 CLS vital as a standalone span if no layout shift occurred', - async ({ getLocalTestUrl, page }) => { - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - - await page.waitForTimeout(1000); - - await hidePage(page); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - - const spanEnvelopeHeaders = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - - expect(spanEnvelopeItem).toEqual({ - data: { - 'sentry.exclusive_time': 0, - 'sentry.op': 'ui.webvital.cls', - 'sentry.origin': 'auto.http.browser.cls', - 'sentry.report_event': 'pagehide', - transaction: expect.stringContaining('index.html'), - 'user_agent.original': expect.stringContaining('Chrome'), - 'sentry.pageload.span_id': expect.stringMatching(/[a-f\d]{16}/), - }, - description: 'Layout shift', - exclusive_time: 0, - measurements: { - cls: { - unit: '', - value: 0, - }, - }, - op: 'ui.webvital.cls', - origin: 'auto.http.browser.cls', - parent_span_id: expect.stringMatching(/[a-f\d]{16}/), - span_id: expect.stringMatching(/[a-f\d]{16}/), - segment_id: expect.stringMatching(/[a-f\d]{16}/), - start_timestamp: expect.any(Number), - timestamp: spanEnvelopeItem.start_timestamp, - trace_id: expect.stringMatching(/[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: spanEnvelopeItem.trace_id, - sample_rand: expect.any(String), - // no transaction, because span source is URL - }, - }); - }, -); - -sentryTest( - 'captures CLS increases after the pageload span ended, when page is hidden', - async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.type).toBe('transaction'); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - - const pageloadSpanId = eventData.contexts?.trace?.span_id; - const pageloadTraceId = eventData.contexts?.trace?.trace_id; - - expect(pageloadSpanId).toMatch(/[a-f\d]{16}/); - expect(pageloadTraceId).toMatch(/[a-f\d]{32}/); - - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - await triggerAndWaitForLayoutShift(page); - - await hidePage(page); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - // Flakey value dependent on timings -> we check for a range - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0.05); - expect(spanEnvelopeItem.measurements?.cls?.value).toBeLessThan(0.15); - - // Ensure the CLS span is connected to the pageload span and trace - expect(spanEnvelopeItem.data?.['sentry.pageload.span_id']).toBe(pageloadSpanId); - expect(spanEnvelopeItem.trace_id).toEqual(pageloadTraceId); - - expect(spanEnvelopeItem.data?.['sentry.report_event']).toBe('pagehide'); - }, -); - -sentryTest('sends CLS of the initial page when soft-navigating to a new page', async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const pageloadEventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(pageloadEventData.type).toBe('transaction'); - expect(pageloadEventData.contexts?.trace?.op).toBe('pageload'); - - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - await triggerAndWaitForLayoutShift(page); - - await page.goto(`${url}#soft-navigation`); - - const pageloadTraceId = pageloadEventData.contexts?.trace?.trace_id; - expect(pageloadTraceId).toMatch(/[a-f\d]{32}/); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - // Flakey value dependent on timings -> we check for a range - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0.05); - expect(spanEnvelopeItem.measurements?.cls?.value).toBeLessThan(0.15); - expect(spanEnvelopeItem.data?.['sentry.pageload.span_id']).toBe(pageloadEventData.contexts?.trace?.span_id); - expect(spanEnvelopeItem.trace_id).toEqual(pageloadTraceId); - - expect(spanEnvelopeItem.data?.['sentry.report_event']).toBe('navigation'); -}); - -sentryTest("doesn't send further CLS after the first navigation", async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.type).toBe('transaction'); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - await triggerAndWaitForLayoutShift(page); - - await page.goto(`${url}#soft-navigation`); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0); - expect(spanEnvelopeItem.data?.['sentry.report_event']).toBe('navigation'); - - getMultipleSentryEnvelopeRequests(page, 1, { envelopeType: 'span' }, () => { - throw new Error('Unexpected span - This should not happen!'); - }); - - const navigationTxnPromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'transaction' }, - properFullEnvelopeRequestParser, - ); - - // activate both CLS emission triggers: - await page.goto(`${url}#soft-navigation-2`); - await hidePage(page); - - // assumption: If we would send another CLS span on the 2nd navigation, it would be sent before the navigation - // transaction ends. This isn't 100% safe to ensure we don't send something but otherwise we'd need to wait for - // a timeout or something similar. - await navigationTxnPromise; -}); - -sentryTest("doesn't send further CLS after the first page hide", async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.type).toBe('transaction'); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - await triggerAndWaitForLayoutShift(page); - - await hidePage(page); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - expect(spanEnvelopeItem.measurements?.cls?.value).toBeGreaterThan(0); - expect(spanEnvelopeItem.data?.['sentry.report_event']).toBe('pagehide'); - - getMultipleSentryEnvelopeRequests(page, 1, { envelopeType: 'span' }, () => { - throw new Error('Unexpected span - This should not happen!'); - }); - - const navigationTxnPromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'transaction' }, - properFullEnvelopeRequestParser, - ); - - // activate both CLS emission triggers: - await page.goto(`${url}#soft-navigation-2`); - await hidePage(page); - - // assumption: If we would send another CLS span on the 2nd navigation, it would be sent before the navigation - // transaction ends. This isn't 100% safe to ensure we don't send something but otherwise we'd need to wait for - // a timeout or something similar. - await navigationTxnPromise; -}); - -sentryTest('CLS span timestamps are set correctly', async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const eventData = await getFirstSentryEnvelopeRequest(page, url); - - expect(eventData.type).toBe('transaction'); - expect(eventData.contexts?.trace?.op).toBe('pageload'); - expect(eventData.timestamp).toBeDefined(); - - const pageloadEndTimestamp = eventData.timestamp!; - - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); - - await triggerAndWaitForLayoutShift(page); - - await hidePage(page); - - const spanEnvelope = (await spanEnvelopePromise)[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; - - expect(spanEnvelopeItem.start_timestamp).toBeDefined(); - expect(spanEnvelopeItem.timestamp).toBeDefined(); - - const clsSpanStartTimestamp = spanEnvelopeItem.start_timestamp!; - const clsSpanEndTimestamp = spanEnvelopeItem.timestamp!; - - // CLS performance entries have no duration ==> start and end timestamp should be the same - expect(clsSpanStartTimestamp).toEqual(clsSpanEndTimestamp); - - // We don't really care that they are very close together but rather about the order of magnitude - // Previously, we had a bug where the timestamps would be significantly off (by multiple hours) - // so we only ensure that this bug is fixed. 60 seconds should be more than enough. - expect(clsSpanStartTimestamp - pageloadEndTimestamp).toBeLessThan(60); - expect(clsSpanStartTimestamp).toBeGreaterThan(pageloadEndTimestamp); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/init.js deleted file mode 100644 index bd3b6ed17872..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/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/metrics/web-vitals-cls-streamed-spans/subject.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/subject.js deleted file mode 100644 index 9742a4a5cc29..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/subject.js +++ /dev/null @@ -1,17 +0,0 @@ -import { simulateCLS } from '../../../../utils/web-vitals/cls.ts'; - -// Simulate Layout shift right at the beginning of the page load, depending on the URL hash -// don't run if expected CLS is NaN -const expectedCLS = Number(location.hash.slice(1)); -if (expectedCLS && expectedCLS >= 0) { - simulateCLS(expectedCLS).then(() => window.dispatchEvent(new Event('cls-done'))); -} - -// Simulate layout shift whenever the trigger-cls event is dispatched -// Cannot trigger via a button click because expected layout shift after -// an interaction doesn't contribute to CLS. -window.addEventListener('trigger-cls', () => { - simulateCLS(0.1).then(() => { - window.dispatchEvent(new Event('cls-done')); - }); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/template.html b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/template.html deleted file mode 100644 index 10e2e22f7d6a..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - -
-

Some content

- - diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts deleted file mode 100644 index 409d79327a91..000000000000 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { Page } from '@playwright/test'; -import { expect } from '@playwright/test'; -import { sentryTest } from '../../../../utils/fixtures'; -import { hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; - -sentryTest.beforeEach(async ({ browserName, page }) => { - if (shouldSkipTracingTest() || browserName !== 'chromium') { - sentryTest.skip(); - } - - await page.setViewportSize({ width: 800, height: 1200 }); -}); - -function waitForLayoutShift(page: Page): Promise { - return page.evaluate(() => { - return new Promise(resolve => { - window.addEventListener('cls-done', () => resolve()); - }); - }); -} - -sentryTest('captures CLS as a streamed span with source attributes', async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const clsSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.webvital.cls'); - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); - - await page.goto(`${url}#0.15`); - await waitForLayoutShift(page); - await hidePage(page); - - const clsSpan = await clsSpanPromise; - const pageloadSpan = await pageloadSpanPromise; - - expect(clsSpan.attributes?.['sentry.op']).toEqual({ type: 'string', value: 'ui.webvital.cls' }); - expect(clsSpan.attributes?.['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.browser.cls' }); - expect(clsSpan.attributes?.['sentry.exclusive_time']).toEqual({ type: 'integer', value: 0 }); - expect(clsSpan.attributes?.['user_agent.original']?.value).toEqual(expect.stringContaining('Chrome')); - - // Check browser.web_vital.cls.source attributes - expect(clsSpan.attributes?.['browser.web_vital.cls.source.1']?.value).toEqual( - expect.stringContaining('body > div#content > p'), - ); - - // Check pageload span id is present - expect(clsSpan.attributes?.['sentry.pageload.span_id']?.value).toBe(pageloadSpan.span_id); - - // CLS is a point-in-time metric - expect(clsSpan.start_timestamp).toEqual(clsSpan.end_timestamp); - - expect(clsSpan.span_id).toMatch(/^[\da-f]{16}$/); - expect(clsSpan.trace_id).toMatch(/^[\da-f]{32}$/); - - expect(clsSpan.parent_span_id).toBe(pageloadSpan.span_id); - expect(clsSpan.trace_id).toBe(pageloadSpan.trace_id); -}); - -sentryTest('CLS streamed span has web vital value attribute', async ({ getLocalTestUrl, page }) => { - const url = await getLocalTestUrl({ testDir: __dirname }); - - const clsSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.webvital.cls'); - - await page.goto(`${url}#0.1`); - await waitForLayoutShift(page); - await hidePage(page); - - const clsSpan = await clsSpanPromise; - - // The CLS value should be set as a browser.web_vital.cls.value attribute - expect(clsSpan.attributes?.['browser.web_vital.cls.value']?.type).toBe('double'); - // Flakey value dependent on timings -> we check for a range - const clsValue = clsSpan.attributes?.['browser.web_vital.cls.value']?.value as number; - expect(clsValue).toBeGreaterThan(0.05); - expect(clsValue).toBeLessThan(0.15); -}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/init.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/init.js index 9a26371a9461..493d3c675de7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-late/init.js @@ -6,21 +6,13 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ + idleTimeout: 1000, enableLongTask: false, enableInp: true, - instrumentPageLoad: false, - instrumentNavigation: false, + beforeStartSpan: options => ({ ...options, name: 'test-url' }), }), + Sentry.spanStreamingIntegration(), ], + traceLifecycle: 'stream', tracesSampleRate: 1, }); - -const client = Sentry.getClient(); - -// Force page load transaction name to a testable value -Sentry.startBrowserTracingPageLoadSpan(client, { - name: 'test-url', - attributes: { - [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - }, -}); 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 a882c06c1e11..1c56134f8248 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,89 +1,36 @@ import { expect } from '@playwright/test'; -import type { Event as SentryEvent, SpanEnvelope } from '@sentry/core'; +import type { Event as SentryEvent } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - getFirstSentryEnvelopeRequest, - getMultipleSentryEnvelopeRequests, - hidePage, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; +import { getFirstSentryEnvelopeRequest, 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']; - - if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { + if (shouldSkipTracingTest() || browserName !== 'chromium') { sentryTest.skip(); } const url = await getLocalTestUrl({ testDir: __dirname }); await page.goto(url); - await getFirstSentryEnvelopeRequest(page); // wait for page load + await getFirstSentryEnvelopeRequest(page); - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); + const inpSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.interaction.click'); await page.locator('[data-test-id=normal-button]').click(); await page.locator('.clicked[data-test-id=normal-button]').isVisible(); await page.waitForTimeout(500); - - // Page hide to trigger INP await hidePage(page); - // Get the INP span envelope - const spanEnvelope = (await spanEnvelopePromise)[0]; - - const spanEnvelopeHeaders = spanEnvelope[0]; - const spanEnvelopeItem = spanEnvelope[1][0][1]; + const inpSpan = await inpSpanPromise; - const traceId = spanEnvelopeHeaders.trace!.trace_id; - expect(traceId).toMatch(/[a-f\d]{32}/); + expect(inpSpan.name).toBe('body > NormalButton'); + expect(inpSpan.attributes?.['sentry.op']).toEqual({ type: 'string', value: 'ui.interaction.click' }); + expect(inpSpan.attributes?.['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.browser.inp' }); + expect(inpSpan.attributes?.['sentry.transaction']?.value).toBe('test-url'); + expect(inpSpan.attributes?.['user_agent.original']?.value).toEqual(expect.stringContaining('Chrome')); - 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; + const inpValue = inpSpan.attributes?.['browser.web_vital.inp.value']?.value as number; 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', - 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, - }); + expect(inpSpan.attributes?.['sentry.exclusive_time']?.value).toBeGreaterThan(0); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/init.js b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/init.js index 1044a4b68bda..493d3c675de7 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-navigate/init.js @@ -9,19 +9,10 @@ Sentry.init({ idleTimeout: 1000, enableLongTask: false, enableInp: true, - instrumentPageLoad: false, - instrumentNavigation: false, + beforeStartSpan: options => ({ ...options, name: 'test-url' }), }), + Sentry.spanStreamingIntegration(), ], + traceLifecycle: 'stream', tracesSampleRate: 1, }); - -const client = Sentry.getClient(); - -// Force page load transaction name to a testable value -Sentry.startBrowserTracingPageLoadSpan(client, { - name: 'test-url', - attributes: { - [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - }, -}); 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 d1cc7cce020d..729f9830cd9d 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,174 +1,69 @@ import { expect } from '@playwright/test'; -import type { Event as SentryEvent, SpanEnvelope } from '@sentry/core'; +import type { Event as SentryEvent } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { - getFirstSentryEnvelopeRequest, - getMultipleSentryEnvelopeRequests, - hidePage, - properFullEnvelopeRequestParser, - shouldSkipTracingTest, -} from '../../../../utils/helpers'; - -const supportedBrowsers = ['chromium']; +import { getFirstSentryEnvelopeRequest, hidePage, shouldSkipTracingTest } from '../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( 'should capture INP with correct target name when navigation keeps DOM element', async ({ browserName, getLocalTestUrl, page }) => { - if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { + if (shouldSkipTracingTest() || browserName !== 'chromium') { sentryTest.skip(); } const url = await getLocalTestUrl({ testDir: __dirname }); await page.goto(url); - await getFirstSentryEnvelopeRequest(page); // wait for page load + await getFirstSentryEnvelopeRequest(page); - const spanEnvelopePromise = getMultipleSentryEnvelopeRequests( - page, - 1, - { envelopeType: 'span' }, - properFullEnvelopeRequestParser, - ); + const inpSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.interaction.click'); - // Simulating route change (keeping