@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,121 @@
|
|
|
1
|
+
import type { Logger } from '@open-draft/logger'
|
|
2
|
+
import { XMLHttpRequestEmitter } from '.'
|
|
3
|
+
import { RequestController } from '../../RequestController'
|
|
4
|
+
import { XMLHttpRequestController } from './XMLHttpRequestController'
|
|
5
|
+
import { handleRequest } from '../../utils/handleRequest'
|
|
6
|
+
import { isResponseError } from '../../utils/responseUtils'
|
|
7
|
+
|
|
8
|
+
export interface XMLHttpRequestProxyOptions {
|
|
9
|
+
emitter: XMLHttpRequestEmitter
|
|
10
|
+
logger: Logger
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a proxied `XMLHttpRequest` class.
|
|
15
|
+
* The proxied class establishes spies on certain methods,
|
|
16
|
+
* allowing us to intercept requests and respond to them.
|
|
17
|
+
*/
|
|
18
|
+
export function createXMLHttpRequestProxy({
|
|
19
|
+
emitter,
|
|
20
|
+
logger,
|
|
21
|
+
}: XMLHttpRequestProxyOptions) {
|
|
22
|
+
const XMLHttpRequestProxy = new Proxy(globalThis.XMLHttpRequest, {
|
|
23
|
+
construct(target, args, newTarget) {
|
|
24
|
+
logger.info('constructed new XMLHttpRequest')
|
|
25
|
+
|
|
26
|
+
const originalRequest = Reflect.construct(
|
|
27
|
+
target,
|
|
28
|
+
args,
|
|
29
|
+
newTarget
|
|
30
|
+
) as XMLHttpRequest
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @note Forward prototype descriptors onto the proxied object.
|
|
34
|
+
* XMLHttpRequest is implemented in JSDOM in a way that assigns
|
|
35
|
+
* a bunch of descriptors, like "set responseType()" on the prototype.
|
|
36
|
+
* With this propagation, we make sure that those descriptors trigger
|
|
37
|
+
* when the user operates with the proxied request instance.
|
|
38
|
+
*/
|
|
39
|
+
const prototypeDescriptors = Object.getOwnPropertyDescriptors(
|
|
40
|
+
target.prototype
|
|
41
|
+
)
|
|
42
|
+
for (const propertyName in prototypeDescriptors) {
|
|
43
|
+
Reflect.defineProperty(
|
|
44
|
+
originalRequest,
|
|
45
|
+
propertyName,
|
|
46
|
+
prototypeDescriptors[propertyName]
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const xhrRequestController = new XMLHttpRequestController(
|
|
51
|
+
originalRequest,
|
|
52
|
+
logger
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
xhrRequestController.onRequest = async function ({ request, requestId }) {
|
|
56
|
+
const controller = new RequestController(request, {
|
|
57
|
+
passthrough: () => {
|
|
58
|
+
this.logger.info(
|
|
59
|
+
'no mocked response received, performing request as-is...'
|
|
60
|
+
)
|
|
61
|
+
},
|
|
62
|
+
respondWith: async (response) => {
|
|
63
|
+
if (isResponseError(response)) {
|
|
64
|
+
this.errorWith(new TypeError('Network error'))
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await this.respondWith(response)
|
|
69
|
+
},
|
|
70
|
+
errorWith: (reason) => {
|
|
71
|
+
this.logger.info('request errored!', { error: reason })
|
|
72
|
+
|
|
73
|
+
if (reason instanceof Error) {
|
|
74
|
+
this.errorWith(reason)
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
this.logger.info('awaiting mocked response...')
|
|
80
|
+
|
|
81
|
+
this.logger.info(
|
|
82
|
+
'emitting the "request" event for %s listener(s)...',
|
|
83
|
+
emitter.listenerCount('request')
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
await handleRequest({
|
|
87
|
+
request,
|
|
88
|
+
requestId,
|
|
89
|
+
controller,
|
|
90
|
+
emitter,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
xhrRequestController.onResponse = async function ({
|
|
95
|
+
response,
|
|
96
|
+
isMockedResponse,
|
|
97
|
+
request,
|
|
98
|
+
requestId,
|
|
99
|
+
}) {
|
|
100
|
+
this.logger.info(
|
|
101
|
+
'emitting the "response" event for %s listener(s)...',
|
|
102
|
+
emitter.listenerCount('response')
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
emitter.emit('response', {
|
|
106
|
+
response,
|
|
107
|
+
isMockedResponse,
|
|
108
|
+
request,
|
|
109
|
+
requestId,
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Return the proxied request from the controller
|
|
114
|
+
// so that the controller can react to the consumer's interactions
|
|
115
|
+
// with this request (opening/sending/etc).
|
|
116
|
+
return xhrRequestController.request
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
return XMLHttpRequestProxy
|
|
121
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { invariant } from 'outvariant'
|
|
2
|
+
import { Emitter } from 'strict-event-emitter'
|
|
3
|
+
import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
|
|
4
|
+
import { Interceptor } from '../../Interceptor'
|
|
5
|
+
import { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'
|
|
6
|
+
import { hasConfigurableGlobal } from '../../utils/hasConfigurableGlobal'
|
|
7
|
+
|
|
8
|
+
export type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>
|
|
9
|
+
|
|
10
|
+
export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
11
|
+
static interceptorSymbol = Symbol('xhr')
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
super(XMLHttpRequestInterceptor.interceptorSymbol)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
protected checkEnvironment() {
|
|
18
|
+
return hasConfigurableGlobal('XMLHttpRequest')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected setup() {
|
|
22
|
+
const logger = this.logger.extend('setup')
|
|
23
|
+
|
|
24
|
+
logger.info('patching "XMLHttpRequest" module...')
|
|
25
|
+
|
|
26
|
+
const PureXMLHttpRequest = globalThis.XMLHttpRequest
|
|
27
|
+
|
|
28
|
+
invariant(
|
|
29
|
+
!(PureXMLHttpRequest as any)[IS_PATCHED_MODULE],
|
|
30
|
+
'Failed to patch the "XMLHttpRequest" module: already patched.'
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
globalThis.XMLHttpRequest = createXMLHttpRequestProxy({
|
|
34
|
+
emitter: this.emitter,
|
|
35
|
+
logger: this.logger,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
logger.info(
|
|
39
|
+
'native "XMLHttpRequest" module patched!',
|
|
40
|
+
globalThis.XMLHttpRequest.name
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
configurable: true,
|
|
46
|
+
value: true,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
this.subscriptions.push(() => {
|
|
50
|
+
Object.defineProperty(globalThis.XMLHttpRequest, IS_PATCHED_MODULE, {
|
|
51
|
+
value: undefined,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
globalThis.XMLHttpRequest = PureXMLHttpRequest
|
|
55
|
+
logger.info(
|
|
56
|
+
'native "XMLHttpRequest" module restored!',
|
|
57
|
+
globalThis.XMLHttpRequest.name
|
|
58
|
+
)
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export class EventPolyfill implements Event {
|
|
2
|
+
readonly NONE = 0
|
|
3
|
+
readonly CAPTURING_PHASE = 1
|
|
4
|
+
readonly AT_TARGET = 2
|
|
5
|
+
readonly BUBBLING_PHASE = 3
|
|
6
|
+
|
|
7
|
+
public type: string = ''
|
|
8
|
+
public srcElement: EventTarget | null = null
|
|
9
|
+
public target: EventTarget | null
|
|
10
|
+
public currentTarget: EventTarget | null = null
|
|
11
|
+
public eventPhase: number = 0
|
|
12
|
+
public timeStamp: number
|
|
13
|
+
public isTrusted: boolean = true
|
|
14
|
+
public composed: boolean = false
|
|
15
|
+
public cancelable: boolean = true
|
|
16
|
+
public defaultPrevented: boolean = false
|
|
17
|
+
public bubbles: boolean = true
|
|
18
|
+
public lengthComputable: boolean = true
|
|
19
|
+
public loaded: number = 0
|
|
20
|
+
public total: number = 0
|
|
21
|
+
|
|
22
|
+
cancelBubble: boolean = false
|
|
23
|
+
returnValue: boolean = true
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
type: string,
|
|
27
|
+
options?: { target: EventTarget; currentTarget: EventTarget }
|
|
28
|
+
) {
|
|
29
|
+
this.type = type
|
|
30
|
+
this.target = options?.target || null
|
|
31
|
+
this.currentTarget = options?.currentTarget || null
|
|
32
|
+
this.timeStamp = Date.now()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public composedPath(): EventTarget[] {
|
|
36
|
+
return []
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public initEvent(type: string, bubbles?: boolean, cancelable?: boolean) {
|
|
40
|
+
this.type = type
|
|
41
|
+
this.bubbles = !!bubbles
|
|
42
|
+
this.cancelable = !!cancelable
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public preventDefault() {
|
|
46
|
+
this.defaultPrevented = true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public stopPropagation() {}
|
|
50
|
+
public stopImmediatePropagation() {}
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventPolyfill } from './EventPolyfill'
|
|
2
|
+
|
|
3
|
+
export class ProgressEventPolyfill extends EventPolyfill {
|
|
4
|
+
readonly lengthComputable: boolean
|
|
5
|
+
readonly composed: boolean
|
|
6
|
+
readonly loaded: number
|
|
7
|
+
readonly total: number
|
|
8
|
+
|
|
9
|
+
constructor(type: string, init?: ProgressEventInit) {
|
|
10
|
+
super(type)
|
|
11
|
+
|
|
12
|
+
this.lengthComputable = init?.lengthComputable || false
|
|
13
|
+
this.composed = init?.composed || false
|
|
14
|
+
this.loaded = init?.loaded || 0
|
|
15
|
+
this.total = init?.total || 0
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concatenate two `Uint8Array` buffers.
|
|
3
|
+
*/
|
|
4
|
+
export function concatArrayBuffer(
|
|
5
|
+
left: Uint8Array,
|
|
6
|
+
right: Uint8Array
|
|
7
|
+
): Uint8Array {
|
|
8
|
+
const result = new Uint8Array(left.byteLength + right.byteLength)
|
|
9
|
+
result.set(left, 0)
|
|
10
|
+
result.set(right, left.byteLength)
|
|
11
|
+
return result
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { it, expect } from 'vitest'
|
|
2
|
+
import { concatArrayBuffer } from './concatArrayBuffer'
|
|
3
|
+
|
|
4
|
+
const encoder = new TextEncoder()
|
|
5
|
+
|
|
6
|
+
it('concatenates two Uint8Array buffers', () => {
|
|
7
|
+
const result = concatArrayBuffer(
|
|
8
|
+
encoder.encode('hello'),
|
|
9
|
+
encoder.encode('world')
|
|
10
|
+
)
|
|
11
|
+
expect(result).toEqual(encoder.encode('helloworld'))
|
|
12
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { it, expect } from 'vitest'
|
|
3
|
+
import { createEvent } from './createEvent'
|
|
4
|
+
import { EventPolyfill } from '../polyfills/EventPolyfill'
|
|
5
|
+
|
|
6
|
+
const request = new XMLHttpRequest()
|
|
7
|
+
request.open('POST', '/user')
|
|
8
|
+
|
|
9
|
+
it('returns an EventPolyfill instance with the given target set', () => {
|
|
10
|
+
const event = createEvent(request, 'my-event')
|
|
11
|
+
const target = event.target as XMLHttpRequest
|
|
12
|
+
|
|
13
|
+
expect(event).toBeInstanceOf(EventPolyfill)
|
|
14
|
+
expect(target).toBeInstanceOf(XMLHttpRequest)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('returns the ProgressEvent instance', () => {
|
|
18
|
+
const event = createEvent(request, 'load', {
|
|
19
|
+
loaded: 100,
|
|
20
|
+
total: 500,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
expect(event).toBeInstanceOf(ProgressEvent)
|
|
24
|
+
expect(event.loaded).toBe(100)
|
|
25
|
+
expect(event.total).toBe(500)
|
|
26
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EventPolyfill } from '../polyfills/EventPolyfill'
|
|
2
|
+
import { ProgressEventPolyfill } from '../polyfills/ProgressEventPolyfill'
|
|
3
|
+
|
|
4
|
+
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined'
|
|
5
|
+
|
|
6
|
+
export function createEvent(
|
|
7
|
+
target: XMLHttpRequest | XMLHttpRequestUpload,
|
|
8
|
+
type: string,
|
|
9
|
+
init?: ProgressEventInit
|
|
10
|
+
): EventPolyfill | ProgressEvent {
|
|
11
|
+
const progressEvents = [
|
|
12
|
+
'error',
|
|
13
|
+
'progress',
|
|
14
|
+
'loadstart',
|
|
15
|
+
'loadend',
|
|
16
|
+
'load',
|
|
17
|
+
'timeout',
|
|
18
|
+
'abort',
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `ProgressEvent` is not supported in React Native.
|
|
23
|
+
* @see https://github.com/mswjs/interceptors/issues/40
|
|
24
|
+
*/
|
|
25
|
+
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT
|
|
26
|
+
? ProgressEvent
|
|
27
|
+
: ProgressEventPolyfill
|
|
28
|
+
|
|
29
|
+
const event = progressEvents.includes(type)
|
|
30
|
+
? new ProgressEventClass(type, {
|
|
31
|
+
lengthComputable: true,
|
|
32
|
+
loaded: init?.loaded || 0,
|
|
33
|
+
total: init?.total || 0,
|
|
34
|
+
})
|
|
35
|
+
: new EventPolyfill(type, {
|
|
36
|
+
target,
|
|
37
|
+
currentTarget: target,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return event
|
|
41
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FetchResponse } from '../../../utils/fetchUtils'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Fetch API `Response` instance from the given
|
|
5
|
+
* `XMLHttpRequest` instance and a response body.
|
|
6
|
+
*/
|
|
7
|
+
export function createResponse(
|
|
8
|
+
request: XMLHttpRequest,
|
|
9
|
+
body: BodyInit | null
|
|
10
|
+
): Response {
|
|
11
|
+
/**
|
|
12
|
+
* Handle XMLHttpRequest responses that must have null as the
|
|
13
|
+
* response body when represented using Fetch API Response.
|
|
14
|
+
* XMLHttpRequest response will always have an empty string
|
|
15
|
+
* as the "request.response" in those cases, resulting in an error
|
|
16
|
+
* when constructing a Response instance.
|
|
17
|
+
* @see https://github.com/mswjs/interceptors/issues/379
|
|
18
|
+
*/
|
|
19
|
+
const responseBodyOrNull = FetchResponse.isResponseWithBody(request.status)
|
|
20
|
+
? body
|
|
21
|
+
: null
|
|
22
|
+
|
|
23
|
+
return new FetchResponse(responseBodyOrNull, {
|
|
24
|
+
url: request.responseURL,
|
|
25
|
+
status: request.status,
|
|
26
|
+
statusText: request.statusText,
|
|
27
|
+
headers: createHeadersFromXMLHttpRequestHeaders(
|
|
28
|
+
request.getAllResponseHeaders()
|
|
29
|
+
),
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createHeadersFromXMLHttpRequestHeaders(headersString: string): Headers {
|
|
34
|
+
const headers = new Headers()
|
|
35
|
+
|
|
36
|
+
const lines = headersString.split(/[\r\n]+/)
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
if (line.trim() === '') {
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const [name, ...parts] = line.split(': ')
|
|
43
|
+
const value = parts.join(': ')
|
|
44
|
+
|
|
45
|
+
headers.append(name, value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return headers
|
|
49
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { it, expect } from 'vitest'
|
|
3
|
+
import { getBodyByteLength } from './getBodyByteLength'
|
|
4
|
+
|
|
5
|
+
const url = 'http://localhost'
|
|
6
|
+
|
|
7
|
+
it('returns explicit body length set in the "Content-Length" header', async () => {
|
|
8
|
+
await expect(
|
|
9
|
+
getBodyByteLength(new Request(url, { headers: { 'Content-Length': '10' } }))
|
|
10
|
+
).resolves.toBe(10)
|
|
11
|
+
|
|
12
|
+
await expect(
|
|
13
|
+
getBodyByteLength(
|
|
14
|
+
new Response('hello', { headers: { 'Content-Length': '5' } })
|
|
15
|
+
)
|
|
16
|
+
).resolves.toBe(5)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Request.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
it('returns 0 for a request with an empty body', async () => {
|
|
24
|
+
await expect(getBodyByteLength(new Request(url))).resolves.toBe(0)
|
|
25
|
+
await expect(
|
|
26
|
+
getBodyByteLength(new Request(url, { method: 'POST', body: null }))
|
|
27
|
+
).resolves.toBe(0)
|
|
28
|
+
await expect(
|
|
29
|
+
getBodyByteLength(new Request(url, { method: 'POST', body: undefined }))
|
|
30
|
+
).resolves.toBe(0)
|
|
31
|
+
await expect(
|
|
32
|
+
getBodyByteLength(new Request(url, { method: 'POST', body: '' }))
|
|
33
|
+
).resolves.toBe(0)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('calculates body length from the text request body', async () => {
|
|
37
|
+
await expect(
|
|
38
|
+
getBodyByteLength(
|
|
39
|
+
new Request(url, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
body: 'hello world',
|
|
42
|
+
})
|
|
43
|
+
)
|
|
44
|
+
).resolves.toBe(11)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('calculates body length from the URLSearchParams request body', async () => {
|
|
48
|
+
await expect(
|
|
49
|
+
getBodyByteLength(
|
|
50
|
+
new Request(url, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: new URLSearchParams([['hello', 'world']]),
|
|
53
|
+
})
|
|
54
|
+
)
|
|
55
|
+
).resolves.toBe(11)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('calculates body length from the Blob request body', async () => {
|
|
59
|
+
await expect(
|
|
60
|
+
getBodyByteLength(
|
|
61
|
+
new Request(url, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
body: new Blob(['hello world']),
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
).resolves.toBe(11)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('calculates body length from the ArrayBuffer request body', async () => {
|
|
70
|
+
await expect(
|
|
71
|
+
getBodyByteLength(
|
|
72
|
+
new Request(url, {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
body: await new Blob(['hello world']).arrayBuffer(),
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
).resolves.toBe(11)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('calculates body length from the FormData request body', async () => {
|
|
81
|
+
const formData = new FormData()
|
|
82
|
+
formData.append('hello', 'world')
|
|
83
|
+
|
|
84
|
+
await expect(
|
|
85
|
+
getBodyByteLength(
|
|
86
|
+
new Request(url, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
body: formData,
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
).resolves.toBe(129)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('calculates body length from the ReadableStream request body', async () => {
|
|
95
|
+
const stream = new ReadableStream({
|
|
96
|
+
start(controller) {
|
|
97
|
+
controller.enqueue(new TextEncoder().encode('hello world'))
|
|
98
|
+
controller.close()
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
await expect(
|
|
103
|
+
getBodyByteLength(
|
|
104
|
+
new Request(url, {
|
|
105
|
+
method: 'POST',
|
|
106
|
+
body: stream,
|
|
107
|
+
// @ts-expect-error Undocumented required Undici property.
|
|
108
|
+
duplex: 'half',
|
|
109
|
+
})
|
|
110
|
+
)
|
|
111
|
+
).resolves.toBe(11)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Response.
|
|
116
|
+
*/
|
|
117
|
+
it('returns 0 for a response with an empty body', async () => {
|
|
118
|
+
await expect(getBodyByteLength(new Response())).resolves.toBe(0)
|
|
119
|
+
await expect(getBodyByteLength(new Response(null))).resolves.toBe(0)
|
|
120
|
+
await expect(getBodyByteLength(new Response(undefined))).resolves.toBe(0)
|
|
121
|
+
await expect(getBodyByteLength(new Response(''))).resolves.toBe(0)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('calculates body length from the text response body', async () => {
|
|
125
|
+
await expect(getBodyByteLength(new Response('hello world'))).resolves.toBe(11)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('calculates body length from the URLSearchParams response body', async () => {
|
|
129
|
+
await expect(
|
|
130
|
+
getBodyByteLength(new Response(new URLSearchParams([['hello', 'world']])))
|
|
131
|
+
).resolves.toBe(11)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('calculates body length from the Blob response body', async () => {
|
|
135
|
+
await expect(
|
|
136
|
+
getBodyByteLength(new Response(new Blob(['hello world'])))
|
|
137
|
+
).resolves.toBe(11)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('calculates body length from the ArrayBuffer response body', async () => {
|
|
141
|
+
await expect(
|
|
142
|
+
getBodyByteLength(
|
|
143
|
+
new Response(await new Blob(['hello world']).arrayBuffer())
|
|
144
|
+
)
|
|
145
|
+
).resolves.toBe(11)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('calculates body length from the FormData response body', async () => {
|
|
149
|
+
const formData = new FormData()
|
|
150
|
+
formData.append('hello', 'world')
|
|
151
|
+
|
|
152
|
+
await expect(getBodyByteLength(new Response(formData))).resolves.toBe(129)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('calculates body length from the ReadableStream response body', async () => {
|
|
156
|
+
const stream = new ReadableStream({
|
|
157
|
+
start(controller) {
|
|
158
|
+
controller.enqueue(new TextEncoder().encode('hello world'))
|
|
159
|
+
controller.close()
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
await expect(getBodyByteLength(new Response(stream))).resolves.toBe(11)
|
|
164
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return a total byte length of the given request/response body.
|
|
3
|
+
* If the `Content-Length` header is present, it will be used as the byte length.
|
|
4
|
+
*/
|
|
5
|
+
export async function getBodyByteLength(
|
|
6
|
+
input: Request | Response
|
|
7
|
+
): Promise<number> {
|
|
8
|
+
const explicitContentLength = input.headers.get('content-length')
|
|
9
|
+
|
|
10
|
+
if (explicitContentLength != null && explicitContentLength !== '') {
|
|
11
|
+
return Number(explicitContentLength)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const buffer = await input.arrayBuffer()
|
|
15
|
+
return buffer.byteLength
|
|
16
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function isDomParserSupportedType(
|
|
2
|
+
type: string
|
|
3
|
+
): type is DOMParserSupportedType {
|
|
4
|
+
const supportedTypes: Array<DOMParserSupportedType> = [
|
|
5
|
+
'application/xhtml+xml',
|
|
6
|
+
'application/xml',
|
|
7
|
+
'image/svg+xml',
|
|
8
|
+
'text/html',
|
|
9
|
+
'text/xml',
|
|
10
|
+
]
|
|
11
|
+
return supportedTypes.some((supportedType) => {
|
|
12
|
+
return type.startsWith(supportedType)
|
|
13
|
+
})
|
|
14
|
+
}
|