@mswjs/interceptors 0.34.1 → 0.34.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/{chunk-7A4UJNSW.mjs → chunk-73X2CAEH.mjs} +6 -6
- package/lib/browser/chunk-73X2CAEH.mjs.map +1 -0
- package/lib/browser/{chunk-VRKVKT62.js → chunk-EJYZ4HR3.js} +6 -6
- package/lib/browser/chunk-EJYZ4HR3.js.map +1 -0
- package/lib/browser/{chunk-ZXJSL23E.mjs → chunk-KOFJYPUE.mjs} +108 -37
- package/lib/browser/chunk-KOFJYPUE.mjs.map +1 -0
- package/lib/browser/{chunk-LERABB57.js → chunk-NBHFK2DJ.js} +110 -39
- package/lib/browser/chunk-NBHFK2DJ.js.map +1 -0
- package/lib/browser/{chunk-6MBJUL74.js → chunk-TPZUQHHY.js} +5 -5
- package/lib/browser/{chunk-NU2MPFD6.mjs → chunk-W7UG7MBM.mjs} +2 -2
- package/lib/browser/interceptors/XMLHttpRequest/index.js +3 -3
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +2 -2
- package/lib/browser/interceptors/fetch/index.js +3 -3
- package/lib/browser/interceptors/fetch/index.mjs +2 -2
- package/lib/browser/presets/browser.js +5 -5
- package/lib/browser/presets/browser.mjs +3 -3
- package/lib/node/RemoteHttpInterceptor.js +7 -7
- package/lib/node/RemoteHttpInterceptor.mjs +3 -3
- package/lib/node/{chunk-T34TGCMR.js → chunk-6L3UERDR.js} +110 -39
- package/lib/node/chunk-6L3UERDR.js.map +1 -0
- package/lib/node/{chunk-RTGLFNO3.mjs → chunk-GILG336Y.mjs} +2 -2
- package/lib/node/{chunk-DCMEIPZN.mjs → chunk-KA2KMHFU.mjs} +6 -23
- package/lib/node/chunk-KA2KMHFU.mjs.map +1 -0
- package/lib/node/{chunk-5WWNCLB3.js → chunk-TQD7SQGP.js} +6 -6
- package/lib/node/chunk-TQD7SQGP.js.map +1 -0
- package/lib/node/{chunk-UN335ZTD.js → chunk-W4AQXISM.js} +5 -5
- package/lib/node/{chunk-RJGP7FQP.mjs → chunk-WGBCEHTV.mjs} +108 -37
- package/lib/node/chunk-WGBCEHTV.mjs.map +1 -0
- package/lib/node/{chunk-I57YSVAV.js → chunk-WTJL7BRV.js} +10 -27
- package/lib/node/chunk-WTJL7BRV.js.map +1 -0
- package/lib/node/{chunk-KY3RJ2M3.mjs → chunk-XSXCGXEY.mjs} +6 -6
- package/lib/node/chunk-XSXCGXEY.mjs.map +1 -0
- package/lib/node/interceptors/ClientRequest/index.js +3 -3
- package/lib/node/interceptors/ClientRequest/index.mjs +2 -2
- package/lib/node/interceptors/XMLHttpRequest/index.js +3 -3
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +2 -2
- package/lib/node/interceptors/fetch/index.js +3 -3
- package/lib/node/interceptors/fetch/index.mjs +2 -2
- package/lib/node/presets/node.js +7 -7
- package/lib/node/presets/node.mjs +4 -4
- package/package.json +3 -1
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +12 -14
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +12 -12
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +149 -43
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +2 -2
- package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +4 -4
- package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +1 -1
- package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.test.ts +164 -0
- package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts +16 -0
- package/src/utils/handleRequest.ts +6 -6
- package/lib/browser/chunk-7A4UJNSW.mjs.map +0 -1
- package/lib/browser/chunk-LERABB57.js.map +0 -1
- package/lib/browser/chunk-VRKVKT62.js.map +0 -1
- package/lib/browser/chunk-ZXJSL23E.mjs.map +0 -1
- package/lib/node/chunk-5WWNCLB3.js.map +0 -1
- package/lib/node/chunk-DCMEIPZN.mjs.map +0 -1
- package/lib/node/chunk-I57YSVAV.js.map +0 -1
- package/lib/node/chunk-KY3RJ2M3.mjs.map +0 -1
- package/lib/node/chunk-RJGP7FQP.mjs.map +0 -1
- package/lib/node/chunk-T34TGCMR.js.map +0 -1
- package/src/utils/getRequestOptionsByUrl.ts +0 -29
- /package/lib/browser/{chunk-6MBJUL74.js.map → chunk-TPZUQHHY.js.map} +0 -0
- /package/lib/browser/{chunk-NU2MPFD6.mjs.map → chunk-W7UG7MBM.mjs.map} +0 -0
- /package/lib/node/{chunk-RTGLFNO3.mjs.map → chunk-GILG336Y.mjs.map} +0 -0
- /package/lib/node/{chunk-UN335ZTD.js.map → chunk-W4AQXISM.js.map} +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClientRequestInterceptor
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-KA2KMHFU.mjs";
|
|
4
4
|
import {
|
|
5
5
|
XMLHttpRequestInterceptor
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-WGBCEHTV.mjs";
|
|
7
7
|
import "../chunk-6HYIRFX2.mjs";
|
|
8
8
|
import {
|
|
9
9
|
FetchInterceptor
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-GILG336Y.mjs";
|
|
11
11
|
import "../chunk-BZ3Y7YV5.mjs";
|
|
12
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-XSXCGXEY.mjs";
|
|
13
13
|
import "../chunk-BUCULLYM.mjs";
|
|
14
14
|
|
|
15
15
|
// src/presets/node.ts
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mswjs/interceptors",
|
|
3
3
|
"description": "Low-level HTTP/HTTPS/XHR/fetch request interception library.",
|
|
4
|
-
"version": "0.34.
|
|
4
|
+
"version": "0.34.3",
|
|
5
5
|
"main": "./lib/node/index.js",
|
|
6
6
|
"module": "./lib/node/index.mjs",
|
|
7
7
|
"types": "./lib/node/index.d.ts",
|
|
@@ -117,6 +117,7 @@
|
|
|
117
117
|
"@playwright/test": "^1.37.1",
|
|
118
118
|
"@types/cors": "^2.8.12",
|
|
119
119
|
"@types/express": "^4.17.13",
|
|
120
|
+
"@types/express-fileupload": "^1.5.0",
|
|
120
121
|
"@types/express-rate-limit": "^6.0.0",
|
|
121
122
|
"@types/follow-redirects": "^1.14.1",
|
|
122
123
|
"@types/jest": "^27.0.3",
|
|
@@ -132,6 +133,7 @@
|
|
|
132
133
|
"cz-conventional-changelog": "3.3.0",
|
|
133
134
|
"engine.io-parser": "^5.2.1",
|
|
134
135
|
"express": "^4.17.3",
|
|
136
|
+
"express-fileupload": "^1.5.1",
|
|
135
137
|
"express-rate-limit": "^6.3.0",
|
|
136
138
|
"follow-redirects": "^1.15.1",
|
|
137
139
|
"got": "^11.8.3",
|
|
@@ -186,7 +186,9 @@ it('handles [URL, RequestOptions, callback] input', () => {
|
|
|
186
186
|
expect(url.href).toEqual('https://mswjs.io/resource')
|
|
187
187
|
|
|
188
188
|
// Options must be preserved.
|
|
189
|
-
|
|
189
|
+
// `urlToHttpOptions` from `node:url` generates additional
|
|
190
|
+
// ClientRequest options, some of which are not legally allowed.
|
|
191
|
+
expect(options).toMatchObject<RequestOptions>({
|
|
190
192
|
agent: false,
|
|
191
193
|
_defaultAgent: httpsGlobalAgent,
|
|
192
194
|
protocol: url.protocol,
|
|
@@ -194,8 +196,6 @@ it('handles [URL, RequestOptions, callback] input', () => {
|
|
|
194
196
|
headers: {
|
|
195
197
|
'Content-Type': 'text/plain',
|
|
196
198
|
},
|
|
197
|
-
|
|
198
|
-
host: url.host,
|
|
199
199
|
hostname: url.hostname,
|
|
200
200
|
path: url.pathname,
|
|
201
201
|
})
|
|
@@ -214,7 +214,7 @@ it('handles [URL, RequestOptions] where options have custom "hostname"', () => {
|
|
|
214
214
|
])
|
|
215
215
|
expect(url.href).toBe('http://host-from-options.com/path-from-url')
|
|
216
216
|
expect(options).toMatchObject({
|
|
217
|
-
|
|
217
|
+
hostname: 'host-from-options.com',
|
|
218
218
|
path: '/path-from-url',
|
|
219
219
|
})
|
|
220
220
|
})
|
|
@@ -230,8 +230,8 @@ it('handles [URL, RequestOptions] where options contain "host" and "path" and "p
|
|
|
230
230
|
])
|
|
231
231
|
// Must remove the query string since it's not specified in "options.path"
|
|
232
232
|
expect(url.href).toBe('http://host-from-options.com:1234/path-from-options')
|
|
233
|
-
expect(options).toMatchObject({
|
|
234
|
-
|
|
233
|
+
expect(options).toMatchObject<RequestOptions>({
|
|
234
|
+
hostname: 'host-from-options.com',
|
|
235
235
|
path: '/path-from-options',
|
|
236
236
|
port: 1234,
|
|
237
237
|
})
|
|
@@ -245,8 +245,8 @@ it('handles [URL, RequestOptions] where options contain "path" with query string
|
|
|
245
245
|
},
|
|
246
246
|
])
|
|
247
247
|
expect(url.href).toBe('http://example.com/path-from-options?foo=bar&baz=xyz')
|
|
248
|
-
expect(options).toMatchObject({
|
|
249
|
-
|
|
248
|
+
expect(options).toMatchObject<RequestOptions>({
|
|
249
|
+
hostname: 'example.com',
|
|
250
250
|
path: '/path-from-options?foo=bar&baz=xyz',
|
|
251
251
|
})
|
|
252
252
|
})
|
|
@@ -399,7 +399,6 @@ it('merges URL-based RequestOptions with the custom RequestOptions', () => {
|
|
|
399
399
|
|
|
400
400
|
// Other options must be inferred from the URL.
|
|
401
401
|
expect(options.protocol).toEqual(url.protocol)
|
|
402
|
-
expect(options.host).toEqual(url.host)
|
|
403
402
|
expect(options.hostname).toEqual(url.hostname)
|
|
404
403
|
expect(options.path).toEqual(url.pathname)
|
|
405
404
|
})
|
|
@@ -414,7 +413,6 @@ it('respects custom "options.path" over URL path', () => {
|
|
|
414
413
|
|
|
415
414
|
expect(url.href).toBe('http://example.com/path-from-options')
|
|
416
415
|
expect(options.protocol).toBe('http:')
|
|
417
|
-
expect(options.host).toBe('example.com')
|
|
418
416
|
expect(options.hostname).toBe('example.com')
|
|
419
417
|
expect(options.path).toBe('/path-from-options')
|
|
420
418
|
})
|
|
@@ -430,20 +428,20 @@ it('respects custom "options.path" over URL path with query string', () => {
|
|
|
430
428
|
// Must replace both the path and the query string.
|
|
431
429
|
expect(url.href).toBe('http://example.com/path-from-options')
|
|
432
430
|
expect(options.protocol).toBe('http:')
|
|
433
|
-
expect(options.host).toBe('example.com')
|
|
434
431
|
expect(options.hostname).toBe('example.com')
|
|
435
432
|
expect(options.path).toBe('/path-from-options')
|
|
436
433
|
})
|
|
437
434
|
|
|
438
435
|
it('preserves URL query string', () => {
|
|
439
436
|
const [url, options] = normalizeClientRequestArgs('http:', [
|
|
440
|
-
new URL('http://example.com/resource?a=b&c=d'),
|
|
437
|
+
new URL('http://example.com:8080/resource?a=b&c=d'),
|
|
441
438
|
])
|
|
442
439
|
|
|
443
|
-
expect(url.href).toBe('http://example.com/resource?a=b&c=d')
|
|
440
|
+
expect(url.href).toBe('http://example.com:8080/resource?a=b&c=d')
|
|
444
441
|
expect(options.protocol).toBe('http:')
|
|
445
|
-
expect(options.host).toBe('example.com')
|
|
442
|
+
// expect(options.host).toBe('example.com:8080')
|
|
446
443
|
expect(options.hostname).toBe('example.com')
|
|
447
444
|
// Query string is a part of the options path.
|
|
448
445
|
expect(options.path).toBe('/resource?a=b&c=d')
|
|
446
|
+
expect(options.port).toBe(8080)
|
|
449
447
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { urlToHttpOptions } from 'node:url'
|
|
1
2
|
import {
|
|
2
3
|
Agent as HttpAgent,
|
|
3
4
|
globalAgent as httpGlobalAgent,
|
|
@@ -20,7 +21,6 @@ import {
|
|
|
20
21
|
parse as parseUrl,
|
|
21
22
|
} from 'node:url'
|
|
22
23
|
import { Logger } from '@open-draft/logger'
|
|
23
|
-
import { getRequestOptionsByUrl } from '../../../utils/getRequestOptionsByUrl'
|
|
24
24
|
import {
|
|
25
25
|
ResolvedRequestOptions,
|
|
26
26
|
getUrlByRequestOptions,
|
|
@@ -47,12 +47,12 @@ function resolveRequestOptions(
|
|
|
47
47
|
// without any `RequestOptions` or callback.
|
|
48
48
|
if (typeof args[1] === 'undefined' || typeof args[1] === 'function') {
|
|
49
49
|
logger.info('request options not provided, deriving from the url', url)
|
|
50
|
-
return
|
|
50
|
+
return urlToHttpOptions(url)
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if (args[1]) {
|
|
54
54
|
logger.info('has custom RequestOptions!', args[1])
|
|
55
|
-
const requestOptionsFromUrl =
|
|
55
|
+
const requestOptionsFromUrl = urlToHttpOptions(url)
|
|
56
56
|
|
|
57
57
|
logger.info('derived RequestOptions from the URL:', requestOptionsFromUrl)
|
|
58
58
|
|
|
@@ -103,7 +103,7 @@ function resolveCallback(
|
|
|
103
103
|
export type NormalizedClientRequestArgs = [
|
|
104
104
|
url: URL,
|
|
105
105
|
options: ResolvedRequestOptions,
|
|
106
|
-
callback?: HttpRequestCallback
|
|
106
|
+
callback?: HttpRequestCallback
|
|
107
107
|
]
|
|
108
108
|
|
|
109
109
|
/**
|
|
@@ -137,7 +137,7 @@ export function normalizeClientRequestArgs(
|
|
|
137
137
|
url = new URL(args[0])
|
|
138
138
|
logger.info('created a url:', url)
|
|
139
139
|
|
|
140
|
-
const requestOptionsFromUrl =
|
|
140
|
+
const requestOptionsFromUrl = urlToHttpOptions(url)
|
|
141
141
|
logger.info('request options from url:', requestOptionsFromUrl)
|
|
142
142
|
|
|
143
143
|
options = resolveRequestOptions(args, url)
|
|
@@ -200,17 +200,17 @@ export function normalizeClientRequestArgs(
|
|
|
200
200
|
return args[1] === undefined
|
|
201
201
|
? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl])
|
|
202
202
|
: typeof args[1] === 'function'
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
203
|
+
? normalizeClientRequestArgs(defaultProtocol, [resolvedUrl, args[1]])
|
|
204
|
+
: normalizeClientRequestArgs(defaultProtocol, [
|
|
205
|
+
resolvedUrl,
|
|
206
|
+
args[1],
|
|
207
|
+
args[2],
|
|
208
|
+
])
|
|
209
209
|
}
|
|
210
210
|
// Handle a given "RequestOptions" object as-is
|
|
211
211
|
// and derive the URL instance from it.
|
|
212
212
|
else if (isObject(args[0])) {
|
|
213
|
-
options = { ...
|
|
213
|
+
options = { ...(args[0] as any) }
|
|
214
214
|
logger.info('first argument is RequestOptions:', options)
|
|
215
215
|
|
|
216
216
|
// When handling a "RequestOptions" object without an explicit "protocol",
|
|
@@ -14,9 +14,11 @@ import { parseJson } from '../../utils/parseJson'
|
|
|
14
14
|
import { createResponse } from './utils/createResponse'
|
|
15
15
|
import { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'
|
|
16
16
|
import { createRequestId } from '../../createRequestId'
|
|
17
|
+
import { getBodyByteLength } from './utils/getBodyByteLength'
|
|
17
18
|
|
|
18
|
-
const
|
|
19
|
+
const kIsRequestHandled = Symbol('kIsRequestHandled')
|
|
19
20
|
const IS_NODE = isNodeProcess()
|
|
21
|
+
const kFetchRequest = Symbol('kFetchRequest')
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* An `XMLHttpRequest` instance controller that allows us
|
|
@@ -40,17 +42,25 @@ export class XMLHttpRequestController {
|
|
|
40
42
|
request: Request
|
|
41
43
|
requestId: string
|
|
42
44
|
}
|
|
43
|
-
) => void
|
|
45
|
+
) => void;
|
|
44
46
|
|
|
47
|
+
[kIsRequestHandled]: boolean;
|
|
48
|
+
[kFetchRequest]?: Request
|
|
45
49
|
private method: string = 'GET'
|
|
46
50
|
private url: URL = null as any
|
|
47
51
|
private requestHeaders: Headers
|
|
48
|
-
private requestBody?: XMLHttpRequestBodyInit | Document | null
|
|
49
52
|
private responseBuffer: Uint8Array
|
|
50
53
|
private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>
|
|
54
|
+
private uploadEvents: Map<
|
|
55
|
+
keyof XMLHttpRequestEventTargetEventMap,
|
|
56
|
+
Array<Function>
|
|
57
|
+
>
|
|
51
58
|
|
|
52
59
|
constructor(readonly initialRequest: XMLHttpRequest, public logger: Logger) {
|
|
60
|
+
this[kIsRequestHandled] = false
|
|
61
|
+
|
|
53
62
|
this.events = new Map()
|
|
63
|
+
this.uploadEvents = new Map()
|
|
54
64
|
this.requestId = createRequestId()
|
|
55
65
|
this.requestHeaders = new Headers()
|
|
56
66
|
this.responseBuffer = new Uint8Array()
|
|
@@ -123,11 +133,6 @@ export class XMLHttpRequestController {
|
|
|
123
133
|
body?: XMLHttpRequestBodyInit | Document | null
|
|
124
134
|
]
|
|
125
135
|
|
|
126
|
-
if (body != null) {
|
|
127
|
-
this.requestBody =
|
|
128
|
-
typeof body === 'string' ? encodeBuffer(body) : body
|
|
129
|
-
}
|
|
130
|
-
|
|
131
136
|
this.request.addEventListener('load', () => {
|
|
132
137
|
if (typeof this.onResponse !== 'undefined') {
|
|
133
138
|
// Create a Fetch API Response representation of whichever
|
|
@@ -146,15 +151,20 @@ export class XMLHttpRequestController {
|
|
|
146
151
|
// Notify the consumer about the response.
|
|
147
152
|
this.onResponse.call(this, {
|
|
148
153
|
response: fetchResponse,
|
|
149
|
-
isMockedResponse:
|
|
154
|
+
isMockedResponse: this[kIsRequestHandled],
|
|
150
155
|
request: fetchRequest,
|
|
151
156
|
requestId: this.requestId!,
|
|
152
157
|
})
|
|
153
158
|
}
|
|
154
159
|
})
|
|
155
160
|
|
|
161
|
+
const requestBody =
|
|
162
|
+
typeof body === 'string' ? encodeBuffer(body) : body
|
|
163
|
+
|
|
156
164
|
// Delegate request handling to the consumer.
|
|
157
|
-
const fetchRequest = this.toFetchApiRequest()
|
|
165
|
+
const fetchRequest = this.toFetchApiRequest(requestBody)
|
|
166
|
+
this[kFetchRequest] = fetchRequest
|
|
167
|
+
|
|
158
168
|
const onceRequestSettled =
|
|
159
169
|
this.onRequest?.call(this, {
|
|
160
170
|
request: fetchRequest,
|
|
@@ -162,10 +172,8 @@ export class XMLHttpRequestController {
|
|
|
162
172
|
}) || Promise.resolve()
|
|
163
173
|
|
|
164
174
|
onceRequestSettled.finally(() => {
|
|
165
|
-
// If the consumer didn't handle the request perform it as-is.
|
|
166
|
-
|
|
167
|
-
// be LOADING while the "respondWith" method does its magic.
|
|
168
|
-
if (this.request.readyState < this.request.LOADING) {
|
|
175
|
+
// If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.
|
|
176
|
+
if (!this[kIsRequestHandled]) {
|
|
169
177
|
this.logger.info(
|
|
170
178
|
'request callback settled but request has not been handled (readystate %d), performing as-is...',
|
|
171
179
|
this.request.readyState
|
|
@@ -200,6 +208,49 @@ export class XMLHttpRequestController {
|
|
|
200
208
|
}
|
|
201
209
|
},
|
|
202
210
|
})
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Proxy the `.upload` property to gather the event listeners/callbacks.
|
|
214
|
+
*/
|
|
215
|
+
define(
|
|
216
|
+
this.request,
|
|
217
|
+
'upload',
|
|
218
|
+
createProxy(this.request.upload, {
|
|
219
|
+
setProperty: ([propertyName, nextValue], invoke) => {
|
|
220
|
+
switch (propertyName) {
|
|
221
|
+
case 'onloadstart':
|
|
222
|
+
case 'onprogress':
|
|
223
|
+
case 'onaboart':
|
|
224
|
+
case 'onerror':
|
|
225
|
+
case 'onload':
|
|
226
|
+
case 'ontimeout':
|
|
227
|
+
case 'onloadend': {
|
|
228
|
+
const eventName = propertyName.slice(
|
|
229
|
+
2
|
|
230
|
+
) as keyof XMLHttpRequestEventTargetEventMap
|
|
231
|
+
|
|
232
|
+
this.registerUploadEvent(eventName, nextValue as Function)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return invoke()
|
|
237
|
+
},
|
|
238
|
+
methodCall: ([methodName, args], invoke) => {
|
|
239
|
+
switch (methodName) {
|
|
240
|
+
case 'addEventListener': {
|
|
241
|
+
const [eventName, listener] = args as [
|
|
242
|
+
keyof XMLHttpRequestEventTargetEventMap,
|
|
243
|
+
Function
|
|
244
|
+
]
|
|
245
|
+
this.registerUploadEvent(eventName, listener)
|
|
246
|
+
this.logger.info('upload.addEventListener', eventName, listener)
|
|
247
|
+
|
|
248
|
+
return invoke()
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
})
|
|
253
|
+
)
|
|
203
254
|
}
|
|
204
255
|
|
|
205
256
|
private registerEvent(
|
|
@@ -213,23 +264,64 @@ export class XMLHttpRequestController {
|
|
|
213
264
|
this.logger.info('registered event "%s"', eventName, listener)
|
|
214
265
|
}
|
|
215
266
|
|
|
267
|
+
private registerUploadEvent(
|
|
268
|
+
eventName: keyof XMLHttpRequestEventTargetEventMap,
|
|
269
|
+
listener: Function
|
|
270
|
+
): void {
|
|
271
|
+
const prevEvents = this.uploadEvents.get(eventName) || []
|
|
272
|
+
const nextEvents = prevEvents.concat(listener)
|
|
273
|
+
this.uploadEvents.set(eventName, nextEvents)
|
|
274
|
+
|
|
275
|
+
this.logger.info('registered upload event "%s"', eventName, listener)
|
|
276
|
+
}
|
|
277
|
+
|
|
216
278
|
/**
|
|
217
279
|
* Responds to the current request with the given
|
|
218
280
|
* Fetch API `Response` instance.
|
|
219
281
|
*/
|
|
220
|
-
public respondWith(response: Response): void {
|
|
221
|
-
this.logger.info(
|
|
222
|
-
'responding with a mocked response: %d %s',
|
|
223
|
-
response.status,
|
|
224
|
-
response.statusText
|
|
225
|
-
)
|
|
226
|
-
|
|
282
|
+
public async respondWith(response: Response): Promise<void> {
|
|
227
283
|
/**
|
|
228
284
|
* @note Since `XMLHttpRequestController` delegates the handling of the responses
|
|
229
285
|
* to the "load" event listener that doesn't distinguish between the mocked and original
|
|
230
286
|
* responses, mark the request that had a mocked response with a corresponding symbol.
|
|
287
|
+
*
|
|
288
|
+
* Mark this request as having a mocked response immediately since
|
|
289
|
+
* calculating request/response total body length is asynchronous.
|
|
231
290
|
*/
|
|
232
|
-
|
|
291
|
+
this[kIsRequestHandled] = true
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Dispatch request upload events for requests with a body.
|
|
295
|
+
* @see https://github.com/mswjs/interceptors/issues/573
|
|
296
|
+
*/
|
|
297
|
+
if (this[kFetchRequest]) {
|
|
298
|
+
const totalRequestBodyLength = await getBodyByteLength(
|
|
299
|
+
this[kFetchRequest].clone()
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
this.trigger('loadstart', this.request.upload, {
|
|
303
|
+
loaded: 0,
|
|
304
|
+
total: totalRequestBodyLength,
|
|
305
|
+
})
|
|
306
|
+
this.trigger('progress', this.request.upload, {
|
|
307
|
+
loaded: totalRequestBodyLength,
|
|
308
|
+
total: totalRequestBodyLength,
|
|
309
|
+
})
|
|
310
|
+
this.trigger('load', this.request.upload, {
|
|
311
|
+
loaded: totalRequestBodyLength,
|
|
312
|
+
total: totalRequestBodyLength,
|
|
313
|
+
})
|
|
314
|
+
this.trigger('loadend', this.request.upload, {
|
|
315
|
+
loaded: totalRequestBodyLength,
|
|
316
|
+
total: totalRequestBodyLength,
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
this.logger.info(
|
|
321
|
+
'responding with a mocked response: %d %s',
|
|
322
|
+
response.status,
|
|
323
|
+
response.statusText
|
|
324
|
+
)
|
|
233
325
|
|
|
234
326
|
define(this.request, 'status', response.status)
|
|
235
327
|
define(this.request, 'statusText', response.statusText)
|
|
@@ -303,16 +395,11 @@ export class XMLHttpRequestController {
|
|
|
303
395
|
},
|
|
304
396
|
})
|
|
305
397
|
|
|
306
|
-
const totalResponseBodyLength = response.
|
|
307
|
-
? Number(response.headers.get('Content-Length'))
|
|
308
|
-
: /**
|
|
309
|
-
* @todo Infer the response body length from the response body.
|
|
310
|
-
*/
|
|
311
|
-
undefined
|
|
398
|
+
const totalResponseBodyLength = await getBodyByteLength(response.clone())
|
|
312
399
|
|
|
313
400
|
this.logger.info('calculated response body length', totalResponseBodyLength)
|
|
314
401
|
|
|
315
|
-
this.trigger('loadstart', {
|
|
402
|
+
this.trigger('loadstart', this.request, {
|
|
316
403
|
loaded: 0,
|
|
317
404
|
total: totalResponseBodyLength,
|
|
318
405
|
})
|
|
@@ -325,12 +412,12 @@ export class XMLHttpRequestController {
|
|
|
325
412
|
|
|
326
413
|
this.setReadyState(this.request.DONE)
|
|
327
414
|
|
|
328
|
-
this.trigger('load', {
|
|
415
|
+
this.trigger('load', this.request, {
|
|
329
416
|
loaded: this.responseBuffer.byteLength,
|
|
330
417
|
total: totalResponseBodyLength,
|
|
331
418
|
})
|
|
332
419
|
|
|
333
|
-
this.trigger('loadend', {
|
|
420
|
+
this.trigger('loadend', this.request, {
|
|
334
421
|
loaded: this.responseBuffer.byteLength,
|
|
335
422
|
total: totalResponseBodyLength,
|
|
336
423
|
})
|
|
@@ -354,7 +441,7 @@ export class XMLHttpRequestController {
|
|
|
354
441
|
this.logger.info('read response body chunk:', value)
|
|
355
442
|
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)
|
|
356
443
|
|
|
357
|
-
this.trigger('progress', {
|
|
444
|
+
this.trigger('progress', this.request, {
|
|
358
445
|
loaded: this.responseBuffer.byteLength,
|
|
359
446
|
total: totalResponseBodyLength,
|
|
360
447
|
})
|
|
@@ -482,11 +569,16 @@ export class XMLHttpRequestController {
|
|
|
482
569
|
}
|
|
483
570
|
|
|
484
571
|
public errorWith(error?: Error): void {
|
|
572
|
+
/**
|
|
573
|
+
* @note Mark this request as handled even if it received a mock error.
|
|
574
|
+
* This prevents the controller from trying to perform this request as-is.
|
|
575
|
+
*/
|
|
576
|
+
this[kIsRequestHandled] = true
|
|
485
577
|
this.logger.info('responding with an error')
|
|
486
578
|
|
|
487
579
|
this.setReadyState(this.request.DONE)
|
|
488
|
-
this.trigger('error')
|
|
489
|
-
this.trigger('loadend')
|
|
580
|
+
this.trigger('error', this.request)
|
|
581
|
+
this.trigger('loadend', this.request)
|
|
490
582
|
}
|
|
491
583
|
|
|
492
584
|
/**
|
|
@@ -511,7 +603,7 @@ export class XMLHttpRequestController {
|
|
|
511
603
|
if (nextReadyState !== this.request.UNSENT) {
|
|
512
604
|
this.logger.info('triggerring "readystatechange" event...')
|
|
513
605
|
|
|
514
|
-
this.trigger('readystatechange')
|
|
606
|
+
this.trigger('readystatechange', this.request)
|
|
515
607
|
}
|
|
516
608
|
}
|
|
517
609
|
|
|
@@ -522,20 +614,27 @@ export class XMLHttpRequestController {
|
|
|
522
614
|
EventName extends keyof (XMLHttpRequestEventTargetEventMap & {
|
|
523
615
|
readystatechange: ProgressEvent<XMLHttpRequestEventTarget>
|
|
524
616
|
})
|
|
525
|
-
>(
|
|
526
|
-
|
|
527
|
-
|
|
617
|
+
>(
|
|
618
|
+
eventName: EventName,
|
|
619
|
+
target: XMLHttpRequest | XMLHttpRequestUpload,
|
|
620
|
+
options?: ProgressEventInit
|
|
621
|
+
): void {
|
|
622
|
+
const callback = (target as XMLHttpRequest)[`on${eventName}`]
|
|
623
|
+
const event = createEvent(target, eventName, options)
|
|
528
624
|
|
|
529
625
|
this.logger.info('trigger "%s"', eventName, options || '')
|
|
530
626
|
|
|
531
627
|
// Invoke direct callbacks.
|
|
532
628
|
if (typeof callback === 'function') {
|
|
533
629
|
this.logger.info('found a direct "%s" callback, calling...', eventName)
|
|
534
|
-
callback.call(
|
|
630
|
+
callback.call(target as XMLHttpRequest, event)
|
|
535
631
|
}
|
|
536
632
|
|
|
537
633
|
// Invoke event listeners.
|
|
538
|
-
|
|
634
|
+
const events =
|
|
635
|
+
target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events
|
|
636
|
+
|
|
637
|
+
for (const [registeredEventName, listeners] of events) {
|
|
539
638
|
if (registeredEventName === eventName) {
|
|
540
639
|
this.logger.info(
|
|
541
640
|
'found %d listener(s) for "%s" event, calling...',
|
|
@@ -543,7 +642,7 @@ export class XMLHttpRequestController {
|
|
|
543
642
|
eventName
|
|
544
643
|
)
|
|
545
644
|
|
|
546
|
-
listeners.forEach((listener) => listener.call(
|
|
645
|
+
listeners.forEach((listener) => listener.call(target, event))
|
|
547
646
|
}
|
|
548
647
|
}
|
|
549
648
|
}
|
|
@@ -551,9 +650,16 @@ export class XMLHttpRequestController {
|
|
|
551
650
|
/**
|
|
552
651
|
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
|
|
553
652
|
*/
|
|
554
|
-
|
|
653
|
+
private toFetchApiRequest(
|
|
654
|
+
body: XMLHttpRequestBodyInit | Document | null | undefined
|
|
655
|
+
): Request {
|
|
555
656
|
this.logger.info('converting request to a Fetch API Request...')
|
|
556
657
|
|
|
658
|
+
// If the `Document` is used as the body of this XMLHttpRequest,
|
|
659
|
+
// set its inner text as the Fetch API Request body.
|
|
660
|
+
const resolvedBody =
|
|
661
|
+
body instanceof Document ? body.documentElement.innerText : body
|
|
662
|
+
|
|
557
663
|
const fetchRequest = new Request(this.url.href, {
|
|
558
664
|
method: this.method,
|
|
559
665
|
headers: this.requestHeaders,
|
|
@@ -563,7 +669,7 @@ export class XMLHttpRequestController {
|
|
|
563
669
|
credentials: this.request.withCredentials ? 'include' : 'same-origin',
|
|
564
670
|
body: ['GET', 'HEAD'].includes(this.method.toUpperCase())
|
|
565
671
|
? null
|
|
566
|
-
:
|
|
672
|
+
: resolvedBody,
|
|
567
673
|
})
|
|
568
674
|
|
|
569
675
|
const proxyHeaders = createProxy(fetchRequest.headers, {
|
|
@@ -66,8 +66,8 @@ export function createXMLHttpRequestProxy({
|
|
|
66
66
|
requestId,
|
|
67
67
|
controller,
|
|
68
68
|
emitter,
|
|
69
|
-
onResponse: (response) => {
|
|
70
|
-
this.respondWith(response)
|
|
69
|
+
onResponse: async (response) => {
|
|
70
|
+
await this.respondWith(response)
|
|
71
71
|
},
|
|
72
72
|
onRequestError: () => {
|
|
73
73
|
this.errorWith(new TypeError('Network error'))
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export class EventPolyfill implements Event {
|
|
2
|
-
readonly
|
|
3
|
-
readonly
|
|
4
|
-
readonly
|
|
5
|
-
readonly
|
|
2
|
+
readonly NONE = 0
|
|
3
|
+
readonly CAPTURING_PHASE = 1
|
|
4
|
+
readonly AT_TARGET = 2
|
|
5
|
+
readonly BUBBLING_PHASE = 3
|
|
6
6
|
|
|
7
7
|
public type: string = ''
|
|
8
8
|
public srcElement: EventTarget | null = null
|
|
@@ -4,7 +4,7 @@ import { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'
|
|
|
4
4
|
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'
|
|
5
5
|
|
|
6
6
|
export function createEvent(
|
|
7
|
-
target: XMLHttpRequest,
|
|
7
|
+
target: XMLHttpRequest | XMLHttpRequestUpload,
|
|
8
8
|
type: string,
|
|
9
9
|
init?: ProgressEventInit
|
|
10
10
|
): EventPolyfill {
|