@@ -155,8 +155,7 @@ describe('fetch', () => {
155155 lastResponse = undefined ;
156156 } ;
157157
158- const prepareData = (
159- done : any ,
158+ const prepareData = async (
160159 fileUrl : string ,
161160 config : FetchInstrumentationConfig ,
162161 method ?: string ,
@@ -238,37 +237,42 @@ describe('fetch', () => {
238237 new tracing . SimpleSpanProcessor ( dummySpanExporter )
239238 ) ;
240239
240+ // endSpan is called after the whole response body is read
241+ // this process is scheduled at the same time the fetch promise is resolved
242+ // due to this we can't rely on getData resolution to know that the span has ended
243+ let resolveEndSpan : ( value : unknown ) => void ;
244+ const spanEnded = new Promise ( r => resolveEndSpan = r ) ;
245+ const readSpy = sinon . spy ( window . ReadableStreamDefaultReader . prototype , 'read' ) ;
246+ const endSpanStub : sinon . SinonStub < any > = sinon . stub ( FetchInstrumentation . prototype , '_endSpan' as any )
247+ . callsFake ( async function ( this : FetchInstrumentation , ...args : any [ ] ) {
248+ resolveEndSpan ( { } ) ;
249+ return endSpanStub . wrappedMethod . apply ( this , args ) ;
250+ } ) ;
251+
241252 rootSpan = webTracerWithZone . startSpan ( 'root' ) ;
242- api . context . with ( api . trace . setSpan ( api . context . active ( ) , rootSpan ) , ( ) => {
253+ await api . context . with ( api . trace . setSpan ( api . context . active ( ) , rootSpan ) , async ( ) => {
243254 fakeNow = 0 ;
244- void getData ( fileUrl , method )
245- . then (
246- response => {
247- // this is a bit tricky as the only way to get all request headers from
248- // fetch is to use json()
249- return response . json ( ) . then (
250- json => {
251- lastResponse = json ;
252- const headers : { [ key : string ] : string } = { } ;
253- Object . keys ( lastResponse . headers ) . forEach ( key => {
254- headers [ key . toLowerCase ( ) ] = lastResponse . headers [ key ] ;
255- } ) ;
256- lastResponse . headers = headers ;
257- } ,
258- ( ) => {
259- lastResponse = undefined ;
260- }
261- ) ;
262- } ,
263- ( ) => {
264- lastResponse = undefined ;
265- }
266- )
267- . then ( sinon . clock . runAllAsync )
268- . then ( ( ) => {
269- done ( ) ;
255+ try {
256+ const responsePromise = getData ( fileUrl , method ) ;
257+ fakeNow = 300 ;
258+ const response = await responsePromise ;
259+
260+ // if the url is not ignored, body.read should be called by now
261+ // awaiting for the span to end
262+ if ( readSpy . callCount > 0 ) await spanEnded ;
263+
264+ // this is a bit tricky as the only way to get all request headers from
265+ // fetch is to use json()
266+ lastResponse = await response . json ( ) ;
267+ const headers : { [ key : string ] : string } = { } ;
268+ Object . keys ( lastResponse . headers ) . forEach ( key => {
269+ headers [ key . toLowerCase ( ) ] = lastResponse . headers [ key ] ;
270270 } ) ;
271- fakeNow = 300 ;
271+ lastResponse . headers = headers ;
272+ } catch ( e ) {
273+ lastResponse = undefined ;
274+ }
275+ await sinon . clock . runAllAsync ( ) ;
272276 } ) ;
273277 } ;
274278
@@ -290,9 +294,9 @@ describe('fetch', () => {
290294 } ) ;
291295
292296 describe ( 'when request is successful' , ( ) => {
293- beforeEach ( done => {
297+ beforeEach ( async ( ) => {
294298 const propagateTraceHeaderCorsUrls = [ url ] ;
295- prepareData ( done , url , { propagateTraceHeaderCorsUrls } ) ;
299+ await prepareData ( url , { propagateTraceHeaderCorsUrls } ) ;
296300 } ) ;
297301
298302 afterEach ( ( ) => {
@@ -580,13 +584,13 @@ describe('fetch', () => {
580584
581585 describe ( 'when propagateTraceHeaderCorsUrls does NOT MATCH' , ( ) => {
582586 let spyDebug : sinon . SinonSpy ;
583- beforeEach ( done => {
587+ beforeEach ( async ( ) => {
584588 const diagLogger = new api . DiagConsoleLogger ( ) ;
585589 spyDebug = sinon . spy ( ) ;
586590 diagLogger . debug = spyDebug ;
587591 api . diag . setLogger ( diagLogger , api . DiagLogLevel . ALL ) ;
588592 clearData ( ) ;
589- prepareData ( done , url , { } ) ;
593+ await prepareData ( url , { } ) ;
590594 } ) ;
591595 afterEach ( ( ) => {
592596 sinon . restore ( ) ;
@@ -619,15 +623,13 @@ describe('fetch', () => {
619623 } ) ;
620624
621625 describe ( 'applyCustomAttributesOnSpan option' , ( ) => {
622- const noop = ( ) => { } ;
623- const prepare = (
626+ const prepare = async (
624627 url : string ,
625628 applyCustomAttributesOnSpan : FetchCustomAttributeFunction ,
626- cb : VoidFunction = noop
627629 ) => {
628630 const propagateTraceHeaderCorsUrls = [ url ] ;
629631
630- prepareData ( cb , url , {
632+ await prepareData ( url , {
631633 propagateTraceHeaderCorsUrls,
632634 applyCustomAttributesOnSpan,
633635 } ) ;
@@ -637,74 +639,71 @@ describe('fetch', () => {
637639 clearData ( ) ;
638640 } ) ;
639641
640- it ( 'applies attributes when the request is succesful' , done => {
641- prepare (
642+ it ( 'applies attributes when the request is succesful' , async ( ) => {
643+ await prepare (
642644 url ,
643645 span => {
644646 span . setAttribute ( CUSTOM_ATTRIBUTE_KEY , 'custom value' ) ;
645647 } ,
646- ( ) => {
647- const span : tracing . ReadableSpan = exportSpy . args [ 1 ] [ 0 ] [ 0 ] ;
648- const attributes = span . attributes ;
649-
650- assert . ok ( attributes [ CUSTOM_ATTRIBUTE_KEY ] === 'custom value' ) ;
651- done ( ) ;
652- }
653648 ) ;
649+ const span : tracing . ReadableSpan = exportSpy . args [ 1 ] [ 0 ] [ 0 ] ;
650+ const attributes = span . attributes ;
651+
652+ assert . ok ( attributes [ CUSTOM_ATTRIBUTE_KEY ] === 'custom value' ) ;
654653 } ) ;
655654
656- it ( 'applies custom attributes when the request fails' , done => {
657- prepare (
655+ it ( 'applies custom attributes when the request fails' , async ( ) => {
656+ await prepare (
658657 badUrl ,
659658 span => {
660659 span . setAttribute ( CUSTOM_ATTRIBUTE_KEY , 'custom value' ) ;
661660 } ,
662- ( ) => {
663- const span : tracing . ReadableSpan = exportSpy . args [ 1 ] [ 0 ] [ 0 ] ;
664- const attributes = span . attributes ;
665-
666- assert . ok ( attributes [ CUSTOM_ATTRIBUTE_KEY ] === 'custom value' ) ;
667- done ( ) ;
668- }
669661 ) ;
662+ const span : tracing . ReadableSpan = exportSpy . args [ 1 ] [ 0 ] [ 0 ] ;
663+ const attributes = span . attributes ;
664+
665+ assert . ok ( attributes [ CUSTOM_ATTRIBUTE_KEY ] === 'custom value' ) ;
670666 } ) ;
671667
672- it ( 'has request and response objects in callback arguments' , done => {
668+ it ( 'has request and response objects in callback arguments' , async ( ) => {
669+ let request : any ;
670+ let response : any ;
673671 const applyCustomAttributes : FetchCustomAttributeFunction = (
674672 span ,
675- request ,
676- response
673+ req ,
674+ res
677675 ) => {
678- assert . ok ( request . method === 'GET' ) ;
679- assert . ok ( response . status === 200 ) ;
680-
681- done ( ) ;
676+ request = req ;
677+ response = res ;
682678 } ;
683679
684- prepare ( url , applyCustomAttributes ) ;
680+ await prepare ( url , applyCustomAttributes ) ;
681+ assert . ok ( request . method === 'GET' ) ;
682+ assert . ok ( response . status === 200 ) ;
685683 } ) ;
686684
687- it ( 'get response body from callback arguments response' , done => {
685+ it ( 'get response body from callback arguments response' , async ( ) => {
686+ let response : any ;
688687 const applyCustomAttributes : FetchCustomAttributeFunction = async (
689688 span ,
690- request ,
691- response
689+ req ,
690+ res
692691 ) => {
693- if ( response instanceof Response ) {
694- const rsp = await response . json ( ) ;
695- assert . deepStrictEqual ( rsp . args , { } ) ;
696- done ( ) ;
692+ if ( res instanceof Response ) {
693+ response = res ;
697694 }
698695 } ;
699696
700- prepare ( url , applyCustomAttributes ) ;
697+ await prepare ( url , applyCustomAttributes ) ;
698+ const rsp = await response . json ( ) ;
699+ assert . deepStrictEqual ( rsp . args , { } ) ;
701700 } ) ;
702701 } ) ;
703702
704703 describe ( 'when url is ignored' , ( ) => {
705- beforeEach ( done => {
704+ beforeEach ( async ( ) => {
706705 const propagateTraceHeaderCorsUrls = url ;
707- prepareData ( done , url , {
706+ await prepareData ( url , {
708707 propagateTraceHeaderCorsUrls,
709708 ignoreUrls : [ propagateTraceHeaderCorsUrls ] ,
710709 } ) ;
@@ -726,9 +725,9 @@ describe('fetch', () => {
726725 } ) ;
727726
728727 describe ( 'when clearTimingResources is TRUE' , ( ) => {
729- beforeEach ( done => {
728+ beforeEach ( async ( ) => {
730729 const propagateTraceHeaderCorsUrls = url ;
731- prepareData ( done , url , {
730+ await prepareData ( url , {
732731 propagateTraceHeaderCorsUrls,
733732 clearTimingResources : true ,
734733 } ) ;
@@ -746,9 +745,9 @@ describe('fetch', () => {
746745 } ) ;
747746
748747 describe ( 'when request is NOT successful (wrong url)' , ( ) => {
749- beforeEach ( done => {
748+ beforeEach ( async ( ) => {
750749 const propagateTraceHeaderCorsUrls = badUrl ;
751- prepareData ( done , badUrl , { propagateTraceHeaderCorsUrls } ) ;
750+ await prepareData ( badUrl , { propagateTraceHeaderCorsUrls } ) ;
752751 } ) ;
753752 afterEach ( ( ) => {
754753 clearData ( ) ;
@@ -764,9 +763,9 @@ describe('fetch', () => {
764763 } ) ;
765764
766765 describe ( 'when request is NOT successful (405)' , ( ) => {
767- beforeEach ( done => {
766+ beforeEach ( async ( ) => {
768767 const propagateTraceHeaderCorsUrls = url ;
769- prepareData ( done , url , { propagateTraceHeaderCorsUrls } , 'DELETE' ) ;
768+ await prepareData ( url , { propagateTraceHeaderCorsUrls } , 'DELETE' ) ;
770769 } ) ;
771770 afterEach ( ( ) => {
772771 clearData ( ) ;
@@ -783,11 +782,11 @@ describe('fetch', () => {
783782 } ) ;
784783
785784 describe ( 'when PerformanceObserver is used by default' , ( ) => {
786- beforeEach ( done => {
785+ beforeEach ( async ( ) => {
787786 // All above tests test it already but just in case
788787 // lets explicitly turn getEntriesByType off so we can be sure
789788 // that the perf entries come from the observer.
790- prepareData ( done , url , { } , undefined , false , true ) ;
789+ await prepareData ( url , { } , undefined , false , true ) ;
791790 } ) ;
792791 afterEach ( ( ) => {
793792 clearData ( ) ;
@@ -813,8 +812,8 @@ describe('fetch', () => {
813812 } ) ;
814813
815814 describe ( 'when fetching with relative url' , ( ) => {
816- beforeEach ( done => {
817- prepareData ( done , '/get' , { } , undefined , false , true ) ;
815+ beforeEach ( async ( ) => {
816+ await prepareData ( '/get' , { } , undefined , false , true ) ;
818817 } ) ;
819818 afterEach ( ( ) => {
820819 clearData ( ) ;
@@ -841,8 +840,8 @@ describe('fetch', () => {
841840 } ) ;
842841
843842 describe ( 'when PerformanceObserver is undefined' , ( ) => {
844- beforeEach ( done => {
845- prepareData ( done , url , { } , undefined , true , false ) ;
843+ beforeEach ( async ( ) => {
844+ await prepareData ( url , { } , undefined , true , false ) ;
846845 } ) ;
847846
848847 afterEach ( ( ) => {
@@ -868,8 +867,8 @@ describe('fetch', () => {
868867 } ) ;
869868
870869 describe ( 'when PerformanceObserver and performance.getEntriesByType are undefined' , ( ) => {
871- beforeEach ( done => {
872- prepareData ( done , url , { } , undefined , true , true ) ;
870+ beforeEach ( async ( ) => {
871+ await prepareData ( url , { } , undefined , true , true ) ;
873872 } ) ;
874873 afterEach ( ( ) => {
875874 clearData ( ) ;
0 commit comments