@mswjs/interceptors 0.13.1 → 0.13.2
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/package.json +5 -4
- package/src/createInterceptor.ts +100 -0
- package/src/index.ts +5 -0
- package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +283 -0
- package/src/interceptors/ClientRequest/NodeClientRequest.ts +377 -0
- package/src/interceptors/ClientRequest/http.get.ts +32 -0
- package/src/interceptors/ClientRequest/http.request.ts +29 -0
- package/src/interceptors/ClientRequest/index.ts +61 -0
- package/src/interceptors/ClientRequest/utils/bodyBufferToString.test.ts +16 -0
- package/src/interceptors/ClientRequest/utils/bodyBufferToString.ts +7 -0
- package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.test.ts +20 -0
- package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.ts +41 -0
- package/src/interceptors/ClientRequest/utils/concatChunkToBuffer.test.ts +13 -0
- package/src/interceptors/ClientRequest/utils/concatChunkToBuffer.ts +10 -0
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +44 -0
- package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +38 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +336 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +205 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.test.ts +40 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.ts +51 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.test.ts +35 -0
- package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.ts +36 -0
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts +565 -0
- package/src/interceptors/XMLHttpRequest/index.ts +34 -0
- package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
- package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
- package/src/interceptors/XMLHttpRequest/utils/bufferFrom.test.ts +11 -0
- package/src/interceptors/XMLHttpRequest/utils/bufferFrom.ts +16 -0
- package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +27 -0
- package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
- package/src/interceptors/fetch/index.ts +89 -0
- package/src/presets/browser.ts +8 -0
- package/src/presets/node.ts +8 -0
- package/src/remote.ts +176 -0
- package/src/utils/cloneObject.test.ts +93 -0
- package/src/utils/cloneObject.ts +34 -0
- package/src/utils/getCleanUrl.test.ts +31 -0
- package/src/utils/getCleanUrl.ts +6 -0
- package/src/utils/getRequestOptionsByUrl.ts +29 -0
- package/src/utils/getUrlByRequestOptions.test.ts +140 -0
- package/src/utils/getUrlByRequestOptions.ts +108 -0
- package/src/utils/isObject.test.ts +19 -0
- package/src/utils/isObject.ts +6 -0
- package/src/utils/parseJson.test.ts +9 -0
- package/src/utils/parseJson.ts +12 -0
- package/src/utils/toIsoResponse.test.ts +39 -0
- package/src/utils/toIsoResponse.ts +14 -0
- package/src/utils/uuid.ts +7 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Agent as HttpAgent } from 'http'
|
|
2
|
+
import { RequestOptions, Agent as HttpsAgent } from 'https'
|
|
3
|
+
import { getUrlByRequestOptions } from './getUrlByRequestOptions'
|
|
4
|
+
|
|
5
|
+
test('returns a URL based on the basic RequestOptions', () => {
|
|
6
|
+
const options: RequestOptions = {
|
|
7
|
+
protocol: 'https:',
|
|
8
|
+
host: '127.0.0.1',
|
|
9
|
+
path: '/resource',
|
|
10
|
+
}
|
|
11
|
+
const url = getUrlByRequestOptions(options)
|
|
12
|
+
|
|
13
|
+
expect(url).toBeInstanceOf(URL)
|
|
14
|
+
expect(url).toHaveProperty('port', '')
|
|
15
|
+
expect(url).toHaveProperty('href', 'https://127.0.0.1/resource')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('inherits protocol and port from http.Agent, if set', () => {
|
|
19
|
+
const options: RequestOptions = {
|
|
20
|
+
host: '127.0.0.1',
|
|
21
|
+
path: '/',
|
|
22
|
+
agent: new HttpAgent(),
|
|
23
|
+
}
|
|
24
|
+
const url = getUrlByRequestOptions(options)
|
|
25
|
+
|
|
26
|
+
expect(url).toBeInstanceOf(URL)
|
|
27
|
+
expect(url).toHaveProperty('protocol', 'http:')
|
|
28
|
+
expect(url).toHaveProperty('port', '')
|
|
29
|
+
expect(url).toHaveProperty('href', 'http://127.0.0.1/')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('inherits protocol and port from https.Agent, if set', () => {
|
|
33
|
+
const options: RequestOptions = {
|
|
34
|
+
host: '127.0.0.1',
|
|
35
|
+
path: '/',
|
|
36
|
+
agent: new HttpsAgent({
|
|
37
|
+
port: 3080,
|
|
38
|
+
}),
|
|
39
|
+
}
|
|
40
|
+
const url = getUrlByRequestOptions(options)
|
|
41
|
+
|
|
42
|
+
expect(url).toBeInstanceOf(URL)
|
|
43
|
+
expect(url).toHaveProperty('protocol', 'https:')
|
|
44
|
+
expect(url).toHaveProperty('port', '3080')
|
|
45
|
+
expect(url).toHaveProperty('href', 'https://127.0.0.1:3080/')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('resolves protocol to "http" given no explicit protocol and no certificate', () => {
|
|
49
|
+
const options: RequestOptions = {
|
|
50
|
+
host: '127.0.0.1',
|
|
51
|
+
path: '/',
|
|
52
|
+
}
|
|
53
|
+
const url = getUrlByRequestOptions(options)
|
|
54
|
+
|
|
55
|
+
expect(url).toBeInstanceOf(URL)
|
|
56
|
+
expect(url).toHaveProperty('protocol', 'http:')
|
|
57
|
+
expect(url).toHaveProperty('port', '')
|
|
58
|
+
expect(url).toHaveProperty('href', 'http://127.0.0.1/')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('resolves protocol to "https" given no explicit protocol, but certificate', () => {
|
|
62
|
+
const options: RequestOptions = {
|
|
63
|
+
host: '127.0.0.1',
|
|
64
|
+
path: '/secure',
|
|
65
|
+
cert: '<!-- SSL certificate -->',
|
|
66
|
+
}
|
|
67
|
+
const url = getUrlByRequestOptions(options)
|
|
68
|
+
|
|
69
|
+
expect(url).toBeInstanceOf(URL)
|
|
70
|
+
expect(url).toHaveProperty('protocol', 'https:')
|
|
71
|
+
expect(url).toHaveProperty('port', '')
|
|
72
|
+
expect(url).toHaveProperty('href', 'https://127.0.0.1/secure')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('resolves protocol to "https" given no explicit protocol, but port is 443', () => {
|
|
76
|
+
const options: RequestOptions = {
|
|
77
|
+
host: '127.0.0.1',
|
|
78
|
+
port: 443,
|
|
79
|
+
path: '/resource',
|
|
80
|
+
}
|
|
81
|
+
const url = getUrlByRequestOptions(options)
|
|
82
|
+
|
|
83
|
+
expect(url).toBeInstanceOf(URL)
|
|
84
|
+
expect(url).toHaveProperty('port', '')
|
|
85
|
+
expect(url).toHaveProperty('href', 'https://127.0.0.1/resource')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('resolves protocol to "https" given no explicit protocol, but agent port is 443', () => {
|
|
89
|
+
const options: RequestOptions = {
|
|
90
|
+
host: '127.0.0.1',
|
|
91
|
+
agent: new HttpsAgent({
|
|
92
|
+
port: 443,
|
|
93
|
+
}),
|
|
94
|
+
path: '/resource',
|
|
95
|
+
}
|
|
96
|
+
const url = getUrlByRequestOptions(options)
|
|
97
|
+
|
|
98
|
+
expect(url).toBeInstanceOf(URL)
|
|
99
|
+
expect(url).toHaveProperty('port', '')
|
|
100
|
+
expect(url).toHaveProperty('href', 'https://127.0.0.1/resource')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
test('inherits "port" if given', () => {
|
|
104
|
+
const options = {
|
|
105
|
+
protocol: 'http:',
|
|
106
|
+
host: '127.0.0.1',
|
|
107
|
+
port: 4002,
|
|
108
|
+
path: '/',
|
|
109
|
+
}
|
|
110
|
+
const url = getUrlByRequestOptions(options)
|
|
111
|
+
|
|
112
|
+
expect(url).toBeInstanceOf(URL)
|
|
113
|
+
expect(url).toHaveProperty('port', '4002')
|
|
114
|
+
expect(url).toHaveProperty('protocol', 'http:')
|
|
115
|
+
expect(url).toHaveProperty('href', 'http://127.0.0.1:4002/')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('inherits "username" and "password"', () => {
|
|
119
|
+
const options: RequestOptions = {
|
|
120
|
+
protocol: 'https:',
|
|
121
|
+
host: '127.0.0.1',
|
|
122
|
+
path: '/user',
|
|
123
|
+
auth: 'admin:abc-123',
|
|
124
|
+
}
|
|
125
|
+
const url = getUrlByRequestOptions(options)
|
|
126
|
+
|
|
127
|
+
expect(url).toBeInstanceOf(URL)
|
|
128
|
+
expect(url).toHaveProperty('username', 'admin')
|
|
129
|
+
expect(url).toHaveProperty('password', 'abc-123')
|
|
130
|
+
expect(url).toHaveProperty('protocol', 'https:')
|
|
131
|
+
expect(url).toHaveProperty('href', 'https://admin:abc-123@127.0.0.1/user')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('resolves hostname to localhost if none provided', () => {
|
|
135
|
+
const url = getUrlByRequestOptions({})
|
|
136
|
+
|
|
137
|
+
expect(url).toBeInstanceOf(URL)
|
|
138
|
+
expect(url).toHaveProperty('protocol', 'http:')
|
|
139
|
+
expect(url).toHaveProperty('href', 'http://localhost/')
|
|
140
|
+
})
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Agent } from 'http'
|
|
2
|
+
import { RequestOptions, Agent as HttpsAgent } from 'https'
|
|
3
|
+
|
|
4
|
+
const debug = require('debug')('utils getUrlByRequestOptions')
|
|
5
|
+
|
|
6
|
+
// Request instance constructed by the "request" library
|
|
7
|
+
// has a "self" property that has a "uri" field. This is
|
|
8
|
+
// reproducible by performing a "XMLHttpRequest" request in JSDOM.
|
|
9
|
+
export interface RequestSelf {
|
|
10
|
+
uri?: URL
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ResolvedRequestOptions = RequestOptions & RequestSelf
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_PATH = '/'
|
|
16
|
+
const DEFAULT_PROTOCOL = 'http:'
|
|
17
|
+
const DEFAULT_HOST = 'localhost'
|
|
18
|
+
const DEFAULT_PORT = 80
|
|
19
|
+
const SSL_PORT = 443
|
|
20
|
+
|
|
21
|
+
function getAgent(
|
|
22
|
+
options: ResolvedRequestOptions
|
|
23
|
+
): Agent | HttpsAgent | undefined {
|
|
24
|
+
return options.agent instanceof Agent ? options.agent : undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getProtocolByRequestOptions(options: ResolvedRequestOptions): string {
|
|
28
|
+
if (options.protocol) {
|
|
29
|
+
return options.protocol
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const agent = getAgent(options)
|
|
33
|
+
const agentProtocol = (agent as RequestOptions)?.protocol
|
|
34
|
+
|
|
35
|
+
if (agentProtocol) {
|
|
36
|
+
return agentProtocol
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const port = getPortByRequestOptions(options)
|
|
40
|
+
const isSecureRequest = options.cert || port === SSL_PORT
|
|
41
|
+
|
|
42
|
+
return isSecureRequest ? 'https:' : options.uri?.protocol || DEFAULT_PROTOCOL
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getPortByRequestOptions(
|
|
46
|
+
options: ResolvedRequestOptions
|
|
47
|
+
): number | undefined {
|
|
48
|
+
const agent = getAgent(options)
|
|
49
|
+
const agentPort =
|
|
50
|
+
(agent as HttpsAgent)?.options.port ||
|
|
51
|
+
(agent as RequestOptions)?.defaultPort
|
|
52
|
+
const optionsPort = options.port
|
|
53
|
+
|
|
54
|
+
if (optionsPort || agentPort) {
|
|
55
|
+
const explicitPort = optionsPort || agentPort || DEFAULT_PORT
|
|
56
|
+
return Number(explicitPort)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getHostByRequestOptions(options: ResolvedRequestOptions): string {
|
|
61
|
+
return options.hostname || options.host || DEFAULT_HOST
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getAuthByRequestOptions(options: ResolvedRequestOptions) {
|
|
65
|
+
if (options.auth) {
|
|
66
|
+
const [username, password] = options.auth.split(':')
|
|
67
|
+
return { username, password }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates a `URL` instance from a given `RequestOptions` object.
|
|
73
|
+
*/
|
|
74
|
+
export function getUrlByRequestOptions(options: ResolvedRequestOptions): URL {
|
|
75
|
+
debug('request options', options)
|
|
76
|
+
|
|
77
|
+
const protocol = getProtocolByRequestOptions(options)
|
|
78
|
+
const host = getHostByRequestOptions(options)
|
|
79
|
+
const port = getPortByRequestOptions(options)
|
|
80
|
+
const path = options.path || DEFAULT_PATH
|
|
81
|
+
const auth = getAuthByRequestOptions(options)
|
|
82
|
+
|
|
83
|
+
debug('protocol', protocol)
|
|
84
|
+
debug('host', host)
|
|
85
|
+
debug('port', port)
|
|
86
|
+
debug('path', path)
|
|
87
|
+
|
|
88
|
+
const baseUrl = `${protocol}//${host}`
|
|
89
|
+
debug('base URL:', baseUrl)
|
|
90
|
+
|
|
91
|
+
const url = options.uri ? new URL(options.uri.href) : new URL(path, baseUrl)
|
|
92
|
+
|
|
93
|
+
if (port) {
|
|
94
|
+
debug('detected explicit port', port)
|
|
95
|
+
url.port = port.toString()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (auth) {
|
|
99
|
+
debug('resolved auth', auth)
|
|
100
|
+
|
|
101
|
+
url.username = auth.username
|
|
102
|
+
url.password = auth.password
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
debug('created URL:', url)
|
|
106
|
+
|
|
107
|
+
return url
|
|
108
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isObject } from './isObject'
|
|
2
|
+
|
|
3
|
+
test('resolves given an object', () => {
|
|
4
|
+
expect(isObject({})).toBe(true)
|
|
5
|
+
expect(isObject({ a: 1 })).toBe(true)
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
test('rejects given an object-like instance', () => {
|
|
9
|
+
expect(isObject([1])).toBe(false)
|
|
10
|
+
expect(isObject(function () {})).toBe(false)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test('rejects given a non-object instance', () => {
|
|
14
|
+
expect(isObject(null)).toBe(false)
|
|
15
|
+
expect(isObject(undefined)).toBe(false)
|
|
16
|
+
expect(isObject(false)).toBe(false)
|
|
17
|
+
expect(isObject(123)).toBe(false)
|
|
18
|
+
expect(isObject(Symbol('object Object'))).toBe(false)
|
|
19
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a given string into JSON.
|
|
3
|
+
* Gracefully handles invalid JSON by returning `null`.
|
|
4
|
+
*/
|
|
5
|
+
export function parseJson(data: string): Record<string, any> | null {
|
|
6
|
+
try {
|
|
7
|
+
const json = JSON.parse(data)
|
|
8
|
+
return json
|
|
9
|
+
} catch (_) {
|
|
10
|
+
return null
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Headers } from 'headers-utils'
|
|
2
|
+
import { toIsoResponse } from './toIsoResponse'
|
|
3
|
+
|
|
4
|
+
test('returns a well-formed empty response', () => {
|
|
5
|
+
expect(toIsoResponse({})).toEqual({
|
|
6
|
+
status: 200,
|
|
7
|
+
statusText: 'OK',
|
|
8
|
+
headers: new Headers(),
|
|
9
|
+
})
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('uses fallback values for the missing response properties', () => {
|
|
13
|
+
expect(toIsoResponse({ status: 301, body: 'text-body' })).toEqual({
|
|
14
|
+
status: 301,
|
|
15
|
+
statusText: 'OK',
|
|
16
|
+
headers: new Headers(),
|
|
17
|
+
body: 'text-body',
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('returns a full response as-is, converting the headers', () => {
|
|
22
|
+
expect(
|
|
23
|
+
toIsoResponse({
|
|
24
|
+
status: 301,
|
|
25
|
+
statusText: 'Custom Status',
|
|
26
|
+
headers: {
|
|
27
|
+
'X-Allowed': 'yes',
|
|
28
|
+
},
|
|
29
|
+
body: 'text-body',
|
|
30
|
+
})
|
|
31
|
+
).toEqual({
|
|
32
|
+
status: 301,
|
|
33
|
+
statusText: 'Custom Status',
|
|
34
|
+
headers: new Headers({
|
|
35
|
+
'X-Allowed': 'yes',
|
|
36
|
+
}),
|
|
37
|
+
body: 'text-body',
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { objectToHeaders } from 'headers-utils'
|
|
2
|
+
import { IsomorphicResponse, MockedResponse } from '../createInterceptor'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Converts a given mocked response object into an isomorphic response.
|
|
6
|
+
*/
|
|
7
|
+
export function toIsoResponse(response: MockedResponse): IsomorphicResponse {
|
|
8
|
+
return {
|
|
9
|
+
status: response.status || 200,
|
|
10
|
+
statusText: response.statusText || 'OK',
|
|
11
|
+
headers: objectToHeaders(response.headers || {}),
|
|
12
|
+
body: response.body,
|
|
13
|
+
}
|
|
14
|
+
}
|