Skip to content

Commit 76e99de

Browse files
committed
ref(react): Add source to react-router-v3
1 parent 1cfcb0d commit 76e99de

File tree

2 files changed

+101
-22
lines changed

2 files changed

+101
-22
lines changed

packages/react/src/reactrouterv3.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Primitive, Transaction, TransactionContext } from '@sentry/types';
1+
import { Primitive, Transaction, TransactionContext, TransactionSource } from '@sentry/types';
22
import { getGlobalObject } from '@sentry/utils';
33

44
import { Location, ReactRouterInstrumentation } from './types';
@@ -19,6 +19,8 @@ export type Match = (
1919
cb: (error?: Error, _redirectLocation?: Location, renderProps?: { routes?: Route[] }) => void,
2020
) => void;
2121

22+
type ReactRouterV3TransactionSource = Extract<TransactionSource, 'url' | 'route'>;
23+
2224
const global = getGlobalObject<Window>();
2325

2426
/**
@@ -44,16 +46,24 @@ export function reactRouterV3Instrumentation(
4446

4547
// Have to use global.location because history.location might not be defined.
4648
if (startTransactionOnPageLoad && global && global.location) {
47-
normalizeTransactionName(routes, global.location as unknown as Location, match, (localName: string) => {
48-
prevName = localName;
49-
activeTransaction = startTransaction({
50-
name: prevName,
51-
op: 'pageload',
52-
tags: {
53-
'routing.instrumentation': 'react-router-v3',
54-
},
55-
});
56-
});
49+
normalizeTransactionName(
50+
routes,
51+
global.location as unknown as Location,
52+
match,
53+
(localName: string, source: ReactRouterV3TransactionSource = 'url') => {
54+
prevName = localName;
55+
activeTransaction = startTransaction({
56+
name: prevName,
57+
op: 'pageload',
58+
tags: {
59+
'routing.instrumentation': 'react-router-v3',
60+
},
61+
metadata: {
62+
source,
63+
},
64+
});
65+
},
66+
);
5767
}
5868

5969
if (startTransactionOnLocationChange && history.listen) {
@@ -68,12 +78,15 @@ export function reactRouterV3Instrumentation(
6878
if (prevName) {
6979
tags.from = prevName;
7080
}
71-
normalizeTransactionName(routes, location, match, (localName: string) => {
81+
normalizeTransactionName(routes, location, match, (localName: string, source: TransactionSource = 'url') => {
7282
prevName = localName;
7383
activeTransaction = startTransaction({
7484
name: prevName,
7585
op: 'navigation',
7686
tags,
87+
metadata: {
88+
source,
89+
},
7790
});
7891
});
7992
}
@@ -89,7 +102,7 @@ function normalizeTransactionName(
89102
appRoutes: Route[],
90103
location: Location,
91104
match: Match,
92-
callback: (pathname: string) => void,
105+
callback: (pathname: string, source?: ReactRouterV3TransactionSource) => void,
93106
): void {
94107
let name = location.pathname;
95108
match(
@@ -108,7 +121,7 @@ function normalizeTransactionName(
108121
}
109122

110123
name = routePath;
111-
return callback(name);
124+
return callback(name, 'route');
112125
},
113126
);
114127
}

packages/react/test/reactrouterv3.test.tsx

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ declare module 'react-router-3' {
1515
export const createRoutes: (routes: any) => RouteType[];
1616
}
1717

18+
function createMockStartTransaction(opts: { finish?: jest.FunctionLike; setMetadata?: jest.FunctionLike } = {}) {
19+
const { finish = jest.fn(), setMetadata = jest.fn() } = opts;
20+
return jest.fn().mockReturnValue({
21+
finish,
22+
setMetadata,
23+
});
24+
}
25+
1826
describe('React Router V3', () => {
1927
const routes = (
2028
<Route path="/" component={({ children }: { children: JSX.Element }) => <div>{children}</div>}>
@@ -37,24 +45,27 @@ describe('React Router V3', () => {
3745
const instrumentation = reactRouterV3Instrumentation(history, instrumentationRoutes, match);
3846

3947
it('starts a pageload transaction when instrumentation is started', () => {
40-
const mockStartTransaction = jest.fn();
48+
const mockStartTransaction = createMockStartTransaction();
4149
instrumentation(mockStartTransaction);
4250
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
4351
expect(mockStartTransaction).toHaveBeenLastCalledWith({
4452
name: '/',
4553
op: 'pageload',
4654
tags: { 'routing.instrumentation': 'react-router-v3' },
55+
metadata: {
56+
source: 'route',
57+
},
4758
});
4859
});
4960

5061
it('does not start pageload transaction if option is false', () => {
51-
const mockStartTransaction = jest.fn();
62+
const mockStartTransaction = createMockStartTransaction();
5263
instrumentation(mockStartTransaction, false);
5364
expect(mockStartTransaction).toHaveBeenCalledTimes(0);
5465
});
5566

5667
it('starts a navigation transaction', () => {
57-
const mockStartTransaction = jest.fn();
68+
const mockStartTransaction = createMockStartTransaction();
5869
instrumentation(mockStartTransaction);
5970
render(<Router history={history}>{routes}</Router>);
6071

@@ -66,6 +77,9 @@ describe('React Router V3', () => {
6677
name: '/about',
6778
op: 'navigation',
6879
tags: { from: '/', 'routing.instrumentation': 'react-router-v3' },
80+
metadata: {
81+
source: 'route',
82+
},
6983
});
7084

7185
act(() => {
@@ -76,18 +90,21 @@ describe('React Router V3', () => {
7690
name: '/features',
7791
op: 'navigation',
7892
tags: { from: '/about', 'routing.instrumentation': 'react-router-v3' },
93+
metadata: {
94+
source: 'route',
95+
},
7996
});
8097
});
8198

8299
it('does not start a transaction if option is false', () => {
83-
const mockStartTransaction = jest.fn();
100+
const mockStartTransaction = createMockStartTransaction();
84101
instrumentation(mockStartTransaction, true, false);
85102
render(<Router history={history}>{routes}</Router>);
86103
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
87104
});
88105

89106
it('only starts a navigation transaction on push', () => {
90-
const mockStartTransaction = jest.fn();
107+
const mockStartTransaction = createMockStartTransaction();
91108
instrumentation(mockStartTransaction);
92109
render(<Router history={history}>{routes}</Router>);
93110

@@ -99,7 +116,7 @@ describe('React Router V3', () => {
99116

100117
it('finishes a transaction on navigation', () => {
101118
const mockFinish = jest.fn();
102-
const mockStartTransaction = jest.fn().mockReturnValue({ finish: mockFinish });
119+
const mockStartTransaction = createMockStartTransaction({ finish: mockFinish });
103120
instrumentation(mockStartTransaction);
104121
render(<Router history={history}>{routes}</Router>);
105122
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
@@ -112,7 +129,7 @@ describe('React Router V3', () => {
112129
});
113130

114131
it('normalizes transaction names', () => {
115-
const mockStartTransaction = jest.fn();
132+
const mockStartTransaction = createMockStartTransaction();
116133
instrumentation(mockStartTransaction);
117134
const { container } = render(<Router history={history}>{routes}</Router>);
118135

@@ -126,11 +143,14 @@ describe('React Router V3', () => {
126143
name: '/users/:userid',
127144
op: 'navigation',
128145
tags: { from: '/', 'routing.instrumentation': 'react-router-v3' },
146+
metadata: {
147+
source: 'route',
148+
},
129149
});
130150
});
131151

132152
it('normalizes nested transaction names', () => {
133-
const mockStartTransaction = jest.fn();
153+
const mockStartTransaction = createMockStartTransaction();
134154
instrumentation(mockStartTransaction);
135155
const { container } = render(<Router history={history}>{routes}</Router>);
136156

@@ -144,6 +164,9 @@ describe('React Router V3', () => {
144164
name: '/organizations/:orgid/v1/:teamid',
145165
op: 'navigation',
146166
tags: { from: '/', 'routing.instrumentation': 'react-router-v3' },
167+
metadata: {
168+
source: 'route',
169+
},
147170
});
148171

149172
act(() => {
@@ -156,6 +179,49 @@ describe('React Router V3', () => {
156179
name: '/organizations/:orgid',
157180
op: 'navigation',
158181
tags: { from: '/organizations/:orgid/v1/:teamid', 'routing.instrumentation': 'react-router-v3' },
182+
metadata: {
183+
source: 'route',
184+
},
185+
});
186+
});
187+
188+
it('sets metadata to url if on an unknown route', () => {
189+
const mockStartTransaction = createMockStartTransaction();
190+
instrumentation(mockStartTransaction);
191+
render(<Router history={history}>{routes}</Router>);
192+
193+
act(() => {
194+
history.push('/organizations/1234/some/other/route');
195+
});
196+
197+
expect(mockStartTransaction).toHaveBeenCalledTimes(2);
198+
expect(mockStartTransaction).toHaveBeenLastCalledWith({
199+
name: '/organizations/1234/some/other/route',
200+
op: 'navigation',
201+
tags: { from: '/', 'routing.instrumentation': 'react-router-v3' },
202+
metadata: {
203+
source: 'url',
204+
},
205+
});
206+
});
207+
208+
it('sets metadata to url if no routes are provided', () => {
209+
const fakeRoutes = <div>hello</div>;
210+
const mockStartTransaction = createMockStartTransaction();
211+
const mockInstrumentation = reactRouterV3Instrumentation(history, createRoutes(fakeRoutes), match);
212+
mockInstrumentation(mockStartTransaction);
213+
// We render here with `routes` instead of `fakeRoutes` from above to validate the case
214+
// where users provided the instrumentation with a bad set of routes.
215+
render(<Router history={history}>{routes}</Router>);
216+
217+
expect(mockStartTransaction).toHaveBeenCalledTimes(1);
218+
expect(mockStartTransaction).toHaveBeenLastCalledWith({
219+
name: '/',
220+
op: 'pageload',
221+
tags: { 'routing.instrumentation': 'react-router-v3' },
222+
metadata: {
223+
source: 'url',
224+
},
159225
});
160226
});
161227
});

0 commit comments

Comments
 (0)