1
+ import { Transaction } from '@sentry/types' ;
1
2
import { getGlobalObject } from '@sentry/utils' ;
2
3
import { JSDOM } from 'jsdom' ;
3
4
import { NEXT_DATA as NextData } from 'next/dist/next-server/lib/utils' ;
@@ -16,18 +17,24 @@ const globalObject = getGlobalObject<
16
17
const originalBuildManifest = globalObject . __BUILD_MANIFEST ;
17
18
const originalBuildManifestRoutes = globalObject . __BUILD_MANIFEST ?. sortedPages ;
18
19
20
+ let eventHandlers : { [ eventName : string ] : Set < ( ...args : any [ ] ) => void > } = { } ;
21
+
19
22
jest . mock ( 'next/router' , ( ) => {
20
- const eventHandlers : { [ eventName : string ] : ( ( ...args : any [ ] ) => void ) [ ] } = { } ;
21
23
return {
22
24
default : {
23
25
events : {
24
26
on ( type : string , handler : ( ...args : any [ ] ) => void ) {
25
- if ( eventHandlers [ type ] ) {
26
- eventHandlers [ type ] . push ( handler ) ;
27
- } else {
28
- eventHandlers [ type ] = [ handler ] ;
27
+ if ( ! eventHandlers [ type ] ) {
28
+ eventHandlers [ type ] = new Set ( ) ;
29
29
}
30
+
31
+ eventHandlers [ type ] . add ( handler ) ;
30
32
} ,
33
+ off : jest . fn ( ( type : string , handler : ( ...args : any [ ] ) => void ) => {
34
+ if ( eventHandlers [ type ] ) {
35
+ eventHandlers [ type ] . delete ( handler ) ;
36
+ }
37
+ } ) ,
31
38
emit ( type : string , ...eventArgs : any [ ] ) {
32
39
if ( eventHandlers [ type ] ) {
33
40
eventHandlers [ type ] . forEach ( eventHandler => {
@@ -40,6 +47,18 @@ jest.mock('next/router', () => {
40
47
} ;
41
48
} ) ;
42
49
50
+ function createMockStartTransaction ( ) {
51
+ return jest . fn (
52
+ ( ) =>
53
+ ( {
54
+ startChild : ( ) => ( {
55
+ finish : ( ) => undefined ,
56
+ } ) ,
57
+ finish : ( ) => undefined ,
58
+ } as Transaction ) ,
59
+ ) ;
60
+ }
61
+
43
62
describe ( 'nextRouterInstrumentation' , ( ) => {
44
63
const originalGlobalDocument = getGlobalObject < Window > ( ) . document ;
45
64
const originalGlobalLocation = getGlobalObject < Window > ( ) . location ;
@@ -94,6 +113,12 @@ describe('nextRouterInstrumentation', () => {
94
113
if ( ( global as any ) . __BUILD_MANIFEST ) {
95
114
( global as any ) . __BUILD_MANIFEST . sortedPages = originalBuildManifestRoutes ;
96
115
}
116
+
117
+ // Clear all event handlers
118
+ eventHandlers = { } ;
119
+
120
+ // Necessary to clear all Router.events.off() mock call numbers
121
+ jest . clearAllMocks ( ) ;
97
122
} ) ;
98
123
99
124
describe ( 'pageload transactions' , ( ) => {
@@ -187,7 +212,7 @@ describe('nextRouterInstrumentation', () => {
187
212
] ) (
188
213
'creates a pageload transaction (#%#)' ,
189
214
( url , route , query , props , hasNextData , expectedStartTransactionArgument ) => {
190
- const mockStartTransaction = jest . fn ( ) ;
215
+ const mockStartTransaction = createMockStartTransaction ( ) ;
191
216
setUpNextPage ( { url, route, query, props, hasNextData } ) ;
192
217
nextRouterInstrumentation ( mockStartTransaction ) ;
193
218
expect ( mockStartTransaction ) . toHaveBeenCalledTimes ( 1 ) ;
@@ -196,7 +221,7 @@ describe('nextRouterInstrumentation', () => {
196
221
) ;
197
222
198
223
it ( 'does not create a pageload transaction if option not given' , ( ) => {
199
- const mockStartTransaction = jest . fn ( ) ;
224
+ const mockStartTransaction = createMockStartTransaction ( ) ;
200
225
setUpNextPage ( { url : 'https://example.com/' , route : '/' , hasNextData : false } ) ;
201
226
nextRouterInstrumentation ( mockStartTransaction , false ) ;
202
227
expect ( mockStartTransaction ) . toHaveBeenCalledTimes ( 0 ) ;
@@ -225,7 +250,7 @@ describe('nextRouterInstrumentation', () => {
225
250
] ) (
226
251
'should create a parameterized transaction on route change (%s)' ,
227
252
( targetLocation , expectedTransactionName , expectedTransactionSource ) => {
228
- const mockStartTransaction = jest . fn ( ) ;
253
+ const mockStartTransaction = createMockStartTransaction ( ) ;
229
254
230
255
setUpNextPage ( {
231
256
url : 'https://example.com/home' ,
@@ -261,11 +286,17 @@ describe('nextRouterInstrumentation', () => {
261
286
} ) ,
262
287
} ) ,
263
288
) ;
289
+
290
+ Router . events . emit ( 'routeChangeComplete' , targetLocation ) ;
291
+ // eslint-disable-next-line @typescript-eslint/unbound-method
292
+ expect ( Router . events . off ) . toHaveBeenCalledWith ( 'routeChangeComplete' , expect . anything ( ) ) ;
293
+ // eslint-disable-next-line @typescript-eslint/unbound-method
294
+ expect ( Router . events . off ) . toHaveBeenCalledTimes ( 1 ) ;
264
295
} ,
265
296
) ;
266
297
267
298
it ( 'should not create transaction when navigation transactions are disabled' , ( ) => {
268
- const mockStartTransaction = jest . fn ( ) ;
299
+ const mockStartTransaction = createMockStartTransaction ( ) ;
269
300
270
301
setUpNextPage ( {
271
302
url : 'https://example.com/home' ,
0 commit comments