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..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,9 +1,10 @@ +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'; 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(); } @@ -32,6 +33,6 @@ sentryTest('should finish a custom transaction when the page goes background', a 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/navigation-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/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-static/init.js index a93fc742bafb..bb6d4918d1bf 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-static/init.js @@ -3,7 +3,8 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', 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-streamed/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/subject.js 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/template.html 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..626c7cbdcdbf --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-static/test.ts @@ -0,0 +1,23 @@ +import { PAGELOAD } from '@sentry/conventions/op'; +import { SENTRY_CANCELLATION_REASON } from '@sentry/conventions/attributes'; +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } 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 }); + + const pageloadPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + + await page.goto(url); + await page.locator('#go-background').click(); + + const pageloadTransaction = envelopeRequestParser(await pageloadPromise); + + 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/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/linked-traces-streamed/session-storage/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/init.js similarity index 53% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/session-storage/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload/init.js index e51af56c2a9d..83076460599f 100644 --- 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/backgroundtab-pageload/init.js @@ -4,9 +4,6 @@ window.Sentry = Sentry; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [ - Sentry.browserTracingIntegration({ linkPreviousTrace: 'session-storage' }), - 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..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,20 +1,20 @@ +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 { 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..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,47 +1,35 @@ +import { PAGELOAD } from '@sentry/conventions/op'; +import { URL_PATH } from '@sentry/conventions/attributes'; 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-static/init.js similarity index 78% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/init.js index 8ec045483e0c..5e521633ad12 100644 --- 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-static/init.js @@ -3,6 +3,7 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ @@ -10,9 +11,6 @@ Sentry.init({ 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-static/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-streamed/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/subject.js 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..bc64d2630c9b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/http-timings-static/test.ts @@ -0,0 +1,75 @@ +import { PAGELOAD, HTTP_CLIENT } from '@sentry/conventions/op'; +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 { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } 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 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); + + expect(requestSpans).toHaveLength(3); + + 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/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..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,59 +1,113 @@ +import { PAGELOAD, HTTP_CLIENT } from '@sentry/conventions/op'; +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 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-static/consistent-sampling/default/init.js similarity index 93% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/init.js index 63afee65329a..04a299421004 100644 --- 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-static/consistent-sampling/default/init.js @@ -3,13 +3,13 @@ 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, }), - Sentry.spanStreamingIntegration(), ], tracePropagationTargets: ['sentry-test-external.io'], tracesSampler: ctx => { 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-static/consistent-sampling/default/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/subject.js 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-static/consistent-sampling/default/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/default/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/default/template.html 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-streamed/consistent-sampling/meta-negative/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/init.js similarity index 85% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/init.js index d570ac45144c..05c4ec8db85a 100644 --- 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-static/consistent-sampling/meta-negative/init.js @@ -3,15 +3,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({ linkPreviousTrace: 'in-memory', consistentTraceSampling: true, }), - Sentry.spanStreamingIntegration(), ], - traceLifecycle: 'stream', tracePropagationTargets: ['sentry-test-external.io'], tracesSampleRate: 1, debug: 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-static/consistent-sampling/meta-negative/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/subject.js 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-static/consistent-sampling/meta-negative/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/template.html 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-static/consistent-sampling/meta-negative/test.ts similarity index 73% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-negative/test.ts rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-negative/test.ts index efd4ccce4e37..c4eae38fbd89 100644 --- 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-static/consistent-sampling/meta-negative/test.ts @@ -1,15 +1,16 @@ import { expect } from '@playwright/test'; -import type { ClientReport, SerializedStreamedSpan } from '@sentry/core'; +import type { ClientReport } from '@sentry/core'; import { extractTraceparentData, parseBaggageHeader } from '@sentry/core'; import { sentryTest } from '../../../../../../utils/fixtures'; import { envelopeRequestParser, + envelopeUrlRegex, + getEnvelopeType, hidePage, shouldSkipTracingTest, waitForClientReportRequest, waitForTracingHeadersOnUrl, } from '../../../../../../utils/helpers'; -import { observeStreamedSpan } from '../../../../../../utils/spanUtils'; const metaTagSampleRand = 0.9; const metaTagSampleRate = 0.2; @@ -19,31 +20,38 @@ 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 }) => { - sentryTest.skip(shouldSkipTracingTest()); + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } const url = await getLocalTestUrl({ testDir: __dirname }); - const spansReceived: SerializedStreamedSpan[] = []; - observeStreamedSpan(page, span => { - spansReceived.push(span); - return false; + let txnsReceived = 0; + 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); - expect(spansReceived).toHaveLength(0); + expect(txnsReceived).toEqual(0); }); await sentryTest.step('Custom instrumented button click', async () => { await page.locator('#btn1').click(); - expect(spansReceived).toHaveLength(0); + expect(txnsReceived).toEqual(0); }); await sentryTest.step('Navigation', async () => { await page.goto(`${url}#foo`); - expect(spansReceived).toHaveLength(0); + expect(txnsReceived).toEqual(0); }); await sentryTest.step('Make fetch request', async () => { @@ -70,8 +78,6 @@ 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 () => { @@ -81,17 +87,18 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains timestamp: expect.any(Number), discarded_events: [ { - category: 'span', - quantity: expect.any(Number), + category: 'transaction', + quantity: 4, reason: 'sample_rate', }, ], }); - // exact number depends on performance observer emissions - expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(10); }); - expect(spansReceived).toHaveLength(0); + 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-streamed/consistent-sampling/meta-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/init.js similarity index 92% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/init.js index 177fe4c4aeaf..fbf897d47eab 100644 --- 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-static/consistent-sampling/meta-precedence/init.js @@ -3,13 +3,13 @@ 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, }), - Sentry.spanStreamingIntegration(), ], tracePropagationTargets: ['sentry-test-external.io'], tracesSampler: ({ inheritOrSampleWith }) => { 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-static/consistent-sampling/meta-precedence/page-1.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-1.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-1.html 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-static/consistent-sampling/meta-precedence/page-2.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/page-2.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/page-2.html 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-static/consistent-sampling/meta-precedence/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/subject.js 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-static/consistent-sampling/meta-precedence/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/template.html 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-static/consistent-sampling/meta-precedence/test.ts similarity index 63% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta-precedence/test.ts rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta-precedence/test.ts index cd64596f9bfd..3dab9594ba7c 100644 --- 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-static/consistent-sampling/meta-precedence/test.ts @@ -4,12 +4,13 @@ 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; @@ -20,14 +21,16 @@ 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 }) => { - sentryTest.skip(shouldSkipTracingTest()); + 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 span + // negative sampling decision -> no pageload txn await page.goto(url); }); @@ -68,49 +71,37 @@ sentryTest.describe('When `consistentTraceSampling` is `true` and page contains timestamp: expect.any(Number), discarded_events: [ { - category: 'span', - quantity: expect.any(Number), + category: 'transaction', + quantity: 2, 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), - ); + const page1Pageload = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); await page.locator('a').click(); - const envelope = await page1PageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; + const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await page1Pageload); + const pageloadTraceContext = pageloadEvent.contexts?.trace; - 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); + 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 page2PageloadEnvelopePromise = waitForStreamedSpanEnvelope( - page, - env => - !!env[1][0][1].items.find( - s => - getSpanOp(s) === 'pageload' && s.trace_id !== metaTagTraceIdPage1 && s.trace_id !== metaTagTraceIdIndex, - ), - ); + const page2Pageload = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'pageload'); await page.locator('a').click(); - const envelope = await page2PageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; + const [pageloadEvent, pageloadTraceHeader] = eventAndTraceHeaderRequestParser(await page2Pageload); + const pageloadTraceContext = pageloadEvent.contexts?.trace; - 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); + 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-streamed/consistent-sampling/meta/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/init.js similarity index 92% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/init.js index a1ddc5465950..61e2b2daa9ba 100644 --- 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-static/consistent-sampling/meta/init.js @@ -3,13 +3,13 @@ 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, }), - Sentry.spanStreamingIntegration(), ], tracePropagationTargets: ['sentry-test-external.io'], // only take into account sampling from meta tag; otherwise sample negatively 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-static/consistent-sampling/meta/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/subject.js 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-static/consistent-sampling/meta/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/meta/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/meta/template.html 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-streamed/consistent-sampling/tracesSampler-precedence/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/init.js similarity index 94% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/init.js index 623db0ecc028..e2efc43e50f2 100644 --- 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-static/consistent-sampling/tracesSampler-precedence/init.js @@ -3,6 +3,7 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ @@ -10,7 +11,6 @@ Sentry.init({ consistentTraceSampling: true, enableInp: false, }), - Sentry.spanStreamingIntegration(), ], tracePropagationTargets: ['sentry-test-external.io'], tracesSampler: ctx => { 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-static/consistent-sampling/tracesSampler-precedence/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/subject.js 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-static/consistent-sampling/tracesSampler-precedence/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/template.html 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-static/consistent-sampling/tracesSampler-precedence/test.ts similarity index 50% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/consistent-sampling/tracesSampler-precedence/test.ts rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/consistent-sampling/tracesSampler-precedence/test.ts index 37e3b204fe88..a61043dfc109 100644 --- 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-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'; @@ -5,11 +6,12 @@ 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,24 +20,26 @@ import { getSpanOp, waitForStreamedSpanEnvelope } from '../../../../../../utils/ */ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { sentryTest('explicit sampling decisions in `tracesSampler` have precedence', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } 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'), - ); + const { pageloadTraceContext } = await sentryTest.step('Initial pageload', async () => { + const pageloadRequestPromise = waitForTransactionRequest(page, evt => { + return evt.contexts?.trace?.op === PAGELOAD; + }); await page.goto(url); - const envelope = await pageloadEnvelopePromise; - const pageloadSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'pageload')!; + const res = eventAndTraceHeaderRequestParser(await pageloadRequestPromise); + const pageloadSampleRand = Number(res[1]?.sample_rand); + const pageloadTraceContext = res[0].contexts?.trace; - expect(pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toBe(1); - expect(Number(envelope[0].trace?.sample_rand)).toBeGreaterThanOrEqual(0); + expect(pageloadTraceContext?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]).toBe(1); + expect(pageloadSampleRand).toBeGreaterThanOrEqual(0); - return { pageloadSpan }; + return { pageloadTraceContext: res[0].contexts?.trace, pageloadSampleRand }; }); await sentryTest.step('Custom trace is sampled negatively (explicitly in tracesSampler)', async () => { @@ -43,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); @@ -52,7 +55,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { timestamp: expect.any(Number), discarded_events: [ { - category: 'span', + category: 'transaction', quantity: 1, reason: 'sample_rate', }, @@ -65,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); @@ -75,7 +76,7 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { timestamp: expect.any(Number), discarded_events: [ { - category: 'span', + category: 'transaction', quantity: 1, reason: 'sample_rate', }, @@ -83,67 +84,53 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { }); }); - const { customTrace2Span } = await sentryTest.step( + const { customTrace2Context } = 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'), - ); + const customTrace2RequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === 'custom'); await page.locator('#btn2').click(); - const envelope = await customEnvelopePromise; - const customTrace2Span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; + const [customTrace2Event] = eventAndTraceHeaderRequestParser(await customTrace2RequestPromise); + + const customTrace2Context = customTrace2Event.contexts?.trace; - 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?.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.links).toEqual([ + expect(customTrace2Context?.links).toEqual([ { - attributes: { - [SENTRY_LINK_TYPE]: { - type: 'string', - value: 'previous_trace', - }, - }, + attributes: { [SENTRY_LINK_TYPE]: 'previous_trace' }, sampled: false, span_id: expect.stringMatching(/^[\da-f]{16}$/), trace_id: expect.stringMatching(/^[\da-f]{32}$/), }, ]); - return { customTrace2Span }; + return { customTrace2Context }; }, ); 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'), - ); + const navigationRequestPromise = waitForTransactionRequest(page, evt => evt.contexts?.trace?.op === NAVIGATION); await page.goto(`${url}#bar`); - const envelope = await navigationEnvelopePromise; - const navigationSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === 'navigation')!; + const [navigationEvent] = eventAndTraceHeaderRequestParser(await navigationRequestPromise); - 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(); + const navigationTraceContext = navigationEvent.contexts?.trace; - expect(navigationSpan.links).toEqual([ + 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]: { - type: 'string', - value: 'previous_trace', - }, - }, + attributes: { [SENTRY_LINK_TYPE]: 'previous_trace' }, sampled: true, - span_id: customTrace2Span.span_id, - trace_id: customTrace2Span.trace_id, + span_id: customTrace2Context?.span_id, + trace_id: customTrace2Context?.trace_id, }, ]); }); 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/linked-traces-static/custom-trace/init.js similarity index 66% 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/linked-traces-static/custom-trace/init.js index 749560a5c459..bdd43f5ac757 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/backgroundtab-pageload-streamed/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/init.js @@ -3,8 +3,9 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', 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/linked-traces-streamed/custom-trace/subject.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/subject.js 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-static/custom-trace/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/custom-trace/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/custom-trace/template.html 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-streamed/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/init.js similarity index 66% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/default/init.js index 749560a5c459..bdd43f5ac757 100644 --- 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-static/default/init.js @@ -3,8 +3,9 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', 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/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-streamed/interaction-spans/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/init.js similarity index 84% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/init.js index 812e8cb92dc2..2c3e9264d882 100644 --- 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-static/interaction-spans/init.js @@ -4,7 +4,8 @@ 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(), Sentry.spanStreamingIntegration()], + integrations: [Sentry.browserTracingIntegration(), interactionsIntegration()], }); 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-static/interaction-spans/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/interaction-spans/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/interaction-spans/template.html 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-streamed/meta/template.html b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/meta/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/meta/template.html 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-streamed/negatively-sampled/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/init.js similarity index 88% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-streamed/negatively-sampled/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/linked-traces-static/negatively-sampled/init.js index 778092cf026b..79ca4d68e8ba 100644 --- 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-static/negatively-sampled/init.js @@ -3,9 +3,10 @@ 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 }), Sentry.spanStreamingIntegration()], + integrations: [Sentry.browserTracingIntegration({ detectRedirects: false })], tracesSampler: ctx => { if (ctx.attributes['sentry.origin'] === 'auto.pageload.browser') { return 0; 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/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/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/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/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/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/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/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..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 @@ -1,136 +1,140 @@ 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 { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, 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[SEMANTIC_ATTRIBUTE_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(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[SEMANTIC_ATTRIBUTE_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[SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE]?.value).toEqual( + pageloadSpan.attributes[SEMANTIC_ATTRIBUTE_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 +145,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..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 @@ -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,18 @@ 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); - }); + 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/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..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,162 +1,158 @@ import { expect } from '@playwright/test'; import { + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, 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[SEMANTIC_ATTRIBUTE_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 envelope = await customEnvelopePromise; + const span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - const customTraceContext = customTrace1Event.contexts?.trace; + expect(span.trace_id).not.toEqual(pageloadSpan.trace_id); + expect(span.parent_span_id).toBeUndefined(); - 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); + 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[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); + 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[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, - ); + 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[SEMANTIC_ATTRIBUTE_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[SEMANTIC_ATTRIBUTE_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 +163,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..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 @@ -1,15 +1,17 @@ +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'; 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 +20,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 () => { @@ -45,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); @@ -54,12 +53,13 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { timestamp: expect.any(Number), discarded_events: [ { - category: 'transaction', - quantity: 1, + category: 'span', + 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 () => { @@ -67,8 +67,6 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { await page.goto(`${url}#foo`); - await page.waitForTimeout(500); - await hidePage(page); const clientReport = envelopeRequestParser(await clientReportPromise); @@ -77,61 +75,76 @@ sentryTest.describe('When `consistentTraceSampling` is `true`', () => { timestamp: expect.any(Number), discarded_events: [ { - category: 'transaction', - quantity: 1, + category: 'span', + quantity: expect.any(Number), reason: 'sample_rate', }, ], }); + expect(clientReport.discarded_events[0].quantity).toBeGreaterThanOrEqual(1); }); - 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 envelope = await customEnvelopePromise; + const customTrace2Span = envelope[1][0][1].items.find(s => getSpanOp(s) === 'custom')!; - const customTrace2Context = customTrace2Event.contexts?.trace; + 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?.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([ + 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 navigationTraceContext = navigationEvent.contexts?.trace; + const envelope = await navigationEnvelopePromise; + const navigationSpan = envelope[1][0][1].items.find(s => getSpanOp(s) === NAVIGATION)!; - 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(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?.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-static/init.js similarity index 90% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/init.js index d3c861a8ae26..a8a7fc20c7f9 100644 --- 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-static/init.js @@ -3,6 +3,7 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ @@ -11,7 +12,6 @@ Sentry.init({ 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-static/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/subject.js 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/template.html 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..ca072e14b342 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-before-navigation-static/test.ts @@ -0,0 +1,34 @@ +import { NAVIGATION } from '@sentry/conventions/op'; +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } 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 = waitForTransactionRequest( + page, + event => event.contexts?.trace?.op === NAVIGATION, + ); + + await page.locator('#clickme').click(); + + const navigationTransactionEvent = envelopeRequestParser(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-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..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,30 +1,30 @@ +import { SENTRY_SEGMENT_ID } 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 { 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-static/assets/script.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/assets/script.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/assets/script.js 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-animation-frame-disabled-static/init.js similarity index 88% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/init.js index 3c004b952f80..250f2a72311b 100644 --- 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-animation-frame-disabled-static/init.js @@ -3,13 +3,13 @@ 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, }), - 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-static/template.html 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-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-static/assets/script.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/assets/script.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/assets/script.js 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-static/init.js similarity index 88% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/init.js index 2be5f32985d7..cfa9cb5faa3f 100644 --- 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-static/init.js @@ -3,13 +3,13 @@ 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, }), - 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/template.html 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..0a3f97f6a786 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled-static/test.ts @@ -0,0 +1,116 @@ +import { PAGELOAD, UI_LONG_ANIMATION_FRAME } from '@sentry/conventions/op'; +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'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } 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 = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + + await page.goto(url); + + const eventData = envelopeRequestParser(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 = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + + await page.goto(url); + + // trigger long animation frame function + await page.getByRole('button').click(); + + const eventData = envelopeRequestParser(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-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..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,17 +1,22 @@ -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'; 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 +24,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 +68,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-static/assets/script.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/assets/script.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/assets/script.js 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-static/init.js similarity index 88% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/init.js index b0dc0a8d7ff5..37df798364a8 100644 --- 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-static/init.js @@ -3,13 +3,13 @@ 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, }), - 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/template.html 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..fb252c80fa30 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts @@ -0,0 +1,118 @@ +import { PAGELOAD, UI_LONG_ANIMATION_FRAME } from '@sentry/conventions/op'; +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'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } 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 = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + + await page.goto(url); + + const eventData = envelopeRequestParser(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 = waitForTransactionRequest(page, event => event.contexts?.trace?.op === PAGELOAD); + + await page.goto(url); + + // trigger long animation frame function + await page.getByRole('button').click(); + + const eventData = envelopeRequestParser(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-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..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,17 +1,22 @@ -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'; 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 +26,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 +70,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-static/init.js similarity index 90% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/init.js index 9f36511d8ff5..63abc00dea38 100644 --- 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-static/init.js @@ -3,6 +3,7 @@ import * as Sentry from '@sentry/browser'; window.Sentry = Sentry; Sentry.init({ + traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', integrations: [ Sentry.browserTracingIntegration({ @@ -12,7 +13,6 @@ Sentry.init({ 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-static/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/subject.js 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/template.html 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-static/test.ts similarity index 50% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-streamed/test.ts rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-before-navigation-static/test.ts index 7e544f27a107..5236717091f1 100644 --- 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-static/test.ts @@ -1,29 +1,30 @@ import { expect } from '@playwright/test'; +import type { Event } from '@sentry/core'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; -import { getSpanOp, waitForStreamedSpans } from '../../../../utils/spanUtils'; +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 - sentryTest.skip(shouldSkipTracingTest() || browserName !== 'chromium'); + 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` })); - const navigationSpansPromise = waitForStreamedSpans(page, spans => spans.some(s => getSpanOp(s) === 'navigation')); - await page.goto(url); + const navigationTransactionEventPromise = getFirstSentryEnvelopeRequest(page); + await page.locator('#myButton').click(); - const spans = await navigationSpansPromise; + const navigationTransactionEvent = await navigationTransactionEventPromise; - const navigationSpan = spans.find(s => getSpanOp(s) === 'navigation'); - expect(navigationSpan).toBeDefined(); + expect(navigationTransactionEvent.contexts?.trace?.op).toBe('navigation'); - const longTaskSpans = spans.filter(s => getSpanOp(s) === 'ui.long_task'); + 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-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..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,30 +1,33 @@ +import { SENTRY_SEGMENT_ID } 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 { 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-static/assets/script.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/assets/script.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/assets/script.js 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-tasks-disabled-static/init.js similarity index 88% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/init.js index 3c004b952f80..250f2a72311b 100644 --- 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-tasks-disabled-static/init.js @@ -3,13 +3,13 @@ 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, }), - 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-disabled-static/template.html 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-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-static/assets/script.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/assets/script.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/assets/script.js 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-static/init.js similarity index 87% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/init.js index 977ff9c6425e..73d20634b532 100644 --- 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-static/init.js @@ -3,12 +3,12 @@ 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, }), - 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-enabled-static/template.html 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/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..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,34 +1,42 @@ +import { SENTRY_OP } 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 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..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,8 +1,9 @@ +import { SENTRY_OP } 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, waitForStreamedSpanAndTraceHeaderOnUrl } from '../../../../utils/spanUtils'; sentryTest('should capture long task.', async ({ browserName, getLocalTestUrl, page }) => { // Long tasks only work on chrome @@ -14,21 +15,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..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,43 +1,31 @@ +import { SENTRY_SEGMENT_ID } from '@sentry/conventions/attributes'; 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 +35,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..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,69 +1,71 @@ -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'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; -sentryTest( - 'should create a navigation transaction that aborts an ongoing pageload', - async ({ getLocalTestUrl, page }) => { - if (shouldSkipTracingTest()) { - sentryTest.skip(); - } +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 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.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(navigationRequest.contexts?.trace?.op).toBe('navigation'); + expect(getSpanOp(pageloadSpan)).toBe('pageload'); + expect(getSpanOp(navigationSpan)).toBe('navigation'); - expect(navigationRequest.transaction_info?.source).toEqual('url'); + expect(navigationSpan.attributes[SENTRY_SEGMENT_NAME_SOURCE]?.value).toEqual('url'); - const pageloadTraceId = pageloadRequest.contexts?.trace?.trace_id; - const navigationTraceId = navigationRequest.contexts?.trace?.trace_id; + const pageloadTraceId = pageloadSpan.trace_id; + const navigationTraceId = navigationSpan.trace_id; - expect(pageloadTraceId).toBeDefined(); - expect(navigationTraceId).toBeDefined(); - expect(pageloadTraceId).not.toEqual(navigationTraceId); + expect(pageloadTraceId).toBeDefined(); + expect(navigationTraceId).toBeDefined(); + expect(pageloadTraceId).not.toEqual(navigationTraceId); - expect(pageloadRequest.transaction).toEqual('/index.html'); - expect(navigationRequest.transaction).toEqual('/sub-page'); + 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(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', - }); - }, -); + 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..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 @@ -1,8 +1,13 @@ 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_IDLE_SPAN_FINISH_REASON, + USER_AGENT_ORIGINAL, + SENTRY_SEGMENT_ID, + SENTRY_SEGMENT_NAME, SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, URL_PATH, @@ -17,66 +22,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..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,6 +1,8 @@ +import { URL_PATH, SENTRY_SEGMENT_ID } 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 { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'creates navigation root span if click happened within 1.5s of the last navigation', @@ -9,39 +11,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..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,6 +1,8 @@ +import { URL_PATH, SENTRY_SEGMENT_ID } 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 { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( 'creates a navigation root span if a keypress happened within the last 1.5s', @@ -9,22 +11,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 +37,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..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,6 +1,8 @@ +import { URL_PATH, SENTRY_SEGMENT_ID, SENTRY_OP } 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 { 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 +11,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..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,6 +1,8 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } 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 { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if `detectRedirects` is set to false", @@ -9,25 +11,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..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,6 +1,8 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } 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 { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if a click happened before navigation", @@ -9,25 +11,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..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,6 +1,8 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } 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 { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../../utils/spanUtils'; sentryTest( "doesn't create a navigation.redirect span if a keypress happened before navigation", @@ -9,27 +11,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..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,6 +1,8 @@ +import { SENTRY_SEGMENT_ID, SENTRY_OP } 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 { 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 +11,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..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 @@ -1,8 +1,13 @@ 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_IDLE_SPAN_FINISH_REASON, + USER_AGENT_ORIGINAL, + SENTRY_SEGMENT_ID, + SENTRY_SEGMENT_NAME, SENTRY_SEGMENT_NAME_SOURCE, URL_FULL, URL_PATH, @@ -15,56 +20,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-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..bf3324b9809b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation-static/test.ts @@ -0,0 +1,156 @@ +import { PAGELOAD, NAVIGATION } from '@sentry/conventions/op'; +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'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, 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 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(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/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/navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/init.js new file mode 100644 index 000000000000..83076460599f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/init.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + 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/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/navigation/test.ts index ab8a3931e132..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,150 +1,310 @@ -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_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, + 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`, + }, + [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: 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..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,7 +1,8 @@ +import { SENTRY_OP } 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 { 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 +36,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..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,7 +1,8 @@ +import { SENTRY_OP } 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 { 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 +28,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-static/init.js similarity index 65% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/init.js index bd3b6ed17872..b74e6633eadb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-streamed/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/init.js @@ -4,8 +4,8 @@ window.Sentry = Sentry; window._testBaseTimestamp = performance.timeOrigin / 1000; Sentry.init({ + traceLifecycle: 'static', dsn: 'https://public@dsn.ingest.sentry.io/1337', - integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], - traceLifecycle: 'stream', + 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..2f89771057f7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/pageload-static/test.ts @@ -0,0 +1,43 @@ +import { PAGELOAD } from '@sentry/conventions/op'; +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'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } 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 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; + + 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/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..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 @@ -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-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/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..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 @@ -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..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,35 +1,208 @@ -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 { + 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, + 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..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,25 +1,32 @@ +import { SENTRY_SEGMENT_NAME_SOURCE } 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 { 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..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 @@ -1,28 +1,33 @@ +import { SENTRY_IDLE_SPAN_FINISH_REASON } from '@sentry/conventions/attributes'; 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(2); + 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-static/default/init.js similarity index 86% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/init.js index ded3ca204b6b..054a40d79664 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/init.js @@ -4,8 +4,9 @@ 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 }), Sentry.spanStreamingIntegration()], + integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true })], tracesSampleRate: 1, debug: true, }); 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-static/default/test.ts similarity index 50% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts index 3c4a37bc9de3..a20d10297c4b 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/default/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/default/test.ts @@ -7,30 +7,32 @@ import { 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'; +import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../../utils/helpers'; sentryTest( 'waits for Sentry.reportPageLoaded() to be called when `enableReportPageLoaded` is true', async ({ getLocalTestUrl, page }) => { - sentryTest.skip(shouldSkipTracingTest()); + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } - const url = await getLocalTestUrl({ testDir: __dirname }); + const pageloadEventPromise = waitForTransactionRequest(page, event => event.contexts?.trace?.op === 'pageload'); - const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); + const url = await getLocalTestUrl({ testDir: __dirname }); await page.goto(url); - const pageloadSpan = await pageloadSpanPromise; + const eventData = envelopeRequestParser(await pageloadEventPromise); - const spanDurationSeconds = pageloadSpan.end_timestamp - pageloadSpan.start_timestamp; + const traceContextData = eventData.contexts?.trace?.data; + const spanDurationSeconds = eventData.timestamp! - eventData.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' }, + 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, 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-static/finalTimeout/init.js similarity index 65% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/init.js index b1c19f779713..2f633a9c6f1e 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/finalTimeout/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/init.js @@ -4,11 +4,9 @@ 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 }), - Sentry.spanStreamingIntegration(), - ], + integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, finalTimeout: 3000 })], tracesSampleRate: 1, debug: true, }); 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..8244b6f83c7a --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/finalTimeout/test.ts @@ -0,0 +1,43 @@ +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 { 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-streamed/navigation/init.js b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js similarity index 71% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js index ac42880742a3..3e937c873954 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-streamed/navigation/init.js +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/init.js @@ -4,11 +4,9 @@ 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 }), - Sentry.spanStreamingIntegration(), - ], + integrations: [Sentry.browserTracingIntegration({ enableReportPageLoaded: true, instrumentNavigation: false })], tracesSampleRate: 1, debug: true, }); 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..376c35d25a7f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/reportPageLoaded-static/navigation/test.ts @@ -0,0 +1,41 @@ +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 { 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/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/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..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,33 +1,36 @@ -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'; -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..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,33 +1,36 @@ -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'; -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-static/init.js similarity index 90% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/init.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/init.js index 53029a40b668..f2b876b3bf7d 100644 --- 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-static/init.js @@ -4,13 +4,13 @@ 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.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-static/subject.js similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/subject.js rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/subject.js 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-static/template.html similarity index 100% rename from dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-streamed/template.html rename to dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/template.html 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..ae6640054caa --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/spotlight-interaction-filter-static/test.ts @@ -0,0 +1,54 @@ +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'; +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 }); + const pageloadPromise = waitForTransactionRequest(page, txn => txn.contexts?.trace?.op === PAGELOAD); + await page.goto(url); + await pageloadPromise; + + // 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); + + // 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'); + }, +); 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..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,57 +1,50 @@ +import { BROWSER_WEB_VITAL_INP_TARGET } from '@sentry/conventions/attributes'; 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());