Skip to content

Commit e6ea537

Browse files
authored
fix(vue): Don't call next in Vue router 4 instrumentation (#8351)
Vue Router 4 no longer exposes a `next` resolver function to call inside a `beforeEach` [router guard callback](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards). Instead it will resolve the hook when the callback function returns. This PR checks if `next` is available and only calls it then. Also it adjusts the types accordingly and adds a test that previously failed. fixes #8349
1 parent a4c858f commit e6ea537

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

packages/vue/src/router.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type Route = {
3939

4040
interface VueRouter {
4141
onError: (fn: (err: Error) => void) => void;
42-
beforeEach: (fn: (to: Route, from: Route, next: () => void) => void) => void;
42+
beforeEach: (fn: (to: Route, from: Route, next?: () => void) => void) => void;
4343
}
4444

4545
/**
@@ -129,7 +129,12 @@ export function vueRouterInstrumentation(
129129
});
130130
}
131131

132-
next();
132+
// Vue Router 4 no longer exposes the `next` function, so we need to
133+
// check if it's available before calling it.
134+
// `next` needs to be called in Vue Router 3 so that the hook is resolved.
135+
if (next) {
136+
next();
137+
}
133138
});
134139
};
135140
}

packages/vue/test/router.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const captureExceptionSpy = jest.spyOn(SentryBrowser, 'captureException');
99

1010
const mockVueRouter = {
1111
onError: jest.fn<void, [(error: Error) => void]>(),
12-
beforeEach: jest.fn<void, [(from: Route, to: Route, next: () => void) => void]>(),
12+
beforeEach: jest.fn<void, [(from: Route, to: Route, next?: () => void) => void]>(),
1313
};
1414

1515
const mockStartTransaction = jest.fn();
@@ -324,4 +324,30 @@ describe('vueRouterInstrumentation()', () => {
324324
expect(mockStartTransaction).toHaveBeenCalledTimes(expectedCallsAmount + 1);
325325
},
326326
);
327+
328+
it("doesn't throw when `next` is not available in the beforeEach callback (Vue Router 4)", () => {
329+
const instrument = vueRouterInstrumentation(mockVueRouter, { routeLabel: 'path' });
330+
instrument(mockStartTransaction, true, true);
331+
const beforeEachCallback = mockVueRouter.beforeEach.mock.calls[0][0];
332+
333+
const from = testRoutes.normalRoute1;
334+
const to = testRoutes.namedRoute;
335+
beforeEachCallback(to, from, undefined);
336+
337+
// first startTx call happens when the instrumentation is initialized (for pageloads)
338+
expect(mockStartTransaction).toHaveBeenLastCalledWith({
339+
name: '/login',
340+
metadata: {
341+
source: 'route',
342+
},
343+
data: {
344+
params: to.params,
345+
query: to.query,
346+
},
347+
op: 'navigation',
348+
tags: {
349+
'routing.instrumentation': 'vue-router',
350+
},
351+
});
352+
});
327353
});

0 commit comments

Comments
 (0)