@mswjs/interceptors 0.16.5 → 0.17.1
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/README.md +177 -56
- package/lib/InteractiveIsomorphicRequest.d.ts +7 -0
- package/lib/InteractiveIsomorphicRequest.js +37 -0
- package/lib/InteractiveIsomorphicRequest.js.map +1 -0
- package/lib/IsomorphicRequest.d.ts +24 -0
- package/lib/IsomorphicRequest.js +107 -0
- package/lib/IsomorphicRequest.js.map +1 -0
- package/lib/RemoteHttpInterceptor.d.ts +1 -1
- package/lib/RemoteHttpInterceptor.js +12 -8
- package/lib/RemoteHttpInterceptor.js.map +1 -1
- package/lib/glossary.d.ts +3 -17
- package/lib/glossary.js +2 -0
- package/lib/glossary.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/interceptors/ClientRequest/NodeClientRequest.js +9 -27
- package/lib/interceptors/ClientRequest/NodeClientRequest.js.map +1 -1
- package/lib/interceptors/ClientRequest/index.d.ts +5 -2
- package/lib/interceptors/ClientRequest/index.js +11 -0
- package/lib/interceptors/ClientRequest/index.js.map +1 -1
- package/lib/interceptors/ClientRequest/utils/getIncomingMessageBody.js.map +1 -1
- package/lib/interceptors/XMLHttpRequest/XMLHttpRequestOverride.d.ts +1 -2
- package/lib/interceptors/XMLHttpRequest/XMLHttpRequestOverride.js +19 -27
- package/lib/interceptors/XMLHttpRequest/XMLHttpRequestOverride.js.map +1 -1
- package/lib/interceptors/XMLHttpRequest/index.d.ts +2 -1
- package/lib/interceptors/XMLHttpRequest/index.js +11 -0
- package/lib/interceptors/XMLHttpRequest/index.js.map +1 -1
- package/lib/interceptors/fetch/index.d.ts +1 -1
- package/lib/interceptors/fetch/index.js +33 -25
- package/lib/interceptors/fetch/index.js.map +1 -1
- package/lib/utils/bufferUtils.d.ts +3 -0
- package/lib/utils/bufferUtils.js +20 -0
- package/lib/utils/bufferUtils.js.map +1 -0
- package/package.json +3 -2
- package/src/InteractiveIsomorphicRequest.ts +24 -0
- package/src/IsomorphicRequest.test.ts +106 -0
- package/src/IsomorphicRequest.ts +86 -0
- package/src/RemoteHttpInterceptor.ts +18 -18
- package/src/glossary.ts +4 -19
- package/src/index.ts +2 -0
- package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +2 -8
- package/src/interceptors/ClientRequest/NodeClientRequest.ts +14 -35
- package/src/interceptors/ClientRequest/index.ts +26 -2
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +4 -4
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts +21 -23
- package/src/interceptors/XMLHttpRequest/index.ts +18 -4
- package/src/interceptors/fetch/index.ts +47 -20
- package/src/utils/bufferUtils.test.ts +20 -0
- package/src/utils/bufferUtils.ts +19 -0
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
headersToString,
|
|
12
12
|
} from 'headers-polyfill'
|
|
13
13
|
import { DOMParser } from '@xmldom/xmldom'
|
|
14
|
-
import { InteractiveIsomorphicRequest, IsomorphicRequest } from '../../glossary'
|
|
15
14
|
import { parseJson } from '../../utils/parseJson'
|
|
16
15
|
import { toIsoResponse } from '../../utils/toIsoResponse'
|
|
17
|
-
import { uuidv4 } from '../../utils/uuid'
|
|
18
16
|
import { bufferFrom } from './utils/bufferFrom'
|
|
19
17
|
import { createEvent } from './utils/createEvent'
|
|
20
18
|
import type { XMLHttpRequestEmitter } from '.'
|
|
21
|
-
import {
|
|
19
|
+
import { IsomorphicRequest } from '../../IsomorphicRequest'
|
|
20
|
+
import { encodeBuffer } from '../../utils/bufferUtils'
|
|
21
|
+
import { InteractiveIsomorphicRequest } from '../../InteractiveIsomorphicRequest'
|
|
22
22
|
|
|
23
23
|
type XMLHttpRequestEventHandler = (
|
|
24
24
|
this: XMLHttpRequest,
|
|
@@ -84,7 +84,6 @@ export const createXMLHttpRequestOverride = (
|
|
|
84
84
|
public statusText: string
|
|
85
85
|
public user?: string
|
|
86
86
|
public password?: string
|
|
87
|
-
public data: string
|
|
88
87
|
public async?: boolean
|
|
89
88
|
public response: any
|
|
90
89
|
public responseText: string
|
|
@@ -132,7 +131,6 @@ export const createXMLHttpRequestOverride = (
|
|
|
132
131
|
this.withCredentials = false
|
|
133
132
|
this.status = 200
|
|
134
133
|
this.statusText = 'OK'
|
|
135
|
-
this.data = ''
|
|
136
134
|
this.response = ''
|
|
137
135
|
this.responseType = 'text'
|
|
138
136
|
this.responseText = ''
|
|
@@ -154,7 +152,7 @@ export const createXMLHttpRequestOverride = (
|
|
|
154
152
|
this.readyState = nextState
|
|
155
153
|
|
|
156
154
|
if (nextState !== this.UNSENT) {
|
|
157
|
-
this.log('
|
|
155
|
+
this.log('triggering readystate change...')
|
|
158
156
|
this.trigger('readystatechange')
|
|
159
157
|
}
|
|
160
158
|
}
|
|
@@ -195,7 +193,6 @@ export const createXMLHttpRequestOverride = (
|
|
|
195
193
|
this.setReadyState(this.UNSENT)
|
|
196
194
|
this.status = 200
|
|
197
195
|
this.statusText = 'OK'
|
|
198
|
-
this.data = ''
|
|
199
196
|
this.response = null as any
|
|
200
197
|
this.responseText = null as any
|
|
201
198
|
this.responseXML = null as any
|
|
@@ -229,10 +226,14 @@ export const createXMLHttpRequestOverride = (
|
|
|
229
226
|
}
|
|
230
227
|
}
|
|
231
228
|
|
|
232
|
-
public send(data?: string) {
|
|
229
|
+
public send(data?: string | ArrayBuffer) {
|
|
233
230
|
this.log('send %s %s', this.method, this.url)
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
let buffer: ArrayBuffer
|
|
232
|
+
if (typeof data === 'string') {
|
|
233
|
+
buffer = encodeBuffer(data)
|
|
234
|
+
} else {
|
|
235
|
+
buffer = data || new ArrayBuffer(0)
|
|
236
|
+
}
|
|
236
237
|
|
|
237
238
|
let url: URL
|
|
238
239
|
|
|
@@ -248,19 +249,16 @@ export const createXMLHttpRequestOverride = (
|
|
|
248
249
|
this.log('request headers', this._requestHeaders)
|
|
249
250
|
|
|
250
251
|
// Create an intercepted request instance exposed to the request intercepting middleware.
|
|
251
|
-
const isomorphicRequest
|
|
252
|
-
|
|
253
|
-
url,
|
|
252
|
+
const isomorphicRequest = new IsomorphicRequest(url, {
|
|
253
|
+
body: buffer,
|
|
254
254
|
method: this.method,
|
|
255
255
|
headers: this._requestHeaders,
|
|
256
256
|
credentials: this.withCredentials ? 'include' : 'omit',
|
|
257
|
-
|
|
258
|
-
}
|
|
257
|
+
})
|
|
259
258
|
|
|
260
|
-
const interactiveIsomorphicRequest
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
259
|
+
const interactiveIsomorphicRequest = new InteractiveIsomorphicRequest(
|
|
260
|
+
isomorphicRequest
|
|
261
|
+
)
|
|
264
262
|
|
|
265
263
|
this.log(
|
|
266
264
|
'emitting the "request" event for %d listener(s)...',
|
|
@@ -331,8 +329,8 @@ export const createXMLHttpRequestOverride = (
|
|
|
331
329
|
if (mockedResponse.body && this.response) {
|
|
332
330
|
this.setReadyState(this.LOADING)
|
|
333
331
|
|
|
334
|
-
//
|
|
335
|
-
//
|
|
332
|
+
// Presence of the mocked response implies a response body (not null).
|
|
333
|
+
// Presence of the coerced `this.response` implies the mocked body is valid.
|
|
336
334
|
const bodyBuffer = bufferFrom(mockedResponse.body)
|
|
337
335
|
|
|
338
336
|
// Trigger a progress event based on the mocked response body.
|
|
@@ -428,8 +426,8 @@ export const createXMLHttpRequestOverride = (
|
|
|
428
426
|
originalRequest.timeout = this.timeout
|
|
429
427
|
}
|
|
430
428
|
|
|
431
|
-
this.log('send',
|
|
432
|
-
originalRequest.send(
|
|
429
|
+
this.log('send', data)
|
|
430
|
+
originalRequest.send(data)
|
|
433
431
|
}
|
|
434
432
|
})
|
|
435
433
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '../../glossary'
|
|
1
|
+
import { invariant } from 'outvariant'
|
|
2
|
+
import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
|
|
3
|
+
import { InteractiveIsomorphicRequest } from '../../InteractiveIsomorphicRequest'
|
|
5
4
|
import { Interceptor } from '../../Interceptor'
|
|
6
5
|
import { AsyncEventEmitter } from '../../utils/AsyncEventEmitter'
|
|
7
6
|
import { createXMLHttpRequestOverride } from './XMLHttpRequestOverride'
|
|
@@ -33,6 +32,11 @@ export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap>
|
|
|
33
32
|
|
|
34
33
|
const PureXMLHttpRequest = window.XMLHttpRequest
|
|
35
34
|
|
|
35
|
+
invariant(
|
|
36
|
+
!(PureXMLHttpRequest as any)[IS_PATCHED_MODULE],
|
|
37
|
+
'Failed to patch the "XMLHttpRequest" module: already patched.'
|
|
38
|
+
)
|
|
39
|
+
|
|
36
40
|
window.XMLHttpRequest = createXMLHttpRequestOverride({
|
|
37
41
|
XMLHttpRequest: PureXMLHttpRequest,
|
|
38
42
|
emitter: this.emitter,
|
|
@@ -41,7 +45,17 @@ export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap>
|
|
|
41
45
|
|
|
42
46
|
log('native "XMLHttpRequest" module patched!', window.XMLHttpRequest.name)
|
|
43
47
|
|
|
48
|
+
Object.defineProperty(window.XMLHttpRequest, IS_PATCHED_MODULE, {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
value: true,
|
|
52
|
+
})
|
|
53
|
+
|
|
44
54
|
this.subscriptions.push(() => {
|
|
55
|
+
Object.defineProperty(window.XMLHttpRequest, IS_PATCHED_MODULE, {
|
|
56
|
+
value: undefined,
|
|
57
|
+
})
|
|
58
|
+
|
|
45
59
|
window.XMLHttpRequest = PureXMLHttpRequest
|
|
46
60
|
log(
|
|
47
61
|
'native "XMLHttpRequest" module restored!',
|
|
@@ -4,15 +4,16 @@ import {
|
|
|
4
4
|
objectToHeaders,
|
|
5
5
|
headersToObject,
|
|
6
6
|
} from 'headers-polyfill'
|
|
7
|
-
import
|
|
7
|
+
import { invariant } from 'outvariant'
|
|
8
|
+
import { IsomorphicRequest } from '../../IsomorphicRequest'
|
|
9
|
+
import {
|
|
8
10
|
HttpRequestEventMap,
|
|
9
|
-
InteractiveIsomorphicRequest,
|
|
10
11
|
IsomorphicResponse,
|
|
12
|
+
IS_PATCHED_MODULE,
|
|
11
13
|
} from '../../glossary'
|
|
12
14
|
import { Interceptor } from '../../Interceptor'
|
|
13
|
-
import { createLazyCallback } from '../../utils/createLazyCallback'
|
|
14
15
|
import { toIsoResponse } from '../../utils/toIsoResponse'
|
|
15
|
-
import {
|
|
16
|
+
import { InteractiveIsomorphicRequest } from '../../InteractiveIsomorphicRequest'
|
|
16
17
|
|
|
17
18
|
export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
18
19
|
static symbol = Symbol('fetch')
|
|
@@ -31,6 +32,11 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
31
32
|
protected setup() {
|
|
32
33
|
const pureFetch = globalThis.fetch
|
|
33
34
|
|
|
35
|
+
invariant(
|
|
36
|
+
!(pureFetch as any)[IS_PATCHED_MODULE],
|
|
37
|
+
'Failed to patch the "fetch" module: already patched.'
|
|
38
|
+
)
|
|
39
|
+
|
|
34
40
|
globalThis.fetch = async (input, init) => {
|
|
35
41
|
const request = new Request(input, init)
|
|
36
42
|
|
|
@@ -39,32 +45,38 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
39
45
|
|
|
40
46
|
this.log('[%s] %s', method, url)
|
|
41
47
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const body = await request.clone().arrayBuffer()
|
|
49
|
+
const isomorphicRequest = new IsomorphicRequest(
|
|
50
|
+
new URL(url, location.origin),
|
|
51
|
+
{
|
|
52
|
+
body,
|
|
53
|
+
method,
|
|
54
|
+
headers: new Headers(request.headers),
|
|
55
|
+
credentials: request.credentials,
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const interactiveIsomorphicRequest = new InteractiveIsomorphicRequest(
|
|
60
|
+
isomorphicRequest
|
|
61
|
+
)
|
|
51
62
|
|
|
52
|
-
this.log('isomorphic request',
|
|
63
|
+
this.log('isomorphic request', interactiveIsomorphicRequest)
|
|
53
64
|
|
|
54
65
|
this.log(
|
|
55
66
|
'emitting the "request" event for %d listener(s)...',
|
|
56
67
|
this.emitter.listenerCount('request')
|
|
57
68
|
)
|
|
58
|
-
this.emitter.emit('request',
|
|
69
|
+
this.emitter.emit('request', interactiveIsomorphicRequest)
|
|
59
70
|
|
|
60
71
|
this.log('awaiting for the mocked response...')
|
|
61
72
|
|
|
62
73
|
await this.emitter.untilIdle('request', ({ args: [request] }) => {
|
|
63
|
-
return request.id ===
|
|
74
|
+
return request.id === interactiveIsomorphicRequest.id
|
|
64
75
|
})
|
|
65
76
|
this.log('all request listeners have been resolved!')
|
|
66
77
|
|
|
67
|
-
const [mockedResponse] =
|
|
78
|
+
const [mockedResponse] =
|
|
79
|
+
await interactiveIsomorphicRequest.respondWith.invoked()
|
|
68
80
|
this.log('event.respondWith called with:', mockedResponse)
|
|
69
81
|
|
|
70
82
|
if (mockedResponse) {
|
|
@@ -73,7 +85,11 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
73
85
|
const isomorphicResponse = toIsoResponse(mockedResponse)
|
|
74
86
|
this.log('derived isomorphic response:', isomorphicResponse)
|
|
75
87
|
|
|
76
|
-
this.emitter.emit(
|
|
88
|
+
this.emitter.emit(
|
|
89
|
+
'response',
|
|
90
|
+
interactiveIsomorphicRequest,
|
|
91
|
+
isomorphicResponse
|
|
92
|
+
)
|
|
77
93
|
|
|
78
94
|
const response = new Response(mockedResponse.body, {
|
|
79
95
|
...isomorphicResponse,
|
|
@@ -88,7 +104,7 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
88
104
|
writable: false,
|
|
89
105
|
enumerable: true,
|
|
90
106
|
configurable: false,
|
|
91
|
-
value:
|
|
107
|
+
value: interactiveIsomorphicRequest.url.href,
|
|
92
108
|
})
|
|
93
109
|
|
|
94
110
|
return response
|
|
@@ -102,15 +118,26 @@ export class FetchInterceptor extends Interceptor<HttpRequestEventMap> {
|
|
|
102
118
|
|
|
103
119
|
this.emitter.emit(
|
|
104
120
|
'response',
|
|
105
|
-
|
|
121
|
+
interactiveIsomorphicRequest,
|
|
106
122
|
await normalizeFetchResponse(cloneResponse)
|
|
107
123
|
)
|
|
108
124
|
return response
|
|
109
125
|
})
|
|
110
126
|
}
|
|
111
127
|
|
|
128
|
+
Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
|
|
129
|
+
enumerable: true,
|
|
130
|
+
configurable: true,
|
|
131
|
+
value: true,
|
|
132
|
+
})
|
|
133
|
+
|
|
112
134
|
this.subscriptions.push(() => {
|
|
135
|
+
Object.defineProperty(globalThis.fetch, IS_PATCHED_MODULE, {
|
|
136
|
+
value: undefined,
|
|
137
|
+
})
|
|
138
|
+
|
|
113
139
|
globalThis.fetch = pureFetch
|
|
140
|
+
|
|
114
141
|
this.log('restored native "globalThis.fetch"!', globalThis.fetch.name)
|
|
115
142
|
})
|
|
116
143
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { decodeBuffer, encodeBuffer } from './bufferUtils'
|
|
2
|
+
|
|
3
|
+
it('encodes utf-8 string', () => {
|
|
4
|
+
const encoded = encodeBuffer('😁')
|
|
5
|
+
expect(new Uint8Array(encoded)).toEqual(new Uint8Array([240, 159, 152, 129]))
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('decodes utf-8 string', () => {
|
|
9
|
+
const array = new Uint8Array([240, 159, 152, 129])
|
|
10
|
+
const decoded = decodeBuffer(array.buffer)
|
|
11
|
+
expect(decoded).toEqual('😁')
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('decodes string with custom encoding', () => {
|
|
15
|
+
const array = new Uint8Array([
|
|
16
|
+
207, 240, 232, 226, 229, 242, 44, 32, 236, 232, 240, 33,
|
|
17
|
+
])
|
|
18
|
+
const decoded = decodeBuffer(array.buffer, 'windows-1251')
|
|
19
|
+
expect(decoded).toEqual('Привет, мир!')
|
|
20
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TextDecoder, TextEncoder } from 'web-encoding'
|
|
2
|
+
|
|
3
|
+
export function encodeBuffer(text: string): ArrayBuffer {
|
|
4
|
+
const encoder = new TextEncoder()
|
|
5
|
+
const encoded = encoder.encode(text)
|
|
6
|
+
return getArrayBuffer(encoded)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function decodeBuffer(buffer: ArrayBuffer, encoding?: string): string {
|
|
10
|
+
const decoder = new TextDecoder(encoding)
|
|
11
|
+
return decoder.decode(buffer)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getArrayBuffer(array: Uint8Array): ArrayBuffer {
|
|
15
|
+
return array.buffer.slice(
|
|
16
|
+
array.byteOffset,
|
|
17
|
+
array.byteOffset + array.byteLength
|
|
18
|
+
)
|
|
19
|
+
}
|