Skip to content

Commit 18c058a

Browse files
committed
test(browser): Add test for current DSC transaction name updating behaviour
1 parent 86c626e commit 18c058a

File tree

4 files changed

+216
-0
lines changed

4 files changed

+216
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
window.Sentry = Sentry;
4+
5+
Sentry.init({
6+
dsn: 'https://[email protected]/1337',
7+
integrations: [Sentry.browserTracingIntegration({ instrumentNavigation: false, instrumentPageLoad: false })],
8+
tracesSampleRate: 1,
9+
tracePropagationTargets: ['example.com'],
10+
release: '1.1.1',
11+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const btnStartSpan = document.getElementById('btnStartSpan');
2+
const btnUpdateName = document.getElementById('btnUpdateName');
3+
const btnMakeRequest = document.getElementById('btnMakeRequest');
4+
const btnCaptureError = document.getElementById('btnCaptureError');
5+
const btnEndSpan = document.getElementById('btnEndSpan');
6+
7+
btnStartSpan.addEventListener('click', () => {
8+
Sentry.startSpanManual(
9+
{ name: 'test-root-span', attributes: { [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url' } },
10+
async span => {
11+
console.log('Root span started');
12+
window.__traceId = span.spanContext().traceId;
13+
await new Promise(resolve => {
14+
btnEndSpan.addEventListener('click', resolve);
15+
});
16+
span.end();
17+
console.log('Root span ended');
18+
},
19+
);
20+
});
21+
22+
let updateCnt = 0;
23+
btnUpdateName.addEventListener('click', () => {
24+
const span = Sentry.getActiveSpan();
25+
const rootSpan = Sentry.getRootSpan(span);
26+
rootSpan.updateName(`updated-root-span-${++updateCnt}`);
27+
rootSpan.setAttribute(Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
28+
});
29+
30+
btnMakeRequest.addEventListener('click', () => {
31+
fetch('https://example.com/api');
32+
});
33+
34+
btnCaptureError.addEventListener('click', () => {
35+
Sentry.captureException(new Error('test-error'));
36+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<button id="btnStartSpan">Start Span</button>
2+
<button id="btnUpdateName">Update Name</button>
3+
<button id="btnMakeRequest">Make Request</button>
4+
<button id="btnCaptureError">Capture Error</button>
5+
<button id="btnEndSpan">End Span</button>
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { expect } from '@playwright/test';
2+
import type { Page } from '@playwright/test';
3+
import type { DynamicSamplingContext } from '@sentry/types';
4+
import { sentryTest } from '../../../utils/fixtures';
5+
import type { EventAndTraceHeader } from '../../../utils/helpers';
6+
import {
7+
eventAndTraceHeaderRequestParser,
8+
getMultipleSentryEnvelopeRequests,
9+
shouldSkipTracingTest,
10+
} from '../../../utils/helpers';
11+
12+
sentryTest('updates the DSC when the txn name is updated and high-quality', async ({ getLocalTestUrl, page }) => {
13+
if (shouldSkipTracingTest()) {
14+
sentryTest.skip();
15+
}
16+
17+
const url = await getLocalTestUrl({ testDir: __dirname });
18+
19+
await page.goto(url);
20+
21+
/*
22+
Test Steps:
23+
1. Start new span with LQ txn name (source: url)
24+
2. Make request and check that baggage has no transaction name
25+
3. Capture error and check that envelope trace header has no transaction name
26+
4. Update span name and source to HQ (source: route)
27+
5. Make request and check that baggage has HQ txn name
28+
6. Capture error and check that envelope trace header has HQ txn name
29+
7. Update span name again with HQ name (source: route)
30+
8. Make request and check that baggage has updated HQ txn name
31+
9. Capture error and check that envelope trace header has updated HQ txn name
32+
10. End span and check that envelope trace header has updated HQ txn name
33+
*/
34+
35+
// 1
36+
await page.locator('#btnStartSpan').click();
37+
const traceId = await page.evaluate(() => {
38+
return (window as any).__traceId;
39+
});
40+
41+
expect(traceId).toMatch(/^[0-9a-f]{32}$/);
42+
43+
// 2
44+
const baggageItems = await makeRequestAndGetBaggageItems(page);
45+
expect(baggageItems).toEqual([
46+
'sentry-environment=production',
47+
'sentry-public_key=public',
48+
'sentry-release=1.1.1',
49+
'sentry-sample_rate=1',
50+
'sentry-sampled=true',
51+
`sentry-trace_id=${traceId}`,
52+
]);
53+
54+
// 3
55+
const errorEnvelopeTraceHeader = await captureErrorAndGetEnvelopeTraceHeader(page);
56+
expect(errorEnvelopeTraceHeader).toEqual({
57+
environment: 'production',
58+
public_key: 'public',
59+
release: '1.1.1',
60+
sample_rate: '1',
61+
sampled: 'true',
62+
trace_id: traceId,
63+
});
64+
65+
// 4
66+
await page.locator('#btnUpdateName').click();
67+
68+
// 5
69+
const baggageItemsAfterUpdate = await makeRequestAndGetBaggageItems(page);
70+
expect(baggageItemsAfterUpdate).toEqual([
71+
'sentry-environment=production',
72+
'sentry-public_key=public',
73+
'sentry-release=1.1.1',
74+
'sentry-sample_rate=1',
75+
'sentry-sampled=true',
76+
`sentry-trace_id=${traceId}`,
77+
'sentry-transaction=updated-root-span-1',
78+
]);
79+
80+
// 6
81+
const errorEnvelopeTraceHeaderAfterUpdate = await captureErrorAndGetEnvelopeTraceHeader(page);
82+
expect(errorEnvelopeTraceHeaderAfterUpdate).toEqual({
83+
environment: 'production',
84+
public_key: 'public',
85+
release: '1.1.1',
86+
sample_rate: '1',
87+
sampled: 'true',
88+
trace_id: traceId,
89+
transaction: 'updated-root-span-1',
90+
});
91+
92+
// 7
93+
await page.locator('#btnUpdateName').click();
94+
95+
// 8
96+
const baggageItemsAfterSecondUpdate = await makeRequestAndGetBaggageItems(page);
97+
expect(baggageItemsAfterSecondUpdate).toEqual([
98+
'sentry-environment=production',
99+
'sentry-public_key=public',
100+
'sentry-release=1.1.1',
101+
'sentry-sample_rate=1',
102+
'sentry-sampled=true',
103+
`sentry-trace_id=${traceId}`,
104+
'sentry-transaction=updated-root-span-2',
105+
]);
106+
107+
// 9
108+
const errorEnvelopeTraceHeaderAfterSecondUpdate = await captureErrorAndGetEnvelopeTraceHeader(page);
109+
expect(errorEnvelopeTraceHeaderAfterSecondUpdate).toEqual({
110+
environment: 'production',
111+
public_key: 'public',
112+
release: '1.1.1',
113+
sample_rate: '1',
114+
sampled: 'true',
115+
trace_id: traceId,
116+
transaction: 'updated-root-span-2',
117+
});
118+
119+
// 10
120+
const txnEventPromise = getMultipleSentryEnvelopeRequests<EventAndTraceHeader>(
121+
page,
122+
1,
123+
{ envelopeType: 'transaction' },
124+
eventAndTraceHeaderRequestParser,
125+
);
126+
127+
await page.locator('#btnEndSpan').click();
128+
129+
const [txnEvent, txnEnvelopeTraceHeader] = (await txnEventPromise)[0];
130+
expect(txnEnvelopeTraceHeader).toEqual({
131+
environment: 'production',
132+
public_key: 'public',
133+
release: '1.1.1',
134+
sample_rate: '1',
135+
sampled: 'true',
136+
trace_id: traceId,
137+
transaction: 'updated-root-span-2',
138+
});
139+
140+
expect(txnEvent.transaction).toEqual('updated-root-span-2');
141+
});
142+
143+
async function makeRequestAndGetBaggageItems(page: Page): Promise<string[]> {
144+
const requestPromise = page.waitForRequest('https://example.com/*');
145+
await page.locator('#btnMakeRequest').click();
146+
const request = await requestPromise;
147+
148+
const baggage = await request.headerValue('baggage');
149+
return baggage?.split(',').sort() ?? [];
150+
}
151+
152+
async function captureErrorAndGetEnvelopeTraceHeader(page: Page): Promise<DynamicSamplingContext | undefined> {
153+
const errorEventPromise = getMultipleSentryEnvelopeRequests<EventAndTraceHeader>(
154+
page,
155+
1,
156+
{ envelopeType: 'event' },
157+
eventAndTraceHeaderRequestParser,
158+
);
159+
160+
await page.locator('#btnCaptureError').click();
161+
162+
const [, errorEnvelopeTraceHeader] = (await errorEventPromise)[0];
163+
return errorEnvelopeTraceHeader;
164+
}

0 commit comments

Comments
 (0)