Skip to content

Commit

Permalink
test(browser-integration-test): Add trace lifetime tests for XHR requ…
Browse files Browse the repository at this point in the history
…ests
  • Loading branch information
Lms24 committed Apr 16, 2024
1 parent 39c8290 commit dc19460
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 62 deletions.
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);
},
);

0 comments on commit dc19460

Please sign in to comment.