Skip to content

Commit 574aa19

Browse files
s1gr1dJPeer264
authored andcommitted
test(vue): Add test cases for using Options API (mixins) (#23564)
Adds tests for the mixins API (we had none so far). And also adds an E2E test variant without the Options API, to demonstrate that the SDK currently does not send UI spans without it (`ui.render`, `ui.mount`, ...). Ref: #23375 Vue Options API Compile time flag: https://vuejs.org/api/compile-time-flags.html#VUE_OPTIONS_API
1 parent 9171bdd commit 574aa19

4 files changed

Lines changed: 285 additions & 70 deletions

File tree

dev-packages/e2e-tests/test-applications/vue-3/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"test:assert": "pnpm test:print-version && playwright test",
1616
"test:build-canary": "pnpm install && pnpm test:install-canary && pnpm build",
1717
"test:build-latest": "pnpm install && pnpm add vue@latest && pnpm build",
18+
"test:build-no-options-api": "pnpm install && VUE_OPTIONS_API=false pnpm build",
19+
"test:assert-no-options-api": "VUE_OPTIONS_API=false pnpm test:assert",
1820
"test:install-canary": "pnpm add vue@$(git ls-remote --tags --sort='v:refname' https://github.com/vuejs/core.git | tail -n1 | awk -F'/' '{print $NF}')",
1921
"test:print-version": "node -p \"'Vue version: ' + require('vue/package.json').version\""
2022
},
@@ -52,6 +54,11 @@
5254
{
5355
"build-command": "pnpm test:build-canary",
5456
"label": "vue-3 (canary)"
57+
},
58+
{
59+
"build-command": "pnpm test:build-no-options-api",
60+
"assert-command": "pnpm test:assert-no-options-api",
61+
"label": "vue-3 (no Options API)"
5562
}
5663
]
5764
}

dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts

Lines changed: 58 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { expect, test } from '@playwright/test';
22
import { 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+
47
test('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(/^https?:\/\/localhost:\d+\/components$/),
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
});

dev-packages/e2e-tests/test-applications/vue-3/vite.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ import vue from '@vitejs/plugin-vue';
44
import vueJsx from '@vitejs/plugin-vue-jsx';
55
import { defineConfig } from 'vite';
66

7+
// Nuxt 5 disables the Options API by default (users can disable it too for smaller bundle size)
8+
const optionsApi = process.env.VUE_OPTIONS_API === 'false' ? 'false' : 'true';
9+
710
// https://vitejs.dev/config/
811
export default defineConfig({
912
plugins: [vue(), vueJsx()],
13+
define: {
14+
__VUE_OPTIONS_API__: optionsApi,
15+
},
1016
resolve: {
1117
alias: {
1218
'@': fileURLToPath(new URL('./src', import.meta.url)),

0 commit comments

Comments
 (0)