@@ -184,4 +184,222 @@ test.describe('performance events', () => {
184
184
} ,
185
185
} ) ;
186
186
} ) ;
187
+
188
+ test ( 'captures a navigation transaction directly after pageload' , async ( { page } ) => {
189
+ await page . goto ( '/' ) ;
190
+
191
+ const clientPageloadTxnPromise = waitForTransaction ( 'sveltekit-2' , txnEvent => {
192
+ return txnEvent ?. contexts ?. trace ?. op === 'pageload' && txnEvent ?. tags ?. runtime === 'browser' ;
193
+ } ) ;
194
+
195
+ const clientNavigationTxnPromise = waitForTransaction ( 'sveltekit-2' , txnEvent => {
196
+ return txnEvent ?. contexts ?. trace ?. op === 'navigation' && txnEvent ?. tags ?. runtime === 'browser' ;
197
+ } ) ;
198
+
199
+ const navigationClickPromise = page . locator ( '#routeWithParamsLink' ) . click ( ) ;
200
+
201
+ const [ pageloadTxnEvent , navigationTxnEvent , _ ] = await Promise . all ( [
202
+ clientPageloadTxnPromise ,
203
+ clientNavigationTxnPromise ,
204
+ navigationClickPromise ,
205
+ ] ) ;
206
+
207
+ expect ( pageloadTxnEvent ) . toMatchObject ( {
208
+ transaction : '/' ,
209
+ tags : { runtime : 'browser' } ,
210
+ transaction_info : { source : 'route' } ,
211
+ type : 'transaction' ,
212
+ contexts : {
213
+ trace : {
214
+ op : 'pageload' ,
215
+ origin : 'auto.pageload.sveltekit' ,
216
+ } ,
217
+ } ,
218
+ } ) ;
219
+
220
+ expect ( navigationTxnEvent ) . toMatchObject ( {
221
+ transaction : '/users/[id]' ,
222
+ tags : { runtime : 'browser' } ,
223
+ transaction_info : { source : 'route' } ,
224
+ type : 'transaction' ,
225
+ contexts : {
226
+ trace : {
227
+ op : 'navigation' ,
228
+ origin : 'auto.navigation.sveltekit' ,
229
+ data : {
230
+ 'sentry.sveltekit.navigation.from' : '/' ,
231
+ 'sentry.sveltekit.navigation.to' : '/users/[id]' ,
232
+ 'sentry.sveltekit.navigation.type' : 'link' ,
233
+ } ,
234
+ } ,
235
+ } ,
236
+ } ) ;
237
+
238
+ const routingSpans = navigationTxnEvent . spans ?. filter ( s => s . op === 'ui.sveltekit.routing' ) ;
239
+ expect ( routingSpans ) . toHaveLength ( 1 ) ;
240
+
241
+ const routingSpan = routingSpans && routingSpans [ 0 ] ;
242
+ expect ( routingSpan ) . toMatchObject ( {
243
+ op : 'ui.sveltekit.routing' ,
244
+ description : 'SvelteKit Route Change' ,
245
+ data : {
246
+ 'sentry.op' : 'ui.sveltekit.routing' ,
247
+ 'sentry.origin' : 'auto.ui.sveltekit' ,
248
+ 'sentry.sveltekit.navigation.from' : '/' ,
249
+ 'sentry.sveltekit.navigation.to' : '/users/[id]' ,
250
+ 'sentry.sveltekit.navigation.type' : 'link' ,
251
+ } ,
252
+ } ) ;
253
+ } ) ;
254
+
255
+ test ( 'captures one navigation transaction per redirect' , async ( { page } ) => {
256
+ await page . goto ( '/' ) ;
257
+
258
+ const clientNavigationRedirect1TxnPromise = waitForTransaction ( 'sveltekit-2' , txnEvent => {
259
+ return (
260
+ txnEvent ?. contexts ?. trace ?. op === 'navigation' &&
261
+ txnEvent ?. tags ?. runtime === 'browser' &&
262
+ txnEvent ?. transaction === '/redirect1'
263
+ ) ;
264
+ } ) ;
265
+
266
+ const clientNavigationRedirect2TxnPromise = waitForTransaction ( 'sveltekit-2' , txnEvent => {
267
+ return (
268
+ txnEvent ?. contexts ?. trace ?. op === 'navigation' &&
269
+ txnEvent ?. tags ?. runtime === 'browser' &&
270
+ txnEvent ?. transaction === '/redirect2'
271
+ ) ;
272
+ } ) ;
273
+
274
+ const clientNavigationRedirect3TxnPromise = waitForTransaction ( 'sveltekit-2' , txnEvent => {
275
+ return (
276
+ txnEvent ?. contexts ?. trace ?. op === 'navigation' &&
277
+ txnEvent ?. tags ?. runtime === 'browser' &&
278
+ txnEvent ?. transaction === '/users/[id]'
279
+ ) ;
280
+ } ) ;
281
+
282
+ const navigationClickPromise = page . locator ( '#redirectLink' ) . click ( ) ;
283
+
284
+ const [ redirect1TxnEvent , redirect2TxnEvent , redirect3TxnEvent , _ ] = await Promise . all ( [
285
+ clientNavigationRedirect1TxnPromise ,
286
+ clientNavigationRedirect2TxnPromise ,
287
+ clientNavigationRedirect3TxnPromise ,
288
+ navigationClickPromise ,
289
+ ] ) ;
290
+
291
+ expect ( redirect1TxnEvent ) . toMatchObject ( {
292
+ transaction : '/redirect1' ,
293
+ tags : { runtime : 'browser' } ,
294
+ transaction_info : { source : 'route' } ,
295
+ type : 'transaction' ,
296
+ contexts : {
297
+ trace : {
298
+ op : 'navigation' ,
299
+ origin : 'auto.navigation.sveltekit' ,
300
+ data : {
301
+ 'sentry.origin' : 'auto.navigation.sveltekit' ,
302
+ 'sentry.op' : 'navigation' ,
303
+ 'sentry.source' : 'route' ,
304
+ 'sentry.sveltekit.navigation.type' : 'link' ,
305
+ 'sentry.sveltekit.navigation.from' : '/' ,
306
+ 'sentry.sveltekit.navigation.to' : '/redirect1' ,
307
+ 'sentry.sample_rate' : 1 ,
308
+ } ,
309
+ } ,
310
+ } ,
311
+ } ) ;
312
+
313
+ const redirect1Spans = redirect1TxnEvent . spans ?. filter ( s => s . op === 'ui.sveltekit.routing' ) ;
314
+ expect ( redirect1Spans ) . toHaveLength ( 1 ) ;
315
+
316
+ const redirect1Span = redirect1Spans && redirect1Spans [ 0 ] ;
317
+ expect ( redirect1Span ) . toMatchObject ( {
318
+ op : 'ui.sveltekit.routing' ,
319
+ description : 'SvelteKit Route Change' ,
320
+ data : {
321
+ 'sentry.op' : 'ui.sveltekit.routing' ,
322
+ 'sentry.origin' : 'auto.ui.sveltekit' ,
323
+ 'sentry.sveltekit.navigation.from' : '/' ,
324
+ 'sentry.sveltekit.navigation.to' : '/redirect1' ,
325
+ 'sentry.sveltekit.navigation.type' : 'link' ,
326
+ } ,
327
+ } ) ;
328
+
329
+ expect ( redirect2TxnEvent ) . toMatchObject ( {
330
+ transaction : '/redirect2' ,
331
+ tags : { runtime : 'browser' } ,
332
+ transaction_info : { source : 'route' } ,
333
+ type : 'transaction' ,
334
+ contexts : {
335
+ trace : {
336
+ op : 'navigation' ,
337
+ origin : 'auto.navigation.sveltekit' ,
338
+ data : {
339
+ 'sentry.origin' : 'auto.navigation.sveltekit' ,
340
+ 'sentry.op' : 'navigation' ,
341
+ 'sentry.source' : 'route' ,
342
+ 'sentry.sveltekit.navigation.type' : 'goto' ,
343
+ 'sentry.sveltekit.navigation.from' : '/' ,
344
+ 'sentry.sveltekit.navigation.to' : '/redirect2' ,
345
+ 'sentry.sample_rate' : 1 ,
346
+ } ,
347
+ } ,
348
+ } ,
349
+ } ) ;
350
+
351
+ const redirect2Spans = redirect2TxnEvent . spans ?. filter ( s => s . op === 'ui.sveltekit.routing' ) ;
352
+ expect ( redirect2Spans ) . toHaveLength ( 1 ) ;
353
+
354
+ const redirect2Span = redirect2Spans && redirect2Spans [ 0 ] ;
355
+ expect ( redirect2Span ) . toMatchObject ( {
356
+ op : 'ui.sveltekit.routing' ,
357
+ description : 'SvelteKit Route Change' ,
358
+ data : {
359
+ 'sentry.op' : 'ui.sveltekit.routing' ,
360
+ 'sentry.origin' : 'auto.ui.sveltekit' ,
361
+ 'sentry.sveltekit.navigation.from' : '/' ,
362
+ 'sentry.sveltekit.navigation.to' : '/redirect2' ,
363
+ 'sentry.sveltekit.navigation.type' : 'goto' ,
364
+ } ,
365
+ } ) ;
366
+
367
+ expect ( redirect3TxnEvent ) . toMatchObject ( {
368
+ transaction : '/users/[id]' ,
369
+ tags : { runtime : 'browser' } ,
370
+ transaction_info : { source : 'route' } ,
371
+ type : 'transaction' ,
372
+ contexts : {
373
+ trace : {
374
+ op : 'navigation' ,
375
+ origin : 'auto.navigation.sveltekit' ,
376
+ data : {
377
+ 'sentry.origin' : 'auto.navigation.sveltekit' ,
378
+ 'sentry.op' : 'navigation' ,
379
+ 'sentry.source' : 'route' ,
380
+ 'sentry.sveltekit.navigation.type' : 'goto' ,
381
+ 'sentry.sveltekit.navigation.from' : '/' ,
382
+ 'sentry.sveltekit.navigation.to' : '/users/[id]' ,
383
+ 'sentry.sample_rate' : 1 ,
384
+ } ,
385
+ } ,
386
+ } ,
387
+ } ) ;
388
+
389
+ const redirect3Spans = redirect3TxnEvent . spans ?. filter ( s => s . op === 'ui.sveltekit.routing' ) ;
390
+ expect ( redirect3Spans ) . toHaveLength ( 1 ) ;
391
+
392
+ const redirect3Span = redirect3Spans && redirect3Spans [ 0 ] ;
393
+ expect ( redirect3Span ) . toMatchObject ( {
394
+ op : 'ui.sveltekit.routing' ,
395
+ description : 'SvelteKit Route Change' ,
396
+ data : {
397
+ 'sentry.op' : 'ui.sveltekit.routing' ,
398
+ 'sentry.origin' : 'auto.ui.sveltekit' ,
399
+ 'sentry.sveltekit.navigation.from' : '/' ,
400
+ 'sentry.sveltekit.navigation.to' : '/users/[id]' ,
401
+ 'sentry.sveltekit.navigation.type' : 'goto' ,
402
+ } ,
403
+ } ) ;
404
+ } ) ;
187
405
} ) ;
0 commit comments