From db9563fa3fe96712d8347f5d18c215923e79e71b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 4 Apr 2024 12:13:02 +0200 Subject: [PATCH 1/2] feat(vue): Update scope's `transactionName` when resolving a route --- .../vue-3/src/router/index.ts | 4 +++ .../vue-3/src/views/UserIdErrorView.vue | 10 +++++++ .../vue-3/tests/errors.test.ts | 29 +++++++++++++++++++ packages/vue/src/router.ts | 13 +++++---- packages/vue/test/router.test.ts | 29 +++++++++++++++++++ 5 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdErrorView.vue diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts b/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts index a17208711eff..a8b146ef3c4e 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts @@ -17,6 +17,10 @@ const router = createRouter({ path: '/users/:id', component: () => import('../views/UserIdView.vue'), }, + { + path: '/users-error/:id', + component: () => import('../views/UserIdView.vue'), + }, ], }); diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdErrorView.vue b/dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdErrorView.vue new file mode 100644 index 000000000000..dab7ea873f37 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdErrorView.vue @@ -0,0 +1,10 @@ + + + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts index b9933299c8c0..f829339bfb33 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts @@ -25,5 +25,34 @@ test('sends an error', async ({ page }) => { }, ], }, + transaction: '/', + }); +}); + +test('sends an error with a parameterized transaction name', async ({ page }) => { + const errorPromise = waitForError('vue-3', async errorEvent => { + return !errorEvent.type; + }); + + await page.goto(`/users-error/456`); + + await page.locator('#userErrorBtn').click(); + + const error = await errorPromise; + + expect(error).toMatchObject({ + exception: { + values: [ + { + type: 'Error', + value: 'This is a Vue test error', + mechanism: { + type: 'generic', + handled: false, + }, + }, + ], + }, + transaction: '/users-error/:id', }); }); diff --git a/packages/vue/src/router.ts b/packages/vue/src/router.ts index 8edb6eefd640..016dac913986 100644 --- a/packages/vue/src/router.ts +++ b/packages/vue/src/router.ts @@ -3,6 +3,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getActiveSpan, + getCurrentScope, getRootSpan, spanToJSON, } from '@sentry/core'; @@ -77,22 +78,24 @@ export function instrumentVueRouter( } // Determine a name for the routing transaction and where that name came from - let transactionName: string = to.path; + let spanName: string = to.path; let transactionSource: TransactionSource = 'url'; if (to.name && options.routeLabel !== 'path') { - transactionName = to.name.toString(); + spanName = to.name.toString(); transactionSource = 'custom'; } else if (to.matched[0] && to.matched[0].path) { - transactionName = to.matched[0].path; + spanName = to.matched[0].path; transactionSource = 'route'; } + getCurrentScope().setTransactionName(spanName); + if (options.instrumentPageLoad && isPageLoadNavigation) { const activeRootSpan = getActiveRootSpan(); if (activeRootSpan) { const existingAttributes = spanToJSON(activeRootSpan).data || {}; if (existingAttributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] !== 'custom') { - activeRootSpan.updateName(transactionName); + activeRootSpan.updateName(spanName); activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, transactionSource); } // Set router attributes on the existing pageload transaction @@ -108,7 +111,7 @@ export function instrumentVueRouter( attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = transactionSource; attributes[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] = 'auto.navigation.vue'; startNavigationSpanFn({ - name: transactionName, + name: spanName, op: 'navigation', attributes, }); diff --git a/packages/vue/test/router.test.ts b/packages/vue/test/router.test.ts index 1a27e84961b1..8ff42d49e2b9 100644 --- a/packages/vue/test/router.test.ts +++ b/packages/vue/test/router.test.ts @@ -276,6 +276,35 @@ describe('instrumentVueRouter()', () => { expect(mockRootSpan.name).toEqual('customTxnName'); }); + it("updates the scope's `transactionName` when a route is resolved", () => { + const mockStartSpan = jest.fn().mockImplementation(_ => { + return {}; + }); + + const scopeSetTransactionNameSpy = jest.fn(); + + // @ts-expect-error - only creating a partial scope but that's fine + jest.spyOn(SentryCore, 'getCurrentScope').mockImplementation(() => ({ + setTransactionName: scopeSetTransactionNameSpy, + })); + + instrumentVueRouter( + mockVueRouter, + { routeLabel: 'name', instrumentPageLoad: true, instrumentNavigation: true }, + mockStartSpan, + ); + + const beforeEachCallback = mockVueRouter.beforeEach.mock.calls[0][0]; + + const from = testRoutes['initialPageloadRoute']; + const to = testRoutes['normalRoute1']; + + beforeEachCallback(to, from, mockNext); + + expect(scopeSetTransactionNameSpy).toHaveBeenCalledTimes(1); + expect(scopeSetTransactionNameSpy).toHaveBeenCalledWith('/books/:bookId/chapter/:chapterId'); + }); + test.each([ [false, 0], [true, 1], From b00bb795958f65376f6338ba9f86916d7041e142 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Thu, 4 Apr 2024 12:22:28 +0200 Subject: [PATCH 2/2] fix vue router e2e setup --- .../e2e-tests/test-applications/vue-3/src/router/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts b/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts index a8b146ef3c4e..8c3ac217716f 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts @@ -19,7 +19,7 @@ const router = createRouter({ }, { path: '/users-error/:id', - component: () => import('../views/UserIdView.vue'), + component: () => import('../views/UserIdErrorView.vue'), }, ], });