Comparing version
@@ -178,2 +178,43 @@ 'use strict' | ||
const update = { | ||
setup: function () { | ||
recorder.restore() | ||
recorder.clear() | ||
cleanAll() | ||
activate() | ||
disableNetConnect() | ||
}, | ||
start: function (fixture, options) { | ||
if (!fs) { | ||
throw new Error('no fs') | ||
} | ||
const context = removeFixture(fixture) | ||
recorder.record({ | ||
dont_print: true, | ||
output_objects: true, | ||
...options.recorder, | ||
}) | ||
context.isRecording = true | ||
return context | ||
}, | ||
finish: function (fixture, options, context) { | ||
let outputs = recorder.outputs() | ||
if (typeof options.afterRecord === 'function') { | ||
outputs = options.afterRecord(outputs) | ||
} | ||
outputs = | ||
typeof outputs === 'string' ? outputs : JSON.stringify(outputs, null, 4) | ||
debug('recorder outputs:', outputs) | ||
fs.mkdirSync(path.dirname(fixture), { recursive: true }) | ||
fs.writeFileSync(fixture, outputs) | ||
}, | ||
} | ||
const lockdown = { | ||
@@ -219,2 +260,16 @@ setup: function () { | ||
function removeFixture(fixture, options) { | ||
const context = { | ||
scopes: [], | ||
assertScopesFinished: function () {}, | ||
} | ||
if (fixture && fixtureExists(fixture)) { | ||
/* istanbul ignore next - fs.unlinkSync is for node 10 support */ | ||
fs.rmSync ? fs.rmSync(fixture) : fs.unlinkSync(fixture) | ||
} | ||
context.isLoaded = false | ||
return context | ||
} | ||
function applyHook(scopes, fn) { | ||
@@ -263,2 +318,4 @@ if (!fn) { | ||
update, // allow http calls, record all nocks, don't use recorded nocks | ||
lockdown, // use recorded nocks, disables all http calls even when not nocked, doesnt record | ||
@@ -265,0 +322,0 @@ } |
'use strict' | ||
const debug = require('debug')('nock.common') | ||
const set = require('lodash.set') | ||
const isPlainObject = require('lodash/isPlainObject') | ||
const set = require('lodash/set') | ||
const timers = require('timers') | ||
@@ -197,3 +198,3 @@ const url = require('url') | ||
*/ | ||
function headersFieldNamesToLowerCase(headers) { | ||
function headersFieldNamesToLowerCase(headers, throwOnDuplicate) { | ||
if (!isPlainObject(headers)) { | ||
@@ -207,5 +208,11 @@ throw Error('Headers must be provided as an object') | ||
if (lowerCaseHeaders[key] !== undefined) { | ||
throw Error( | ||
`Failed to convert header keys to lower case due to field name conflict: ${key}` | ||
) | ||
if (throwOnDuplicate) { | ||
throw Error( | ||
`Failed to convert header keys to lower case due to field name conflict: ${key}` | ||
) | ||
} else { | ||
debug( | ||
`Duplicate header provided in request: ${key}. Only the last value can be matched.` | ||
) | ||
} | ||
} | ||
@@ -421,5 +428,8 @@ lowerCaseHeaders[key] = fieldValue | ||
return pattern instanceof RegExp | ||
? pattern.test(targetStr) | ||
: targetStr === String(pattern) | ||
if (pattern instanceof RegExp) { | ||
// if the regexp happens to have a global flag, we want to ensure we test the entire target | ||
pattern.lastIndex = 0 | ||
return pattern.test(targetStr) | ||
} | ||
return targetStr === String(pattern) | ||
} | ||
@@ -550,3 +560,3 @@ | ||
* | ||
* Performs a general recursive strict comparision with two caveats: | ||
* Performs a general recursive strict comparison with two caveats: | ||
* - The expected data can use regexp to compare values | ||
@@ -606,47 +616,2 @@ * - JSON path notation and nested objects are considered equal | ||
/** | ||
* Checks if `value` is a plain object, that is, an object created by the | ||
* `Object` constructor or one with a `[[Prototype]]` of `null`. | ||
* https://github.com/lodash/lodash/blob/588bf3e20db0ae039a822a14a8fa238c5b298e65/isPlainObject.js | ||
* | ||
* @param {*} value The value to check. | ||
* @return {boolean} | ||
*/ | ||
function isPlainObject(value) { | ||
const isObjectLike = typeof value === 'object' && value !== null | ||
const tag = Object.prototype.toString.call(value) | ||
if (!isObjectLike || tag !== '[object Object]') { | ||
return false | ||
} | ||
if (Object.getPrototypeOf(value) === null) { | ||
return true | ||
} | ||
let proto = value | ||
while (Object.getPrototypeOf(proto) !== null) { | ||
proto = Object.getPrototypeOf(proto) | ||
} | ||
return Object.getPrototypeOf(value) === proto | ||
} | ||
/** | ||
* Creates an object with the same keys as `object` and values generated | ||
* by running each own enumerable string keyed property of `object` thru | ||
* `iteratee`. (iteration order is not guaranteed) | ||
* The iteratee is invoked with three arguments: (value, key, object). | ||
* https://github.com/lodash/lodash/blob/588bf3e20db0ae039a822a14a8fa238c5b298e65/mapValue.js | ||
* | ||
* @param {Object} object The object to iterate over. | ||
* @param {Function} iteratee The function invoked per iteration. | ||
* @returns {Object} Returns the new mapped object. | ||
*/ | ||
function mapValue(object, iteratee) { | ||
object = Object(object) | ||
const result = {} | ||
Object.keys(object).forEach(key => { | ||
result[key] = iteratee(object[key], key, object) | ||
}) | ||
return result | ||
} | ||
const timeouts = [] | ||
@@ -656,7 +621,9 @@ const intervals = [] | ||
const wrapTimer = (timer, ids) => (...args) => { | ||
const id = timer(...args) | ||
ids.push(id) | ||
return id | ||
} | ||
const wrapTimer = | ||
(timer, ids) => | ||
(...args) => { | ||
const id = timer(...args) | ||
ids.push(id) | ||
return id | ||
} | ||
@@ -679,2 +646,30 @@ const setTimeout = wrapTimer(timers.setTimeout, timeouts) | ||
/** | ||
* Check if the Client Request has been cancelled. | ||
* | ||
* Until Node 14 is the minimum, we need to look at both flags to see if the request has been cancelled. | ||
* The two flags have the same purpose, but the Node maintainers are migrating from `abort(ed)` to | ||
* `destroy(ed)` terminology, to be more consistent with `stream.Writable`. | ||
* In Node 14.x+, Calling `abort()` will set both `aborted` and `destroyed` to true, however, | ||
* calling `destroy()` will only set `destroyed` to true. | ||
* Falling back on checking if the socket is destroyed to cover the case of Node <14.x where | ||
* `destroy()` is called, but `destroyed` is undefined. | ||
* | ||
* Node Client Request history: | ||
* - `request.abort()`: Added in: v0.3.8, Deprecated since: v14.1.0, v13.14.0 | ||
* - `request.aborted`: Added in: v0.11.14, Became a boolean instead of a timestamp: v11.0.0, Not deprecated (yet) | ||
* - `request.destroy()`: Added in: v0.3.0 | ||
* - `request.destroyed`: Added in: v14.1.0, v13.14.0 | ||
* | ||
* @param {ClientRequest} req | ||
* @returns {boolean} | ||
*/ | ||
function isRequestDestroyed(req) { | ||
return !!( | ||
req.destroyed === true || | ||
req.aborted || | ||
(req.socket && req.socket.destroyed) | ||
) | ||
} | ||
module.exports = { | ||
@@ -693,5 +688,5 @@ contentEncoding, | ||
isPlainObject, | ||
isRequestDestroyed, | ||
isStream, | ||
isUtf8Representable, | ||
mapValue, | ||
matchStringOrRegexp, | ||
@@ -698,0 +693,0 @@ normalizeClientRequestArgs, |
@@ -43,3 +43,6 @@ 'use strict' | ||
// We use lower-case header field names throughout Nock. | ||
headers: common.headersFieldNamesToLowerCase(options.headers || {}), | ||
headers: common.headersFieldNamesToLowerCase( | ||
options.headers || {}, | ||
false | ||
), | ||
} | ||
@@ -52,4 +55,10 @@ this.interceptors = interceptors | ||
// https://nodejs.org/docs/latest-v12.x/api/http.html#http_http_request_url_options_callback | ||
if (options.timeout) { | ||
this.socket.setTimeout(options.timeout) | ||
// any timeout in the request options override any timeout in the agent options. | ||
// per https://github.com/nodejs/node/pull/21204 | ||
const timeout = | ||
options.timeout || | ||
(options.agent && options.agent.options && options.agent.options.timeout) | ||
if (timeout) { | ||
this.socket.setTimeout(timeout) | ||
} | ||
@@ -108,4 +117,3 @@ | ||
// Until Node 14 is the minimum, we need to look at both flags to see if the request has been cancelled. | ||
if (req.destroyed || req.aborted) { | ||
if (common.isRequestDestroyed(req)) { | ||
return | ||
@@ -141,4 +149,7 @@ } | ||
// However, actually implementation checks the state of finished and aborted before checking if the first arg is empty. | ||
handleWrite(buffer, encoding, callback) { | ||
handleWrite(...args) { | ||
debug('request write') | ||
let [buffer, encoding] = args | ||
const { req } = this | ||
@@ -161,3 +172,3 @@ | ||
if (!buffer || buffer.length === 0) { | ||
if (!buffer) { | ||
return true | ||
@@ -171,2 +182,5 @@ } | ||
// writable.write encoding param is optional | ||
// so if callback is present it's the last argument | ||
const callback = args.length > 1 ? args[args.length - 1] : undefined | ||
// can't use instanceof Function because some test runners | ||
@@ -204,3 +218,5 @@ // run tests in vm.runInNewContext where Function is not same | ||
req.write(chunk, encoding) | ||
if (chunk) { | ||
req.write(chunk, encoding) | ||
} | ||
req.finished = true | ||
@@ -258,4 +274,3 @@ this.maybeStartPlayback() | ||
// Until Node 14 is the minimum, we need to look at both flags to see if the request has been cancelled. | ||
if (!req.destroyed && !req.aborted && !playbackStarted) { | ||
if (!common.isRequestDestroyed(req) && !playbackStarted) { | ||
this.startPlayback() | ||
@@ -290,5 +305,4 @@ } | ||
// representation. | ||
const requestBodyIsUtf8Representable = common.isUtf8Representable( | ||
requestBodyBuffer | ||
) | ||
const requestBodyIsUtf8Representable = | ||
common.isUtf8Representable(requestBodyBuffer) | ||
const requestBodyString = requestBodyBuffer.toString( | ||
@@ -295,0 +309,0 @@ requestBodyIsUtf8Representable ? 'utf8' : 'hex' |
@@ -35,4 +35,5 @@ 'use strict' | ||
if ( | ||
uriIsStr && | ||
!scope.scopeOptions.filteringScope && | ||
uriIsStr && | ||
!scope.basePathname && | ||
!uri.startsWith('/') && | ||
@@ -69,3 +70,4 @@ !uri.startsWith('*') | ||
this.reqheaders = common.headersFieldNamesToLowerCase( | ||
scope.scopeOptions.reqheaders || {} | ||
scope.scopeOptions.reqheaders || {}, | ||
true | ||
) | ||
@@ -159,3 +161,3 @@ this.badheaders = common.headersFieldsArrayToLowerCase( | ||
// If the content is not encoded we may need to transform the response body. | ||
// Otherwise we leave it as it is. | ||
// Otherwise, we leave it as it is. | ||
if ( | ||
@@ -178,6 +180,10 @@ body && | ||
} | ||
} | ||
if (this.scope.contentLen) { | ||
// https://tools.ietf.org/html/rfc7230#section-3.3.2 | ||
if (this.scope.contentLen) { | ||
// https://tools.ietf.org/html/rfc7230#section-3.3.2 | ||
if (typeof body === 'string') { | ||
this.rawHeaders.push('Content-Length', body.length) | ||
} else if (Buffer.isBuffer(body)) { | ||
this.rawHeaders.push('Content-Length', body.byteLength) | ||
} | ||
@@ -418,2 +424,8 @@ } | ||
matchHostName(options) { | ||
const { basePath } = this.scope | ||
if (basePath instanceof RegExp) { | ||
return basePath.test(options.hostname) | ||
} | ||
return options.hostname === this.scope.urlParts.hostname | ||
@@ -449,5 +461,7 @@ } | ||
remove(this) | ||
if ((this.scope.shouldPersist() || this.counter > 0) && this.filePath) { | ||
if ( | ||
(this.scope.shouldPersist() || this.counter > 0) && | ||
this.interceptionCounter > 1 && | ||
this.filePath | ||
) { | ||
this.body = fs.createReadStream(this.filePath) | ||
@@ -457,2 +471,4 @@ this.body.pause() | ||
remove(this) | ||
if (!this.scope.shouldPersist() && this.counter < 1) { | ||
@@ -459,0 +475,0 @@ this.scope.remove(this._key, this) |
'use strict' | ||
const mapValues = require('lodash/mapValues') | ||
const querystring = require('querystring') | ||
@@ -46,3 +47,3 @@ | ||
// strip line endings from both so that we get a match no matter what OS we are running on | ||
// if Content-Type does not contains 'multipart' | ||
// if Content-Type does not contain 'multipart' | ||
if (!isMultipart && typeof body === 'string') { | ||
@@ -56,4 +57,4 @@ body = body.replace(/\r?\n|\r/g, '') | ||
// Because the nature of URL encoding, all the values in the body have been cast to strings. | ||
// dataEqual does strict checking so we we have to cast the non-regexp values in the spec too. | ||
// Because the nature of URL encoding, all the values in the body must be cast to strings. | ||
// dataEqual does strict checking, so we have to cast the non-regexp values in the spec too. | ||
if (isUrlencoded) { | ||
@@ -75,5 +76,5 @@ spec = mapValuesDeep(spec, val => (val instanceof RegExp ? val : `${val}`)) | ||
if (common.isPlainObject(obj)) { | ||
return common.mapValue(obj, v => mapValuesDeep(v, cb)) | ||
return mapValues(obj, v => mapValuesDeep(v, cb)) | ||
} | ||
return cb(obj) | ||
} |
@@ -82,3 +82,3 @@ 'use strict' | ||
_read(size) { | ||
_read(_size) { | ||
while (this.buffers.length) { | ||
@@ -297,3 +297,3 @@ if (!this.push(this.buffers.shift())) { | ||
function respond() { | ||
if (req.aborted) { | ||
if (common.isRequestDestroyed(req)) { | ||
return | ||
@@ -320,6 +320,6 @@ } | ||
// during a single microtask execution. This setImmediate stalls the playback to ensure the | ||
// correct events are emitted first ('socket', 'finish') and any aborts in the in the queue or | ||
// correct events are emitted first ('socket', 'finish') and any aborts in the queue or | ||
// called during a 'finish' listener can be called. | ||
common.setImmediate(() => { | ||
if (!req.aborted) { | ||
if (!common.isRequestDestroyed(req)) { | ||
start() | ||
@@ -326,0 +326,0 @@ } |
@@ -113,2 +113,6 @@ 'use strict' | ||
} | ||
// Escape any single quotes in the path as the output uses them | ||
path = path.replace(/'/g, `\\'`) | ||
// Always encode the query parameters when recording. | ||
@@ -168,3 +172,3 @@ const encodedQueryObj = {} | ||
enable_reqheaders_recording: false, | ||
logging: console.log, | ||
logging: console.log, // eslint-disable-line no-console | ||
output_objects: false, | ||
@@ -310,4 +314,2 @@ use_separator: true, | ||
callback(res, options, callback) | ||
} else { | ||
res.resume() | ||
} | ||
@@ -314,0 +316,0 @@ |
@@ -14,2 +14,3 @@ 'use strict' | ||
const { URL, Url: LegacyUrl } = url | ||
let fs | ||
@@ -24,3 +25,42 @@ | ||
/** | ||
* @param {string|RegExp|url.url} basePath | ||
* Normalizes the passed url for consistent internal processing | ||
* @param {string|LegacyUrl|URL} u | ||
*/ | ||
function normalizeUrl(u) { | ||
if (!(u instanceof URL)) { | ||
if (u instanceof LegacyUrl) { | ||
return normalizeUrl(new URL(url.format(u))) | ||
} | ||
// If the url is invalid, let the URL library report it | ||
return normalizeUrl(new URL(u)) | ||
} | ||
if (!/https?:/.test(u.protocol)) { | ||
throw new TypeError( | ||
`Protocol '${u.protocol}' not recognized. This commonly occurs when a hostname and port are included without a protocol, producing a URL that is valid but confusing, and probably not what you want.` | ||
) | ||
} | ||
return { | ||
href: u.href, | ||
origin: u.origin, | ||
protocol: u.protocol, | ||
username: u.username, | ||
password: u.password, | ||
host: u.host, | ||
hostname: | ||
// strip brackets from IPv6 | ||
typeof u.hostname === 'string' && u.hostname.startsWith('[') | ||
? u.hostname.slice(1, -1) | ||
: u.hostname, | ||
port: u.port || (u.protocol === 'http:' ? 80 : 443), | ||
pathname: u.pathname, | ||
search: u.search, | ||
searchParams: u.searchParams, | ||
hash: u.hash, | ||
} | ||
} | ||
/** | ||
* @param {string|RegExp|LegacyUrl|URL} basePath | ||
* @param {Object} options | ||
@@ -57,5 +97,4 @@ * @param {boolean} options.allowUnmocked | ||
if (!(basePath instanceof RegExp)) { | ||
this.urlParts = url.parse(basePath) | ||
this.port = | ||
this.urlParts.port || (this.urlParts.protocol === 'http:' ? 80 : 443) | ||
this.urlParts = normalizeUrl(basePath) | ||
this.port = this.urlParts.port | ||
this.basePathname = this.urlParts.pathname.replace(/\/$/, '') | ||
@@ -337,3 +376,3 @@ this.basePath = `${this.urlParts.protocol}//${this.urlParts.hostname}:${this.port}` | ||
response = '' | ||
// TODO: Rename `responseIsBinary` to `reponseIsUtf8Representable`. | ||
// TODO: Rename `responseIsBinary` to `responseIsUtf8Representable`. | ||
} else if (nockDef.responseIsBinary) { | ||
@@ -340,0 +379,0 @@ response = Buffer.from(nockDef.response, 'hex') |
@@ -10,5 +10,8 @@ 'use strict' | ||
// Pretend this is a TLSSocket | ||
if (options.proto === 'https') { | ||
// https://github.com/nock/nock/issues/158 | ||
this.authorized = true | ||
// https://github.com/nock/nock/issues/2147 | ||
this.encrypted = true | ||
} | ||
@@ -41,2 +44,3 @@ | ||
unref() {} | ||
write() {} | ||
@@ -94,2 +98,3 @@ address() { | ||
this.readable = this.writable = false | ||
this.readableEnded = this.writableFinished = true | ||
@@ -96,0 +101,0 @@ process.nextTick(() => { |
@@ -10,3 +10,3 @@ { | ||
], | ||
"version": "13.0.4", | ||
"version": "13.3.1", | ||
"author": "Pedro Teixeira <pedro.teixeira@gmail.com>", | ||
@@ -18,3 +18,3 @@ "repository": { | ||
"bugs": { | ||
"url": "http://github.com/nock/nock/issues" | ||
"url": "https://github.com/nock/nock/issues" | ||
}, | ||
@@ -29,33 +29,30 @@ "engines": { | ||
"json-stringify-safe": "^5.0.1", | ||
"lodash.set": "^4.3.2", | ||
"lodash": "^4.17.21", | ||
"propagate": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"@sinonjs/fake-timers": "^6.0.0", | ||
"@definitelytyped/dtslint": "^0.0.159", | ||
"@sinonjs/fake-timers": "^10.0.0", | ||
"assert-rejects": "^1.0.0", | ||
"chai": "^4.1.2", | ||
"dirty-chai": "^2.0.1", | ||
"dtslint": "^3.0.0", | ||
"eslint": "^7.3.1", | ||
"eslint-config-prettier": "^6.0.0", | ||
"eslint-config-standard": "^14.0.0", | ||
"eslint": "^8.8.0", | ||
"eslint-config-prettier": "^8.1.0", | ||
"eslint-config-standard": "^17.0.0-0", | ||
"eslint-plugin-import": "^2.16.0", | ||
"eslint-plugin-mocha": "^7.0.1", | ||
"eslint-plugin-mocha": "^10.0.3", | ||
"eslint-plugin-node": "^11.0.0", | ||
"eslint-plugin-promise": "^4.1.1", | ||
"eslint-plugin-standard": "^4.0.0", | ||
"form-data": "^3.0.0", | ||
"eslint-plugin-promise": "^6.0.0", | ||
"form-data": "^4.0.0", | ||
"got": "^11.3.0", | ||
"mocha": "^8.0.1", | ||
"mocha": "^9.1.3", | ||
"npm-run-all": "^4.1.5", | ||
"nyc": "^15.0.0", | ||
"prettier": "2.0.5", | ||
"prettier": "2.7.1", | ||
"proxyquire": "^2.1.0", | ||
"request": "^2.83.0", | ||
"rimraf": "^3.0.0", | ||
"semantic-release": "^17.0.2", | ||
"sinon": "^9.0.0", | ||
"sinon-chai": "^3.3.0", | ||
"superagent": "^5.0.2", | ||
"tap": "14.6.1" | ||
"semantic-release": "^19.0.2", | ||
"sinon": "^15.0.1", | ||
"sinon-chai": "^3.7.0", | ||
"typescript": "^4.2.2" | ||
}, | ||
@@ -68,17 +65,6 @@ "scripts": { | ||
"lint:js:fix": "eslint --cache --cache-location './.cache/eslint' --fix '**/*.js'", | ||
"lint:ts": "dtslint types", | ||
"test": "run-s test:mocha test:tap", | ||
"test:coverage": "tap --coverage-report=html && open coverage/lcov-report/index.html", | ||
"test:mocha": "nyc mocha $(grep -lr '^\\s*it(' tests)", | ||
"test:tap": "tap --100 --coverage --coverage-report=text ./tests/test_*.js" | ||
"lint:ts": "dtslint --expectOnly types", | ||
"test": "nyc --reporter=lcov --reporter=text mocha tests", | ||
"test:coverage": "open coverage/lcov-report/index.html" | ||
}, | ||
"nyc": { | ||
"reporter": [ | ||
"lcov", | ||
"text-summary" | ||
], | ||
"exclude": [ | ||
"tests/" | ||
] | ||
}, | ||
"license": "MIT", | ||
@@ -85,0 +71,0 @@ "files": [ |
150
README.md
@@ -6,3 +6,2 @@ # Nock | ||
 | ||
 | ||
[](#backers) | ||
@@ -90,3 +89,3 @@ [](#sponsors) | ||
- [Example](#example) | ||
- [Modes](#modes) | ||
- [Modes](#modes) | ||
- [Common issues](#common-issues) | ||
@@ -165,3 +164,3 @@ - [Axios](#axios) | ||
The request hostname can be a string or a RegExp. | ||
The request hostname can be a string, URL, or a RegExp. | ||
@@ -175,2 +174,8 @@ ```js | ||
```js | ||
const scope = nock(new URL('http://www.example.com')) | ||
.get('/resource') | ||
.reply(200, 'domain matched') | ||
``` | ||
```js | ||
const scope = nock(/example\.com/) | ||
@@ -383,3 +388,3 @@ .get('/resource') | ||
code from the array would take precedence over the one passed directly to | ||
reply.) This is no longer allowed. In 12.x, either call `.reply()` with a | ||
reply.) This is no longer allowed. In Nock 12 and later, either call `.reply()` with a | ||
status code and a function that returns the body, or call it with a single | ||
@@ -642,3 +647,2 @@ argument: a function that returns an array containing both the status code and | ||
const scope = nock('http://my.server.com:8081') | ||
... | ||
``` | ||
@@ -650,2 +654,4 @@ | ||
**NOTE:** When request times is more than the number you specified, you will get an error before cleaning this interceptor. | ||
```js | ||
@@ -658,3 +664,11 @@ nock('http://zombo.com').get('/').times(4).reply(200, 'Ok') | ||
http.get('http://zombo.com/') // respond body "Ok" | ||
http.get('http://zombo.com/') // respond with zombo.com result | ||
// This code will get an error with message: | ||
// Nock: No match for request | ||
http.get('http://zombo.com/') | ||
// clean your interceptor | ||
nock.cleanAll() | ||
http.get('http://zombo.com/') // real respond with zombo.com result | ||
``` | ||
@@ -849,2 +863,3 @@ | ||
const scope = nock('http://api.myservice.com') | ||
// Interceptors created after here will only match when the header `accept` equals `application/json`. | ||
.matchHeader('accept', 'application/json') | ||
@@ -855,2 +870,14 @@ .get('/') | ||
}) | ||
.get('/') | ||
// Only this interceptor will match the header value `x-my-action` with `MyFirstAction` | ||
.matchHeader('x-my-action', 'MyFirstAction') | ||
.reply(200, { | ||
data: 'FirstActionResponse', | ||
}) | ||
.get('/') | ||
// Only this interceptor will match the header value `x-my-action` with `MySecondAction` | ||
.matchHeader('x-my-action', 'MySecondAction') | ||
.reply(200, { | ||
data: 'SecondActionResponse', | ||
}) | ||
``` | ||
@@ -1077,3 +1104,3 @@ | ||
```js | ||
```shell script | ||
$ NOCK_OFF=true node my_test.js | ||
@@ -1228,9 +1255,8 @@ ``` | ||
const recordedTimestamp = recordedBodyResult[1] | ||
return body.replace(/(timestamp):([0-9]+)/g, function ( | ||
match, | ||
key, | ||
value | ||
) { | ||
return key + ':' + recordedTimestamp | ||
}) | ||
return body.replace( | ||
/(timestamp):([0-9]+)/g, | ||
function (match, key, value) { | ||
return key + ':' + recordedTimestamp | ||
} | ||
) | ||
} else { | ||
@@ -1319,6 +1345,6 @@ return body | ||
nock.removeInterceptor({ | ||
hostname : 'localhost', | ||
path : '/login' | ||
method: 'POST' | ||
proto : 'https' | ||
hostname: 'localhost', | ||
path: '/login', | ||
method: 'POST', | ||
proto: 'https', | ||
}) | ||
@@ -1332,2 +1358,14 @@ ``` | ||
**Note** `.reply(...)` method returns Scope, not Interceptor, and so it is not a valid argument for `nock.removeInterceptor`. So if your method chain ends with `.reply` to be used with `nock.removeInterceptor` the chain need to be break in between: | ||
```js | ||
// this will NOT work | ||
const interceptor = nock('http://example.org').get('somePath').reply(200, 'OK') | ||
nock.removeInterceptor(interceptor) | ||
// this is how it should be | ||
const interceptor = nock('http://example.org').get('somePath') | ||
interceptor.reply(200, 'OK') | ||
nock.removeInterceptor(interceptor) | ||
``` | ||
## Events | ||
@@ -1406,10 +1444,16 @@ | ||
```js | ||
return nockBack('promisedFixture.json') | ||
.then(({ nockDone, context }) => { | ||
// do your tests returning a promise and chain it with | ||
// `.then(nockDone)` | ||
}) | ||
return nockBack('promisedFixture.json').then(({ nockDone, context }) => { | ||
// do your tests returning a promise and chain it with | ||
// `.then(nockDone)` | ||
}) | ||
``` | ||
Or, with async/await: | ||
```js | ||
const { nockDone, context } = await nockBack('promisedFixture.json') | ||
// your test code | ||
nockDone() | ||
``` | ||
#### Options | ||
@@ -1429,3 +1473,3 @@ | ||
scope.filteringRequestBody = (body, aRecordedBody) => { | ||
if (typeof(body) !== 'string' || typeof(aRecordedBody) !== 'string') { | ||
if (typeof body !== 'string' || typeof aRecordedBody !== 'string') { | ||
return body | ||
@@ -1447,11 +1491,11 @@ } | ||
nockBack('zomboFixture.json', { before: prepareScope }, nockDone => { | ||
request.get('http://zombo.com', function(err, res, body) { | ||
nockBack('exampleFixture.json', { before: prepareScope }, nockDone => { | ||
request.get('http://example.com', function (err, res, body) { | ||
// do your tests | ||
nockDone() | ||
} | ||
} | ||
}) | ||
}) | ||
``` | ||
#### Modes | ||
### Modes | ||
@@ -1466,2 +1510,4 @@ To set the mode call `nockBack.setMode(mode)` or run the tests with the `NOCK_BACK_MODE` environment variable set before loading nock. If the mode needs to be changed programmatically, the following is valid: `nockBack.setMode(nockBack.currentMode)` | ||
- update: remove recorded nocks, record nocks | ||
- lockdown: use recorded nocks, disables all http calls even when not nocked, doesn't record | ||
@@ -1514,6 +1560,6 @@ | ||
// References: | ||
// https://github.com/nock/nock/issues/699#issuecomment-272708264 | ||
// https://github.com/axios/axios/issues/305 | ||
axios.defaults.adapter = require('axios/lib/adapters/http') | ||
// https://github.com/axios/axios/pull/5277 | ||
axios.defaults.adapter = 'http' | ||
test('can fetch test response', async t => { | ||
@@ -1535,2 +1581,14 @@ // Set up the mock request. | ||
For Nock + Axios + Jest to work, you'll have to also adapt your jest.config.js, like so: | ||
```js | ||
const config = { | ||
moduleNameMapper: { | ||
// Force CommonJS build for http adapter to be available. | ||
// via https://github.com/axios/axios/issues/5101#issuecomment-1276572468 | ||
'^axios$': require.resolve('axios'), | ||
}, | ||
} | ||
``` | ||
[axios]: https://github.com/axios/axios | ||
@@ -1583,7 +1641,27 @@ | ||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
<!-- prettier-ignore --> | ||
| [<img src="https://avatars1.githubusercontent.com/u/47910?v=4" width="100px;" alt="Pedro Teixeira"/><br /><sub><b>Pedro Teixeira</b></sub>](http://pgte.me)<br />[π»](https://github.com/nock/nock/commits?author=pgte "Code") [π§](#maintenance-pgte "Maintenance") | [<img src="https://avatars3.githubusercontent.com/u/10771967?v=4" width="100px;" alt="n30n0v"/><br /><sub><b>n30n0v</b></sub>](https://github.com/n30n0v)<br />[π»](https://github.com/nock/nock/commits?author=n30n0v "Code") | [<img src="https://avatars3.githubusercontent.com/u/910753?v=4" width="100px;" alt="Richard Littauer"/><br /><sub><b>Richard Littauer</b></sub>](https://burntfen.com)<br />[π§](#maintenance-RichardLitt "Maintenance") [π»](https://github.com/nock/nock/commits?author=RichardLitt "Code") [π](#blog-RichardLitt "Blogposts") | [<img src="https://avatars1.githubusercontent.com/u/3731165?v=4" width="100px;" alt="Ian Walker-Sperber"/><br /><sub><b>Ian Walker-Sperber</b></sub>](http://ianwsperber.com)<br />[π»](https://github.com/nock/nock/commits?author=ianwsperber "Code") | [<img src="https://avatars2.githubusercontent.com/u/1505203?v=4" width="100px;" alt="Ivan Erceg"/><br /><sub><b>Ivan Erceg</b></sub>](http://ilovacha.com)<br />[π»](https://github.com/nock/nock/commits?author=ierceg "Code") [π§](#maintenance-ierceg "Maintenance") | [<img src="https://avatars2.githubusercontent.com/u/1487036?v=4" width="100px;" alt="Paul Melnikow"/><br /><sub><b>Paul Melnikow</b></sub>](https://twitter.com/paulmelnikow)<br />[π»](https://github.com/nock/nock/commits?author=paulmelnikow "Code") [π§](#maintenance-paulmelnikow "Maintenance") | [<img src="https://avatars3.githubusercontent.com/u/39992?v=4" width="100px;" alt="Gregor Martynus"/><br /><sub><b>Gregor Martynus</b></sub>](https://twitter.com/gr2m)<br />[π»](https://github.com/nock/nock/commits?author=gr2m "Code") [π§](#maintenance-gr2m "Maintenance") [πΌ](#business-gr2m "Business development") [π΅](#financial-gr2m "Financial") [π](#blog-gr2m "Blogposts") | | ||
| :---: | :---: | :---: | :---: | :---: | :---: | :---: | | ||
| [<img src="https://avatars1.githubusercontent.com/u/6701030?v=4" width="100px;" alt="Hutson Betts"/><br /><sub><b>Hutson Betts</b></sub>](https://gitlab.com/hutson)<br />[π΅](#financial-hutson "Financial") | [<img src="https://avatars2.githubusercontent.com/u/6105119?v=4" width="100px;" alt="Jonas Lilja"/><br /><sub><b>Jonas Lilja</b></sub>](http://lilja.io)<br />[π΅](#financial-jlilja "Financial") [π»](https://github.com/nock/nock/commits?author=jlilja "Code") | [<img src="https://avatars0.githubusercontent.com/u/4446950?v=4" width="100px;" alt="Benjamin Ki"/><br /><sub><b>Benjamin Ki</b></sub>](https://github.com/benrki)<br />[π΅](#financial-benrki "Financial") | [<img src="https://avatars2.githubusercontent.com/u/3250463?v=4" width="100px;" alt="Chad Fawcett"/><br /><sub><b>Chad Fawcett</b></sub>](http://chadf.ca)<br />[π΅](#financial-chadfawcett "Financial") | | ||
<!-- prettier-ignore-start --> | ||
<!-- markdownlint-disable --> | ||
<table> | ||
<tr> | ||
<td align="center"><a href="http://pgte.me"><img src="https://avatars1.githubusercontent.com/u/47910?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pedro Teixeira</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=pgte" title="Code">π»</a> <a href="#maintenance-pgte" title="Maintenance">π§</a></td> | ||
<td align="center"><a href="https://github.com/n30n0v"><img src="https://avatars3.githubusercontent.com/u/10771967?v=4?s=100" width="100px;" alt=""/><br /><sub><b>n30n0v</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=n30n0v" title="Code">π»</a></td> | ||
<td align="center"><a href="https://burntfen.com"><img src="https://avatars3.githubusercontent.com/u/910753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Littauer</b></sub></a><br /><a href="#maintenance-RichardLitt" title="Maintenance">π§</a> <a href="https://github.com/nock/nock/commits?author=RichardLitt" title="Code">π»</a> <a href="#blog-RichardLitt" title="Blogposts">π</a></td> | ||
<td align="center"><a href="http://ianwsperber.com"><img src="https://avatars1.githubusercontent.com/u/3731165?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ian Walker-Sperber</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=ianwsperber" title="Code">π»</a></td> | ||
<td align="center"><a href="http://ilovacha.com"><img src="https://avatars2.githubusercontent.com/u/1505203?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ivan Erceg</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=ierceg" title="Code">π»</a> <a href="#maintenance-ierceg" title="Maintenance">π§</a></td> | ||
<td align="center"><a href="https://twitter.com/paulmelnikow"><img src="https://avatars2.githubusercontent.com/u/1487036?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul Melnikow</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=paulmelnikow" title="Code">π»</a> <a href="#maintenance-paulmelnikow" title="Maintenance">π§</a></td> | ||
<td align="center"><a href="https://twitter.com/gr2m"><img src="https://avatars3.githubusercontent.com/u/39992?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gregor Martynus</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=gr2m" title="Code">π»</a> <a href="#maintenance-gr2m" title="Maintenance">π§</a> <a href="#business-gr2m" title="Business development">πΌ</a> <a href="#financial-gr2m" title="Financial">π΅</a> <a href="#blog-gr2m" title="Blogposts">π</a></td> | ||
</tr> | ||
<tr> | ||
<td align="center"><a href="https://gitlab.com/hutson"><img src="https://avatars1.githubusercontent.com/u/6701030?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hutson Betts</b></sub></a><br /><a href="#financial-hutson" title="Financial">π΅</a></td> | ||
<td align="center"><a href="http://lilja.io"><img src="https://avatars2.githubusercontent.com/u/6105119?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonas Lilja</b></sub></a><br /><a href="#financial-jlilja" title="Financial">π΅</a> <a href="https://github.com/nock/nock/commits?author=jlilja" title="Code">π»</a></td> | ||
<td align="center"><a href="https://github.com/benrki"><img src="https://avatars0.githubusercontent.com/u/4446950?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benjamin Ki</b></sub></a><br /><a href="#financial-benrki" title="Financial">π΅</a></td> | ||
<td align="center"><a href="http://chadf.ca"><img src="https://avatars2.githubusercontent.com/u/3250463?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chad Fawcett</b></sub></a><br /><a href="#financial-chadfawcett" title="Financial">π΅</a></td> | ||
<td align="center"><a href="http://www.laurencemyers.com.au"><img src="https://avatars.githubusercontent.com/u/6336048?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Laurence Dougal Myers</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=laurence-myers" title="Code">π»</a></td> | ||
<td align="center"><a href="https://github.com/Beretta1979"><img src="https://avatars.githubusercontent.com/u/10073962?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SΓ©bastien Van Bruaene</b></sub></a><br /><a href="https://github.com/nock/nock/commits?author=Beretta1979" title="Code">π»</a> <a href="https://github.com/nock/nock/commits?author=Beretta1979" title="Tests">β οΈ</a></td> | ||
</tr> | ||
</table> | ||
<!-- markdownlint-restore --> | ||
<!-- prettier-ignore-end --> | ||
<!-- ALL-CONTRIBUTORS-LIST:END --> | ||
@@ -1590,0 +1668,0 @@ |
@@ -55,3 +55,3 @@ // TypeScript Version: 3.5 | ||
| DataMatcherMap | ||
interface DataMatcherArray extends Array<DataMatcher> {} | ||
interface DataMatcherArray extends ReadonlyArray<DataMatcher> {} | ||
interface DataMatcherMap { | ||
@@ -202,3 +202,3 @@ [key: string]: DataMatcher | ||
thrice(): this | ||
optionally(): this | ||
optionally(flag?: boolean): this | ||
@@ -233,4 +233,4 @@ delay(opts: number | { head?: number; body?: number }): this | ||
interface Definition { | ||
scope: string | ||
path: string | ||
scope: string | RegExp | ||
path: string | RegExp | ||
port?: number | string | ||
@@ -246,3 +246,3 @@ method?: string | ||
type BackMode = 'wild' | 'dryrun' | 'record' | 'lockdown' | ||
type BackMode = 'wild' | 'dryrun' | 'record' | 'update' | 'lockdown' | ||
@@ -249,0 +249,0 @@ interface Back { |
179686
3.43%24
-11.11%3505
3.24%1670
4.9%16
-5.88%+ Added
+ Added
- Removed
- Removed