1
+ import { FetchHttpHandler } from './fetch-http-handler' ;
2
+ import { AbortController } from '@aws/abort-controller' ;
3
+ import * as timeouts from './request-timeout' ;
4
+
5
+ const mockRequest = jest . fn ( ) ;
6
+ let mockResponse : any ;
7
+ let timeoutSpy : jest . SpyInstance < any > ;
8
+
9
+ ( global as any ) . Request = mockRequest ;
10
+ ( global as any ) . Headers = jest . fn ( ) ;
11
+
12
+ describe ( 'httpHandler' , ( ) => {
13
+ beforeEach ( ( ) => {
14
+ ( global as any ) . AbortController = void 0 ;
15
+ jest . clearAllMocks ( ) ;
16
+ mockResponse = {
17
+ headers : {
18
+ entries : jest . fn ( ( ) => {
19
+ return [
20
+ [ 'foo' , 'bar' ] ,
21
+ [ 'bizz' , 'bazz' ]
22
+ ] ;
23
+ } )
24
+ } ,
25
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) )
26
+ }
27
+ } ) ;
28
+
29
+ afterEach ( ( ) => {
30
+ jest . clearAllTimers ( ) ;
31
+ if ( timeoutSpy ) {
32
+ timeoutSpy . mockRestore ( ) ;
33
+ }
34
+ } ) ;
35
+
36
+ it ( 'makes requests using fetch' , async ( ) => {
37
+ const mockResponse = {
38
+ headers : {
39
+ entries : jest . fn ( ( ) => {
40
+ return [
41
+ [ 'foo' , 'bar' ] ,
42
+ [ 'bizz' , 'bazz' ]
43
+ ] ;
44
+ } )
45
+ } ,
46
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) )
47
+ }
48
+ const mockFetch = jest . fn ( ( ) => {
49
+ return Promise . resolve ( mockResponse ) ;
50
+ } ) ;
51
+
52
+ ( global as any ) . fetch = mockFetch ;
53
+ const fetchHttpHandler = new FetchHttpHandler ( ) ;
54
+
55
+ let response = await fetchHttpHandler . handle ( { } as any , { } ) ;
56
+
57
+ expect ( mockFetch . mock . calls . length ) . toBe ( 1 ) ;
58
+ } ) ;
59
+
60
+ it ( 'properly constructs url' , async ( ) => {
61
+ let mockResponse = {
62
+ headers : {
63
+ entries : jest . fn ( ( ) => {
64
+ return [
65
+ [ 'foo' , 'bar' ] ,
66
+ [ 'bizz' , 'bazz' ]
67
+ ] ;
68
+ } )
69
+ } ,
70
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) )
71
+ }
72
+ const mockFetch = jest . fn ( ( ) => {
73
+ return Promise . resolve ( mockResponse ) ;
74
+ } ) ;
75
+
76
+ ( global as any ) . fetch = mockFetch ;
77
+
78
+ let httpRequest = {
79
+ headers : { } ,
80
+ hostname : 'foo.amazonaws.com' ,
81
+ method : 'GET' ,
82
+ path : '/test/?bar=baz' ,
83
+ protocol : 'https:' ,
84
+ port : 443 ,
85
+ } ;
86
+ const fetchHttpHandler = new FetchHttpHandler ( ) ;
87
+
88
+ let response = await fetchHttpHandler . handle ( httpRequest , { } ) ;
89
+
90
+ expect ( mockFetch . mock . calls . length ) . toBe ( 1 ) ;
91
+ let requestCall = mockRequest . mock . calls [ 0 ] ;
92
+ expect ( requestCall [ 0 ] ) . toBe (
93
+ 'https://foo.amazonaws.com:443/test/?bar=baz'
94
+ ) ;
95
+ } ) ;
96
+
97
+ it ( 'prefers response body if it is available' , async ( ) => {
98
+ let mockResponse = {
99
+ headers : {
100
+ entries : jest . fn ( ( ) => {
101
+ return [
102
+ [ 'foo' , 'bar' ] ,
103
+ [ 'bizz' , 'bazz' ]
104
+ ] ;
105
+ } )
106
+ } ,
107
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) ) ,
108
+ body : 'test'
109
+ }
110
+ const mockFetch = jest . fn ( ( ) => {
111
+ return Promise . resolve ( mockResponse ) ;
112
+ } ) ;
113
+
114
+ ( global as any ) . fetch = mockFetch ;
115
+
116
+ let httpRequest = {
117
+ headers : { } ,
118
+ hostname : 'foo.amazonaws.com' ,
119
+ method : 'GET' ,
120
+ path : '/test/?bar=baz' ,
121
+ protocol : 'https:' ,
122
+ port : 443 ,
123
+ } ;
124
+ const fetchHttpHandler = new FetchHttpHandler ( ) ;
125
+
126
+ let response = await fetchHttpHandler . handle ( httpRequest , { } ) ;
127
+
128
+ expect ( mockFetch . mock . calls . length ) . toBe ( 1 ) ;
129
+ expect ( mockResponse . arrayBuffer . mock . calls . length ) . toBe ( 0 ) ;
130
+ expect ( response . body ) . toBe ( 'test' ) ;
131
+ } ) ;
132
+
133
+ it ( 'will not make request if already aborted' , async ( ) => {
134
+ let mockResponse = {
135
+ headers : {
136
+ entries : jest . fn ( ( ) => {
137
+ return [
138
+ [ 'foo' , 'bar' ] ,
139
+ [ 'bizz' , 'bazz' ]
140
+ ] ;
141
+ } )
142
+ } ,
143
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) ) ,
144
+ body : 'test'
145
+ } ;
146
+ const mockFetch = jest . fn ( ( ) => {
147
+ return Promise . resolve ( mockResponse ) ;
148
+ } ) ;
149
+
150
+ ( global as any ) . fetch = mockFetch ;
151
+ const fetchHttpHandler = new FetchHttpHandler ( ) ;
152
+
153
+ await expect ( fetchHttpHandler . handle ( { } as any , {
154
+ abortSignal : {
155
+ aborted : true
156
+ }
157
+ } ) ) . rejects . toHaveProperty ( 'name' , 'AbortError' ) ;
158
+
159
+ expect ( mockFetch . mock . calls . length ) . toBe ( 0 ) ;
160
+ } ) ;
161
+
162
+ it ( 'will pass abortSignal to fetch if supported' , async ( ) => {
163
+ let mockResponse = {
164
+ headers : {
165
+ entries : jest . fn ( ( ) => {
166
+ return [
167
+ [ 'foo' , 'bar' ] ,
168
+ [ 'bizz' , 'bazz' ]
169
+ ] ;
170
+ } )
171
+ } ,
172
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) ) ,
173
+ body : 'test'
174
+ } ;
175
+ const mockFetch = jest . fn ( ( ) => {
176
+ return Promise . resolve ( mockResponse ) ;
177
+ } ) ;
178
+ ( global as any ) . fetch = mockFetch ;
179
+ ( global as any ) . AbortController = jest . fn ( ) ;
180
+ const fetchHttpHandler = new FetchHttpHandler ( ) ;
181
+
182
+ let response = await fetchHttpHandler . handle ( { } as any , {
183
+ abortSignal : {
184
+ aborted : false
185
+ }
186
+ } ) ;
187
+
188
+ expect ( mockRequest . mock . calls [ 0 ] [ 1 ] ) . toHaveProperty ( 'signal' ) ;
189
+ expect ( mockFetch . mock . calls . length ) . toBe ( 1 ) ;
190
+ } ) ;
191
+
192
+ it ( 'will pass timeout to request timeout' , async ( ) => {
193
+ let mockResponse = {
194
+ headers : {
195
+ entries : jest . fn ( ( ) => {
196
+ return [
197
+ [ 'foo' , 'bar' ] ,
198
+ [ 'bizz' , 'bazz' ]
199
+ ] ;
200
+ } )
201
+ } ,
202
+ arrayBuffer : jest . fn ( ( ) => Promise . resolve ( ) ) ,
203
+ body : 'test'
204
+ } ;
205
+ const mockFetch = jest . fn ( ( ) => {
206
+ return Promise . resolve ( mockResponse ) ;
207
+ } ) ;
208
+ ( global as any ) . fetch = mockFetch ;
209
+
210
+ timeoutSpy = jest . spyOn ( timeouts , 'requestTimeout' ) ;
211
+ const fetchHttpHandler = new FetchHttpHandler ( {
212
+ requestTimeout : 500
213
+ } ) ;
214
+
215
+ let response = await fetchHttpHandler . handle ( { } as any , { } ) ;
216
+
217
+ expect ( mockFetch . mock . calls . length ) . toBe ( 1 ) ;
218
+ expect ( timeoutSpy . mock . calls [ 0 ] [ 0 ] ) . toBe ( 500 ) ;
219
+ } ) ;
220
+
221
+ it ( 'will throw timeout error it timeout finishes before request' , async ( ) => {
222
+ const mockFetch = jest . fn ( ( ) => {
223
+ return new Promise ( ( resolve , reject ) => { } ) ;
224
+ } ) ;
225
+ ( global as any ) . fetch = mockFetch ;
226
+ const fetchHttpHandler = new FetchHttpHandler ( {
227
+ requestTimeout : 5
228
+ } ) ;
229
+
230
+ await expect ( fetchHttpHandler . handle (
231
+ { } as any ,
232
+ { } )
233
+ ) . rejects . toHaveProperty ( 'name' , 'TimeoutError' ) ;
234
+ expect ( mockFetch . mock . calls . length ) . toBe ( 1 ) ;
235
+ } ) ;
236
+
237
+ it ( 'can be aborted before fetch completes' , async ( ) => {
238
+ const abortController = new AbortController ( ) ;
239
+
240
+ const mockFetch = jest . fn ( ( ) => {
241
+ return new Promise ( ( resolve , reject ) => { } ) ;
242
+ } ) ;
243
+ ( global as any ) . fetch = mockFetch ;
244
+
245
+ setTimeout ( ( ) => {
246
+ abortController . abort ( ) ;
247
+ } , 100 )
248
+ const fetchHttpHandler = new FetchHttpHandler ( ) ;
249
+
250
+ await expect ( fetchHttpHandler . handle ( { } as any , {
251
+ abortSignal : abortController . signal
252
+ } ) ) . rejects . toHaveProperty ( 'name' , 'AbortError' ) ;
253
+
254
+ // ensure that fetch's built-in mechanism isn't being used
255
+ expect ( mockRequest . mock . calls [ 0 ] [ 1 ] ) . not . toHaveProperty ( 'signal' ) ;
256
+ } ) ;
257
+ } ) ;
0 commit comments