Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/clarify interceptor.reply param juggling. #1520

Merged
merged 21 commits into from May 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -360,6 +360,14 @@ const scope = nock('http://www.google.com')
.reply(201, (uri, requestBody) => requestBody)
```

In Nock 11.x it was possible to invoke `.reply()` with a status code and a
function that returns an array containing a status code and body. (The status
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
status code and a function that returns the body, or call it with a single
argument: a function that returns an array containing both the status code and
body.

An asynchronous function that gets an error-first callback as its last argument also works:

```js
Expand Down
8 changes: 4 additions & 4 deletions lib/common.js
Expand Up @@ -40,7 +40,7 @@ const normalizeRequestOptions = function(options) {
* from its utf8 representation.
*
* TODO: Reverse the semantics of this method and refactor calling code
* accordingly. We've inadvertantly gotten it flipped.
* accordingly. We've inadvertently gotten it flipped.
*
* @param {Object} buffer - a Buffer object
*/
Expand Down Expand Up @@ -245,7 +245,7 @@ const headersArrayToObject = function(rawHeaders) {
const value = rawHeaders[i + 1]

if (headers[key]) {
headers[key] = _.isArray(headers[key]) ? headers[key] : [headers[key]]
headers[key] = Array.isArray(headers[key]) ? headers[key] : [headers[key]]
headers[key].push(value)
} else {
headers[key] = value
Expand Down Expand Up @@ -324,7 +324,7 @@ function matchStringOrRegexp(target, pattern) {
* @param stringFormattingFn The function used to format string values. Can
* be used to encode or decode the query value.
*
* @returns the formatted [key, value] pair.
* @returns *[] the formatted [key, value] pair.
*/
function formatQueryValue(key, value, stringFormattingFn) {
// TODO-coverage: Find out what's not covered. Probably refactor code to
Expand Down Expand Up @@ -371,7 +371,7 @@ function formatQueryValue(key, value, stringFormattingFn) {
function isStream(obj) {
return (
obj &&
typeof a !== 'string' &&
typeof obj !== 'string' &&
!Buffer.isBuffer(obj) &&
_.isFunction(obj.setEncoding)
)
Expand Down
86 changes: 51 additions & 35 deletions lib/interceptor.js
Expand Up @@ -78,12 +78,28 @@ Interceptor.prototype.replyWithError = function replyWithError(errorMessage) {
}

Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {
if (arguments.length <= 2 && _.isFunction(statusCode)) {
body = statusCode
statusCode = 200
}
// support the format of only passing in a callback
if (_.isFunction(statusCode)) {
if (arguments.length > 1) {
// It's not very Javascript-y to throw an error for extra args to a function, but because
// of legacy behavior, this error was added to reduce confusion for those migrating.
throw Error(
'Invalid arguments. When providing a function for the first argument, .reply does not accept other arguments.'
)
}
this.statusCode = null
this.fullReplyFunction = statusCode
} else {
if (statusCode && !Number.isInteger(statusCode)) {
throw new Error(`Invalid ${typeof statusCode} value for status code`)
}

this.statusCode = statusCode
this.statusCode = statusCode || 200
if (_.isFunction(body)) {
this.replyFunction = body
body = null
}
}

_.defaults(this.options, this.scope.scopeOptions)

Expand All @@ -94,6 +110,8 @@ Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {

if (this.scope._defaultReplyHeaders) {
headers = headers || {}
// Because of this, this.rawHeaders gets lower-cased versions of the `rawHeaders` param.
// https://github.com/nock/nock/issues/1553
headers = common.headersFieldNamesToLowerCase(headers)
headers = mixin(this.scope._defaultReplyHeaders, headers)
}
Expand All @@ -107,8 +125,7 @@ Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {
this.rawHeaders = []

for (const key in headers) {
this.rawHeaders.push(key)
this.rawHeaders.push(headers[key])
this.rawHeaders.push(key, headers[key])
}

// We use lower-case headers throughout Nock.
Expand All @@ -120,28 +137,27 @@ Interceptor.prototype.reply = function reply(statusCode, body, rawHeaders) {

// If the content is not encoded we may need to transform the response body.
// Otherwise we leave it as it is.
if (!common.isContentEncoded(this.headers)) {
if (
body &&
mastermatt marked this conversation as resolved.
Show resolved Hide resolved
typeof body !== 'string' &&
typeof body !== 'function' &&
!Buffer.isBuffer(body) &&
!common.isStream(body)
) {
try {
body = stringify(body)
if (!this.headers) {
this.headers = {}
}
if (!this.headers['content-type']) {
this.headers['content-type'] = 'application/json'
}
if (this.scope.contentLen) {
this.headers['content-length'] = body.length
}
} catch (err) {
throw new Error('Error encoding response body into JSON')
if (
body &&
typeof body !== 'string' &&
typeof body !== 'function' &&
!Buffer.isBuffer(body) &&
!common.isStream(body) &&
!common.isContentEncoded(this.headers)
) {
try {
body = stringify(body)
if (!this.headers) {
this.headers = {}
}
if (!this.headers['content-type']) {
this.headers['content-type'] = 'application/json'
}
if (this.scope.contentLen) {
this.headers['content-length'] = body.length
}
} catch (err) {
throw new Error('Error encoding response body into JSON')
}
}

Expand Down Expand Up @@ -454,7 +470,7 @@ Interceptor.prototype.basicAuth = function basicAuth(options) {
/**
* Set query strings for the interceptor
* @name query
* @param Object Object of query string name,values (accepts regexp values)
* @param queries Object of query string name,values (accepts regexp values)
* @public
* @example
* // Will match 'http://zombo.com/?q=t'
Expand Down Expand Up @@ -496,7 +512,7 @@ Interceptor.prototype.query = function query(queries) {
/**
* Set number of times will repeat the interceptor
* @name times
* @param Integer Number of times to repeat (should be > 0)
* @param newCounter Number of times to repeat (should be > 0)
* @public
* @example
* // Will repeat mock 5 times for same king of request
Expand Down Expand Up @@ -554,7 +570,7 @@ Interceptor.prototype.thrice = function thrice() {
* @param {(integer|object)} opts - Number of milliseconds to wait, or an object
* @param {integer} [opts.head] - Number of milliseconds to wait before response is sent
* @param {integer} [opts.body] - Number of milliseconds to wait before response body is sent
* @return {interceptor} - the current interceptor for chaining
* @return {Interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.delay = function delay(opts) {
let headDelay = 0
Expand All @@ -565,7 +581,7 @@ Interceptor.prototype.delay = function delay(opts) {
headDelay = opts.head || 0
bodyDelay = opts.body || 0
} else {
throw new Error(`Unexpected input opts${opts}`)
throw new Error(`Unexpected input opts ${opts}`)
}

return this.delayConnection(headDelay).delayBody(bodyDelay)
Expand All @@ -575,7 +591,7 @@ Interceptor.prototype.delay = function delay(opts) {
* Delay the response body by a certain number of ms.
*
* @param {integer} ms - Number of milliseconds to wait before response is sent
* @return {interceptor} - the current interceptor for chaining
* @return {Interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.delayBody = function delayBody(ms) {
this.delayInMs += ms
Expand All @@ -586,7 +602,7 @@ Interceptor.prototype.delayBody = function delayBody(ms) {
* Delay the connection by a certain number of ms.
*
* @param {integer} ms - Number of milliseconds to wait
* @return {interceptor} - the current interceptor for chaining
* @return {Interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.delayConnection = function delayConnection(ms) {
this.delayConnectionInMs += ms
Expand All @@ -601,7 +617,7 @@ Interceptor.prototype.getTotalDelay = function getTotalDelay() {
* Make the socket idle for a certain number of ms (simulated).
*
* @param {integer} ms - Number of milliseconds to wait
* @return {interceptor} - the current interceptor for chaining
* @return {Interceptor} - the current interceptor for chaining
*/
Interceptor.prototype.socketDelay = function socketDelay(ms) {
this.socketDelayInMs = ms
Expand Down