Skip to content

Commit

Permalink
fix(vue): Ensure Vue trackComponents list matches components with o…
Browse files Browse the repository at this point in the history
…r without `<>` (#13543)

Ensure that the component names listed in the `trackComponent` option match regardless of if they were specified as `<Name>` or `Name`. Add unit and e2e tests for component tracking. 

---------

Signed-off-by: Kaung Zin Hein <kaungzinhein113@gmail.com>
  • Loading branch information
Zen-cronic committed Sep 9, 2024
1 parent facaae4 commit a7d3a9d
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 2 deletions.
1 change: 1 addition & 0 deletions dev-packages/e2e-tests/test-applications/vue-3/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Sentry.init({
}),
],
tunnel: `http://localhost:3031/`, // proxy server
trackComponents: ['ComponentMainView', '<ComponentOneView>'],
});

app.use(router);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const router = createRouter({
},
],
},
{
path: '/components',
component: () => import('../views/ComponentMainView.vue'),
},
],
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script setup lang="ts">
import ComponentOneView from './ComponentOneView.vue';
import ComponentTwoView from './ComponentTwoView.vue';
</script>

<template>
<h1>Demonstrating Component Tracking</h1>
<ComponentOneView />
<ComponentTwoView />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script setup lang="ts">
function log() {
console.log('Component One!');
}
</script>

<template>
<h1>Component One</h1>
<button id="componentOneBtn" @click="log">Click to Log</button>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script setup lang="ts">
function log() {
console.log('Component Two!');
}
</script>

<template>
<h1>Component One</h1>
<button id="componentTwoBtn" @click="log">Click to Log</button>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,86 @@ test('sends a pageload transaction with a route name as transaction name if avai
},
});
});

test('sends a lifecycle span for each tracked components', async ({ page }) => {
const transactionPromise = waitForTransaction('vue-3', async transactionEvent => {
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
});

await page.goto(`/components`);

const rootSpan = await transactionPromise;

expect(rootSpan).toMatchObject({
contexts: {
trace: {
data: {
'sentry.source': 'route',
'sentry.origin': 'auto.pageload.vue',
'sentry.op': 'pageload',
},
op: 'pageload',
origin: 'auto.pageload.vue',
},
},
spans: expect.arrayContaining([
// enabled by default
expect.objectContaining({
data: {
'sentry.op': 'ui.vue.render',
'sentry.origin': 'auto.ui.vue',
},
description: 'Application Render',
op: 'ui.vue.render',
origin: 'auto.ui.vue',
}),
// enabled by default
expect.objectContaining({
data: {
'sentry.op': 'ui.vue.mount',
'sentry.origin': 'auto.ui.vue',
},
description: 'Vue <Root>',
op: 'ui.vue.mount',
origin: 'auto.ui.vue',
}),

// without `<>`
expect.objectContaining({
data: {
'sentry.op': 'ui.vue.mount',
'sentry.origin': 'auto.ui.vue',
},
description: 'Vue <ComponentMainView>',
op: 'ui.vue.mount',
origin: 'auto.ui.vue',
}),

// with `<>`
expect.objectContaining({
data: {
'sentry.op': 'ui.vue.mount',
'sentry.origin': 'auto.ui.vue',
},
description: 'Vue <ComponentOneView>',
op: 'ui.vue.mount',
origin: 'auto.ui.vue',
}),

// not tracked
expect.not.objectContaining({
data: {
'sentry.op': 'ui.vue.mount',
'sentry.origin': 'auto.ui.vue',
},
description: 'Vue <ComponentTwoView>',
op: 'ui.vue.mount',
origin: 'auto.ui.vue',
}),
]),
transaction: '/components',
transaction_info: {
source: 'route',
},
});
});
18 changes: 16 additions & 2 deletions packages/vue/src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ function finishRootSpan(vm: VueSentry, timestamp: number, timeout: number): void
}, timeout);
}

/** Find if the current component exists in the provided `TracingOptions.trackComponents` array option. */
export function findTrackComponent(trackComponents: string[], formattedName: string): boolean {
function extractComponentName(name: string): string {
return name.replace(/^<([^\s]*)>(?: at [^\s]*)?$/, '$1');
}

const isMatched = trackComponents.some(compo => {
return extractComponentName(formattedName) === extractComponentName(compo);
});

return isMatched;
}

export const createTracingMixins = (options: TracingOptions): Mixins => {
const hooks = (options.hooks || [])
.concat(DEFAULT_HOOKS)
Expand Down Expand Up @@ -84,8 +97,9 @@ export const createTracingMixins = (options: TracingOptions): Mixins => {

// Skip components that we don't want to track to minimize the noise and give a more granular control to the user
const name = formatComponentName(this, false);

const shouldTrack = Array.isArray(options.trackComponents)
? options.trackComponents.indexOf(name) > -1
? findTrackComponent(options.trackComponents, name)
: options.trackComponents;

// We always want to track root component
Expand All @@ -109,7 +123,7 @@ export const createTracingMixins = (options: TracingOptions): Mixins => {
}

this.$_sentrySpans[operation] = startInactiveSpan({
name: `Vue <${name}>`,
name: `Vue ${name}`,
op: `${VUE_OP}.${operation}`,
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.vue',
Expand Down
44 changes: 44 additions & 0 deletions packages/vue/test/tracing/trackComponents.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { describe, expect, it } from 'vitest';
import { findTrackComponent } from '../../src/tracing';

describe('findTrackComponent', () => {
describe('when user-defined array contains `<Component>`', () => {
it('returns true if a match is found', () => {
// arrange
const trackComponents = ['<ABC>', '<XYZ>'];
const formattedComponentName = '<XYZ>';

// act
const shouldTrack = findTrackComponent(trackComponents, formattedComponentName);

// assert
expect(shouldTrack).toBe(true);
});
});
describe('when user-defined array contains `Component` without the `<>`', () => {
it('returns true if a match is found', () => {
// arrange
const trackComponents = ['ABC', 'XYZ'];
const formattedComponentName = '<XYZ>';

// act
const shouldTrack = findTrackComponent(trackComponents, formattedComponentName);

// assert
expect(shouldTrack).toBe(true);
});
});
describe('when the vue file name is include in the formatted component name', () => {
it('returns true if a match is found', () => {
// arrange
const trackComponents = ['ABC', 'XYZ'];
const formattedComponentName = '<XYZ> at XYZ.vue';

// act
const shouldTrack = findTrackComponent(trackComponents, formattedComponentName);

// assert
expect(shouldTrack).toBe(true);
});
});
});

0 comments on commit a7d3a9d

Please sign in to comment.