1
1
import { BrowserClient } from '@sentry/browser' ;
2
2
import { Hub , makeMain } from '@sentry/hub' ;
3
+ import * as utils from '@sentry/utils' ;
3
4
4
5
import { Span , SpanStatus , Transaction } from '../../src' ;
5
6
import { fetchCallback , FetchData , registerRequestInstrumentation } from '../../src/browser/request' ;
6
7
import { addExtensionMethods } from '../../src/hubextensions' ;
7
8
8
- declare global {
9
- // eslint-disable-next-line @typescript-eslint/no-namespace
10
- namespace NodeJS {
11
- interface Global {
12
- // Have to mock out Request because it is not defined in jest environment
13
- Request : Request ;
14
- }
15
- }
16
- }
17
-
18
9
beforeAll ( ( ) => {
19
10
addExtensionMethods ( ) ;
20
- // @ts -ignore need to override global Request
11
+ // @ts -ignore need to override global Request because it's not in the jest environment (even with an
12
+ // `@jest-environment jsdom` directive, for some reason)
21
13
global . Request = { } ;
22
14
} ) ;
23
15
24
- const mockAddInstrumentationHandler = jest . fn ( ) ;
25
- let mockFetchCallback = jest . fn ( ) ;
26
- let mockXHRCallback = jest . fn ( ) ;
27
- jest . mock ( '@sentry/utils' , ( ) => {
28
- const actual = jest . requireActual ( '@sentry/utils' ) ;
29
- return {
30
- ...actual ,
31
- addInstrumentationHandler : ( { callback, type } : any ) => {
32
- if ( type === 'fetch' ) {
33
- mockFetchCallback = jest . fn ( callback ) ;
34
- }
35
- if ( type === 'xhr' ) {
36
- mockXHRCallback = jest . fn ( callback ) ;
37
- }
38
- if ( typeof mockAddInstrumentationHandler === 'function' ) {
39
- return mockAddInstrumentationHandler ( { callback, type } ) ;
40
- }
41
- } ,
42
- } ;
43
- } ) ;
16
+ const addInstrumentationHandler = jest . spyOn ( utils , 'addInstrumentationHandler' ) ;
44
17
45
18
describe ( 'registerRequestInstrumentation' , ( ) => {
46
19
beforeEach ( ( ) => {
47
- mockFetchCallback . mockReset ( ) ;
48
- mockXHRCallback . mockReset ( ) ;
49
- mockAddInstrumentationHandler . mockReset ( ) ;
20
+ jest . clearAllMocks ( ) ;
50
21
} ) ;
51
22
52
- it ( 'tracks fetch and xhr requests' , ( ) => {
23
+ it ( 'instruments fetch and xhr requests' , ( ) => {
53
24
registerRequestInstrumentation ( ) ;
54
- expect ( mockAddInstrumentationHandler ) . toHaveBeenCalledTimes ( 2 ) ;
55
- // fetch
56
- expect ( mockAddInstrumentationHandler ) . toHaveBeenNthCalledWith ( 1 , { callback : expect . any ( Function ) , type : 'fetch' } ) ;
57
- // xhr
58
- expect ( mockAddInstrumentationHandler ) . toHaveBeenNthCalledWith ( 2 , { callback : expect . any ( Function ) , type : 'xhr' } ) ;
25
+
26
+ expect ( addInstrumentationHandler ) . toHaveBeenCalledWith ( {
27
+ callback : expect . any ( Function ) ,
28
+ type : 'fetch' ,
29
+ } ) ;
30
+ expect ( addInstrumentationHandler ) . toHaveBeenCalledWith ( {
31
+ callback : expect . any ( Function ) ,
32
+ type : 'xhr' ,
33
+ } ) ;
59
34
} ) ;
60
35
61
- it ( 'does not add fetch requests spans if traceFetch is false' , ( ) => {
36
+ it ( 'does not instrument fetch requests if traceFetch is false' , ( ) => {
62
37
registerRequestInstrumentation ( { traceFetch : false } ) ;
63
- expect ( mockAddInstrumentationHandler ) . toHaveBeenCalledTimes ( 1 ) ;
64
- expect ( mockFetchCallback ( ) ) . toBe ( undefined ) ;
38
+
39
+ expect ( addInstrumentationHandler ) . not . toHaveBeenCalledWith ( { callback : expect . any ( Function ) , type : 'fetch' } ) ;
65
40
} ) ;
66
41
67
- it ( 'does not add xhr requests spans if traceXHR is false' , ( ) => {
42
+ it ( 'does not instrument xhr requests if traceXHR is false' , ( ) => {
68
43
registerRequestInstrumentation ( { traceXHR : false } ) ;
69
- expect ( mockAddInstrumentationHandler ) . toHaveBeenCalledTimes ( 1 ) ;
70
- expect ( mockXHRCallback ( ) ) . toBe ( undefined ) ;
44
+
45
+ expect ( addInstrumentationHandler ) . not . toHaveBeenCalledWith ( { callback : expect . any ( Function ) , type : 'xhr' } ) ;
71
46
} ) ;
72
47
} ) ;
73
48
74
- describe ( '_fetchCallback() ' , ( ) => {
49
+ describe ( 'callbacks ' , ( ) => {
75
50
let hub : Hub ;
76
51
let transaction : Transaction ;
52
+ const alwaysCreateSpan = jest . fn ( ) . mockReturnValue ( true ) ;
53
+ const neverCreateSpan = jest . fn ( ) . mockReturnValue ( false ) ;
54
+ const fetchHandlerData : FetchData = {
55
+ args : [ 'http://dogs.are.great/' , { } ] ,
56
+ fetchData : { url : 'http://dogs.are.great/' , method : 'GET' } ,
57
+ startTimestamp : 1356996072000 ,
58
+ } ;
59
+ const endTimestamp = 1356996072000 ;
60
+
77
61
beforeAll ( ( ) => {
78
62
hub = new Hub ( new BrowserClient ( { tracesSampleRate : 1 } ) ) ;
79
63
makeMain ( hub ) ;
@@ -84,106 +68,78 @@ describe('_fetchCallback()', () => {
84
68
hub . configureScope ( scope => scope . setSpan ( transaction ) ) ;
85
69
} ) ;
86
70
87
- it ( 'does not create span if it should not be created' , ( ) => {
88
- const shouldCreateSpan = ( url : string ) : boolean => url === '/organizations' ;
89
- const data : FetchData = {
90
- args : [ '/users' ] ,
91
- fetchData : {
92
- method : 'GET' ,
93
- url : '/users' ,
94
- } ,
95
- startTimestamp : 1595509730275 ,
96
- } ;
97
- const spans = { } ;
98
-
99
- fetchCallback ( data , shouldCreateSpan , spans ) ;
100
- expect ( spans ) . toEqual ( { } ) ;
101
- } ) ;
71
+ describe ( 'fetchCallback()' , ( ) => {
72
+ it ( 'does not create span if shouldCreateSpan returns false' , ( ) => {
73
+ const spans = { } ;
102
74
103
- it ( 'does not create span if there is no fetch data' , ( ) => {
104
- const shouldCreateSpan = ( _ : string ) : boolean => true ;
105
- const data : FetchData = {
106
- args : [ ] ,
107
- startTimestamp : 1595509730275 ,
108
- } ;
109
- const spans = { } ;
75
+ fetchCallback ( fetchHandlerData , neverCreateSpan , spans ) ;
110
76
111
- fetchCallback ( data , shouldCreateSpan , spans ) ;
112
- expect ( spans ) . toEqual ( { } ) ;
113
- } ) ;
77
+ expect ( spans ) . toEqual ( { } ) ;
78
+ } ) ;
114
79
115
- it ( 'creates and finishes fetch span on active transaction' , ( ) => {
116
- const shouldCreateSpan = ( _ : string ) : boolean => true ;
117
- const data : FetchData = {
118
- args : [ '/users' ] ,
119
- fetchData : {
120
- method : 'GET' ,
121
- url : '/users' ,
122
- } ,
123
- startTimestamp : 1595509730275 ,
124
- } ;
125
- const spans : Record < string , Span > = { } ;
126
-
127
- // Start fetch request
128
- fetchCallback ( data , shouldCreateSpan , spans ) ;
129
- const spanKey = Object . keys ( spans ) [ 0 ] ;
130
-
131
- const fetchSpan = spans [ spanKey ] ;
132
- expect ( fetchSpan ) . toBeInstanceOf ( Span ) ;
133
- expect ( fetchSpan . data ) . toEqual ( {
134
- method : 'GET' ,
135
- type : 'fetch' ,
136
- url : '/users' ,
80
+ it ( 'does not create span if there is no fetch data in handler data' , ( ) => {
81
+ const noFetchData = { args : fetchHandlerData . args , startTimestamp : fetchHandlerData . startTimestamp } ;
82
+ const spans = { } ;
83
+
84
+ fetchCallback ( noFetchData , alwaysCreateSpan , spans ) ;
85
+ expect ( spans ) . toEqual ( { } ) ;
137
86
} ) ;
138
- expect ( fetchSpan . description ) . toBe ( 'GET /users' ) ;
139
- expect ( fetchSpan . op ) . toBe ( 'http' ) ;
140
- if ( data . fetchData ) {
141
- expect ( data . fetchData . __span ) . toBeDefined ( ) ;
142
- } else {
143
- fail ( 'Fetch data does not exist' ) ;
144
- }
145
-
146
- const newData = {
147
- ...data ,
148
- endTimestamp : data . startTimestamp + 12343234 ,
149
- } ;
150
-
151
- // End fetch request
152
- fetchCallback ( newData , shouldCreateSpan , spans ) ;
153
- expect ( spans ) . toEqual ( { } ) ;
154
- if ( transaction . spanRecorder ) {
155
- expect ( transaction . spanRecorder . spans [ 1 ] . endTimestamp ) . toBeDefined ( ) ;
156
- } else {
157
- fail ( 'Transaction does not have span recorder' ) ;
158
- }
159
- } ) ;
160
87
161
- it ( 'sets response status on finish' , ( ) => {
162
- const shouldCreateSpan = ( _ : string ) : boolean => true ;
163
- const data : FetchData = {
164
- args : [ '/users' ] ,
165
- fetchData : {
88
+
89
+ it ( 'creates and finishes fetch span on active transaction' , ( ) => {
90
+ const spans = { } ;
91
+
92
+ // triggered by request being sent
93
+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
94
+
95
+ const newSpan = transaction . spanRecorder ?. spans [ 1 ] ;
96
+
97
+ expect ( newSpan ) . toBeDefined ( ) ;
98
+ expect ( newSpan ) . toBeInstanceOf ( Span ) ;
99
+ expect ( newSpan ! . data ) . toEqual ( {
166
100
method : 'GET' ,
167
- url : '/users' ,
168
- } ,
169
- startTimestamp : 1595509730275 ,
170
- } ;
171
- const spans : Record < string , Span > = { } ;
172
-
173
- // Start fetch request
174
- fetchCallback ( data , shouldCreateSpan , spans ) ;
175
-
176
- const newData = {
177
- ...data ,
178
- endTimestamp : data . startTimestamp + 12343234 ,
179
- response : { status : 404 } as Response ,
180
- } ;
181
- // End fetch request
182
- fetchCallback ( newData , shouldCreateSpan , spans ) ;
183
- if ( transaction . spanRecorder ) {
184
- expect ( transaction . spanRecorder . spans [ 1 ] . status ) . toBe ( SpanStatus . fromHttpCode ( 404 ) ) ;
185
- } else {
186
- fail ( 'Transaction does not have span recorder' ) ;
187
- }
101
+ type : 'fetch' ,
102
+ url : 'http://dogs.are.great/' ,
103
+ } ) ;
104
+ expect ( newSpan ! . description ) . toBe ( 'GET http://dogs.are.great/' ) ;
105
+ expect ( newSpan ! . op ) . toBe ( 'http' ) ;
106
+ expect ( fetchHandlerData . fetchData ?. __span ) . toBeDefined ( ) ;
107
+
108
+ const postRequestFetchHandlerData = {
109
+ ...fetchHandlerData ,
110
+ endTimestamp,
111
+ } ;
112
+
113
+ // triggered by response coming back
114
+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , spans ) ;
115
+
116
+ expect ( newSpan ! . endTimestamp ) . toBeDefined ( ) ;
117
+ } ) ;
118
+
119
+ it ( 'sets response status on finish' , ( ) => {
120
+ const spans : Record < string , Span > = { } ;
121
+
122
+ // triggered by request being sent
123
+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
124
+
125
+ const newSpan = transaction . spanRecorder ?. spans [ 1 ] ;
126
+
127
+ expect ( newSpan ) . toBeDefined ( ) ;
128
+
129
+ const postRequestFetchHandlerData = {
130
+ ...fetchHandlerData ,
131
+ endTimestamp,
132
+ response : { status : 404 } as Response ,
133
+ } ;
134
+
135
+ // triggered by response coming back
136
+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , spans ) ;
137
+
138
+ expect ( newSpan ! . status ) . toBe ( SpanStatus . fromHttpCode ( 404 ) ) ;
139
+ } ) ;
140
+
141
+ it ( 'adds sentry-trace header to fetch requests' , ( ) => {
142
+ // TODO
143
+ } ) ;
188
144
} ) ;
189
145
} ) ;
0 commit comments