nock 13.5.4 → 14.0.0-beta.10
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 +6 -8
- package/lib/back.js +1 -3
- package/lib/common.js +44 -91
- package/lib/create_response.js +51 -0
- package/lib/debug.js +12 -0
- package/lib/intercept.js +63 -76
- package/lib/intercepted_request_router.js +3 -15
- package/lib/interceptor.js +1 -8
- package/lib/match_body.js +3 -2
- package/lib/playback_interceptor.js +1 -1
- package/lib/recorder.js +109 -142
- package/lib/scope.js +2 -2
- package/lib/socket.js +1 -1
- package/package.json +5 -5
- package/types/index.d.ts +2 -2
package/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# Nock
|
|
2
2
|
|
|
3
3
|
[][npmjs]
|
|
4
|
-
[][build]
|
|
5
4
|

|
|
6
5
|
[](#backers)
|
|
7
6
|
[](#sponsors)
|
|
8
7
|
|
|
9
8
|
[npmjs]: https://www.npmjs.com/package/nock
|
|
10
|
-
[build]: https://travis-ci.org/nock/nock
|
|
11
9
|
|
|
12
10
|
> **Notice**
|
|
13
11
|
>
|
|
@@ -1643,10 +1641,10 @@ It does this by manipulating the modules cache of Node in a way that conflicts w
|
|
|
1643
1641
|
|
|
1644
1642
|
## Debugging
|
|
1645
1643
|
|
|
1646
|
-
Nock uses [`
|
|
1644
|
+
Nock uses node internals [`debuglog`](https://nodejs.org/api/util.html#utildebuglogsection-callbackg), so just run with environmental variable `NODE_DEBUG` set to `nock:*`.
|
|
1647
1645
|
|
|
1648
1646
|
```console
|
|
1649
|
-
user@local$
|
|
1647
|
+
user@local$ NODE_DEBUG=nock:* node my_test.js
|
|
1650
1648
|
```
|
|
1651
1649
|
|
|
1652
1650
|
Each step in the matching process is logged this way and can be useful when determining why a request was not intercepted by Nock.
|
|
@@ -1660,11 +1658,11 @@ await got('http://example.com/?foo=bar&baz=foz')
|
|
|
1660
1658
|
```
|
|
1661
1659
|
|
|
1662
1660
|
```console
|
|
1663
|
-
user@local$ DEBUG=nock
|
|
1661
|
+
user@local$ DEBUG=nock:scope:example.com node my_test.js
|
|
1664
1662
|
...
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1663
|
+
NOCK:SCOPE:EXAMPLE.COM 103514: Interceptor queries: {"foo":"bar"}
|
|
1664
|
+
NOCK:SCOPE:EXAMPLE.COM 103514: Request queries: {"foo":"bar","baz":"foz"}
|
|
1665
|
+
NOCK:SCOPE:EXAMPLE.COM 103514: query matching failed
|
|
1668
1666
|
```
|
|
1669
1667
|
|
|
1670
1668
|
## Contributing
|
package/lib/back.js
CHANGED
|
@@ -9,10 +9,9 @@ const {
|
|
|
9
9
|
removeAll: cleanAll,
|
|
10
10
|
} = require('./intercept')
|
|
11
11
|
const { loadDefs, define } = require('./scope')
|
|
12
|
-
|
|
12
|
+
const { back: debug } = require('./debug')
|
|
13
13
|
const { format } = require('util')
|
|
14
14
|
const path = require('path')
|
|
15
|
-
const debug = require('debug')('nock.back')
|
|
16
15
|
|
|
17
16
|
let _mode = null
|
|
18
17
|
|
|
@@ -78,7 +77,6 @@ function Back(fixtureName, options, nockedFn) {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
debug('context:', context)
|
|
81
|
-
|
|
82
80
|
// If nockedFn is a function then invoke it, otherwise return a promise resolving to nockDone.
|
|
83
81
|
if (typeof nockedFn === 'function') {
|
|
84
82
|
nockedFn.call(context, nockDone)
|
package/lib/common.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const debug = require('debug')
|
|
3
|
+
const { common: debug } = require('./debug')
|
|
4
4
|
const timers = require('timers')
|
|
5
5
|
const url = require('url')
|
|
6
6
|
const util = require('util')
|
|
7
|
+
const http = require('http')
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Normalizes the request options so that it always has `host` property.
|
|
@@ -50,82 +51,6 @@ function isUtf8Representable(buffer) {
|
|
|
50
51
|
return reconstructedBuffer.equals(buffer)
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
// Array where all information about all the overridden requests are held.
|
|
54
|
-
let requestOverrides = {}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Overrides the current `request` function of `http` and `https` modules with
|
|
58
|
-
* our own version which intercepts issues HTTP/HTTPS requests and forwards them
|
|
59
|
-
* to the given `newRequest` function.
|
|
60
|
-
*
|
|
61
|
-
* @param {Function} newRequest - a function handling requests; it accepts four arguments:
|
|
62
|
-
* - proto - a string with the overridden module's protocol name (either `http` or `https`)
|
|
63
|
-
* - overriddenRequest - the overridden module's request function already bound to module's object
|
|
64
|
-
* - options - the options of the issued request
|
|
65
|
-
* - callback - the callback of the issued request
|
|
66
|
-
*/
|
|
67
|
-
function overrideRequests(newRequest) {
|
|
68
|
-
debug('overriding requests')
|
|
69
|
-
;['http', 'https'].forEach(function (proto) {
|
|
70
|
-
debug('- overriding request for', proto)
|
|
71
|
-
|
|
72
|
-
const moduleName = proto // 1 to 1 match of protocol and module is fortunate :)
|
|
73
|
-
const module = require(proto)
|
|
74
|
-
const overriddenRequest = module.request
|
|
75
|
-
const overriddenGet = module.get
|
|
76
|
-
|
|
77
|
-
if (requestOverrides[moduleName]) {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`Module's request already overridden for ${moduleName} protocol.`,
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Store the properties of the overridden request so that it can be restored later on.
|
|
84
|
-
requestOverrides[moduleName] = {
|
|
85
|
-
module,
|
|
86
|
-
request: overriddenRequest,
|
|
87
|
-
get: overriddenGet,
|
|
88
|
-
}
|
|
89
|
-
// https://nodejs.org/api/http.html#http_http_request_url_options_callback
|
|
90
|
-
module.request = function (input, options, callback) {
|
|
91
|
-
return newRequest(proto, overriddenRequest.bind(module), [
|
|
92
|
-
input,
|
|
93
|
-
options,
|
|
94
|
-
callback,
|
|
95
|
-
])
|
|
96
|
-
}
|
|
97
|
-
// https://nodejs.org/api/http.html#http_http_get_options_callback
|
|
98
|
-
module.get = function (input, options, callback) {
|
|
99
|
-
const req = newRequest(proto, overriddenGet.bind(module), [
|
|
100
|
-
input,
|
|
101
|
-
options,
|
|
102
|
-
callback,
|
|
103
|
-
])
|
|
104
|
-
req.end()
|
|
105
|
-
return req
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
debug('- overridden request for', proto)
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Restores `request` function of `http` and `https` modules to values they
|
|
114
|
-
* held before they were overridden by us.
|
|
115
|
-
*/
|
|
116
|
-
function restoreOverriddenRequests() {
|
|
117
|
-
debug('restoring requests')
|
|
118
|
-
Object.entries(requestOverrides).forEach(
|
|
119
|
-
([proto, { module, request, get }]) => {
|
|
120
|
-
debug('- restoring request for', proto)
|
|
121
|
-
module.request = request
|
|
122
|
-
module.get = get
|
|
123
|
-
debug('- restored request for', proto)
|
|
124
|
-
},
|
|
125
|
-
)
|
|
126
|
-
requestOverrides = {}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
54
|
/**
|
|
130
55
|
* In WHATWG URL vernacular, this returns the origin portion of a URL.
|
|
131
56
|
* However, the port is not included if it's standard and not already present on the host.
|
|
@@ -593,31 +518,36 @@ function deepEqual(expected, actual) {
|
|
|
593
518
|
return expected === actual
|
|
594
519
|
}
|
|
595
520
|
|
|
596
|
-
const timeouts =
|
|
597
|
-
const
|
|
598
|
-
const immediates = []
|
|
521
|
+
const timeouts = new Set()
|
|
522
|
+
const immediates = new Set()
|
|
599
523
|
|
|
600
524
|
const wrapTimer =
|
|
601
525
|
(timer, ids) =>
|
|
602
|
-
(...
|
|
603
|
-
const
|
|
604
|
-
|
|
526
|
+
(callback, ...timerArgs) => {
|
|
527
|
+
const cb = (...callbackArgs) => {
|
|
528
|
+
try {
|
|
529
|
+
// eslint-disable-next-line n/no-callback-literal
|
|
530
|
+
callback(...callbackArgs)
|
|
531
|
+
} finally {
|
|
532
|
+
ids.delete(id)
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const id = timer(cb, ...timerArgs)
|
|
536
|
+
ids.add(id)
|
|
605
537
|
return id
|
|
606
538
|
}
|
|
607
539
|
|
|
608
540
|
const setTimeout = wrapTimer(timers.setTimeout, timeouts)
|
|
609
|
-
const setInterval = wrapTimer(timers.setInterval, intervals)
|
|
610
541
|
const setImmediate = wrapTimer(timers.setImmediate, immediates)
|
|
611
542
|
|
|
612
543
|
function clearTimer(clear, ids) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
}
|
|
544
|
+
ids.forEach(clear)
|
|
545
|
+
ids.clear()
|
|
616
546
|
}
|
|
617
547
|
|
|
618
548
|
function removeAllTimers() {
|
|
549
|
+
debug('remove all timers')
|
|
619
550
|
clearTimer(clearTimeout, timeouts)
|
|
620
|
-
clearTimer(clearInterval, intervals)
|
|
621
551
|
clearTimer(clearImmediate, immediates)
|
|
622
552
|
}
|
|
623
553
|
|
|
@@ -649,6 +579,31 @@ function isRequestDestroyed(req) {
|
|
|
649
579
|
)
|
|
650
580
|
}
|
|
651
581
|
|
|
582
|
+
/**
|
|
583
|
+
* @param {Request} request
|
|
584
|
+
*/
|
|
585
|
+
function convertFetchRequestToClientRequest(request) {
|
|
586
|
+
const url = new URL(request.url)
|
|
587
|
+
const options = {
|
|
588
|
+
...urlToOptions(url),
|
|
589
|
+
method: request.method,
|
|
590
|
+
host: url.hostname,
|
|
591
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
592
|
+
path: url.pathname + url.search,
|
|
593
|
+
proto: url.protocol.slice(0, -1),
|
|
594
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// By default, Node adds a host header, but for maximum backward compatibility, we are now removing it.
|
|
598
|
+
// However, we need to consider leaving the header and fixing the tests.
|
|
599
|
+
if (options.headers.host === options.host) {
|
|
600
|
+
const { host, ...restHeaders } = options.headers
|
|
601
|
+
options.headers = restHeaders
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return new http.ClientRequest(options)
|
|
605
|
+
}
|
|
606
|
+
|
|
652
607
|
/**
|
|
653
608
|
* Returns true if the given value is a plain object and not an Array.
|
|
654
609
|
* @param {*} value
|
|
@@ -756,13 +711,11 @@ module.exports = {
|
|
|
756
711
|
normalizeClientRequestArgs,
|
|
757
712
|
normalizeOrigin,
|
|
758
713
|
normalizeRequestOptions,
|
|
759
|
-
overrideRequests,
|
|
760
714
|
percentDecode,
|
|
761
715
|
percentEncode,
|
|
762
716
|
removeAllTimers,
|
|
763
|
-
restoreOverriddenRequests,
|
|
764
717
|
setImmediate,
|
|
765
|
-
setInterval,
|
|
766
718
|
setTimeout,
|
|
767
719
|
stringifyRequest,
|
|
720
|
+
convertFetchRequestToClientRequest,
|
|
768
721
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { STATUS_CODES } = require('http')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a Fetch API `Response` instance from the given
|
|
7
|
+
* `http.IncomingMessage` instance.
|
|
8
|
+
* Inspired by: https://github.com/mswjs/interceptors/blob/04152ed914f8041272b6e92ed374216b8177e1b2/src/interceptors/ClientRequest/utils/createResponse.ts#L8
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Response status codes for responses that cannot have body.
|
|
13
|
+
* @see https://fetch.spec.whatwg.org/#statuses
|
|
14
|
+
*/
|
|
15
|
+
const responseStatusCodesWithoutBody = [204, 205, 304]
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import('http').IncomingMessage} message
|
|
19
|
+
*/
|
|
20
|
+
function createResponse(message) {
|
|
21
|
+
const responseBodyOrNull = responseStatusCodesWithoutBody.includes(
|
|
22
|
+
message.statusCode || 200,
|
|
23
|
+
)
|
|
24
|
+
? null
|
|
25
|
+
: new ReadableStream({
|
|
26
|
+
start(controller) {
|
|
27
|
+
message.on('data', chunk => controller.enqueue(chunk))
|
|
28
|
+
message.on('end', () => controller.close())
|
|
29
|
+
message.on('error', error => controller.error(error))
|
|
30
|
+
},
|
|
31
|
+
cancel() {
|
|
32
|
+
message.destroy()
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const rawHeaders = new Headers()
|
|
37
|
+
for (let i = 0; i < message.rawHeaders.length; i += 2) {
|
|
38
|
+
rawHeaders.append(message.rawHeaders[i], message.rawHeaders[i + 1])
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// @mswjs/interceptors supports rawHeaders. https://github.com/mswjs/interceptors/pull/598
|
|
42
|
+
const response = new Response(responseBodyOrNull, {
|
|
43
|
+
status: message.statusCode,
|
|
44
|
+
statusText: message.statusMessage || STATUS_CODES[message.statusCode],
|
|
45
|
+
headers: rawHeaders,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return response
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { createResponse }
|
package/lib/debug.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { debuglog } = require('util')
|
|
4
|
+
|
|
5
|
+
module.exports.back = debuglog('nock:back')
|
|
6
|
+
module.exports.common = debuglog('nock:common')
|
|
7
|
+
module.exports.intercept = debuglog('nock:intercept')
|
|
8
|
+
module.exports.request_overrider = debuglog('nock:request_overrider')
|
|
9
|
+
module.exports.playback_interceptor = debuglog('nock:playback_interceptor')
|
|
10
|
+
module.exports.recorder = debuglog('nock:recorder')
|
|
11
|
+
module.exports.socket = debuglog('nock:socket')
|
|
12
|
+
module.exports.scopeDebuglog = namespace => debuglog(`nock:scope:${namespace}`)
|
package/lib/intercept.js
CHANGED
|
@@ -8,8 +8,20 @@ const { InterceptedRequestRouter } = require('./intercepted_request_router')
|
|
|
8
8
|
const common = require('./common')
|
|
9
9
|
const { inherits } = require('util')
|
|
10
10
|
const http = require('http')
|
|
11
|
-
const debug = require('debug')
|
|
11
|
+
const { intercept: debug } = require('./debug')
|
|
12
12
|
const globalEmitter = require('./global_emitter')
|
|
13
|
+
const { BatchInterceptor } = require('@mswjs/interceptors')
|
|
14
|
+
const {
|
|
15
|
+
default: nodeInterceptors,
|
|
16
|
+
} = require('@mswjs/interceptors/presets/node')
|
|
17
|
+
const { createResponse } = require('./create_response')
|
|
18
|
+
const { once } = require('events')
|
|
19
|
+
|
|
20
|
+
const interceptor = new BatchInterceptor({
|
|
21
|
+
name: 'nock-interceptor',
|
|
22
|
+
interceptors: nodeInterceptors,
|
|
23
|
+
})
|
|
24
|
+
let isNockActive = false
|
|
13
25
|
|
|
14
26
|
/**
|
|
15
27
|
* @name NetConnectNotAllowedError
|
|
@@ -238,17 +250,6 @@ function removeInterceptor(options) {
|
|
|
238
250
|
// (which might or might not be node's original http.ClientRequest)
|
|
239
251
|
let originalClientRequest
|
|
240
252
|
|
|
241
|
-
function ErroringClientRequest(error) {
|
|
242
|
-
http.OutgoingMessage.call(this)
|
|
243
|
-
process.nextTick(
|
|
244
|
-
function () {
|
|
245
|
-
this.emit('error', error)
|
|
246
|
-
}.bind(this),
|
|
247
|
-
)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
inherits(ErroringClientRequest, http.ClientRequest)
|
|
251
|
-
|
|
252
253
|
function overrideClientRequest() {
|
|
253
254
|
// Here's some background discussion about overriding ClientRequest:
|
|
254
255
|
// - https://github.com/nodejitsu/mock-request/issues/4
|
|
@@ -333,6 +334,8 @@ function restoreOverriddenClientRequest() {
|
|
|
333
334
|
if (!originalClientRequest) {
|
|
334
335
|
debug('- ClientRequest was not overridden')
|
|
335
336
|
} else {
|
|
337
|
+
isNockActive = false
|
|
338
|
+
interceptor.dispose()
|
|
336
339
|
http.ClientRequest = originalClientRequest
|
|
337
340
|
originalClientRequest = undefined
|
|
338
341
|
|
|
@@ -341,9 +344,7 @@ function restoreOverriddenClientRequest() {
|
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
function isActive() {
|
|
344
|
-
|
|
345
|
-
// This means that Nock has been activated.
|
|
346
|
-
return originalClientRequest !== undefined
|
|
347
|
+
return isNockActive
|
|
347
348
|
}
|
|
348
349
|
|
|
349
350
|
function interceptorScopes() {
|
|
@@ -367,75 +368,61 @@ function activeMocks() {
|
|
|
367
368
|
}
|
|
368
369
|
|
|
369
370
|
function activate() {
|
|
370
|
-
if (
|
|
371
|
+
if (isNockActive) {
|
|
371
372
|
throw new Error('Nock already active')
|
|
372
373
|
}
|
|
373
374
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
// The option per the docs is `protocol`. Its unclear if this line is meant to override that and is misspelled or if
|
|
396
|
-
// the intend is to explicitly keep track of which module was called using a separate name.
|
|
397
|
-
// Either way, `proto` is used as the source of truth from here on out.
|
|
398
|
-
options.proto = proto
|
|
399
|
-
|
|
400
|
-
const interceptors = interceptorsFor(options)
|
|
401
|
-
|
|
402
|
-
if (isOn() && interceptors) {
|
|
403
|
-
const matches = interceptors.some(interceptor =>
|
|
404
|
-
interceptor.matchOrigin(options),
|
|
405
|
-
)
|
|
406
|
-
const allowUnmocked = interceptors.some(
|
|
407
|
-
interceptor => interceptor.options.allowUnmocked,
|
|
408
|
-
)
|
|
375
|
+
overrideClientRequest()
|
|
376
|
+
interceptor.apply()
|
|
377
|
+
// Force msw to forward Nock's error instead of coerce it into 500 error
|
|
378
|
+
interceptor.on('unhandledException', ({ controller, error }) => {
|
|
379
|
+
controller.errorWith(error)
|
|
380
|
+
})
|
|
381
|
+
interceptor.on(
|
|
382
|
+
'request',
|
|
383
|
+
async function ({ request: mswRequest, controller }) {
|
|
384
|
+
const request = mswRequest.clone()
|
|
385
|
+
const { options } = common.normalizeClientRequestArgs(request.url)
|
|
386
|
+
options.proto = options.protocol.slice(0, -1)
|
|
387
|
+
options.method = request.method
|
|
388
|
+
const interceptors = interceptorsFor(options)
|
|
389
|
+
if (isOn() && interceptors) {
|
|
390
|
+
const matches = interceptors.some(interceptor =>
|
|
391
|
+
interceptor.matchOrigin(options),
|
|
392
|
+
)
|
|
393
|
+
const allowUnmocked = interceptors.some(
|
|
394
|
+
interceptor => interceptor.options.allowUnmocked,
|
|
395
|
+
)
|
|
409
396
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
const { ClientRequest } = http
|
|
414
|
-
http.ClientRequest = originalClientRequest
|
|
415
|
-
req = overriddenRequest(options, callback)
|
|
416
|
-
http.ClientRequest = ClientRequest
|
|
397
|
+
const nockRequest = common.convertFetchRequestToClientRequest(request)
|
|
398
|
+
if (!matches && allowUnmocked) {
|
|
399
|
+
globalEmitter.emit('no match', nockRequest)
|
|
417
400
|
} else {
|
|
418
|
-
|
|
401
|
+
nockRequest.on('response', nockResponse => {
|
|
402
|
+
const response = createResponse(nockResponse)
|
|
403
|
+
controller.respondWith(response)
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
const promise = Promise.race([
|
|
407
|
+
// TODO: temp hacky way to handle allowUnmocked in startPlayback
|
|
408
|
+
once(nockRequest, 'real-request'),
|
|
409
|
+
once(nockRequest, 'error'),
|
|
410
|
+
once(nockRequest, 'response'),
|
|
411
|
+
])
|
|
412
|
+
const buffer = await request.arrayBuffer()
|
|
413
|
+
nockRequest.write(buffer)
|
|
414
|
+
nockRequest.end()
|
|
415
|
+
await promise
|
|
419
416
|
}
|
|
420
|
-
globalEmitter.emit('no match', req)
|
|
421
|
-
return req
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// NOTE: Since we already overrode the http.ClientRequest we are in fact constructing
|
|
425
|
-
// our own OverriddenClientRequest.
|
|
426
|
-
return new http.ClientRequest(options, callback)
|
|
427
|
-
} else {
|
|
428
|
-
globalEmitter.emit('no match', options)
|
|
429
|
-
if (isOff() || isEnabledForNetConnect(options)) {
|
|
430
|
-
return overriddenRequest(options, callback)
|
|
431
417
|
} else {
|
|
432
|
-
|
|
433
|
-
|
|
418
|
+
globalEmitter.emit('no match', options)
|
|
419
|
+
if (!(isOff() || isEnabledForNetConnect(options))) {
|
|
420
|
+
throw new NetConnectNotAllowedError(options.host, options.path)
|
|
421
|
+
}
|
|
434
422
|
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
overrideClientRequest()
|
|
423
|
+
},
|
|
424
|
+
)
|
|
425
|
+
isNockActive = true
|
|
439
426
|
}
|
|
440
427
|
|
|
441
428
|
module.exports = {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const debug = require('debug')
|
|
4
|
-
const {
|
|
5
|
-
IncomingMessage,
|
|
6
|
-
ClientRequest,
|
|
7
|
-
request: originalHttpRequest,
|
|
8
|
-
} = require('http')
|
|
9
|
-
const { request: originalHttpsRequest } = require('https')
|
|
3
|
+
const { request_overrider: debug } = require('./debug')
|
|
4
|
+
const { IncomingMessage, ClientRequest } = require('http')
|
|
10
5
|
const propagate = require('propagate')
|
|
11
6
|
const common = require('./common')
|
|
12
7
|
const globalEmitter = require('./global_emitter')
|
|
@@ -333,14 +328,7 @@ class InterceptedRequestRouter {
|
|
|
333
328
|
)
|
|
334
329
|
|
|
335
330
|
if (allowUnmocked && req instanceof ClientRequest) {
|
|
336
|
-
|
|
337
|
-
options.proto === 'https'
|
|
338
|
-
? originalHttpsRequest(options)
|
|
339
|
-
: originalHttpRequest(options)
|
|
340
|
-
|
|
341
|
-
propagate(newReq, req)
|
|
342
|
-
// We send the raw buffer as we received it, not as we interpreted it.
|
|
343
|
-
newReq.end(requestBodyBuffer)
|
|
331
|
+
req.emit('real-request')
|
|
344
332
|
} else {
|
|
345
333
|
const reqStr = common.stringifyRequest(options, requestBodyString)
|
|
346
334
|
const err = new Error(`Nock: No match for request ${reqStr}`)
|
package/lib/interceptor.js
CHANGED
|
@@ -248,14 +248,7 @@ module.exports = class Interceptor {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
match(req, options, body) {
|
|
251
|
-
|
|
252
|
-
if (this.scope.logger.enabled) {
|
|
253
|
-
this.scope.logger(
|
|
254
|
-
'attempting match %s, body = %s',
|
|
255
|
-
stringify(options),
|
|
256
|
-
stringify(body),
|
|
257
|
-
)
|
|
258
|
-
}
|
|
251
|
+
this.scope.logger('attempting match %j, body = %j', options, body)
|
|
259
252
|
|
|
260
253
|
const method = (options.method || 'GET').toUpperCase()
|
|
261
254
|
let { path = '/' } = options
|
package/lib/match_body.js
CHANGED
|
@@ -63,10 +63,11 @@ module.exports = function matchBody(options, spec, body) {
|
|
|
63
63
|
|
|
64
64
|
function mapValues(object, cb) {
|
|
65
65
|
const keys = Object.keys(object)
|
|
66
|
+
const clonedObject = { ...object }
|
|
66
67
|
for (const key of keys) {
|
|
67
|
-
|
|
68
|
+
clonedObject[key] = cb(clonedObject[key], key, clonedObject)
|
|
68
69
|
}
|
|
69
|
-
return
|
|
70
|
+
return clonedObject
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const stream = require('stream')
|
|
4
4
|
const util = require('util')
|
|
5
5
|
const zlib = require('zlib')
|
|
6
|
-
const debug = require('debug')
|
|
6
|
+
const { playback_interceptor: debug } = require('./debug')
|
|
7
7
|
const common = require('./common')
|
|
8
8
|
|
|
9
9
|
function parseJSONRequestBody(req, requestBody) {
|
package/lib/recorder.js
CHANGED
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const debug = require('debug')
|
|
3
|
+
const { recorder: debug } = require('./debug')
|
|
4
4
|
const querystring = require('querystring')
|
|
5
5
|
const { inspect } = require('util')
|
|
6
6
|
|
|
7
7
|
const common = require('./common')
|
|
8
8
|
const { restoreOverriddenClientRequest } = require('./intercept')
|
|
9
|
+
const { BatchInterceptor } = require('@mswjs/interceptors')
|
|
10
|
+
const {
|
|
11
|
+
default: nodeInterceptors,
|
|
12
|
+
} = require('@mswjs/interceptors/presets/node')
|
|
13
|
+
const { EventEmitter } = require('stream')
|
|
9
14
|
|
|
10
15
|
const SEPARATOR = '\n<<<<<<-- cut here -->>>>>>\n'
|
|
11
16
|
let recordingInProgress = false
|
|
12
17
|
let outputs = []
|
|
13
18
|
|
|
19
|
+
// TODO: Consider use one BatchInterceptor (and not one for intercept and one for record)
|
|
20
|
+
const interceptor = new BatchInterceptor({
|
|
21
|
+
name: 'nock-interceptor',
|
|
22
|
+
interceptors: nodeInterceptors,
|
|
23
|
+
})
|
|
24
|
+
|
|
14
25
|
function getScope(options) {
|
|
15
26
|
const { proto, host, port } = common.normalizeRequestOptions(options)
|
|
16
27
|
return common.normalizeOrigin(proto, host, port)
|
|
@@ -210,158 +221,114 @@ function record(recOptions) {
|
|
|
210
221
|
// we restore any requests that may have been overridden by other parts of nock (e.g. intercept)
|
|
211
222
|
// NOTE: This is hacky as hell but it keeps the backward compatibility *and* allows correct
|
|
212
223
|
// behavior in the face of other modules also overriding ClientRequest.
|
|
213
|
-
common.restoreOverriddenRequests()
|
|
224
|
+
// common.restoreOverriddenRequests()
|
|
214
225
|
// We restore ClientRequest as it messes with recording of modules that also override ClientRequest (e.g. xhr2)
|
|
215
226
|
restoreOverriddenClientRequest()
|
|
216
227
|
|
|
217
228
|
// We override the requests so that we can save information on them before executing.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
229
|
+
interceptor.apply()
|
|
230
|
+
interceptor.on(
|
|
231
|
+
'request',
|
|
232
|
+
async function ({ request: mswRequest, requestId }) {
|
|
233
|
+
const request = mswRequest.clone()
|
|
234
|
+
const { options } = common.normalizeClientRequestArgs(request.url)
|
|
235
|
+
options.method = request.method
|
|
236
|
+
const proto = options.protocol.slice(0, -1)
|
|
237
|
+
|
|
238
|
+
// Node 0.11 https.request calls http.request -- don't want to record things
|
|
239
|
+
// twice.
|
|
240
|
+
/* istanbul ignore if */
|
|
241
|
+
if (options._recording) {
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
options._recording = true
|
|
245
|
+
|
|
246
|
+
const req = new EventEmitter()
|
|
247
|
+
req.on('response', function () {
|
|
248
|
+
debug(thisRecordingId, 'intercepting', proto, 'request to record')
|
|
249
|
+
|
|
250
|
+
// Intercept "res.once('end', ...)"-like event
|
|
251
|
+
interceptor.once(
|
|
252
|
+
'response',
|
|
253
|
+
async function ({ response: mswResponse }) {
|
|
254
|
+
const response = mswResponse.clone()
|
|
255
|
+
debug(thisRecordingId, proto, 'intercepted request ended')
|
|
256
|
+
|
|
257
|
+
let reqheaders
|
|
258
|
+
// Ignore request headers completely unless it was explicitly enabled by the user (see README)
|
|
259
|
+
if (enableReqHeadersRecording) {
|
|
260
|
+
// We never record user-agent headers as they are worse than useless -
|
|
261
|
+
// they actually make testing more difficult without providing any benefit (see README)
|
|
262
|
+
reqheaders = Object.fromEntries(request.headers.entries())
|
|
263
|
+
common.deleteHeadersField(reqheaders, 'user-agent')
|
|
264
|
+
}
|
|
245
265
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
options,
|
|
253
|
-
res,
|
|
254
|
-
dataChunks,
|
|
255
|
-
reqheaders,
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
debug('out:', out)
|
|
259
|
-
|
|
260
|
-
// Check that the request was made during the current recording.
|
|
261
|
-
// If it hasn't then skip it. There is no other simple way to handle
|
|
262
|
-
// this as it depends on the timing of requests and responses. Throwing
|
|
263
|
-
// will make some recordings/unit tests fail randomly depending on how
|
|
264
|
-
// fast/slow the response arrived.
|
|
265
|
-
// If you are seeing this error then you need to make sure that all
|
|
266
|
-
// the requests made during a single recording session finish before
|
|
267
|
-
// ending the same recording session.
|
|
268
|
-
if (thisRecordingId !== currentRecordingId) {
|
|
269
|
-
debug('skipping recording of an out-of-order request', out)
|
|
270
|
-
return
|
|
271
|
-
}
|
|
266
|
+
const headers = Object.fromEntries(response.headers.entries())
|
|
267
|
+
const res = {
|
|
268
|
+
statusCode: response.status,
|
|
269
|
+
headers,
|
|
270
|
+
rawHeaders: headers,
|
|
271
|
+
}
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
const generateFn = outputObjects
|
|
274
|
+
? generateRequestAndResponseObject
|
|
275
|
+
: generateRequestAndResponse
|
|
276
|
+
let out = generateFn({
|
|
277
|
+
req: options,
|
|
278
|
+
bodyChunks: [Buffer.from(await request.arrayBuffer())],
|
|
279
|
+
options,
|
|
280
|
+
res,
|
|
281
|
+
dataChunks: [Buffer.from(await response.arrayBuffer())],
|
|
282
|
+
reqheaders,
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
debug('out:', out)
|
|
286
|
+
|
|
287
|
+
// Check that the request was made during the current recording.
|
|
288
|
+
// If it hasn't then skip it. There is no other simple way to handle
|
|
289
|
+
// this as it depends on the timing of requests and responses. Throwing
|
|
290
|
+
// will make some recordings/unit tests fail randomly depending on how
|
|
291
|
+
// fast/slow the response arrived.
|
|
292
|
+
// If you are seeing this error then you need to make sure that all
|
|
293
|
+
// the requests made during a single recording session finish before
|
|
294
|
+
// ending the same recording session.
|
|
295
|
+
if (thisRecordingId !== currentRecordingId) {
|
|
296
|
+
debug('skipping recording of an out-of-order request', out)
|
|
297
|
+
return
|
|
298
|
+
}
|
|
274
299
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (
|
|
278
|
-
|
|
300
|
+
outputs.push(out)
|
|
301
|
+
|
|
302
|
+
if (!dontPrint) {
|
|
303
|
+
if (useSeparator) {
|
|
304
|
+
if (typeof out !== 'string') {
|
|
305
|
+
out = JSON.stringify(out, null, 2)
|
|
306
|
+
}
|
|
307
|
+
logging(SEPARATOR + out + SEPARATOR)
|
|
308
|
+
} else {
|
|
309
|
+
logging(out)
|
|
310
|
+
}
|
|
279
311
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
logging(out)
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
})
|
|
312
|
+
},
|
|
313
|
+
)
|
|
286
314
|
|
|
287
|
-
|
|
288
|
-
// We need to be aware of changes to the stream's encoding so that we
|
|
289
|
-
// don't accidentally mangle the data.
|
|
290
|
-
const { setEncoding } = res
|
|
291
|
-
res.setEncoding = function (newEncoding) {
|
|
292
|
-
encoding = newEncoding
|
|
293
|
-
return setEncoding.apply(this, arguments)
|
|
294
|
-
}
|
|
315
|
+
debug('finished setting up intercepting')
|
|
295
316
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (
|
|
301
|
-
|
|
302
|
-
data = Buffer.from(data, encoding)
|
|
303
|
-
}
|
|
304
|
-
dataChunks.push(data)
|
|
317
|
+
// We override both the http and the https modules; when we are
|
|
318
|
+
// serializing the request, we need to know which was called.
|
|
319
|
+
// By stuffing the state, we can make sure that nock records
|
|
320
|
+
// the intended protocol.
|
|
321
|
+
if (proto === 'https') {
|
|
322
|
+
options.proto = 'https'
|
|
305
323
|
}
|
|
324
|
+
})
|
|
306
325
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
debug('finished setting up intercepting')
|
|
315
|
-
|
|
316
|
-
// We override both the http and the https modules; when we are
|
|
317
|
-
// serializing the request, we need to know which was called.
|
|
318
|
-
// By stuffing the state, we can make sure that nock records
|
|
319
|
-
// the intended protocol.
|
|
320
|
-
if (proto === 'https') {
|
|
321
|
-
options.proto = 'https'
|
|
322
|
-
}
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
const recordChunk = (chunk, encoding) => {
|
|
326
|
-
debug(thisRecordingId, 'new', proto, 'body chunk')
|
|
327
|
-
if (!Buffer.isBuffer(chunk)) {
|
|
328
|
-
chunk = Buffer.from(chunk, encoding)
|
|
329
|
-
}
|
|
330
|
-
bodyChunks.push(chunk)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const oldWrite = req.write
|
|
334
|
-
req.write = function (chunk, encoding) {
|
|
335
|
-
if (typeof chunk !== 'undefined') {
|
|
336
|
-
recordChunk(chunk, encoding)
|
|
337
|
-
oldWrite.apply(req, arguments)
|
|
338
|
-
} else {
|
|
339
|
-
throw new Error('Data was undefined.')
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Starting in Node 8, `OutgoingMessage.end()` directly calls an internal
|
|
344
|
-
// `write_` function instead of proxying to the public
|
|
345
|
-
// `OutgoingMessage.write()` method, so we have to wrap `end` too.
|
|
346
|
-
const oldEnd = req.end
|
|
347
|
-
req.end = function (chunk, encoding, callback) {
|
|
348
|
-
debug('req.end')
|
|
349
|
-
if (typeof chunk === 'function') {
|
|
350
|
-
callback = chunk
|
|
351
|
-
chunk = null
|
|
352
|
-
} else if (typeof encoding === 'function') {
|
|
353
|
-
callback = encoding
|
|
354
|
-
encoding = null
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (chunk) {
|
|
358
|
-
recordChunk(chunk, encoding)
|
|
359
|
-
}
|
|
360
|
-
oldEnd.call(req, chunk, encoding, callback)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return req
|
|
364
|
-
})
|
|
326
|
+
// This is a massive change, we are trying to change minimum code, so we emit end event here
|
|
327
|
+
// because mswjs take care for these events
|
|
328
|
+
// TODO: refactor the recorder, we no longer need all the listeners and can just record the request we get from MSW
|
|
329
|
+
req.emit('response')
|
|
330
|
+
},
|
|
331
|
+
)
|
|
365
332
|
}
|
|
366
333
|
|
|
367
334
|
// Restore *all* the overridden http/https modules' properties.
|
|
@@ -371,7 +338,7 @@ function restore() {
|
|
|
371
338
|
'restoring all the overridden http/https properties',
|
|
372
339
|
)
|
|
373
340
|
|
|
374
|
-
|
|
341
|
+
interceptor.dispose()
|
|
375
342
|
restoreOverriddenClientRequest()
|
|
376
343
|
recordingInProgress = false
|
|
377
344
|
}
|
package/lib/scope.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @module nock/scope
|
|
5
5
|
*/
|
|
6
|
+
const { scopeDebuglog } = require('./debug')
|
|
6
7
|
const { addInterceptor, isOn } = require('./intercept')
|
|
7
8
|
const common = require('./common')
|
|
8
9
|
const assert = require('assert')
|
|
9
10
|
const url = require('url')
|
|
10
|
-
const debug = require('debug')('nock.scope')
|
|
11
11
|
const { EventEmitter } = require('events')
|
|
12
12
|
const Interceptor = require('./interceptor')
|
|
13
13
|
|
|
@@ -99,7 +99,7 @@ class Scope extends EventEmitter {
|
|
|
99
99
|
logNamespace = this.urlParts.host
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
this.logger =
|
|
102
|
+
this.logger = scopeDebuglog(logNamespace)
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
add(key, interceptor) {
|
package/lib/socket.js
CHANGED
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"testing",
|
|
8
8
|
"isolation"
|
|
9
9
|
],
|
|
10
|
-
"version": "
|
|
10
|
+
"version": "14.0.0-beta.10",
|
|
11
11
|
"author": "Pedro Teixeira <pedro.teixeira@gmail.com>",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"url": "https://github.com/nock/nock/issues"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">= 18"
|
|
21
21
|
},
|
|
22
22
|
"main": "./index.js",
|
|
23
23
|
"types": "types",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"
|
|
25
|
+
"@mswjs/interceptors": "^0.34.0",
|
|
26
26
|
"json-stringify-safe": "^5.0.1",
|
|
27
27
|
"propagate": "^2.0.0"
|
|
28
28
|
},
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"mocha": "^9.1.3",
|
|
46
46
|
"npm-run-all": "^4.1.5",
|
|
47
47
|
"nyc": "^15.0.0",
|
|
48
|
-
"prettier": "3.2.
|
|
48
|
+
"prettier": "3.2.5",
|
|
49
49
|
"proxyquire": "^2.1.0",
|
|
50
50
|
"rimraf": "^3.0.0",
|
|
51
51
|
"semantic-release": "^22.0.5",
|
|
52
|
-
"sinon": "^
|
|
52
|
+
"sinon": "^17.0.1",
|
|
53
53
|
"sinon-chai": "^3.7.0",
|
|
54
54
|
"typescript": "^5.0.4"
|
|
55
55
|
},
|
package/types/index.d.ts
CHANGED
|
@@ -256,13 +256,13 @@ declare namespace nock {
|
|
|
256
256
|
(
|
|
257
257
|
fixtureName: string,
|
|
258
258
|
options: BackOptions,
|
|
259
|
-
nockedFn: (nockDone: () => void) => void,
|
|
259
|
+
nockedFn: (nockDone: () => Promise<void>) => void,
|
|
260
260
|
): void
|
|
261
261
|
(
|
|
262
262
|
fixtureName: string,
|
|
263
263
|
options?: BackOptions,
|
|
264
264
|
): Promise<{
|
|
265
|
-
nockDone: () => void
|
|
265
|
+
nockDone: () => Promise<void>
|
|
266
266
|
context: BackContext
|
|
267
267
|
}>
|
|
268
268
|
}
|