Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(browser-integration-test): Add trace lifetime tests for XHR requests #11624

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,24 @@ sentryTest('creates a new trace on each navigation', async ({ getLocalTestPath,
const navigationEvent1 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
const navigationEvent2 = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#bar`);

expect(navigationEvent1.contexts?.trace?.op).toBe('navigation');
expect(navigationEvent2.contexts?.trace?.op).toBe('navigation');

const navigation1TraceId = navigationEvent1.contexts?.trace?.trace_id;
const navigation2TraceId = navigationEvent2.contexts?.trace?.trace_id;

expect(navigation1TraceId).toMatch(/^[0-9a-f]{32}$/);
expect(navigation2TraceId).toMatch(/^[0-9a-f]{32}$/);
expect(navigation1TraceId).not.toEqual(navigation2TraceId);
const navigation1TraceContext = navigationEvent1.contexts?.trace;
const navigation2TraceContext = navigationEvent2.contexts?.trace;

expect(navigation1TraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigation1TraceContext).not.toHaveProperty('parent_span_id');

expect(navigation2TraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigation2TraceContext).not.toHaveProperty('parent_span_id');

expect(navigation1TraceContext?.trace_id).not.toEqual(navigation2TraceContext?.trace_id);
});

sentryTest('error after navigation has navigation traceId', async ({ getLocalTestPath, page }) => {
Expand All @@ -40,17 +49,24 @@ sentryTest('error after navigation has navigation traceId', async ({ getLocalTes
await getFirstSentryEnvelopeRequest<Event>(page, url);

const navigationEvent = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
expect(navigationEvent.contexts?.trace?.op).toBe('navigation');
const navigationTraceContext = navigationEvent.contexts?.trace;

const navigationTraceId = navigationEvent.contexts?.trace?.trace_id;
expect(navigationTraceId).toMatch(/^[0-9a-f]{32}$/);
expect(navigationTraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigationTraceContext).not.toHaveProperty('parent_span_id');

const errorEventPromise = getFirstSentryEnvelopeRequest<Event>(page);
await page.locator('#errorBtn').click();
const errorEvent = await errorEventPromise;

const errorTraceId = errorEvent.contexts?.trace?.trace_id;
expect(errorTraceId).toBe(navigationTraceId);
const errorTraceContext = errorEvent.contexts?.trace;
expect(errorTraceContext).toEqual({
trace_id: navigationTraceContext?.trace_id,
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
});

sentryTest('error during navigation has new navigation traceId', async ({ getLocalTestPath, page }) => {
Expand All @@ -71,13 +87,20 @@ sentryTest('error during navigation has new navigation traceId', async ({ getLoc
const navigationEvent = events.find(event => event.type === 'transaction');
const errorEvent = events.find(event => !event.type);

expect(navigationEvent?.contexts?.trace?.op).toBe('navigation');

const navigationTraceId = navigationEvent?.contexts?.trace?.trace_id;
expect(navigationTraceId).toMatch(/^[0-9a-f]{32}$/);

const errorTraceId = errorEvent?.contexts?.trace?.trace_id;
expect(errorTraceId).toBe(navigationTraceId);
const navigationTraceContext = navigationEvent?.contexts?.trace;
expect(navigationTraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigationTraceContext).not.toHaveProperty('parent_span_id');

const errorTraceContext = errorEvent?.contexts?.trace;
expect(errorTraceContext).toMatchObject({
op: 'navigation',
trace_id: errorTraceContext?.trace_id,
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
});

sentryTest(
Expand All @@ -93,17 +116,22 @@ sentryTest(
await getFirstSentryEnvelopeRequest<Event>(page, url);

const navigationEvent = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
expect(navigationEvent.contexts?.trace?.op).toBe('navigation');

const navigationTraceId = navigationEvent.contexts?.trace?.trace_id;
expect(navigationTraceId).toMatch(/^[0-9a-f]{32}$/);
const navigationTraceContext = navigationEvent.contexts?.trace;
expect(navigationTraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigationTraceContext).not.toHaveProperty('parent_span_id');

const requestPromise = page.waitForRequest('http://example.com/*');
await page.locator('#fetchBtn').click();
const request = await requestPromise;
const headers = request.headers();

// sampling decision is deferred b/c of no active span at the time of request
const navigationTraceId = navigationTraceContext?.trace_id;
expect(headers['sentry-trace']).toMatch(new RegExp(`^${navigationTraceId}-[0-9a-f]{16}$`));
expect(headers['baggage']).toEqual(
`sentry-environment=production,sentry-public_key=public,sentry-trace_id=${navigationTraceId}`,
Expand All @@ -129,14 +157,90 @@ sentryTest(
await page.locator('#fetchBtn').click();
const [navigationEvent, request] = await Promise.all([navigationEventPromise, requestPromise]);

expect(navigationEvent.contexts?.trace?.op).toBe('navigation');
const navigationTraceContext = navigationEvent.contexts?.trace;
expect(navigationTraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigationTraceContext).not.toHaveProperty('parent_span_id');

const headers = request.headers();

// sampling decision is propagated from active span sampling decision
const navigationTraceId = navigationTraceContext?.trace_id;
expect(headers['sentry-trace']).toMatch(new RegExp(`^${navigationTraceId}-[0-9a-f]{16}-1$`));
expect(headers['baggage']).toEqual(
`sentry-environment=production,sentry-public_key=public,sentry-trace_id=${navigationTraceId},sentry-sample_rate=1,sentry-sampled=true`,
);
},
);

sentryTest(
'outgoing XHR request after navigation has navigation traceId in headers',
async ({ getLocalTestPath, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestPath({ testDir: __dirname });

// ensure navigation transaction is finished
await getFirstSentryEnvelopeRequest<Event>(page, url);

const navigationEvent = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);

const navigationTraceContext = navigationEvent.contexts?.trace;
expect(navigationTraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigationTraceContext).not.toHaveProperty('parent_span_id');

const xhrPromise = page.waitForRequest('http://example.com/*');
await page.locator('#xhrBtn').click();
const request = await xhrPromise;
const headers = request.headers();

// sampling decision is deferred b/c of no active span at the time of request
const navigationTraceId = navigationTraceContext?.trace_id;
expect(headers['sentry-trace']).toMatch(new RegExp(`^${navigationTraceId}-[0-9a-f]{16}$`));
expect(headers['baggage']).toEqual(
`sentry-environment=production,sentry-public_key=public,sentry-trace_id=${navigationTraceId}`,
);
},
);

sentryTest(
'outgoing XHR request during navigation has navigation traceId in headers',
async ({ getLocalTestPath, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const navigationTraceId = navigationEvent.contexts?.trace?.trace_id;
expect(navigationTraceId).toMatch(/^[0-9a-f]{32}$/);
const url = await getLocalTestPath({ testDir: __dirname });

// ensure navigation transaction is finished
await getFirstSentryEnvelopeRequest<Event>(page, url);

const navigationEventPromise = getFirstSentryEnvelopeRequest<Event>(page);
const requestPromise = page.waitForRequest('http://example.com/*');
await page.goto(`${url}#foo`);
await page.locator('#xhrBtn').click();
const [navigationEvent, request] = await Promise.all([navigationEventPromise, requestPromise]);

const navigationTraceContext = navigationEvent.contexts?.trace;
expect(navigationTraceContext).toMatchObject({
op: 'navigation',
trace_id: expect.stringMatching(/^[0-9a-f]{32}$/),
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});
expect(navigationTraceContext).not.toHaveProperty('parent_span_id');
const headers = request.headers();

// sampling decision is propagated from active span sampling decision
const navigationTraceId = navigationTraceContext?.trace_id;
expect(headers['sentry-trace']).toMatch(new RegExp(`^${navigationTraceId}-[0-9a-f]{16}-1$`));
expect(headers['baggage']).toEqual(
`sentry-environment=production,sentry-public_key=public,sentry-trace_id=${navigationTraceId},sentry-sample_rate=1,sentry-sampled=true`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
<body>
<button id="errorBtn">Throw Error</button>
<button id="fetchBtn">Fetch Request</button>
<button id="xhrBtn">XHR Request</button>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,61 @@ sentryTest(
expect(headers['baggage']).toBe(META_TAG_BAGGAGE);
},
);

sentryTest(
'outgoing XHR request after <meta> tag pageload has pageload traceId in headers',
async ({ getLocalTestPath, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestPath({ testDir: __dirname });

const pageloadEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
expect(pageloadEvent?.contexts?.trace).toMatchObject({
op: 'pageload',
trace_id: META_TAG_TRACE_ID,
parent_span_id: META_TAG_PARENT_SPAN_ID,
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});

const requestPromise = page.waitForRequest('http://example.com/*');
await page.locator('#xhrBtn').click();
const request = await requestPromise;
const headers = request.headers();

// sampling decision is propagated from meta tag's sentry-trace sampled flag
expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}-1$`));
expect(headers['baggage']).toBe(META_TAG_BAGGAGE);
},
);

sentryTest(
'outgoing XHR request during <meta> tag pageload has pageload traceId in headers',
async ({ getLocalTestPath, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

const url = await getLocalTestPath({ testDir: __dirname });

const pageloadEventPromise = getFirstSentryEnvelopeRequest<Event>(page);
const requestPromise = page.waitForRequest('http://example.com/*');
await page.goto(url);
await page.locator('#xhrBtn').click();
const [pageloadEvent, request] = await Promise.all([pageloadEventPromise, requestPromise]);

expect(pageloadEvent?.contexts?.trace).toMatchObject({
op: 'pageload',
trace_id: META_TAG_TRACE_ID,
parent_span_id: META_TAG_PARENT_SPAN_ID,
span_id: expect.stringMatching(/^[0-9a-f]{16}$/),
});

const headers = request.headers();

// sampling decision is propagated from meta tag's sentry-trace sampled flag
expect(headers['sentry-trace']).toMatch(new RegExp(`^${META_TAG_TRACE_ID}-[0-9a-f]{16}-1$`));
expect(headers['baggage']).toBe(META_TAG_BAGGAGE);
},
);
Loading
Loading