Skip to content

Commit 7680875

Browse files
committed
ReactRouter 3 and 6
1 parent 05262ff commit 7680875

File tree

4 files changed

+234
-4
lines changed

4 files changed

+234
-4
lines changed

packages/react/src/reactrouterv6.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
1414
getActiveSpan,
1515
getClient,
16+
getCurrentScope,
1617
getRootSpan,
1718
spanToJSON,
1819
} from '@sentry/core';
@@ -198,10 +199,15 @@ function updatePageloadTransaction(
198199
? matches
199200
: (_matchRoutes(routes, location, basename) as unknown as RouteMatch[]);
200201

201-
if (activeRootSpan && branches) {
202+
if (branches) {
202203
const [name, source] = getNormalizedName(routes, location, branches, basename);
203-
activeRootSpan.updateName(name);
204-
activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
204+
205+
getCurrentScope().setTransactionName(name);
206+
207+
if (activeRootSpan) {
208+
activeRootSpan.updateName(name);
209+
activeRootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
210+
}
205211
}
206212
}
207213

packages/react/test/reactrouterv3.test.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ const mockStartBrowserTracingPageLoadSpan = jest.fn();
2929
const mockStartBrowserTracingNavigationSpan = jest.fn();
3030

3131
const mockRootSpan = {
32-
updateName: jest.fn(),
3332
setAttribute: jest.fn(),
3433
getSpanJSON() {
3534
return { op: 'pageload' };
@@ -115,6 +114,18 @@ describe('browserTracingReactRouterV3', () => {
115114
});
116115
});
117116

117+
it("updates the scope's `transactionName` on pageload", () => {
118+
const client = createMockBrowserClient();
119+
setCurrentClient(client);
120+
121+
client.addIntegration(reactRouterV3BrowserTracingIntegration({ history, routes: instrumentationRoutes, match }));
122+
123+
client.init();
124+
render(<Router history={history}>{routes}</Router>);
125+
126+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/');
127+
});
128+
118129
it('starts a navigation transaction', () => {
119130
const client = createMockBrowserClient();
120131
setCurrentClient(client);
@@ -192,4 +203,23 @@ describe('browserTracingReactRouterV3', () => {
192203
},
193204
});
194205
});
206+
207+
it("updates the scope's `transactionName` on a navigation", () => {
208+
const client = createMockBrowserClient();
209+
210+
const history = createMemoryHistory();
211+
client.addIntegration(reactRouterV3BrowserTracingIntegration({ history, routes: instrumentationRoutes, match }));
212+
213+
client.init();
214+
const { container } = render(<Router history={history}>{routes}</Router>);
215+
216+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/');
217+
218+
act(() => {
219+
history.push('/users/123');
220+
});
221+
expect(container.innerHTML).toContain('123');
222+
223+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/users/:userid');
224+
});
195225
});

packages/react/test/reactrouterv6.4.test.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,39 @@ describe('reactRouterV6BrowserTracingIntegration (v6.4)', () => {
122122
});
123123
});
124124

125+
it("updates the scope's `transactionName` on a pageload", () => {
126+
const client = createMockBrowserClient();
127+
setCurrentClient(client);
128+
129+
client.addIntegration(
130+
reactRouterV6BrowserTracingIntegration({
131+
useEffect: React.useEffect,
132+
useLocation,
133+
useNavigationType,
134+
createRoutesFromChildren,
135+
matchRoutes,
136+
}),
137+
);
138+
const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction);
139+
140+
const router = sentryCreateBrowserRouter(
141+
[
142+
{
143+
path: '/',
144+
element: <div>TEST</div>,
145+
},
146+
],
147+
{
148+
initialEntries: ['/'],
149+
},
150+
);
151+
152+
// @ts-expect-error router is fine
153+
render(<RouterProvider router={router} />);
154+
155+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/');
156+
});
157+
125158
it('starts a navigation transaction', () => {
126159
const client = createMockBrowserClient();
127160
setCurrentClient(client);
@@ -590,5 +623,42 @@ describe('reactRouterV6BrowserTracingIntegration (v6.4)', () => {
590623
},
591624
});
592625
});
626+
627+
it("updates the scope's `transactionName` on a navigation", () => {
628+
const client = createMockBrowserClient();
629+
setCurrentClient(client);
630+
631+
client.addIntegration(
632+
reactRouterV6BrowserTracingIntegration({
633+
useEffect: React.useEffect,
634+
useLocation,
635+
useNavigationType,
636+
createRoutesFromChildren,
637+
matchRoutes,
638+
}),
639+
);
640+
const sentryCreateBrowserRouter = wrapCreateBrowserRouter(createMemoryRouter as CreateRouterFunction);
641+
642+
const router = sentryCreateBrowserRouter(
643+
[
644+
{
645+
path: '/',
646+
element: <Navigate to="/about" />,
647+
},
648+
{
649+
path: 'about',
650+
element: <div>About</div>,
651+
},
652+
],
653+
{
654+
initialEntries: ['/'],
655+
},
656+
);
657+
658+
// @ts-expect-error router is fine
659+
render(<RouterProvider router={router} />);
660+
661+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/about');
662+
});
593663
});
594664
});

