@@ -3,7 +3,7 @@ import { Hub, makeMain } from '@sentry/core';
3
3
import * as utils from '@sentry/utils' ;
4
4
5
5
import { Span , spanStatusfromHttpCode , Transaction } from '../../src' ;
6
- import { fetchCallback , FetchData , instrumentOutgoingRequests , xhrCallback } from '../../src/browser/request' ;
6
+ import { fetchCallback , FetchData , instrumentOutgoingRequests , xhrCallback , XHRData } from '../../src/browser/request' ;
7
7
import { addExtensionMethods } from '../../src/hubextensions' ;
8
8
import * as tracingUtils from '../../src/utils' ;
9
9
import { getDefaultBrowserClientOptions } from '../testutils' ;
@@ -48,29 +48,9 @@ describe('callbacks', () => {
48
48
let hub : Hub ;
49
49
let transaction : Transaction ;
50
50
const alwaysCreateSpan = ( ) => true ;
51
- const neverCreateSpan = ( ) => false ;
51
+ const alwaysAttachHeaders = ( ) => true ;
52
52
const startTimestamp = 1356996072000 ;
53
53
const endTimestamp = 1356996072000 ;
54
- const fetchHandlerData : FetchData = {
55
- args : [ 'http://dogs.are.great/' , { } ] ,
56
- fetchData : { url : 'http://dogs.are.great/' , method : 'GET' } ,
57
- startTimestamp,
58
- } ;
59
- const xhrHandlerData = {
60
- xhr : {
61
- __sentry_xhr__ : {
62
- method : 'GET' ,
63
- url : 'http://dogs.are.great/' ,
64
- status_code : 200 ,
65
- data : { } ,
66
- } ,
67
- __sentry_xhr_span_id__ : '1231201211212012' ,
68
- // eslint-disable-next-line @typescript-eslint/unbound-method
69
- // setRequestHeader: XMLHttpRequest.prototype.setRequestHeader,
70
- setRequestHeader,
71
- } ,
72
- startTimestamp,
73
- } ;
74
54
75
55
beforeAll ( ( ) => {
76
56
const options = getDefaultBrowserClientOptions ( { tracesSampleRate : 1 } ) ;
@@ -83,52 +63,91 @@ describe('callbacks', () => {
83
63
hub . configureScope ( scope => scope . setSpan ( transaction ) ) ;
84
64
} ) ;
85
65
86
- describe ( 'fetchCallback()' , ( ) => {
87
- it ( 'does not create span if shouldCreateSpan returns false' , ( ) => {
88
- const spans = { } ;
66
+ afterEach ( ( ) => {
67
+ jest . clearAllMocks ( ) ;
68
+ } ) ;
89
69
90
- fetchCallback ( fetchHandlerData , neverCreateSpan , spans ) ;
70
+ describe ( 'fetchCallback()' , ( ) => {
71
+ let fetchHandlerData : FetchData ;
91
72
92
- expect ( spans ) . toEqual ( { } ) ;
73
+ const fetchSpan = {
74
+ data : {
75
+ method : 'GET' ,
76
+ url : 'http://dogs.are.great/' ,
77
+ type : 'fetch' ,
78
+ } ,
79
+ description : 'GET http://dogs.are.great/' ,
80
+ op : 'http.client' ,
81
+ parentSpanId : expect . any ( String ) ,
82
+ spanId : expect . any ( String ) ,
83
+ startTimestamp : expect . any ( Number ) ,
84
+ traceId : expect . any ( String ) ,
85
+ } ;
86
+
87
+ beforeEach ( ( ) => {
88
+ fetchHandlerData = {
89
+ args : [ 'http://dogs.are.great/' , { } ] ,
90
+ fetchData : { url : 'http://dogs.are.great/' , method : 'GET' } ,
91
+ startTimestamp,
92
+ } ;
93
93
} ) ;
94
94
95
- it ( 'does not create span if there is no fetch data in handler data' , ( ) => {
96
- const noFetchData = { args : fetchHandlerData . args , startTimestamp : fetchHandlerData . startTimestamp } ;
97
- const spans = { } ;
98
-
99
- fetchCallback ( noFetchData , alwaysCreateSpan , spans ) ;
100
- expect ( spans ) . toEqual ( { } ) ;
101
- } ) ;
95
+ it . each ( [
96
+ // each case is [shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys]
97
+ [ true , true , expect . objectContaining ( fetchSpan ) , [ 'sentry-trace' , 'baggage' ] ] ,
98
+ [ true , false , expect . objectContaining ( fetchSpan ) , [ ] ] ,
99
+ // If there's no span then there's no parent span id to stick into a header, so no headers, even if there's a
100
+ // `tracingOrigins` match
101
+ [ false , true , undefined , [ ] ] ,
102
+ [ false , false , undefined , [ ] ] ,
103
+ ] ) (
104
+ 'span creation/header attachment interaction - shouldCreateSpan: %s, shouldAttachHeaders: %s' ,
105
+ ( shouldCreateSpanReturnValue , shouldAttachHeadersReturnValue , expectedSpan , expectedHeaderKeys ) => {
106
+ fetchCallback (
107
+ fetchHandlerData ,
108
+ ( ) => shouldCreateSpanReturnValue ,
109
+ ( ) => shouldAttachHeadersReturnValue ,
110
+ { } ,
111
+ ) ;
112
+
113
+ // spans[0] is the transaction itself
114
+ const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
115
+ expect ( newSpan ) . toEqual ( expectedSpan ) ;
116
+
117
+ const headers = ( fetchHandlerData . args [ 1 ] . headers as Record < string , string > ) || { } ;
118
+ expect ( Object . keys ( headers ) ) . toEqual ( expectedHeaderKeys ) ;
119
+ } ,
120
+ ) ;
102
121
103
- it ( 'does not add fetch request spans if tracing is disabled ' , ( ) => {
104
- hasTracingEnabled . mockReturnValueOnce ( false ) ;
122
+ it ( 'adds neither fetch request spans nor fetch request headers if there is no fetch data in handler data ' , ( ) => {
123
+ delete fetchHandlerData . fetchData ;
105
124
const spans = { } ;
106
125
107
- fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
126
+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
127
+
108
128
expect ( spans ) . toEqual ( { } ) ;
129
+
130
+ const headers = ( fetchHandlerData . args [ 1 ] . headers as Record < string , string > ) || { } ;
131
+ expect ( Object . keys ( headers ) ) . toEqual ( [ ] ) ;
109
132
} ) ;
110
133
111
- it ( 'does not add fetch request headers if tracing is disabled' , ( ) => {
134
+ it ( 'adds neither fetch request spans nor fetch request headers if tracing is disabled' , ( ) => {
112
135
hasTracingEnabled . mockReturnValueOnce ( false ) ;
136
+ const spans = { } ;
113
137
114
- // make a local copy so the global one doesn't get mutated
115
- const handlerData : FetchData = {
116
- args : [ 'http://dogs.are.great/' , { } ] ,
117
- fetchData : { url : 'http://dogs.are.great/' , method : 'GET' } ,
118
- startTimestamp : 1353501072000 ,
119
- } ;
138
+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
120
139
121
- fetchCallback ( handlerData , alwaysCreateSpan , { } ) ;
140
+ expect ( spans ) . toEqual ( { } ) ;
122
141
123
- const headers = ( handlerData . args [ 1 ] . headers as Record < string , string > ) || { } ;
124
- expect ( headers [ 'sentry-trace' ] ) . not . toBeDefined ( ) ;
142
+ const headers = ( fetchHandlerData . args [ 1 ] . headers as Record < string , string > ) || { } ;
143
+ expect ( Object . keys ( headers ) ) . toEqual ( [ ] ) ;
125
144
} ) ;
126
145
127
146
it ( 'creates and finishes fetch span on active transaction' , ( ) => {
128
147
const spans = { } ;
129
148
130
149
// triggered by request being sent
131
- fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
150
+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
132
151
133
152
const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
134
153
@@ -149,7 +168,7 @@ describe('callbacks', () => {
149
168
} ;
150
169
151
170
// triggered by response coming back
152
- fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , spans ) ;
171
+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
153
172
154
173
expect ( newSpan . endTimestamp ) . toBeDefined ( ) ;
155
174
} ) ;
@@ -158,7 +177,7 @@ describe('callbacks', () => {
158
177
const spans : Record < string , Span > = { } ;
159
178
160
179
// triggered by request being sent
161
- fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
180
+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
162
181
163
182
const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
164
183
@@ -171,7 +190,7 @@ describe('callbacks', () => {
171
190
} ;
172
191
173
192
// triggered by response coming back
174
- fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , spans ) ;
193
+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
175
194
176
195
expect ( newSpan . status ) . toBe ( spanStatusfromHttpCode ( 404 ) ) ;
177
196
} ) ;
@@ -186,7 +205,7 @@ describe('callbacks', () => {
186
205
} ;
187
206
188
207
// in that case, the response coming back will be ignored
189
- fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , { } ) ;
208
+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
190
209
191
210
const newSpan = transaction . spanRecorder ?. spans [ 1 ] ;
192
211
@@ -199,57 +218,89 @@ describe('callbacks', () => {
199
218
200
219
expect ( transaction . metadata . propagations ) . toBe ( 0 ) ;
201
220
202
- fetchCallback ( firstReqData , alwaysCreateSpan , { } ) ;
221
+ fetchCallback ( firstReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
203
222
expect ( transaction . metadata . propagations ) . toBe ( 1 ) ;
204
223
205
- fetchCallback ( secondReqData , alwaysCreateSpan , { } ) ;
224
+ fetchCallback ( secondReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
206
225
expect ( transaction . metadata . propagations ) . toBe ( 2 ) ;
207
226
} ) ;
208
-
209
- it ( 'adds sentry-trace header to fetch requests' , ( ) => {
210
- // TODO
211
- } ) ;
212
227
} ) ;
213
228
214
229
describe ( 'xhrCallback()' , ( ) => {
215
- it ( 'does not create span if shouldCreateSpan returns false' , ( ) => {
216
- const spans = { } ;
217
-
218
- xhrCallback ( xhrHandlerData , neverCreateSpan , spans ) ;
230
+ let xhrHandlerData : XHRData ;
219
231
220
- expect ( spans ) . toEqual ( { } ) ;
232
+ const xhrSpan = {
233
+ data : {
234
+ method : 'GET' ,
235
+ url : 'http://dogs.are.great/' ,
236
+ type : 'xhr' ,
237
+ } ,
238
+ description : 'GET http://dogs.are.great/' ,
239
+ op : 'http.client' ,
240
+ parentSpanId : expect . any ( String ) ,
241
+ spanId : expect . any ( String ) ,
242
+ startTimestamp : expect . any ( Number ) ,
243
+ traceId : expect . any ( String ) ,
244
+ } ;
245
+
246
+ beforeEach ( ( ) => {
247
+ xhrHandlerData = {
248
+ xhr : {
249
+ __sentry_xhr__ : {
250
+ method : 'GET' ,
251
+ url : 'http://dogs.are.great/' ,
252
+ status_code : 200 ,
253
+ data : { } ,
254
+ } ,
255
+ __sentry_xhr_span_id__ : '1231201211212012' ,
256
+ setRequestHeader,
257
+ } ,
258
+ startTimestamp,
259
+ } ;
221
260
} ) ;
222
261
223
- it ( 'does not add xhr request spans if tracing is disabled' , ( ) => {
224
- hasTracingEnabled . mockReturnValueOnce ( false ) ;
225
- const spans = { } ;
226
-
227
- xhrCallback ( xhrHandlerData , alwaysCreateSpan , spans ) ;
228
- expect ( spans ) . toEqual ( { } ) ;
229
- } ) ;
262
+ it . each ( [
263
+ // each case is [shouldCreateSpanReturnValue, shouldAttachHeadersReturnValue, expectedSpan, expectedHeaderKeys]
264
+ [ true , true , expect . objectContaining ( xhrSpan ) , [ 'sentry-trace' , 'baggage' ] ] ,
265
+ [ true , false , expect . objectContaining ( xhrSpan ) , [ ] ] ,
266
+ // If there's no span then there's no parent span id to stick into a header, so no headers, even if there's a
267
+ // `tracingOrigins` match
268
+ [ false , true , undefined , [ ] ] ,
269
+ [ false , false , undefined , [ ] ] ,
270
+ ] ) (
271
+ 'span creation/header attachment interaction - shouldCreateSpan: %s, shouldAttachHeaders: %s' ,
272
+ ( shouldCreateSpanReturnValue , shouldAttachHeadersReturnValue , expectedSpan , expectedHeaderKeys ) => {
273
+ xhrCallback (
274
+ xhrHandlerData ,
275
+ ( ) => shouldCreateSpanReturnValue ,
276
+ ( ) => shouldAttachHeadersReturnValue ,
277
+ { } ,
278
+ ) ;
279
+
280
+ // spans[0] is the transaction itself
281
+ const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
282
+ expect ( newSpan ) . toEqual ( expectedSpan ) ;
283
+
284
+ const headerKeys = setRequestHeader . mock . calls . map ( header => header [ 0 ] ) ;
285
+ expect ( headerKeys ) . toEqual ( expectedHeaderKeys ) ;
286
+ } ,
287
+ ) ;
230
288
231
- it ( 'does not add xhr request headers if tracing is disabled' , ( ) => {
289
+ it ( 'adds neither xhr request spans nor xhr request headers if tracing is disabled' , ( ) => {
232
290
hasTracingEnabled . mockReturnValueOnce ( false ) ;
291
+ const spans = { } ;
233
292
234
- xhrCallback ( xhrHandlerData , alwaysCreateSpan , { } ) ;
293
+ xhrCallback ( xhrHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
235
294
295
+ expect ( spans ) . toEqual ( { } ) ;
236
296
expect ( setRequestHeader ) . not . toHaveBeenCalled ( ) ;
237
297
} ) ;
238
298
239
- it ( 'adds sentry-trace header to XHR requests' , ( ) => {
240
- xhrCallback ( xhrHandlerData , alwaysCreateSpan , { } ) ;
241
-
242
- expect ( setRequestHeader ) . toHaveBeenCalledWith (
243
- 'sentry-trace' ,
244
- expect . stringMatching ( tracingUtils . TRACEPARENT_REGEXP ) ,
245
- ) ;
246
- } ) ;
247
-
248
299
it ( 'creates and finishes XHR span on active transaction' , ( ) => {
249
300
const spans = { } ;
250
301
251
302
// triggered by request being sent
252
- xhrCallback ( xhrHandlerData , alwaysCreateSpan , spans ) ;
303
+ xhrCallback ( xhrHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
253
304
254
305
const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
255
306
@@ -261,16 +312,16 @@ describe('callbacks', () => {
261
312
} ) ;
262
313
expect ( newSpan . description ) . toBe ( 'GET http://dogs.are.great/' ) ;
263
314
expect ( newSpan . op ) . toBe ( 'http.client' ) ;
264
- expect ( xhrHandlerData . xhr . __sentry_xhr_span_id__ ) . toBeDefined ( ) ;
265
- expect ( xhrHandlerData . xhr . __sentry_xhr_span_id__ ) . toEqual ( newSpan ?. spanId ) ;
315
+ expect ( xhrHandlerData . xhr ? .__sentry_xhr_span_id__ ) . toBeDefined ( ) ;
316
+ expect ( xhrHandlerData . xhr ? .__sentry_xhr_span_id__ ) . toEqual ( newSpan ?. spanId ) ;
266
317
267
318
const postRequestXHRHandlerData = {
268
319
...xhrHandlerData ,
269
320
endTimestamp,
270
321
} ;
271
322
272
323
// triggered by response coming back
273
- xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , spans ) ;
324
+ xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
274
325
275
326
expect ( newSpan . endTimestamp ) . toBeDefined ( ) ;
276
327
} ) ;
@@ -279,7 +330,7 @@ describe('callbacks', () => {
279
330
const spans = { } ;
280
331
281
332
// triggered by request being sent
282
- xhrCallback ( xhrHandlerData , alwaysCreateSpan , spans ) ;
333
+ xhrCallback ( xhrHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
283
334
284
335
const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
285
336
@@ -289,10 +340,10 @@ describe('callbacks', () => {
289
340
...xhrHandlerData ,
290
341
endTimestamp,
291
342
} ;
292
- postRequestXHRHandlerData . xhr . __sentry_xhr__ . status_code = 404 ;
343
+ postRequestXHRHandlerData . xhr ! . __sentry_xhr__ ! . status_code = 404 ;
293
344
294
345
// triggered by response coming back
295
- xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , spans ) ;
346
+ xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
296
347
297
348
expect ( newSpan . status ) . toBe ( spanStatusfromHttpCode ( 404 ) ) ;
298
349
} ) ;
@@ -303,15 +354,15 @@ describe('callbacks', () => {
303
354
const postRequestXHRHandlerData = {
304
355
...{
305
356
xhr : {
306
- __sentry_xhr__ : xhrHandlerData . xhr . __sentry_xhr__ ,
357
+ __sentry_xhr__ : xhrHandlerData . xhr ? .__sentry_xhr__ ,
307
358
} ,
308
359
} ,
309
360
startTimestamp,
310
361
endTimestamp,
311
362
} ;
312
363
313
364
// in that case, the response coming back will be ignored
314
- xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , { } ) ;
365
+ xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
315
366
316
367
const newSpan = transaction . spanRecorder ?. spans [ 1 ] ;
317
368
@@ -324,10 +375,10 @@ describe('callbacks', () => {
324
375
325
376
expect ( transaction . metadata . propagations ) . toBe ( 0 ) ;
326
377
327
- xhrCallback ( firstReqData , alwaysCreateSpan , { } ) ;
378
+ xhrCallback ( firstReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
328
379
expect ( transaction . metadata . propagations ) . toBe ( 1 ) ;
329
380
330
- xhrCallback ( secondReqData , alwaysCreateSpan , { } ) ;
381
+ xhrCallback ( secondReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
331
382
expect ( transaction . metadata . propagations ) . toBe ( 2 ) ;
332
383
} ) ;
333
384
} ) ;
0 commit comments