@@ -3,7 +3,7 @@ import { Hub, makeMain } from '@sentry/core';
33import * as utils from '@sentry/utils' ;
44
55import { 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' ;
77import { addExtensionMethods } from '../../src/hubextensions' ;
88import * as tracingUtils from '../../src/utils' ;
99import { getDefaultBrowserClientOptions } from '../testutils' ;
@@ -48,29 +48,9 @@ describe('callbacks', () => {
4848 let hub : Hub ;
4949 let transaction : Transaction ;
5050 const alwaysCreateSpan = ( ) => true ;
51- const neverCreateSpan = ( ) => false ;
51+ const alwaysAttachHeaders = ( ) => true ;
5252 const startTimestamp = 1356996072000 ;
5353 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- } ;
7454
7555 beforeAll ( ( ) => {
7656 const options = getDefaultBrowserClientOptions ( { tracesSampleRate : 1 } ) ;
@@ -83,52 +63,91 @@ describe('callbacks', () => {
8363 hub . configureScope ( scope => scope . setSpan ( transaction ) ) ;
8464 } ) ;
8565
86- describe ( 'fetchCallback()' , ( ) => {
87- it ( 'does not create span if shouldCreateSpan returns false' , ( ) => {
88- const spans = { } ;
66+ afterEach ( ( ) => {
67+ jest . clearAllMocks ( ) ;
68+ } ) ;
8969
90- fetchCallback ( fetchHandlerData , neverCreateSpan , spans ) ;
70+ describe ( 'fetchCallback()' , ( ) => {
71+ let fetchHandlerData : FetchData ;
9172
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+ } ;
9393 } ) ;
9494
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+ ) ;
102121
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 ;
105124 const spans = { } ;
106125
107- fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
126+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
127+
108128 expect ( spans ) . toEqual ( { } ) ;
129+
130+ const headers = ( fetchHandlerData . args [ 1 ] . headers as Record < string , string > ) || { } ;
131+ expect ( Object . keys ( headers ) ) . toEqual ( [ ] ) ;
109132 } ) ;
110133
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' , ( ) => {
112135 hasTracingEnabled . mockReturnValueOnce ( false ) ;
136+ const spans = { } ;
113137
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 ) ;
120139
121- fetchCallback ( handlerData , alwaysCreateSpan , { } ) ;
140+ expect ( spans ) . toEqual ( { } ) ;
122141
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 ( [ ] ) ;
125144 } ) ;
126145
127146 it ( 'creates and finishes fetch span on active transaction' , ( ) => {
128147 const spans = { } ;
129148
130149 // triggered by request being sent
131- fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
150+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
132151
133152 const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
134153
@@ -149,7 +168,7 @@ describe('callbacks', () => {
149168 } ;
150169
151170 // triggered by response coming back
152- fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , spans ) ;
171+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
153172
154173 expect ( newSpan . endTimestamp ) . toBeDefined ( ) ;
155174 } ) ;
@@ -158,7 +177,7 @@ describe('callbacks', () => {
158177 const spans : Record < string , Span > = { } ;
159178
160179 // triggered by request being sent
161- fetchCallback ( fetchHandlerData , alwaysCreateSpan , spans ) ;
180+ fetchCallback ( fetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
162181
163182 const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
164183
@@ -171,7 +190,7 @@ describe('callbacks', () => {
171190 } ;
172191
173192 // triggered by response coming back
174- fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , spans ) ;
193+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
175194
176195 expect ( newSpan . status ) . toBe ( spanStatusfromHttpCode ( 404 ) ) ;
177196 } ) ;
@@ -186,7 +205,7 @@ describe('callbacks', () => {
186205 } ;
187206
188207 // in that case, the response coming back will be ignored
189- fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , { } ) ;
208+ fetchCallback ( postRequestFetchHandlerData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
190209
191210 const newSpan = transaction . spanRecorder ?. spans [ 1 ] ;
192211
@@ -199,57 +218,89 @@ describe('callbacks', () => {
199218
200219 expect ( transaction . metadata . propagations ) . toBe ( 0 ) ;
201220
202- fetchCallback ( firstReqData , alwaysCreateSpan , { } ) ;
221+ fetchCallback ( firstReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
203222 expect ( transaction . metadata . propagations ) . toBe ( 1 ) ;
204223
205- fetchCallback ( secondReqData , alwaysCreateSpan , { } ) ;
224+ fetchCallback ( secondReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
206225 expect ( transaction . metadata . propagations ) . toBe ( 2 ) ;
207226 } ) ;
208-
209- it ( 'adds sentry-trace header to fetch requests' , ( ) => {
210- // TODO
211- } ) ;
212227 } ) ;
213228
214229 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 ;
219231
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+ } ;
221260 } ) ;
222261
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+ ) ;
230288
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' , ( ) => {
232290 hasTracingEnabled . mockReturnValueOnce ( false ) ;
291+ const spans = { } ;
233292
234- xhrCallback ( xhrHandlerData , alwaysCreateSpan , { } ) ;
293+ xhrCallback ( xhrHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
235294
295+ expect ( spans ) . toEqual ( { } ) ;
236296 expect ( setRequestHeader ) . not . toHaveBeenCalled ( ) ;
237297 } ) ;
238298
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-
248299 it ( 'creates and finishes XHR span on active transaction' , ( ) => {
249300 const spans = { } ;
250301
251302 // triggered by request being sent
252- xhrCallback ( xhrHandlerData , alwaysCreateSpan , spans ) ;
303+ xhrCallback ( xhrHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
253304
254305 const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
255306
@@ -261,16 +312,16 @@ describe('callbacks', () => {
261312 } ) ;
262313 expect ( newSpan . description ) . toBe ( 'GET http://dogs.are.great/' ) ;
263314 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 ) ;
266317
267318 const postRequestXHRHandlerData = {
268319 ...xhrHandlerData ,
269320 endTimestamp,
270321 } ;
271322
272323 // triggered by response coming back
273- xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , spans ) ;
324+ xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
274325
275326 expect ( newSpan . endTimestamp ) . toBeDefined ( ) ;
276327 } ) ;
@@ -279,7 +330,7 @@ describe('callbacks', () => {
279330 const spans = { } ;
280331
281332 // triggered by request being sent
282- xhrCallback ( xhrHandlerData , alwaysCreateSpan , spans ) ;
333+ xhrCallback ( xhrHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
283334
284335 const newSpan = transaction . spanRecorder ?. spans [ 1 ] as Span ;
285336
@@ -289,10 +340,10 @@ describe('callbacks', () => {
289340 ...xhrHandlerData ,
290341 endTimestamp,
291342 } ;
292- postRequestXHRHandlerData . xhr . __sentry_xhr__ . status_code = 404 ;
343+ postRequestXHRHandlerData . xhr ! . __sentry_xhr__ ! . status_code = 404 ;
293344
294345 // triggered by response coming back
295- xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , spans ) ;
346+ xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , alwaysAttachHeaders , spans ) ;
296347
297348 expect ( newSpan . status ) . toBe ( spanStatusfromHttpCode ( 404 ) ) ;
298349 } ) ;
@@ -303,15 +354,15 @@ describe('callbacks', () => {
303354 const postRequestXHRHandlerData = {
304355 ...{
305356 xhr : {
306- __sentry_xhr__ : xhrHandlerData . xhr . __sentry_xhr__ ,
357+ __sentry_xhr__ : xhrHandlerData . xhr ? .__sentry_xhr__ ,
307358 } ,
308359 } ,
309360 startTimestamp,
310361 endTimestamp,
311362 } ;
312363
313364 // in that case, the response coming back will be ignored
314- xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , { } ) ;
365+ xhrCallback ( postRequestXHRHandlerData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
315366
316367 const newSpan = transaction . spanRecorder ?. spans [ 1 ] ;
317368
@@ -324,10 +375,10 @@ describe('callbacks', () => {
324375
325376 expect ( transaction . metadata . propagations ) . toBe ( 0 ) ;
326377
327- xhrCallback ( firstReqData , alwaysCreateSpan , { } ) ;
378+ xhrCallback ( firstReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
328379 expect ( transaction . metadata . propagations ) . toBe ( 1 ) ;
329380
330- xhrCallback ( secondReqData , alwaysCreateSpan , { } ) ;
381+ xhrCallback ( secondReqData , alwaysCreateSpan , alwaysAttachHeaders , { } ) ;
331382 expect ( transaction . metadata . propagations ) . toBe ( 2 ) ;
332383 } ) ;
333384 } ) ;
0 commit comments