diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js new file mode 100644 index 000000000000..09af5f3e4ab4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/init.js @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +// Force this so that the initial sampleRand is consistent +Math.random = () => 0.45; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration()], + tracePropagationTargets: ['http://sentry-test-site.example'], + tracesSampler: ({ name }) => { + if (name === 'new-trace') { + return 0.9; + } + + return 0.5; + }, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/subject.js b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/subject.js new file mode 100644 index 000000000000..711620998cb2 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/subject.js @@ -0,0 +1,13 @@ +const newTraceBtn = document.getElementById('newTrace'); +newTraceBtn.addEventListener('click', async () => { + Sentry.startNewTrace(() => { + // We want to ensure the new trace is sampled, so we force the sample_rand to a value above 0.9 + Sentry.getCurrentScope().setPropagationContext({ + ...Sentry.getCurrentScope().getPropagationContext(), + sampleRand: 0.85, + }); + Sentry.startSpan({ op: 'ui.interaction.click', name: 'new-trace' }, async () => { + await fetch('http://sentry-test-site.example'); + }); + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/template.html b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/template.html new file mode 100644 index 000000000000..11b051919b55 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/template.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/test.ts b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/test.ts new file mode 100644 index 000000000000..616c89cd66f8 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/trace-lifetime/startNewTraceSampling/test.ts @@ -0,0 +1,89 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import type { EventAndTraceHeader } from '../../../../utils/helpers'; +import { + eventAndTraceHeaderRequestParser, + getFirstSentryEnvelopeRequest, + shouldSkipTracingTest, + waitForTransactionRequest, +} from '../../../../utils/helpers'; + +sentryTest( + 'new trace started with `startNewTrace` is sampled according to the `tracesSampler`', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.route('http://sentry-test-site.example/**', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({}), + }); + }); + + const [pageloadEvent, pageloadTraceHeaders] = await getFirstSentryEnvelopeRequest( + page, + url, + eventAndTraceHeaderRequestParser, + ); + + const pageloadTraceContext = pageloadEvent.contexts?.trace; + + expect(pageloadEvent.type).toEqual('transaction'); + + expect(pageloadTraceContext).toMatchObject({ + op: 'pageload', + trace_id: expect.stringMatching(/^[0-9a-f]{32}$/), + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + data: { + 'sentry.sample_rate': 0.5, + }, + }); + expect(pageloadTraceContext).not.toHaveProperty('parent_span_id'); + + expect(pageloadTraceHeaders).toEqual({ + environment: 'production', + public_key: 'public', + sample_rate: '0.5', + sampled: 'true', + trace_id: pageloadTraceContext?.trace_id, + sample_rand: '0.45', + }); + + const transactionPromise = waitForTransactionRequest(page, event => { + return event.transaction === 'new-trace'; + }); + + await page.locator('#newTrace').click(); + + const [newTraceTransactionEvent, newTraceTransactionTraceHeaders] = eventAndTraceHeaderRequestParser( + await transactionPromise, + ); + + const newTraceTransactionTraceContext = newTraceTransactionEvent.contexts?.trace; + expect(newTraceTransactionTraceContext).toMatchObject({ + op: 'ui.interaction.click', + trace_id: expect.stringMatching(/^[0-9a-f]{32}$/), + span_id: expect.stringMatching(/^[0-9a-f]{16}$/), + data: { + 'sentry.sample_rate': 0.9, + }, + }); + + expect(newTraceTransactionTraceHeaders).toEqual({ + environment: 'production', + public_key: 'public', + sample_rate: '0.9', + sampled: 'true', + trace_id: newTraceTransactionTraceContext?.trace_id, + transaction: 'new-trace', + sample_rand: '0.85', + }); + + expect(newTraceTransactionTraceContext?.trace_id).not.toEqual(pageloadTraceContext?.trace_id); + }, +);