Skip to content

Commit cde0037

Browse files
authored
test(astro): Add dynamic routes astro-4 tests (#17102)
Extracting astro-4 test cases (same as in astro-5) in a new PR while working on route parametrization. Part of #16686
1 parent 662da80 commit cde0037

File tree

4 files changed

+232
-0
lines changed

4 files changed

+232
-0
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const prerender = false;
2+
3+
export function GET({ params }) {
4+
return new Response(
5+
JSON.stringify({
6+
greeting: `Hello ${params.userId}`,
7+
userId: params.userId,
8+
}),
9+
);
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
import Layout from '../../layouts/Layout.astro';
3+
4+
export const prerender = false;
5+
6+
const params = Astro.params;
7+
8+
---
9+
10+
<Layout title="CatchAll SSR page">
11+
<p>params: {params}</p>
12+
</Layout>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
import Layout from '../../layouts/Layout.astro';
3+
4+
export const prerender = false;
5+
6+
const { userId } = Astro.params;
7+
8+
const response = await fetch(Astro.url.origin + `/api/user/${userId}.json`)
9+
const data = await response.json();
10+
11+
---
12+
13+
<Layout title="Dynamic SSR page">
14+
<h1>{data.greeting}</h1>
15+
16+
<p>data: {JSON.stringify(data)}</p>
17+
</Layout>

dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,196 @@ test.describe('tracing in dynamically rendered (ssr) routes', () => {
120120
});
121121
});
122122
});
123+
124+
test.describe('nested SSR routes (client, server, server request)', () => {
125+
/** The user-page route fetches from an endpoint and creates a deeply nested span structure:
126+
* pageload — /user-page/myUsername123
127+
* ├── browser.** — multiple browser spans
128+
* └── browser.request — /user-page/myUsername123
129+
* └── http.server — GET /user-page/[userId] (SSR page request)
130+
* └── http.client — GET /api/user/myUsername123.json (executing fetch call from SSR page - span)
131+
* └── http.server — GET /api/user/myUsername123.json (server request)
132+
*/
133+
test('sends connected server and client pageload and request spans with the same trace id', async ({ page }) => {
134+
const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
135+
return txnEvent?.transaction?.startsWith('/user-page/') ?? false;
136+
});
137+
138+
const serverPageRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
139+
return txnEvent?.transaction?.startsWith('GET /user-page/') ?? false;
140+
});
141+
142+
const serverHTTPServerRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
143+
return txnEvent?.transaction?.startsWith('GET /api/user/') ?? false;
144+
});
145+
146+
await page.goto('/user-page/myUsername123');
147+
148+
const clientPageloadTxn = await clientPageloadTxnPromise;
149+
const serverPageRequestTxn = await serverPageRequestTxnPromise;
150+
const serverHTTPServerRequestTxn = await serverHTTPServerRequestTxnPromise;
151+
const serverRequestHTTPClientSpan = serverPageRequestTxn.spans?.find(
152+
span => span.op === 'http.client' && span.description?.includes('/api/user/'),
153+
);
154+
155+
const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id;
156+
157+
// Verify all spans have the same trace ID
158+
expect(clientPageloadTraceId).toEqual(serverPageRequestTxn.contexts?.trace?.trace_id);
159+
expect(clientPageloadTraceId).toEqual(serverHTTPServerRequestTxn.contexts?.trace?.trace_id);
160+
expect(clientPageloadTraceId).toEqual(serverRequestHTTPClientSpan?.trace_id);
161+
162+
// serverPageRequest has no parent (root span)
163+
expect(serverPageRequestTxn.contexts?.trace?.parent_span_id).toBeUndefined();
164+
165+
// clientPageload's parent and serverRequestHTTPClient's parent is serverPageRequest
166+
const serverPageRequestSpanId = serverPageRequestTxn.contexts?.trace?.span_id;
167+
expect(clientPageloadTxn.contexts?.trace?.parent_span_id).toEqual(serverPageRequestSpanId);
168+
expect(serverRequestHTTPClientSpan?.parent_span_id).toEqual(serverPageRequestSpanId);
169+
170+
// serverHTTPServerRequest's parent is serverRequestHTTPClient
171+
expect(serverHTTPServerRequestTxn.contexts?.trace?.parent_span_id).toEqual(serverRequestHTTPClientSpan?.span_id);
172+
});
173+
174+
test('sends parametrized pageload, server and API request transaction names', async ({ page }) => {
175+
const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
176+
return txnEvent?.transaction?.startsWith('/user-page/') ?? false;
177+
});
178+
179+
const serverPageRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
180+
return txnEvent?.transaction?.startsWith('GET /user-page/') ?? false;
181+
});
182+
183+
const serverHTTPServerRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
184+
return txnEvent?.transaction?.startsWith('GET /api/user/') ?? false;
185+
});
186+
187+
await page.goto('/user-page/myUsername123');
188+
189+
const clientPageloadTxn = await clientPageloadTxnPromise;
190+
const serverPageRequestTxn = await serverPageRequestTxnPromise;
191+
const serverHTTPServerRequestTxn = await serverHTTPServerRequestTxnPromise;
192+
193+
const serverRequestHTTPClientSpan = serverPageRequestTxn.spans?.find(
194+
span => span.op === 'http.client' && span.description?.includes('/api/user/'),
195+
);
196+
197+
// Client pageload transaction - actual URL with pageload operation
198+
expect(clientPageloadTxn).toMatchObject({
199+
transaction: '/user-page/myUsername123', // todo: parametrize
200+
transaction_info: { source: 'url' },
201+
contexts: {
202+
trace: {
203+
op: 'pageload',
204+
origin: 'auto.pageload.browser',
205+
data: {
206+
'sentry.op': 'pageload',
207+
'sentry.origin': 'auto.pageload.browser',
208+
'sentry.source': 'url',
209+
},
210+
},
211+
},
212+
});
213+
214+
// Server page request transaction - parametrized transaction name with actual URL in data
215+
expect(serverPageRequestTxn).toMatchObject({
216+
transaction: 'GET /user-page/[userId]',
217+
transaction_info: { source: 'route' },
218+
contexts: {
219+
trace: {
220+
op: 'http.server',
221+
origin: 'auto.http.astro',
222+
data: {
223+
'sentry.op': 'http.server',
224+
'sentry.origin': 'auto.http.astro',
225+
'sentry.source': 'route',
226+
url: expect.stringContaining('/user-page/myUsername123'),
227+
},
228+
},
229+
},
230+
request: { url: expect.stringContaining('/user-page/myUsername123') },
231+
});
232+
233+
// HTTP client span - actual API URL with client operation
234+
expect(serverRequestHTTPClientSpan).toMatchObject({
235+
op: 'http.client',
236+
origin: 'auto.http.otel.node_fetch',
237+
description: 'GET http://localhost:3030/api/user/myUsername123.json', // http.client does not need to be parametrized
238+
data: {
239+
'sentry.op': 'http.client',
240+
'sentry.origin': 'auto.http.otel.node_fetch',
241+
'url.full': expect.stringContaining('/api/user/myUsername123.json'),
242+
'url.path': '/api/user/myUsername123.json',
243+
url: expect.stringContaining('/api/user/myUsername123.json'),
244+
},
245+
});
246+
247+
// Server HTTP request transaction - should be parametrized
248+
expect(serverHTTPServerRequestTxn).toMatchObject({
249+
transaction: 'GET /api/user/myUsername123.json', // todo: parametrize
250+
transaction_info: { source: 'route' },
251+
contexts: {
252+
trace: {
253+
op: 'http.server',
254+
origin: 'auto.http.astro',
255+
data: {
256+
'sentry.op': 'http.server',
257+
'sentry.origin': 'auto.http.astro',
258+
'sentry.source': 'route',
259+
url: expect.stringContaining('/api/user/myUsername123.json'),
260+
},
261+
},
262+
},
263+
request: { url: expect.stringContaining('/api/user/myUsername123.json') },
264+
});
265+
});
266+
267+
test('sends parametrized pageload and server transaction names for catch-all routes', async ({ page }) => {
268+
const clientPageloadTxnPromise = waitForTransaction('astro-4', txnEvent => {
269+
return txnEvent?.transaction?.startsWith('/catchAll/') ?? false;
270+
});
271+
272+
const serverPageRequestTxnPromise = waitForTransaction('astro-4', txnEvent => {
273+
return txnEvent?.transaction?.startsWith('GET /catchAll/') ?? false;
274+
});
275+
276+
await page.goto('/catchAll/hell0/whatever-do');
277+
278+
const clientPageloadTxn = await clientPageloadTxnPromise;
279+
const serverPageRequestTxn = await serverPageRequestTxnPromise;
280+
281+
expect(clientPageloadTxn).toMatchObject({
282+
transaction: '/catchAll/hell0/whatever-do', // todo: parametrize
283+
transaction_info: { source: 'url' },
284+
contexts: {
285+
trace: {
286+
op: 'pageload',
287+
origin: 'auto.pageload.browser',
288+
data: {
289+
'sentry.op': 'pageload',
290+
'sentry.origin': 'auto.pageload.browser',
291+
'sentry.source': 'url',
292+
},
293+
},
294+
},
295+
});
296+
297+
expect(serverPageRequestTxn).toMatchObject({
298+
transaction: 'GET /catchAll/[path]',
299+
transaction_info: { source: 'route' },
300+
contexts: {
301+
trace: {
302+
op: 'http.server',
303+
origin: 'auto.http.astro',
304+
data: {
305+
'sentry.op': 'http.server',
306+
'sentry.origin': 'auto.http.astro',
307+
'sentry.source': 'route',
308+
url: expect.stringContaining('/catchAll/hell0/whatever-do'),
309+
},
310+
},
311+
},
312+
request: { url: expect.stringContaining('/catchAll/hell0/whatever-do') },
313+
});
314+
});
315+
});

0 commit comments

Comments
 (0)