@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.
Files changed (48) hide show
  1. package/package.json +5 -4
  2. package/src/createInterceptor.ts +100 -0
  3. package/src/index.ts +5 -0
  4. package/src/interceptors/ClientRequest/NodeClientRequest.test.ts +283 -0
  5. package/src/interceptors/ClientRequest/NodeClientRequest.ts +377 -0
  6. package/src/interceptors/ClientRequest/http.get.ts +32 -0
  7. package/src/interceptors/ClientRequest/http.request.ts +29 -0
  8. package/src/interceptors/ClientRequest/index.ts +61 -0
  9. package/src/interceptors/ClientRequest/utils/bodyBufferToString.test.ts +16 -0
  10. package/src/interceptors/ClientRequest/utils/bodyBufferToString.ts +7 -0
  11. package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.test.ts +20 -0
  12. package/src/interceptors/ClientRequest/utils/cloneIncomingMessage.ts +41 -0
  13. package/src/interceptors/ClientRequest/utils/concatChunkToBuffer.test.ts +13 -0
  14. package/src/interceptors/ClientRequest/utils/concatChunkToBuffer.ts +10 -0
  15. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.test.ts +44 -0
  16. package/src/interceptors/ClientRequest/utils/getIncomingMessageBody.ts +38 -0
  17. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.test.ts +336 -0
  18. package/src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts +205 -0
  19. package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.test.ts +40 -0
  20. package/src/interceptors/ClientRequest/utils/normalizeClientRequestEndArgs.ts +51 -0
  21. package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.test.ts +35 -0
  22. package/src/interceptors/ClientRequest/utils/normalizeClientRequestWriteArgs.ts +36 -0
  23. package/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts +565 -0
  24. package/src/interceptors/XMLHttpRequest/index.ts +34 -0
  25. package/src/interceptors/XMLHttpRequest/polyfills/EventPolyfill.ts +51 -0
  26. package/src/interceptors/XMLHttpRequest/polyfills/ProgressEventPolyfill.ts +17 -0
  27. package/src/interceptors/XMLHttpRequest/utils/bufferFrom.test.ts +11 -0
  28. package/src/interceptors/XMLHttpRequest/utils/bufferFrom.ts +16 -0
  29. package/src/interceptors/XMLHttpRequest/utils/createEvent.test.ts +27 -0
  30. package/src/interceptors/XMLHttpRequest/utils/createEvent.ts +41 -0
  31. package/src/interceptors/fetch/index.ts +89 -0
  32. package/src/presets/browser.ts +8 -0
  33. package/src/presets/node.ts +8 -0
  34. package/src/remote.ts +176 -0
  35. package/src/utils/cloneObject.test.ts +93 -0
  36. package/src/utils/cloneObject.ts +34 -0
  37. package/src/utils/getCleanUrl.test.ts +31 -0
  38. package/src/utils/getCleanUrl.ts +6 -0
  39. package/src/utils/getRequestOptionsByUrl.ts +29 -0
  40. package/src/utils/getUrlByRequestOptions.test.ts +140 -0
  41. package/src/utils/getUrlByRequestOptions.ts +108 -0
  42. package/src/utils/isObject.test.ts +19 -0
  43. package/src/utils/isObject.ts +6 -0
  44. package/src/utils/parseJson.test.ts +9 -0
  45. package/src/utils/parseJson.ts +12 -0
  46. package/src/utils/toIsoResponse.test.ts +39 -0
  47. package/src/utils/toIsoResponse.ts +14 -0
  48. 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,6 @@
1
+ /**
2
+ * Determines if a given value is an instance of object.
3
+ */
4
+ export function isObject(value: any): boolean {
5
+ return Object.prototype.toString.call(value) === '[object Object]'
6
+ }
@@ -0,0 +1,9 @@
1
+ import { parseJson } from './parseJson'
2
+
3
+ test('parses a given string into JSON', () => {
4
+ expect(parseJson('{"id":1}')).toEqual({ id: 1 })
5
+ })
6
+
7
+ test('returns null given invalid JSON string', () => {
8
+ expect(parseJson('{"o:2\'')).toBeNull()
9
+ })
@@ -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
+ }
@@ -0,0 +1,7 @@
1
+ export function uuidv4(): string {
2
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
3
+ const r = (Math.random() * 16) | 0
4
+ const v = c == 'x' ? r : (r & 0x3) | 0x8
5
+ return v.toString(16)
6
+ })
7
+ }