1 | const crossFetch = require('cross-fetch')
|
2 | global.fetch = crossFetch
|
3 | global.Response = crossFetch.Response
|
4 | global.Headers = crossFetch.Headers
|
5 | global.Request = crossFetch.Request
|
6 |
|
7 | if (!Promise) {
|
8 | Promise = require('promise-polyfill')
|
9 | } else if (!Promise.finally) {
|
10 | Promise.finally = require('promise-polyfill').finally
|
11 | }
|
12 |
|
13 | const ActualResponse = Response
|
14 |
|
15 | function responseWrapper(body, init) {
|
16 | if (
|
17 | body &&
|
18 | typeof body.constructor === 'function' &&
|
19 | body.constructor.__isFallback
|
20 | ) {
|
21 | const response = new ActualResponse(null, init)
|
22 | response.body = body
|
23 |
|
24 | const actualClone = response.clone
|
25 | response.clone = () => {
|
26 | const clone = actualClone.call(response)
|
27 | const [body1, body2] = body.tee()
|
28 | response.body = body1
|
29 | clone.body = body2
|
30 | return clone
|
31 | }
|
32 |
|
33 | return response
|
34 | }
|
35 |
|
36 | return new ActualResponse(body, init)
|
37 | }
|
38 |
|
39 | function responseInit(resp, init) {
|
40 | if (typeof resp.init === 'object') {
|
41 | return resp.init
|
42 | } else {
|
43 | init = Object.assign({}, init || {})
|
44 | for (const field of ['status', 'statusText', 'headers', 'url']) {
|
45 | if (field in resp) {
|
46 | init[field] = resp[field]
|
47 | }
|
48 | }
|
49 | return init
|
50 | }
|
51 | }
|
52 |
|
53 | function requestMatches(urlOrPredicate) {
|
54 | if (typeof urlOrPredicate === 'function') {
|
55 | return urlOrPredicate
|
56 | }
|
57 | const predicate =
|
58 | urlOrPredicate instanceof RegExp
|
59 | ? input => urlOrPredicate.exec(input) !== null
|
60 | : input => input === urlOrPredicate
|
61 | return input => {
|
62 | const requestUrl = typeof input === 'object' ? input.url : input
|
63 | return predicate(requestUrl)
|
64 | }
|
65 | }
|
66 |
|
67 | function requestNotMatches(urlOrPredicate) {
|
68 | const matches = requestMatches(urlOrPredicate)
|
69 | return input => {
|
70 | return !matches(input)
|
71 | }
|
72 | }
|
73 |
|
74 | const isFn = unknown => typeof unknown === 'function'
|
75 |
|
76 | const isMocking = jest.fn(() => true)
|
77 |
|
78 | const abortError = () =>
|
79 | new DOMException('The operation was aborted. ', 'AbortError')
|
80 |
|
81 | const abort = () => {
|
82 | throw abortError()
|
83 | }
|
84 |
|
85 | const abortAsync = () => {
|
86 | return Promise.reject(abortError())
|
87 | }
|
88 |
|
89 | const normalizeResponse = (bodyOrFunction, init) => (input, reqInit) => {
|
90 | const request = normalizeRequest(input, reqInit)
|
91 | return isMocking(input, reqInit)
|
92 | ? isFn(bodyOrFunction)
|
93 | ? bodyOrFunction(request).then(resp => {
|
94 | if (request.signal && request.signal.aborted) {
|
95 | abort()
|
96 | }
|
97 | return typeof resp === 'string'
|
98 | ? responseWrapper(resp, init)
|
99 | : responseWrapper(resp.body, responseInit(resp, init))
|
100 | })
|
101 | : new Promise((resolve, reject) => {
|
102 | if (request.signal && request.signal.aborted) {
|
103 | reject(abortError())
|
104 | return
|
105 | }
|
106 | resolve(responseWrapper(bodyOrFunction, init))
|
107 | })
|
108 | : crossFetch.fetch(input, reqInit)
|
109 | }
|
110 |
|
111 | const normalizeRequest = (input, reqInit) => {
|
112 | if (input instanceof Request) {
|
113 | if (input.signal && input.signal.aborted) {
|
114 | abort()
|
115 | }
|
116 | return input
|
117 | } else if (typeof input === 'string') {
|
118 | if (reqInit && reqInit.signal && reqInit.signal.aborted) {
|
119 | abort()
|
120 | }
|
121 | return new Request(input, reqInit)
|
122 | } else {
|
123 | throw new TypeError('Unable to parse input as string or Request')
|
124 | }
|
125 | }
|
126 |
|
127 | const normalizeError = errorOrFunction =>
|
128 | isFn(errorOrFunction)
|
129 | ? errorOrFunction
|
130 | : () => Promise.reject(errorOrFunction)
|
131 |
|
132 | const fetch = jest.fn(normalizeResponse(''))
|
133 | fetch.Headers = Headers
|
134 | fetch.Response = responseWrapper
|
135 | fetch.Request = Request
|
136 | fetch.mockResponse = (bodyOrFunction, init) =>
|
137 | fetch.mockImplementation(normalizeResponse(bodyOrFunction, init))
|
138 |
|
139 | fetch.mockReject = errorOrFunction =>
|
140 | fetch.mockImplementation(normalizeError(errorOrFunction))
|
141 |
|
142 | fetch.mockAbort = () => fetch.mockImplementation(abortAsync)
|
143 | fetch.mockAbortOnce = () => fetch.mockImplementationOnce(abortAsync)
|
144 |
|
145 | const mockResponseOnce = (bodyOrFunction, init) =>
|
146 | fetch.mockImplementationOnce(normalizeResponse(bodyOrFunction, init))
|
147 |
|
148 | fetch.mockResponseOnce = mockResponseOnce
|
149 |
|
150 | fetch.once = mockResponseOnce
|
151 |
|
152 | fetch.mockRejectOnce = errorOrFunction =>
|
153 | fetch.mockImplementationOnce(normalizeError(errorOrFunction))
|
154 |
|
155 | fetch.mockResponses = (...responses) => {
|
156 | responses.forEach(response => {
|
157 | if (Array.isArray(response)) {
|
158 | const [body, init] = response
|
159 | fetch.mockImplementationOnce(normalizeResponse(body, init))
|
160 | } else {
|
161 | fetch.mockImplementationOnce(normalizeResponse(response))
|
162 | }
|
163 | })
|
164 | return fetch
|
165 | }
|
166 |
|
167 | fetch.isMocking = isMocking
|
168 |
|
169 | fetch.mockIf = fetch.doMockIf = (urlOrPredicate, bodyOrFunction, init) => {
|
170 | isMocking.mockImplementation(requestMatches(urlOrPredicate))
|
171 | if (bodyOrFunction) {
|
172 | fetch.mockResponse(bodyOrFunction, init)
|
173 | }
|
174 | return fetch
|
175 | }
|
176 |
|
177 | fetch.dontMockIf = (urlOrPredicate, bodyOrFunction, init) => {
|
178 | isMocking.mockImplementation(requestNotMatches(urlOrPredicate))
|
179 | if (bodyOrFunction) {
|
180 | fetch.mockResponse(bodyOrFunction, init)
|
181 | }
|
182 | return fetch
|
183 | }
|
184 |
|
185 | fetch.mockOnceIf = fetch.doMockOnceIf = (
|
186 | urlOrPredicate,
|
187 | bodyOrFunction,
|
188 | init
|
189 | ) => {
|
190 | isMocking.mockImplementationOnce(requestMatches(urlOrPredicate))
|
191 | if (bodyOrFunction) {
|
192 | mockResponseOnce(bodyOrFunction, init)
|
193 | }
|
194 | return fetch
|
195 | }
|
196 |
|
197 | fetch.dontMockOnceIf = (urlOrPredicate, bodyOrFunction, init) => {
|
198 | isMocking.mockImplementationOnce(requestNotMatches(urlOrPredicate))
|
199 | if (bodyOrFunction) {
|
200 | mockResponseOnce(bodyOrFunction, init)
|
201 | }
|
202 | return fetch
|
203 | }
|
204 |
|
205 | fetch.dontMock = () => {
|
206 | isMocking.mockImplementation(() => false)
|
207 | return fetch
|
208 | }
|
209 |
|
210 | fetch.dontMockOnce = () => {
|
211 | isMocking.mockImplementationOnce(() => false)
|
212 | return fetch
|
213 | }
|
214 |
|
215 | fetch.doMock = (bodyOrFunction, init) => {
|
216 | isMocking.mockImplementation(() => true)
|
217 | if (bodyOrFunction) {
|
218 | fetch.mockResponse(bodyOrFunction, init)
|
219 | }
|
220 | return fetch
|
221 | }
|
222 |
|
223 | fetch.mockOnce = fetch.doMockOnce = (bodyOrFunction, init) => {
|
224 | isMocking.mockImplementationOnce(() => true)
|
225 | if (bodyOrFunction) {
|
226 | mockResponseOnce(bodyOrFunction, init)
|
227 | }
|
228 | return fetch
|
229 | }
|
230 |
|
231 | fetch.resetMocks = () => {
|
232 | fetch.mockReset()
|
233 | isMocking.mockReset()
|
234 |
|
235 |
|
236 | fetch.mockImplementation(normalizeResponse(''))
|
237 | fetch.doMock()
|
238 | fetch.isMocking = isMocking
|
239 | }
|
240 |
|
241 | fetch.enableMocks = fetch.enableFetchMocks = () => {
|
242 | global.fetchMock = global.fetch = fetch
|
243 | try {
|
244 | jest.setMock('node-fetch', fetch)
|
245 | } catch (error) {
|
246 |
|
247 | }
|
248 | }
|
249 |
|
250 | fetch.disableMocks = fetch.disableFetchMocks = () => {
|
251 | global.fetch = crossFetch
|
252 | try {
|
253 | jest.dontMock('node-fetch')
|
254 | } catch (error) {
|
255 |
|
256 | }
|
257 | }
|
258 |
|
259 | module.exports = fetch.default = fetch
|