packages/react/test/reactrouterv6.test.tsx

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,32 @@ describe('reactRouterV6BrowserTracingIntegration', () => {
114114
});
115115
});
116116

117+
it("updates the scope's `transactionName` on a pageload", () => {
118+
const client = createMockBrowserClient();
119+
setCurrentClient(client);
120+
121+
client.addIntegration(
122+
reactRouterV6BrowserTracingIntegration({
123+
useEffect: React.useEffect,
124+
useLocation,
125+
useNavigationType,
126+
createRoutesFromChildren,
127+
matchRoutes,
128+
}),
129+
);
130+
const SentryRoutes = withSentryReactRouterV6Routing(Routes);
131+
132+
render(
133+
<MemoryRouter initialEntries={['/']}>
134+
<SentryRoutes>
135+
<Route path="/" element={<div>Home</div>} />
136+
</SentryRoutes>
137+
</MemoryRouter>,
138+
);
139+
140+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/');
141+
});
142+
117143
it('skips pageload transaction with `instrumentPageLoad: false`', () => {
118144
const client = createMockBrowserClient();
119145
setCurrentClient(client);
@@ -364,6 +390,35 @@ describe('reactRouterV6BrowserTracingIntegration', () => {
364390
},
365391
});
366392
});
393+
394+
it("updates the scope's `transactionName` on a navigation", () => {
395+
const client = createMockBrowserClient();
396+
setCurrentClient(client);
397+
398+
client.addIntegration(
399+
reactRouterV6BrowserTracingIntegration({
400+
useEffect: React.useEffect,
401+
useLocation,
402+
useNavigationType,
403+
createRoutesFromChildren,
404+
matchRoutes,
405+
}),
406+
);
407+
const SentryRoutes = withSentryReactRouterV6Routing(Routes);
408+
409+
render(
410+
<MemoryRouter initialEntries={['/']}>
411+
<SentryRoutes>
412+
<Route path="/about" element={<div>About</div>}>
413+
<Route path="/about/:page" element={<div>page</div>} />
414+
</Route>
415+
<Route path="/" element={<Navigate to="/about/us" />} />
416+
</SentryRoutes>
417+
</MemoryRouter>,
418+
);
419+
420+
expect(getCurrentScope().getScopeData()?.transactionName).toBe('/about/:page');
421+
});
367422
});
368423

369424
describe('wrapUseRoutes', () => {
@@ -408,6 +463,39 @@ describe('reactRouterV6BrowserTracingIntegration', () => {
408463
});
409464
});
410465

466+
it("updates the scope's `transactionName` on a pageload", () => {
467+
const client = createMockBrowserClient();
468+
setCurrentClient(client);
469+
470+
client.addIntegration(
471+
reactRouterV6BrowserTracingIntegration({
472+
useEffect: React.useEffect,
473+
useLocation,
474+
useNavigationType,
475+
createRoutesFromChildren,
476+
matchRoutes,
477+
}),
478+
);
479+
480+
const wrappedUseRoutes = wrapUseRoutes(useRoutes);
481+
482+
const Routes = () =>
483+
wrappedUseRoutes([
484+
{
485+
path: '/',
486+
element: <div>Home</div>,
487+
},
488+
]);
489+
490+
render(
491+
<MemoryRouter initialEntries={['/']}>
492+
<Routes />
493+
</MemoryRouter>,
494+
);
495+
496+
expect(getCurrentScope().getScopeData()?.transactionName).toEqual('/');
497+
});
498+
411499
it('skips pageload transaction with `instrumentPageLoad: false`', () => {
412500
const client = createMockBrowserClient();
413501
setCurrentClient(client);
@@ -875,5 +963,41 @@ describe('reactRouterV6BrowserTracingIntegration', () => {
875963
expect(mockRootSpan.updateName).toHaveBeenLastCalledWith('/tests/:testId/*');
876964
expect(mockRootSpan.setAttribute).toHaveBeenCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
877965
});
966+
967+
it("updates the scope's `transactionName` on a navigation", () => {
968+
const client = createMockBrowserClient();
969+
setCurrentClient(client);
970+
971+
client.addIntegration(
972+
reactRouterV6BrowserTracingIntegration({
973+
useEffect: React.useEffect,
974+
useLocation,
975+
useNavigationType,
976+
createRoutesFromChildren,
977+
matchRoutes,
978+
}),
979+
);
980+
const wrappedUseRoutes = wrapUseRoutes(useRoutes);
981+
982+
const Routes = () =>
983+
wrappedUseRoutes([
984+
{
985+
path: '/',
986+
element: <Navigate to="/about" />,
987+
},
988+
{
989+
path: '/about',
990+
element: <div>About</div>,
991+
},
992+
]);
993+
994+
render(
995+
<MemoryRouter initialEntries={['/']}>
996+
<Routes />
997+
</MemoryRouter>,
998+
);
999+
1000+
expect(getCurrentScope().getScopeData()?.transactionName).toBe('/about');
1001+
});
8781002
});
8791003
});

0 commit comments

Comments
 (0)