11import { expect , test } from '@playwright/test' ;
22import { waitForTransaction } from '@sentry-internal/test-utils' ;
33
4+ // Set by the `assert-command` of the `vue-3 (no Options API)` variant
5+ const OPTIONS_API_DISABLED = process . env . VUE_OPTIONS_API === 'false' ;
6+
47test ( 'sends a pageload transaction with a parameterized URL' , async ( { page } ) => {
58 const transactionPromise = waitForTransaction ( 'vue-3' , async transactionEvent => {
69 return ! ! transactionEvent ?. transaction && transactionEvent . contexts ?. trace ?. op === 'pageload' ;
@@ -135,7 +138,10 @@ test('sends a pageload transaction with a route name as transaction name if avai
135138 } ) ;
136139} ) ;
137140
138- test ( 'sends a lifecycle span for each tracked components' , async ( { page } ) => {
141+ test ( 'sends a lifecycle span for the root and for each tracked component only' , async ( { page } ) => {
142+ // Vue compiles `app.mixin()` down to a no-op when the Options API is disabled, so the SDK creates no UI spans at all.
143+ test . fail ( OPTIONS_API_DISABLED , 'Vue tracing is registered through app.mixin(), which needs the Options API' ) ;
144+
139145 const transactionPromise = waitForTransaction ( 'vue-3' , async transactionEvent => {
140146 return ! ! transactionEvent ?. transaction && transactionEvent . contexts ?. trace ?. op === 'pageload' ;
141147 } ) ;
@@ -144,79 +150,61 @@ test('sends a lifecycle span for each tracked components', async ({ page }) => {
144150
145151 const rootSpan = await transactionPromise ;
146152
147- expect ( rootSpan ) . toMatchObject ( {
148- contexts : {
149- trace : {
150- data : {
151- 'sentry.source' : 'route' ,
152- 'sentry.origin' : 'auto.pageload.vue' ,
153- 'sentry.op' : 'pageload' ,
154- 'url.template' : '/components' ,
155- 'url.path' : '/components' ,
156- 'url.full' : expect . stringMatching ( / ^ h t t p s ? : \/ \/ l o c a l h o s t : \d + \/ c o m p o n e n t s $ / ) ,
157- } ,
158- op : 'pageload' ,
159- origin : 'auto.pageload.vue' ,
160- } ,
153+ const uiSpans = ( rootSpan . spans || [ ] ) . filter ( span => span . origin === 'auto.ui.vue' ) ;
154+ const uiSpanDescriptions = uiSpans . map ( span => span . description ) . sort ( ) ;
155+
156+ expect ( uiSpanDescriptions ) . toEqual ( [
157+ 'Application Render' ,
158+ 'Vue <ComponentMainView>' ,
159+ 'Vue <ComponentOneView>' ,
160+ 'Vue <Root>' ,
161+ ] ) ;
162+
163+ // enabled by default
164+ const applicationRenderSpan = uiSpans . find ( span => span . description === 'Application Render' ) ;
165+ expect ( applicationRenderSpan ) . toMatchObject ( {
166+ data : {
167+ 'sentry.op' : 'ui.render' ,
168+ 'sentry.origin' : 'auto.ui.vue' ,
161169 } ,
162- spans : expect . arrayContaining ( [
163- // enabled by default
164- expect . objectContaining ( {
165- data : {
166- 'sentry.op' : 'ui.render' ,
167- 'sentry.origin' : 'auto.ui.vue' ,
168- } ,
169- description : 'Application Render' ,
170- op : 'ui.render' ,
171- origin : 'auto.ui.vue' ,
172- } ) ,
173- // enabled by default
174- expect . objectContaining ( {
175- data : {
176- 'sentry.op' : 'ui.mount' ,
177- 'sentry.origin' : 'auto.ui.vue' ,
178- } ,
179- description : 'Vue <Root>' ,
180- op : 'ui.mount' ,
181- origin : 'auto.ui.vue' ,
182- } ) ,
170+ op : 'ui.render' ,
171+ origin : 'auto.ui.vue' ,
172+ } ) ;
183173
184- // without `<>`
185- expect . objectContaining ( {
186- data : {
187- 'sentry.op' : 'ui.mount' ,
188- 'sentry.origin ' : 'auto. ui.vue ' ,
189- } ,
190- description : 'Vue <ComponentMainView>' ,
191- op : 'ui.mount' ,
192- origin : 'auto.ui.vue' ,
193- } ) ,
174+ // enabled by default
175+ const rootComponentSpan = uiSpans . find ( span => span . description === 'Vue <Root>' ) ;
176+ expect ( rootComponentSpan ) . toMatchObject ( {
177+ data : {
178+ 'sentry.op ' : 'ui.mount ' ,
179+ 'sentry.origin' : 'auto.ui.vue' ,
180+ } ,
181+ op : 'ui.mount' ,
182+ origin : 'auto.ui.vue' ,
183+ } ) ;
194184
195- // with `<>`
196- expect . objectContaining ( {
197- data : {
198- 'sentry.op' : 'ui.mount' ,
199- 'sentry.origin ' : 'auto. ui.vue ' ,
200- } ,
201- description : 'Vue <ComponentOneView>' ,
202- op : 'ui.mount' ,
203- origin : 'auto.ui.vue' ,
204- } ) ,
185+ // without `<>`
186+ const componentMainViewSpan = uiSpans . find ( span => span . description === 'Vue <ComponentMainView>' ) ;
187+ expect ( componentMainViewSpan ) . toMatchObject ( {
188+ data : {
189+ 'sentry.op ' : 'ui.mount ' ,
190+ 'sentry.origin' : 'auto.ui.vue' ,
191+ } ,
192+ op : 'ui.mount' ,
193+ origin : 'auto.ui.vue' ,
194+ } ) ;
205195
206- // not tracked
207- expect . not . objectContaining ( {
208- data : {
209- 'sentry.op' : 'ui.mount' ,
210- 'sentry.origin' : 'auto.ui.vue' ,
211- } ,
212- description : 'Vue <ComponentTwoView>' ,
213- op : 'ui.mount' ,
214- origin : 'auto.ui.vue' ,
215- } ) ,
216- ] ) ,
217- transaction : '/components' ,
218- transaction_info : {
219- source : 'route' ,
196+ // with `<>`
197+ const componentOneViewSpan = uiSpans . find ( span => span . description === 'Vue <ComponentOneView>' ) ;
198+ expect ( componentOneViewSpan ) . toMatchObject ( {
199+ data : {
200+ 'sentry.op' : 'ui.mount' ,
201+ 'sentry.origin' : 'auto.ui.vue' ,
220202 } ,
203+ op : 'ui.mount' ,
204+ origin : 'auto.ui.vue' ,
221205 } ) ;
206+
207+ // `ComponentTwoView` renders on this route but is absent from `trackComponents`
208+ // not tracked
209+ expect ( uiSpanDescriptions ) . not . toContain ( 'Vue <ComponentTwoView>' ) ;
222210} ) ;
0 commit comments