11import { expect , test } from '@playwright/test' ;
2- import { waitForError , waitForTransaction } from '@sentry-internal/test-utils' ;
2+ import { collectStreamedSpans , waitForError , waitForStreamedSpan } from '@sentry-internal/test-utils' ;
3+
4+ // The generation-function spans are children of the segment span, which ends last, so accumulate
5+ // spans until the segment for this request arrives.
6+ function collectSpansForTarget ( httpTarget : string ) {
7+ return collectStreamedSpans ( 'nextjs-14' , spans =>
8+ spans . some ( span => span . is_segment && span . attributes [ 'http.target' ] ?. value === httpTarget ) ,
9+ ) ;
10+ }
311
412test ( 'Should emit a span for a generateMetadata() function invocation' , async ( { page } ) => {
513 const testTitle = 'should-emit-span' ;
14+ const httpTarget = `/generation-functions?metadataTitle=${ testTitle } ` ;
615
7- const transactionPromise = waitForTransaction ( 'nextjs-14' , async transactionEvent => {
8- return (
9- transactionEvent . contexts ?. trace ?. data ?. [ 'http.target' ] === `/generation-functions?metadataTitle=${ testTitle } `
10- ) ;
11- } ) ;
16+ const spansPromise = collectSpansForTarget ( httpTarget ) ;
1217
13- await page . goto ( `/generation-functions?metadataTitle= ${ testTitle } ` ) ;
18+ await page . goto ( httpTarget ) ;
1419
15- const transaction = await transactionPromise ;
20+ const spans = await spansPromise ;
21+ const segmentSpan = spans . find ( span => span . is_segment && span . attributes [ 'http.target' ] ?. value === httpTarget ) ! ;
1622
17- expect ( transaction . spans ) . toContainEqual (
23+ expect ( spans ) . toContainEqual (
1824 expect . objectContaining ( {
19- description : 'generateMetadata /generation-functions/page' ,
20- origin : 'auto' ,
25+ name : 'generateMetadata /generation-functions/page' ,
26+ status : 'ok' ,
27+ trace_id : segmentSpan . trace_id ,
2128 parent_span_id : expect . stringMatching ( / [ a - f 0 - 9 ] { 16 } / ) ,
2229 span_id : expect . stringMatching ( / [ a - f 0 - 9 ] { 16 } / ) ,
23- status : 'ok' ,
24- trace_id : expect . stringMatching ( / [ a - f 0 - 9 ] { 32 } / ) ,
30+ attributes : expect . objectContaining ( {
31+ 'sentry.origin' : { value : 'auto' , type : 'string' } ,
32+ } ) ,
2533 } ) ,
2634 ) ;
2735
2836 const pageTitle = await page . title ( ) ;
2937 expect ( pageTitle ) . toBe ( testTitle ) ;
3038} ) ;
3139
32- test ( 'Should send a transaction and an error event for a faulty generateMetadata() function invocation' , async ( {
33- page,
34- } ) => {
40+ test ( 'Should send a span and an error event for a faulty generateMetadata() function invocation' , async ( { page } ) => {
3541 const testTitle = 'should-emit-error' ;
42+ const httpTarget = `/generation-functions?metadataTitle=${ testTitle } &shouldThrowInGenerateMetadata=1` ;
3643
37- const transactionPromise = waitForTransaction ( 'nextjs-14' , async transactionEvent => {
38- return (
39- transactionEvent . contexts ?. trace ?. data ?. [ 'http.target' ] ===
40- `/generation-functions?metadataTitle=${ testTitle } &shouldThrowInGenerateMetadata=1`
41- ) ;
44+ const spanPromise = waitForStreamedSpan ( 'nextjs-14' , span => {
45+ return span . is_segment && span . attributes [ 'http.target' ] ?. value === httpTarget ;
4246 } ) ;
4347
4448 const errorEventPromise = waitForError ( 'nextjs-14' , errorEvent => {
@@ -48,61 +52,57 @@ test('Should send a transaction and an error event for a faulty generateMetadata
4852 ) ;
4953 } ) ;
5054
51- await page . goto ( `/generation-functions?metadataTitle= ${ testTitle } &shouldThrowInGenerateMetadata=1` ) ;
55+ await page . goto ( httpTarget ) ;
5256
5357 const errorEvent = await errorEventPromise ;
54- const transactionEvent = await transactionPromise ;
58+ expect ( await spanPromise ) . toBeDefined ( ) ;
5559
56- // Assert that isolation scope works properly
60+ // Assert that isolation scope works properly. Span v2 carries no scope tags, so this is only
61+ // asserted on the error event; the span-side assertions were dropped in the streaming port.
5762 expect ( errorEvent . tags ?. [ 'my-isolated-tag' ] ) . toBe ( true ) ;
5863 expect ( errorEvent . tags ?. [ 'my-global-scope-isolated-tag' ] ) . not . toBeDefined ( ) ;
59- expect ( transactionEvent . tags ?. [ 'my-isolated-tag' ] ) . toBe ( true ) ;
60- expect ( transactionEvent . tags ?. [ 'my-global-scope-isolated-tag' ] ) . not . toBeDefined ( ) ;
6164} ) ;
6265
63- test ( 'Should send a transaction event for a generateViewport() function invocation' , async ( { page } ) => {
66+ test ( 'Should send a span for a generateViewport() function invocation' , async ( { page } ) => {
6467 const testTitle = 'floob' ;
68+ const httpTarget = `/generation-functions?viewportThemeColor=${ testTitle } ` ;
6569
66- const transactionPromise = waitForTransaction ( 'nextjs-14' , async transactionEvent => {
67- return (
68- transactionEvent . contexts ?. trace ?. data ?. [ 'http.target' ] ===
69- `/generation-functions?viewportThemeColor=${ testTitle } `
70- ) ;
71- } ) ;
70+ const spansPromise = collectSpansForTarget ( httpTarget ) ;
71+
72+ await page . goto ( httpTarget ) ;
7273
73- await page . goto ( `/generation-functions?viewportThemeColor=${ testTitle } ` ) ;
74+ const spans = await spansPromise ;
75+ const segmentSpan = spans . find ( span => span . is_segment && span . attributes [ 'http.target' ] ?. value === httpTarget ) ! ;
7476
75- expect ( ( await transactionPromise ) . spans ) . toContainEqual (
77+ expect ( spans ) . toContainEqual (
7678 expect . objectContaining ( {
77- description : 'generateViewport /generation-functions/page' ,
78- origin : 'auto' ,
79+ name : 'generateViewport /generation-functions/page' ,
80+ status : 'ok' ,
81+ trace_id : segmentSpan . trace_id ,
7982 parent_span_id : expect . stringMatching ( / [ a - f 0 - 9 ] { 16 } / ) ,
8083 span_id : expect . stringMatching ( / [ a - f 0 - 9 ] { 16 } / ) ,
81- status : 'ok' ,
82- trace_id : expect . stringMatching ( / [ a - f 0 - 9 ] { 32 } / ) ,
84+ attributes : expect . objectContaining ( {
85+ 'sentry.origin' : { value : 'auto' , type : 'string' } ,
86+ } ) ,
8387 } ) ,
8488 ) ;
8589} ) ;
8690
87- test ( 'Should send a transaction and an error event for a faulty generateViewport() function invocation' , async ( {
88- page,
89- } ) => {
91+ test ( 'Should send a span and an error event for a faulty generateViewport() function invocation' , async ( { page } ) => {
9092 const testTitle = 'blargh' ;
93+ const httpTarget = `/generation-functions?viewportThemeColor=${ testTitle } &shouldThrowInGenerateViewport=1` ;
9194
92- const transactionPromise = waitForTransaction ( 'nextjs-14' , async transactionEvent => {
93- return (
94- transactionEvent . contexts ?. trace ?. data ?. [ 'http.target' ] ===
95- `/generation-functions?viewportThemeColor=${ testTitle } &shouldThrowInGenerateViewport=1`
96- ) ;
95+ const spanPromise = waitForStreamedSpan ( 'nextjs-14' , span => {
96+ return span . is_segment && span . attributes [ 'http.target' ] ?. value === httpTarget ;
9797 } ) ;
9898
9999 const errorEventPromise = waitForError ( 'nextjs-14' , errorEvent => {
100100 return errorEvent ?. exception ?. values ?. [ 0 ] ?. value === 'generateViewport Error' ;
101101 } ) ;
102102
103- await page . goto ( `/generation-functions?viewportThemeColor= ${ testTitle } &shouldThrowInGenerateViewport=1` ) ;
103+ await page . goto ( httpTarget ) ;
104104
105- expect ( await transactionPromise ) . toBeDefined ( ) ;
105+ expect ( await spanPromise ) . toBeDefined ( ) ;
106106 expect ( await errorEventPromise ) . toBeDefined ( ) ;
107107
108108 const errorEvent = await errorEventPromise ;
0 commit comments