@depup/mswjs__interceptors 0.41.3-depup.0
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/ClientRequest/package.json +11 -0
- package/LICENSE.md +9 -0
- package/README.md +31 -0
- package/RemoteHttpInterceptor/package.json +11 -0
- package/WebSocket/package.json +12 -0
- package/XMLHttpRequest/package.json +12 -0
- package/changes.json +10 -0
- package/fetch/package.json +12 -0
- package/lib/browser/Interceptor-Deczogc8.d.cts +65 -0
- package/lib/browser/Interceptor-gqKgs-aF.d.mts +65 -0
- package/lib/browser/XMLHttpRequest-BACqefB-.cjs +761 -0
- package/lib/browser/XMLHttpRequest-BACqefB-.cjs.map +1 -0
- package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs +756 -0
- package/lib/browser/XMLHttpRequest-BvxZV0WU.mjs.map +1 -0
- package/lib/browser/bufferUtils-BiiO6HZv.mjs +20 -0
- package/lib/browser/bufferUtils-BiiO6HZv.mjs.map +1 -0
- package/lib/browser/bufferUtils-Uc0eRItL.cjs +38 -0
- package/lib/browser/bufferUtils-Uc0eRItL.cjs.map +1 -0
- package/lib/browser/createRequestId-Cs4oXfa1.cjs +205 -0
- package/lib/browser/createRequestId-Cs4oXfa1.cjs.map +1 -0
- package/lib/browser/createRequestId-DQcIlohW.mjs +170 -0
- package/lib/browser/createRequestId-DQcIlohW.mjs.map +1 -0
- package/lib/browser/fetch-DdKEdDOR.mjs +248 -0
- package/lib/browser/fetch-DdKEdDOR.mjs.map +1 -0
- package/lib/browser/fetch-U3v3Y4ap.cjs +253 -0
- package/lib/browser/fetch-U3v3Y4ap.cjs.map +1 -0
- package/lib/browser/getRawRequest-BTaNLFr0.mjs +218 -0
- package/lib/browser/getRawRequest-BTaNLFr0.mjs.map +1 -0
- package/lib/browser/getRawRequest-zx8rUJL2.cjs +259 -0
- package/lib/browser/getRawRequest-zx8rUJL2.cjs.map +1 -0
- package/lib/browser/glossary-BdLS4k1H.d.cts +70 -0
- package/lib/browser/glossary-DYwOrogs.d.mts +70 -0
- package/lib/browser/handleRequest-CvX2G-Lz.cjs +189 -0
- package/lib/browser/handleRequest-CvX2G-Lz.cjs.map +1 -0
- package/lib/browser/handleRequest-D7kpTI5U.mjs +178 -0
- package/lib/browser/handleRequest-D7kpTI5U.mjs.map +1 -0
- package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs +45 -0
- package/lib/browser/hasConfigurableGlobal-BvCTG97d.cjs.map +1 -0
- package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs +33 -0
- package/lib/browser/hasConfigurableGlobal-npXitu1-.mjs.map +1 -0
- package/lib/browser/index.cjs +70 -0
- package/lib/browser/index.cjs.map +1 -0
- package/lib/browser/index.d.cts +96 -0
- package/lib/browser/index.d.mts +96 -0
- package/lib/browser/index.mjs +56 -0
- package/lib/browser/index.mjs.map +1 -0
- package/lib/browser/interceptors/WebSocket/index.cjs +622 -0
- package/lib/browser/interceptors/WebSocket/index.cjs.map +1 -0
- package/lib/browser/interceptors/WebSocket/index.d.cts +277 -0
- package/lib/browser/interceptors/WebSocket/index.d.mts +277 -0
- package/lib/browser/interceptors/WebSocket/index.mjs +615 -0
- package/lib/browser/interceptors/WebSocket/index.mjs.map +1 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.cjs +7 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.d.cts +15 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.d.mts +15 -0
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +7 -0
- package/lib/browser/interceptors/fetch/index.cjs +6 -0
- package/lib/browser/interceptors/fetch/index.d.cts +13 -0
- package/lib/browser/interceptors/fetch/index.d.mts +13 -0
- package/lib/browser/interceptors/fetch/index.mjs +6 -0
- package/lib/browser/presets/browser.cjs +17 -0
- package/lib/browser/presets/browser.cjs.map +1 -0
- package/lib/browser/presets/browser.d.cts +12 -0
- package/lib/browser/presets/browser.d.mts +14 -0
- package/lib/browser/presets/browser.mjs +17 -0
- package/lib/browser/presets/browser.mjs.map +1 -0
- package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs +31 -0
- package/lib/browser/resolveWebSocketUrl-6K6EgqsA.cjs.map +1 -0
- package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs +25 -0
- package/lib/browser/resolveWebSocketUrl-C83-x9iE.mjs.map +1 -0
- package/lib/node/BatchInterceptor-3LnAnLTx.cjs +49 -0
- package/lib/node/BatchInterceptor-3LnAnLTx.cjs.map +1 -0
- package/lib/node/BatchInterceptor-D7mXzHcQ.d.mts +26 -0
- package/lib/node/BatchInterceptor-DFaBPilf.mjs +44 -0
- package/lib/node/BatchInterceptor-DFaBPilf.mjs.map +1 -0
- package/lib/node/BatchInterceptor-D_YqR8qU.d.cts +26 -0
- package/lib/node/ClientRequest-2rDe54Ui.cjs +1043 -0
- package/lib/node/ClientRequest-2rDe54Ui.cjs.map +1 -0
- package/lib/node/ClientRequest-Ca8Qykuv.mjs +1034 -0
- package/lib/node/ClientRequest-Ca8Qykuv.mjs.map +1 -0
- package/lib/node/Interceptor-DEazpLJd.d.mts +133 -0
- package/lib/node/Interceptor-DJ2akVWI.d.cts +133 -0
- package/lib/node/RemoteHttpInterceptor.cjs +154 -0
- package/lib/node/RemoteHttpInterceptor.cjs.map +1 -0
- package/lib/node/RemoteHttpInterceptor.d.cts +39 -0
- package/lib/node/RemoteHttpInterceptor.d.mts +39 -0
- package/lib/node/RemoteHttpInterceptor.mjs +152 -0
- package/lib/node/RemoteHttpInterceptor.mjs.map +1 -0
- package/lib/node/XMLHttpRequest-B7kJdYYI.cjs +763 -0
- package/lib/node/XMLHttpRequest-B7kJdYYI.cjs.map +1 -0
- package/lib/node/XMLHttpRequest-C8dIZpds.mjs +757 -0
- package/lib/node/XMLHttpRequest-C8dIZpds.mjs.map +1 -0
- package/lib/node/bufferUtils-DiCTqG-7.cjs +38 -0
- package/lib/node/bufferUtils-DiCTqG-7.cjs.map +1 -0
- package/lib/node/bufferUtils-_8XfKIfX.mjs +20 -0
- package/lib/node/bufferUtils-_8XfKIfX.mjs.map +1 -0
- package/lib/node/chunk-CbDLau6x.cjs +34 -0
- package/lib/node/fetch-BmXpK10r.cjs +272 -0
- package/lib/node/fetch-BmXpK10r.cjs.map +1 -0
- package/lib/node/fetch-G1DVwDKG.mjs +265 -0
- package/lib/node/fetch-G1DVwDKG.mjs.map +1 -0
- package/lib/node/fetchUtils-BaY5iWXw.cjs +419 -0
- package/lib/node/fetchUtils-BaY5iWXw.cjs.map +1 -0
- package/lib/node/fetchUtils-CoU35g3M.mjs +359 -0
- package/lib/node/fetchUtils-CoU35g3M.mjs.map +1 -0
- package/lib/node/getRawRequest-BavnMWh_.cjs +36 -0
- package/lib/node/getRawRequest-BavnMWh_.cjs.map +1 -0
- package/lib/node/getRawRequest-DnwmXyOW.mjs +24 -0
- package/lib/node/getRawRequest-DnwmXyOW.mjs.map +1 -0
- package/lib/node/glossary-BLKRyLBd.cjs +12 -0
- package/lib/node/glossary-BLKRyLBd.cjs.map +1 -0
- package/lib/node/glossary-glQBRnVD.mjs +6 -0
- package/lib/node/glossary-glQBRnVD.mjs.map +1 -0
- package/lib/node/handleRequest-Bb7Y-XLw.cjs +220 -0
- package/lib/node/handleRequest-Bb7Y-XLw.cjs.map +1 -0
- package/lib/node/handleRequest-Y97UwBbF.mjs +190 -0
- package/lib/node/handleRequest-Y97UwBbF.mjs.map +1 -0
- package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs +26 -0
- package/lib/node/hasConfigurableGlobal-C97fWuaA.cjs.map +1 -0
- package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs +20 -0
- package/lib/node/hasConfigurableGlobal-DBJA0vjm.mjs.map +1 -0
- package/lib/node/index-BMbJ8FXL.d.cts +113 -0
- package/lib/node/index-C0YAQ36w.d.mts +113 -0
- package/lib/node/index.cjs +54 -0
- package/lib/node/index.cjs.map +1 -0
- package/lib/node/index.d.cts +75 -0
- package/lib/node/index.d.mts +75 -0
- package/lib/node/index.mjs +40 -0
- package/lib/node/index.mjs.map +1 -0
- package/lib/node/interceptors/ClientRequest/index.cjs +6 -0
- package/lib/node/interceptors/ClientRequest/index.d.cts +2 -0
- package/lib/node/interceptors/ClientRequest/index.d.mts +3 -0
- package/lib/node/interceptors/ClientRequest/index.mjs +6 -0
- package/lib/node/interceptors/XMLHttpRequest/index.cjs +6 -0
- package/lib/node/interceptors/XMLHttpRequest/index.d.cts +14 -0
- package/lib/node/interceptors/XMLHttpRequest/index.d.mts +14 -0
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +6 -0
- package/lib/node/interceptors/fetch/index.cjs +5 -0
- package/lib/node/interceptors/fetch/index.d.cts +12 -0
- package/lib/node/interceptors/fetch/index.d.mts +12 -0
- package/lib/node/interceptors/fetch/index.mjs +5 -0
- package/lib/node/node-DwCc6iuP.mjs +27 -0
- package/lib/node/node-DwCc6iuP.mjs.map +1 -0
- package/lib/node/node-dKdAf3tC.cjs +39 -0
- package/lib/node/node-dKdAf3tC.cjs.map +1 -0
- package/lib/node/presets/node.cjs +22 -0
- package/lib/node/presets/node.cjs.map +1 -0
- package/lib/node/presets/node.d.cts +13 -0
- package/lib/node/presets/node.d.mts +15 -0
- package/lib/node/presets/node.mjs +22 -0
- package/lib/node/presets/node.mjs.map +1 -0
- package/lib/node/utils/node/index.cjs +4 -0
- package/lib/node/utils/node/index.d.cts +16 -0
- package/lib/node/utils/node/index.d.mts +16 -0
- package/lib/node/utils/node/index.mjs +3 -0
- package/package.json +204 -0
- package/presets/browser/package.json +5 -0
- package/presets/node/package.json +11 -0
- package/src/BatchInterceptor.test.ts +255 -0
- package/src/BatchInterceptor.ts +95 -0
- package/src/Interceptor.test.ts +205 -0
- package/src/Interceptor.ts +249 -0
- package/src/InterceptorError.ts +7 -0
- package/src/RemoteHttpInterceptor.ts +251 -0
- package/src/RequestController.test.ts +104 -0
- package/src/RequestController.ts +109 -0
- package/src/createRequestId.test.ts +7 -0
- package/src/createRequestId.ts +9 -0
- package/src/getRawRequest.ts +21 -0
- package/src/glossary.ts +37 -0
- package/src/index.ts +15 -0
- package/src/interceptors/ClientRequest/MockHttpSocket.ts +724 -0
- package/src/interceptors/ClientRequest/agents.ts +110 -0
- package/src/interceptors/ClientRequest/index.test.ts +75 -0
- package/src/interceptors/ClientRequest/index.ts +193 -0
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +54 -0
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +45 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +427 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +268 -0
- package/src/interceptors/ClientRequest/utils/parserUtils.ts +48 -0
- package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +258 -0
- package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +262 -0
- package/src/interceptors/Socket/MockSocket.test.ts +264 -0
- package/src/interceptors/Socket/MockSocket.ts +58 -0
- package/src/interceptors/Socket/utils/baseUrlFromConnectionOptions.ts +26 -0
- package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.test.ts +52 -0
- package/src/interceptors/Socket/utils/normalizeSocketWriteArgs.ts +33 -0
- package/src/interceptors/WebSocket/WebSocketClassTransport.ts +116 -0
- package/src/interceptors/WebSocket/WebSocketClientConnection.ts +152 -0
- package/src/interceptors/WebSocket/WebSocketOverride.ts +252 -0
- package/src/interceptors/WebSocket/WebSocketServerConnection.ts +420 -0
- package/src/interceptors/WebSocket/WebSocketTransport.ts +39 -0
- package/src/interceptors/WebSocket/index.ts +191 -0
- package/src/interceptors/WebSocket/utils/bindEvent.test.ts +27 -0
- package/src/interceptors/WebSocket/utils/bindEvent.ts +21 -0
- package/src/interceptors/WebSocket/utils/events.test.ts +101 -0
- package/src/interceptors/WebSocket/utils/events.ts +94 -0
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +746 -0
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +121 -0
- package/src/interceptors/XMLHttpRequest/index.ts +61 -0
- package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
- package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
- package/src/interceptors/XMLHttpRequest/utils/concatArrayBuffer.ts +12 -0
- package/src/interceptors/XMLHttpRequest/utils/concateArrayBuffer.test.ts +12 -0
- package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +26 -0
- package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
- package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +49 -0
- package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.test.ts +164 -0
- package/src/interceptors/XMLHttpRequest/utils/getBodyByteLength.ts +16 -0
- package/src/interceptors/XMLHttpRequest/utils/isDomParserSupportedType.ts +14 -0
- package/src/interceptors/fetch/index.ts +214 -0
- package/src/interceptors/fetch/utils/brotli-decompress.browser.ts +14 -0
- package/src/interceptors/fetch/utils/brotli-decompress.ts +31 -0
- package/src/interceptors/fetch/utils/createNetworkError.ts +5 -0
- package/src/interceptors/fetch/utils/decompression.ts +85 -0
- package/src/interceptors/fetch/utils/followRedirect.ts +114 -0
- package/src/presets/browser.ts +11 -0
- package/src/presets/node.ts +13 -0
- package/src/utils/bufferUtils.test.ts +21 -0
- package/src/utils/bufferUtils.ts +22 -0
- package/src/utils/canParseUrl.ts +13 -0
- package/src/utils/cloneObject.test.ts +94 -0
- package/src/utils/cloneObject.ts +36 -0
- package/src/utils/createProxy.test.ts +164 -0
- package/src/utils/createProxy.ts +104 -0
- package/src/utils/emitAsync.ts +25 -0
- package/src/utils/fetchUtils.ts +119 -0
- package/src/utils/findPropertySource.test.ts +27 -0
- package/src/utils/findPropertySource.ts +20 -0
- package/src/utils/getCleanUrl.test.ts +32 -0
- package/src/utils/getCleanUrl.ts +6 -0
- package/src/utils/getUrlByRequestOptions.test.ts +163 -0
- package/src/utils/getUrlByRequestOptions.ts +152 -0
- package/src/utils/getValueBySymbol.test.ts +14 -0
- package/src/utils/getValueBySymbol.ts +19 -0
- package/src/utils/handleRequest.ts +205 -0
- package/src/utils/hasConfigurableGlobal.test.ts +83 -0
- package/src/utils/hasConfigurableGlobal.ts +34 -0
- package/src/utils/isNodeLikeError.ts +13 -0
- package/src/utils/isObject.test.ts +21 -0
- package/src/utils/isObject.ts +8 -0
- package/src/utils/isPropertyAccessible.ts +19 -0
- package/src/utils/nextTick.ts +11 -0
- package/src/utils/node/index.ts +39 -0
- package/src/utils/parseJson.test.ts +10 -0
- package/src/utils/parseJson.ts +12 -0
- package/src/utils/resolveWebSocketUrl.ts +45 -0
- package/src/utils/responseUtils.ts +59 -0
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
import { invariant } from 'outvariant'
|
|
2
|
+
import { isNodeProcess } from 'is-node-process'
|
|
3
|
+
import type { Logger } from '@open-draft/logger'
|
|
4
|
+
import { concatArrayBuffer } from './utils/concatArrayBuffer'
|
|
5
|
+
import { createEvent } from './utils/createEvent'
|
|
6
|
+
import {
|
|
7
|
+
decodeBuffer,
|
|
8
|
+
encodeBuffer,
|
|
9
|
+
toArrayBuffer,
|
|
10
|
+
} from '../../utils/bufferUtils'
|
|
11
|
+
import { createProxy } from '../../utils/createProxy'
|
|
12
|
+
import { isDomParserSupportedType } from './utils/isDomParserSupportedType'
|
|
13
|
+
import { parseJson } from '../../utils/parseJson'
|
|
14
|
+
import { createResponse } from './utils/createResponse'
|
|
15
|
+
import { INTERNAL_REQUEST_ID_HEADER_NAME } from '../../Interceptor'
|
|
16
|
+
import { createRequestId } from '../../createRequestId'
|
|
17
|
+
import { getBodyByteLength } from './utils/getBodyByteLength'
|
|
18
|
+
import { setRawRequest } from '../../getRawRequest'
|
|
19
|
+
|
|
20
|
+
const kIsRequestHandled = Symbol('kIsRequestHandled')
|
|
21
|
+
const IS_NODE = isNodeProcess()
|
|
22
|
+
const kFetchRequest = Symbol('kFetchRequest')
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* An `XMLHttpRequest` instance controller that allows us
|
|
26
|
+
* to handle any given request instance (e.g. responding to it).
|
|
27
|
+
*/
|
|
28
|
+
export class XMLHttpRequestController {
|
|
29
|
+
public request: XMLHttpRequest
|
|
30
|
+
public requestId: string
|
|
31
|
+
public onRequest?: (
|
|
32
|
+
this: XMLHttpRequestController,
|
|
33
|
+
args: {
|
|
34
|
+
request: Request
|
|
35
|
+
requestId: string
|
|
36
|
+
}
|
|
37
|
+
) => Promise<void>
|
|
38
|
+
public onResponse?: (
|
|
39
|
+
this: XMLHttpRequestController,
|
|
40
|
+
args: {
|
|
41
|
+
response: Response
|
|
42
|
+
isMockedResponse: boolean
|
|
43
|
+
request: Request
|
|
44
|
+
requestId: string
|
|
45
|
+
}
|
|
46
|
+
) => void;
|
|
47
|
+
|
|
48
|
+
[kIsRequestHandled]: boolean;
|
|
49
|
+
[kFetchRequest]?: Request
|
|
50
|
+
private method: string = 'GET'
|
|
51
|
+
private url: URL = null as any
|
|
52
|
+
private requestHeaders: Headers
|
|
53
|
+
private responseBuffer: Uint8Array
|
|
54
|
+
private events: Map<keyof XMLHttpRequestEventTargetEventMap, Array<Function>>
|
|
55
|
+
private uploadEvents: Map<
|
|
56
|
+
keyof XMLHttpRequestEventTargetEventMap,
|
|
57
|
+
Array<Function>
|
|
58
|
+
>
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
readonly initialRequest: XMLHttpRequest,
|
|
62
|
+
public logger: Logger
|
|
63
|
+
) {
|
|
64
|
+
this[kIsRequestHandled] = false
|
|
65
|
+
|
|
66
|
+
this.events = new Map()
|
|
67
|
+
this.uploadEvents = new Map()
|
|
68
|
+
this.requestId = createRequestId()
|
|
69
|
+
this.requestHeaders = new Headers()
|
|
70
|
+
this.responseBuffer = new Uint8Array()
|
|
71
|
+
|
|
72
|
+
this.request = createProxy(initialRequest, {
|
|
73
|
+
setProperty: ([propertyName, nextValue], invoke) => {
|
|
74
|
+
switch (propertyName) {
|
|
75
|
+
case 'ontimeout': {
|
|
76
|
+
const eventName = propertyName.slice(
|
|
77
|
+
2
|
|
78
|
+
) as keyof XMLHttpRequestEventTargetEventMap
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @note Proxy callbacks to event listeners because JSDOM has trouble
|
|
82
|
+
* translating these properties to callbacks. It seemed to be operating
|
|
83
|
+
* on events exclusively.
|
|
84
|
+
*/
|
|
85
|
+
this.request.addEventListener(eventName, nextValue as any)
|
|
86
|
+
|
|
87
|
+
return invoke()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
default: {
|
|
91
|
+
return invoke()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
methodCall: ([methodName, args], invoke) => {
|
|
96
|
+
switch (methodName) {
|
|
97
|
+
case 'open': {
|
|
98
|
+
const [method, url] = args as [string, string | undefined]
|
|
99
|
+
|
|
100
|
+
if (typeof url === 'undefined') {
|
|
101
|
+
this.method = 'GET'
|
|
102
|
+
this.url = toAbsoluteUrl(method)
|
|
103
|
+
} else {
|
|
104
|
+
this.method = method
|
|
105
|
+
this.url = toAbsoluteUrl(url)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.logger = this.logger.extend(`${this.method} ${this.url.href}`)
|
|
109
|
+
this.logger.info('open', this.method, this.url.href)
|
|
110
|
+
|
|
111
|
+
return invoke()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case 'addEventListener': {
|
|
115
|
+
const [eventName, listener] = args as [
|
|
116
|
+
keyof XMLHttpRequestEventTargetEventMap,
|
|
117
|
+
Function,
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
this.registerEvent(eventName, listener)
|
|
121
|
+
this.logger.info('addEventListener', eventName, listener)
|
|
122
|
+
|
|
123
|
+
return invoke()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case 'setRequestHeader': {
|
|
127
|
+
const [name, value] = args as [string, string]
|
|
128
|
+
this.requestHeaders.set(name, value)
|
|
129
|
+
|
|
130
|
+
this.logger.info('setRequestHeader', name, value)
|
|
131
|
+
|
|
132
|
+
return invoke()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
case 'send': {
|
|
136
|
+
const [body] = args as [
|
|
137
|
+
body?: XMLHttpRequestBodyInit | Document | null,
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
this.request.addEventListener('load', () => {
|
|
141
|
+
if (typeof this.onResponse !== 'undefined') {
|
|
142
|
+
// Create a Fetch API Response representation of whichever
|
|
143
|
+
// response this XMLHttpRequest received. Note those may
|
|
144
|
+
// be either a mocked and the original response.
|
|
145
|
+
const fetchResponse = createResponse(
|
|
146
|
+
this.request,
|
|
147
|
+
/**
|
|
148
|
+
* The `response` property is the right way to read
|
|
149
|
+
* the ambiguous response body, as the request's "responseType" may differ.
|
|
150
|
+
* @see https://xhr.spec.whatwg.org/#the-response-attribute
|
|
151
|
+
*/
|
|
152
|
+
this.request.response
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
// Notify the consumer about the response.
|
|
156
|
+
this.onResponse.call(this, {
|
|
157
|
+
response: fetchResponse,
|
|
158
|
+
isMockedResponse: this[kIsRequestHandled],
|
|
159
|
+
request: fetchRequest,
|
|
160
|
+
requestId: this.requestId!,
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const requestBody =
|
|
166
|
+
typeof body === 'string' ? encodeBuffer(body) : body
|
|
167
|
+
|
|
168
|
+
// Delegate request handling to the consumer.
|
|
169
|
+
const fetchRequest = this.toFetchApiRequest(requestBody)
|
|
170
|
+
this[kFetchRequest] = fetchRequest.clone()
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @note Start request handling on the next tick so that the user
|
|
174
|
+
* could add event listeners for "loadend" before the interceptor fires it.
|
|
175
|
+
*/
|
|
176
|
+
queueMicrotask(() => {
|
|
177
|
+
const onceRequestSettled =
|
|
178
|
+
this.onRequest?.call(this, {
|
|
179
|
+
request: fetchRequest,
|
|
180
|
+
requestId: this.requestId!,
|
|
181
|
+
}) || Promise.resolve()
|
|
182
|
+
|
|
183
|
+
onceRequestSettled.finally(() => {
|
|
184
|
+
// If the consumer didn't handle the request (called `.respondWith()`) perform it as-is.
|
|
185
|
+
if (!this[kIsRequestHandled]) {
|
|
186
|
+
this.logger.info(
|
|
187
|
+
'request callback settled but request has not been handled (readystate %d), performing as-is...',
|
|
188
|
+
this.request.readyState
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @note Set the intercepted request ID on the original request in Node.js
|
|
193
|
+
* so that if it triggers any other interceptors, they don't attempt
|
|
194
|
+
* to process it once again.
|
|
195
|
+
*
|
|
196
|
+
* For instance, XMLHttpRequest is often implemented via "http.ClientRequest"
|
|
197
|
+
* and we don't want for both XHR and ClientRequest interceptors to
|
|
198
|
+
* handle the same request at the same time (e.g. emit the "response" event twice).
|
|
199
|
+
*/
|
|
200
|
+
if (IS_NODE) {
|
|
201
|
+
this.request.setRequestHeader(
|
|
202
|
+
INTERNAL_REQUEST_ID_HEADER_NAME,
|
|
203
|
+
this.requestId!
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return invoke()
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
break
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
default: {
|
|
216
|
+
return invoke()
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Proxy the `.upload` property to gather the event listeners/callbacks.
|
|
224
|
+
*/
|
|
225
|
+
define(
|
|
226
|
+
this.request,
|
|
227
|
+
'upload',
|
|
228
|
+
createProxy(this.request.upload, {
|
|
229
|
+
setProperty: ([propertyName, nextValue], invoke) => {
|
|
230
|
+
switch (propertyName) {
|
|
231
|
+
case 'onloadstart':
|
|
232
|
+
case 'onprogress':
|
|
233
|
+
case 'onaboart':
|
|
234
|
+
case 'onerror':
|
|
235
|
+
case 'onload':
|
|
236
|
+
case 'ontimeout':
|
|
237
|
+
case 'onloadend': {
|
|
238
|
+
const eventName = propertyName.slice(
|
|
239
|
+
2
|
|
240
|
+
) as keyof XMLHttpRequestEventTargetEventMap
|
|
241
|
+
|
|
242
|
+
this.registerUploadEvent(eventName, nextValue as Function)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return invoke()
|
|
247
|
+
},
|
|
248
|
+
methodCall: ([methodName, args], invoke) => {
|
|
249
|
+
switch (methodName) {
|
|
250
|
+
case 'addEventListener': {
|
|
251
|
+
const [eventName, listener] = args as [
|
|
252
|
+
keyof XMLHttpRequestEventTargetEventMap,
|
|
253
|
+
Function,
|
|
254
|
+
]
|
|
255
|
+
this.registerUploadEvent(eventName, listener)
|
|
256
|
+
this.logger.info('upload.addEventListener', eventName, listener)
|
|
257
|
+
|
|
258
|
+
return invoke()
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
})
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private registerEvent(
|
|
267
|
+
eventName: keyof XMLHttpRequestEventTargetEventMap,
|
|
268
|
+
listener: Function
|
|
269
|
+
): void {
|
|
270
|
+
const prevEvents = this.events.get(eventName) || []
|
|
271
|
+
const nextEvents = prevEvents.concat(listener)
|
|
272
|
+
this.events.set(eventName, nextEvents)
|
|
273
|
+
|
|
274
|
+
this.logger.info('registered event "%s"', eventName, listener)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private registerUploadEvent(
|
|
278
|
+
eventName: keyof XMLHttpRequestEventTargetEventMap,
|
|
279
|
+
listener: Function
|
|
280
|
+
): void {
|
|
281
|
+
const prevEvents = this.uploadEvents.get(eventName) || []
|
|
282
|
+
const nextEvents = prevEvents.concat(listener)
|
|
283
|
+
this.uploadEvents.set(eventName, nextEvents)
|
|
284
|
+
|
|
285
|
+
this.logger.info('registered upload event "%s"', eventName, listener)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Responds to the current request with the given
|
|
290
|
+
* Fetch API `Response` instance.
|
|
291
|
+
*/
|
|
292
|
+
public async respondWith(response: Response): Promise<void> {
|
|
293
|
+
/**
|
|
294
|
+
* @note Since `XMLHttpRequestController` delegates the handling of the responses
|
|
295
|
+
* to the "load" event listener that doesn't distinguish between the mocked and original
|
|
296
|
+
* responses, mark the request that had a mocked response with a corresponding symbol.
|
|
297
|
+
*
|
|
298
|
+
* Mark this request as having a mocked response immediately since
|
|
299
|
+
* calculating request/response total body length is asynchronous.
|
|
300
|
+
*/
|
|
301
|
+
this[kIsRequestHandled] = true
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Dispatch request upload events for requests with a body.
|
|
305
|
+
* @see https://github.com/mswjs/interceptors/issues/573
|
|
306
|
+
*/
|
|
307
|
+
if (this[kFetchRequest]) {
|
|
308
|
+
const totalRequestBodyLength = await getBodyByteLength(
|
|
309
|
+
this[kFetchRequest]
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
this.trigger('loadstart', this.request.upload, {
|
|
313
|
+
loaded: 0,
|
|
314
|
+
total: totalRequestBodyLength,
|
|
315
|
+
})
|
|
316
|
+
this.trigger('progress', this.request.upload, {
|
|
317
|
+
loaded: totalRequestBodyLength,
|
|
318
|
+
total: totalRequestBodyLength,
|
|
319
|
+
})
|
|
320
|
+
this.trigger('load', this.request.upload, {
|
|
321
|
+
loaded: totalRequestBodyLength,
|
|
322
|
+
total: totalRequestBodyLength,
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
this.trigger('loadend', this.request.upload, {
|
|
326
|
+
loaded: totalRequestBodyLength,
|
|
327
|
+
total: totalRequestBodyLength,
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
this.logger.info(
|
|
332
|
+
'responding with a mocked response: %d %s',
|
|
333
|
+
response.status,
|
|
334
|
+
response.statusText
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
define(this.request, 'status', response.status)
|
|
338
|
+
define(this.request, 'statusText', response.statusText)
|
|
339
|
+
define(this.request, 'responseURL', this.url.href)
|
|
340
|
+
|
|
341
|
+
this.request.getResponseHeader = new Proxy(this.request.getResponseHeader, {
|
|
342
|
+
apply: (_, __, args: [name: string]) => {
|
|
343
|
+
this.logger.info('getResponseHeader', args[0])
|
|
344
|
+
|
|
345
|
+
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
|
|
346
|
+
this.logger.info('headers not received yet, returning null')
|
|
347
|
+
|
|
348
|
+
// Headers not received yet, nothing to return.
|
|
349
|
+
return null
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const headerValue = response.headers.get(args[0])
|
|
353
|
+
this.logger.info(
|
|
354
|
+
'resolved response header "%s" to',
|
|
355
|
+
args[0],
|
|
356
|
+
headerValue
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return headerValue
|
|
360
|
+
},
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
this.request.getAllResponseHeaders = new Proxy(
|
|
364
|
+
this.request.getAllResponseHeaders,
|
|
365
|
+
{
|
|
366
|
+
apply: () => {
|
|
367
|
+
this.logger.info('getAllResponseHeaders')
|
|
368
|
+
|
|
369
|
+
if (this.request.readyState < this.request.HEADERS_RECEIVED) {
|
|
370
|
+
this.logger.info('headers not received yet, returning empty string')
|
|
371
|
+
|
|
372
|
+
// Headers not received yet, nothing to return.
|
|
373
|
+
return ''
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const headersList = Array.from(response.headers.entries())
|
|
377
|
+
const allHeaders = headersList
|
|
378
|
+
.map(([headerName, headerValue]) => {
|
|
379
|
+
return `${headerName}: ${headerValue}`
|
|
380
|
+
})
|
|
381
|
+
.join('\r\n')
|
|
382
|
+
|
|
383
|
+
this.logger.info('resolved all response headers to', allHeaders)
|
|
384
|
+
|
|
385
|
+
return allHeaders
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
// Update the response getters to resolve against the mocked response.
|
|
391
|
+
Object.defineProperties(this.request, {
|
|
392
|
+
response: {
|
|
393
|
+
enumerable: true,
|
|
394
|
+
configurable: false,
|
|
395
|
+
get: () => this.response,
|
|
396
|
+
},
|
|
397
|
+
responseText: {
|
|
398
|
+
enumerable: true,
|
|
399
|
+
configurable: false,
|
|
400
|
+
get: () => this.responseText,
|
|
401
|
+
},
|
|
402
|
+
responseXML: {
|
|
403
|
+
enumerable: true,
|
|
404
|
+
configurable: false,
|
|
405
|
+
get: () => this.responseXML,
|
|
406
|
+
},
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
const totalResponseBodyLength = await getBodyByteLength(response.clone())
|
|
410
|
+
|
|
411
|
+
this.logger.info('calculated response body length', totalResponseBodyLength)
|
|
412
|
+
|
|
413
|
+
this.trigger('loadstart', this.request, {
|
|
414
|
+
loaded: 0,
|
|
415
|
+
total: totalResponseBodyLength,
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
this.setReadyState(this.request.HEADERS_RECEIVED)
|
|
419
|
+
this.setReadyState(this.request.LOADING)
|
|
420
|
+
|
|
421
|
+
const finalizeResponse = () => {
|
|
422
|
+
this.logger.info('finalizing the mocked response...')
|
|
423
|
+
|
|
424
|
+
this.setReadyState(this.request.DONE)
|
|
425
|
+
|
|
426
|
+
this.trigger('load', this.request, {
|
|
427
|
+
loaded: this.responseBuffer.byteLength,
|
|
428
|
+
total: totalResponseBodyLength,
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
this.trigger('loadend', this.request, {
|
|
432
|
+
loaded: this.responseBuffer.byteLength,
|
|
433
|
+
total: totalResponseBodyLength,
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (response.body) {
|
|
438
|
+
this.logger.info('mocked response has body, streaming...')
|
|
439
|
+
|
|
440
|
+
const reader = response.body.getReader()
|
|
441
|
+
|
|
442
|
+
const readNextResponseBodyChunk = async () => {
|
|
443
|
+
const { value, done } = await reader.read()
|
|
444
|
+
|
|
445
|
+
if (done) {
|
|
446
|
+
this.logger.info('response body stream done!')
|
|
447
|
+
finalizeResponse()
|
|
448
|
+
return
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (value) {
|
|
452
|
+
this.logger.info('read response body chunk:', value)
|
|
453
|
+
this.responseBuffer = concatArrayBuffer(this.responseBuffer, value)
|
|
454
|
+
|
|
455
|
+
this.trigger('progress', this.request, {
|
|
456
|
+
loaded: this.responseBuffer.byteLength,
|
|
457
|
+
total: totalResponseBodyLength,
|
|
458
|
+
})
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
readNextResponseBodyChunk()
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
readNextResponseBodyChunk()
|
|
465
|
+
} else {
|
|
466
|
+
finalizeResponse()
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private responseBufferToText(): string {
|
|
471
|
+
return decodeBuffer(this.responseBuffer)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
get response(): unknown {
|
|
475
|
+
this.logger.info(
|
|
476
|
+
'getResponse (responseType: %s)',
|
|
477
|
+
this.request.responseType
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
if (this.request.readyState !== this.request.DONE) {
|
|
481
|
+
return null
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
switch (this.request.responseType) {
|
|
485
|
+
case 'json': {
|
|
486
|
+
const responseJson = parseJson(this.responseBufferToText())
|
|
487
|
+
this.logger.info('resolved response JSON', responseJson)
|
|
488
|
+
|
|
489
|
+
return responseJson
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
case 'arraybuffer': {
|
|
493
|
+
const arrayBuffer = toArrayBuffer(this.responseBuffer)
|
|
494
|
+
this.logger.info('resolved response ArrayBuffer', arrayBuffer)
|
|
495
|
+
|
|
496
|
+
return arrayBuffer
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
case 'blob': {
|
|
500
|
+
const mimeType =
|
|
501
|
+
this.request.getResponseHeader('Content-Type') || 'text/plain'
|
|
502
|
+
const responseBlob = new Blob([this.responseBufferToText()], {
|
|
503
|
+
type: mimeType,
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
this.logger.info(
|
|
507
|
+
'resolved response Blob (mime type: %s)',
|
|
508
|
+
responseBlob,
|
|
509
|
+
mimeType
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
return responseBlob
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
default: {
|
|
516
|
+
const responseText = this.responseBufferToText()
|
|
517
|
+
this.logger.info(
|
|
518
|
+
'resolving "%s" response type as text',
|
|
519
|
+
this.request.responseType,
|
|
520
|
+
responseText
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
return responseText
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
get responseText(): string {
|
|
529
|
+
/**
|
|
530
|
+
* Throw when trying to read the response body as text when the
|
|
531
|
+
* "responseType" doesn't expect text. This just respects the spec better.
|
|
532
|
+
* @see https://xhr.spec.whatwg.org/#the-responsetext-attribute
|
|
533
|
+
*/
|
|
534
|
+
invariant(
|
|
535
|
+
this.request.responseType === '' || this.request.responseType === 'text',
|
|
536
|
+
'InvalidStateError: The object is in invalid state.'
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
if (
|
|
540
|
+
this.request.readyState !== this.request.LOADING &&
|
|
541
|
+
this.request.readyState !== this.request.DONE
|
|
542
|
+
) {
|
|
543
|
+
return ''
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const responseText = this.responseBufferToText()
|
|
547
|
+
this.logger.info('getResponseText: "%s"', responseText)
|
|
548
|
+
|
|
549
|
+
return responseText
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
get responseXML(): Document | null {
|
|
553
|
+
invariant(
|
|
554
|
+
this.request.responseType === '' ||
|
|
555
|
+
this.request.responseType === 'document',
|
|
556
|
+
'InvalidStateError: The object is in invalid state.'
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
if (this.request.readyState !== this.request.DONE) {
|
|
560
|
+
return null
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const contentType = this.request.getResponseHeader('Content-Type') || ''
|
|
564
|
+
|
|
565
|
+
if (typeof DOMParser === 'undefined') {
|
|
566
|
+
console.warn(
|
|
567
|
+
'Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.'
|
|
568
|
+
)
|
|
569
|
+
return null
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (isDomParserSupportedType(contentType)) {
|
|
573
|
+
return new DOMParser().parseFromString(
|
|
574
|
+
this.responseBufferToText(),
|
|
575
|
+
contentType
|
|
576
|
+
)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
return null
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
public errorWith(error?: Error): void {
|
|
583
|
+
/**
|
|
584
|
+
* @note Mark this request as handled even if it received a mock error.
|
|
585
|
+
* This prevents the controller from trying to perform this request as-is.
|
|
586
|
+
*/
|
|
587
|
+
this[kIsRequestHandled] = true
|
|
588
|
+
this.logger.info('responding with an error')
|
|
589
|
+
|
|
590
|
+
this.setReadyState(this.request.DONE)
|
|
591
|
+
this.trigger('error', this.request)
|
|
592
|
+
this.trigger('loadend', this.request)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Transitions this request's `readyState` to the given one.
|
|
597
|
+
*/
|
|
598
|
+
private setReadyState(nextReadyState: number): void {
|
|
599
|
+
this.logger.info(
|
|
600
|
+
'setReadyState: %d -> %d',
|
|
601
|
+
this.request.readyState,
|
|
602
|
+
nextReadyState
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
if (this.request.readyState === nextReadyState) {
|
|
606
|
+
this.logger.info('ready state identical, skipping transition...')
|
|
607
|
+
return
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
define(this.request, 'readyState', nextReadyState)
|
|
611
|
+
|
|
612
|
+
this.logger.info('set readyState to: %d', nextReadyState)
|
|
613
|
+
|
|
614
|
+
if (nextReadyState !== this.request.UNSENT) {
|
|
615
|
+
this.logger.info('triggering "readystatechange" event...')
|
|
616
|
+
|
|
617
|
+
this.trigger('readystatechange', this.request)
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Triggers given event on the `XMLHttpRequest` instance.
|
|
623
|
+
*/
|
|
624
|
+
private trigger<
|
|
625
|
+
EventName extends keyof (XMLHttpRequestEventTargetEventMap & {
|
|
626
|
+
readystatechange: ProgressEvent<XMLHttpRequestEventTarget>
|
|
627
|
+
}),
|
|
628
|
+
>(
|
|
629
|
+
eventName: EventName,
|
|
630
|
+
target: XMLHttpRequest | XMLHttpRequestUpload,
|
|
631
|
+
options?: ProgressEventInit
|
|
632
|
+
): void {
|
|
633
|
+
const callback = (target as XMLHttpRequest)[`on${eventName}`]
|
|
634
|
+
const event = createEvent(target, eventName, options)
|
|
635
|
+
|
|
636
|
+
this.logger.info('trigger "%s"', eventName, options || '')
|
|
637
|
+
|
|
638
|
+
// Invoke direct callbacks.
|
|
639
|
+
if (typeof callback === 'function') {
|
|
640
|
+
this.logger.info('found a direct "%s" callback, calling...', eventName)
|
|
641
|
+
callback.call(target as XMLHttpRequest, event)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Invoke event listeners.
|
|
645
|
+
const events =
|
|
646
|
+
target instanceof XMLHttpRequestUpload ? this.uploadEvents : this.events
|
|
647
|
+
|
|
648
|
+
for (const [registeredEventName, listeners] of events) {
|
|
649
|
+
if (registeredEventName === eventName) {
|
|
650
|
+
this.logger.info(
|
|
651
|
+
'found %d listener(s) for "%s" event, calling...',
|
|
652
|
+
listeners.length,
|
|
653
|
+
eventName
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
listeners.forEach((listener) => listener.call(target, event))
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Converts this `XMLHttpRequest` instance into a Fetch API `Request` instance.
|
|
663
|
+
*/
|
|
664
|
+
private toFetchApiRequest(
|
|
665
|
+
body: XMLHttpRequestBodyInit | Document | null | undefined
|
|
666
|
+
): Request {
|
|
667
|
+
this.logger.info('converting request to a Fetch API Request...')
|
|
668
|
+
|
|
669
|
+
// If the `Document` is used as the body of this XMLHttpRequest,
|
|
670
|
+
// set its inner text as the Fetch API Request body.
|
|
671
|
+
const resolvedBody =
|
|
672
|
+
body instanceof Document ? body.documentElement.innerText : body
|
|
673
|
+
|
|
674
|
+
const fetchRequest = new Request(this.url.href, {
|
|
675
|
+
method: this.method,
|
|
676
|
+
headers: this.requestHeaders,
|
|
677
|
+
/**
|
|
678
|
+
* @see https://xhr.spec.whatwg.org/#cross-origin-credentials
|
|
679
|
+
*/
|
|
680
|
+
credentials: this.request.withCredentials ? 'include' : 'same-origin',
|
|
681
|
+
body: ['GET', 'HEAD'].includes(this.method.toUpperCase())
|
|
682
|
+
? null
|
|
683
|
+
: resolvedBody,
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
const proxyHeaders = createProxy(fetchRequest.headers, {
|
|
687
|
+
methodCall: ([methodName, args], invoke) => {
|
|
688
|
+
// Forward the latest state of the internal request headers
|
|
689
|
+
// because the interceptor might have modified them
|
|
690
|
+
// without responding to the request.
|
|
691
|
+
switch (methodName) {
|
|
692
|
+
case 'append':
|
|
693
|
+
case 'set': {
|
|
694
|
+
const [headerName, headerValue] = args as [string, string]
|
|
695
|
+
this.request.setRequestHeader(headerName, headerValue)
|
|
696
|
+
break
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
case 'delete': {
|
|
700
|
+
const [headerName] = args as [string]
|
|
701
|
+
console.warn(
|
|
702
|
+
`XMLHttpRequest: Cannot remove a "${headerName}" header from the Fetch API representation of the "${fetchRequest.method} ${fetchRequest.url}" request. XMLHttpRequest headers cannot be removed.`
|
|
703
|
+
)
|
|
704
|
+
break
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return invoke()
|
|
709
|
+
},
|
|
710
|
+
})
|
|
711
|
+
define(fetchRequest, 'headers', proxyHeaders)
|
|
712
|
+
setRawRequest(fetchRequest, this.request)
|
|
713
|
+
|
|
714
|
+
this.logger.info('converted request to a Fetch API Request!', fetchRequest)
|
|
715
|
+
|
|
716
|
+
return fetchRequest
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function toAbsoluteUrl(url: string | URL): URL {
|
|
721
|
+
/**
|
|
722
|
+
* @note XMLHttpRequest interceptor may run in environments
|
|
723
|
+
* that implement XMLHttpRequest but don't implement "location"
|
|
724
|
+
* (for example, React Native). If that's the case, return the
|
|
725
|
+
* input URL as-is (nothing to be relative to).
|
|
726
|
+
* @see https://github.com/mswjs/msw/issues/1777
|
|
727
|
+
*/
|
|
728
|
+
if (typeof location === 'undefined') {
|
|
729
|
+
return new URL(url)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
return new URL(url.toString(), location.href)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function define(
|
|
736
|
+
target: object,
|
|
737
|
+
property: string | symbol,
|
|
738
|
+
value: unknown
|
|
739
|
+
): void {
|
|
740
|
+
Reflect.defineProperty(target, property, {
|
|
741
|
+
// Ensure writable properties to allow redefining readonly properties.
|
|
742
|
+
writable: true,
|
|
743
|
+
enumerable: true,
|
|
744
|
+
value,
|
|
745
|
+
})
|
|
746
|
+
}
|