Skip to content

Commit

Permalink
refactor: Convert Scope to a class (#1636)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmelnikow committed Jul 19, 2019
1 parent 3bdadef commit df1a5cd
Showing 1 changed file with 178 additions and 176 deletions.
354 changes: 178 additions & 176 deletions lib/scope.js
Expand Up @@ -37,219 +37,221 @@ function startScope(basePath, options) {
* @param {Object} options.reqheaders
* @constructor
*/
function Scope(basePath, options) {
EventEmitter.apply(this)
this.keyedInterceptors = {}
this.interceptors = []
this.transformPathFunction = null
this.transformRequestBodyFunction = null
this.matchHeaders = []
this.logger = debug
this.scopeOptions = options || {}
this.urlParts = {}
this._persist = false
this.contentLen = false
this.date = null
this.basePath = basePath
this.basePathname = ''
this.port = null
this._defaultReplyHeaders = []

if (!(basePath instanceof RegExp)) {
this.urlParts = url.parse(basePath)
this.port =
this.urlParts.port || (this.urlParts.protocol === 'http:' ? 80 : 443)
this.basePathname = this.urlParts.pathname.replace(/\/$/, '')
this.basePath = `${this.urlParts.protocol}//${this.urlParts.hostname}:${this.port}`
class Scope extends EventEmitter {
constructor(basePath, options) {
super()

this.keyedInterceptors = {}
this.interceptors = []
this.transformPathFunction = null
this.transformRequestBodyFunction = null
this.matchHeaders = []
this.logger = debug
this.scopeOptions = options || {}
this.urlParts = {}
this._persist = false
this.contentLen = false
this.date = null
this.basePath = basePath
this.basePathname = ''
this.port = null
this._defaultReplyHeaders = []

if (!(basePath instanceof RegExp)) {
this.urlParts = url.parse(basePath)
this.port =
this.urlParts.port || (this.urlParts.protocol === 'http:' ? 80 : 443)
this.basePathname = this.urlParts.pathname.replace(/\/$/, '')
this.basePath = `${this.urlParts.protocol}//${this.urlParts.hostname}:${this.port}`
}
}
}

util.inherits(Scope, EventEmitter)

Scope.prototype.add = function add(key, interceptor) {
if (!(key in this.keyedInterceptors)) {
this.keyedInterceptors[key] = []
add(key, interceptor) {
if (!(key in this.keyedInterceptors)) {
this.keyedInterceptors[key] = []
}
this.keyedInterceptors[key].push(interceptor)
globalIntercept(
this.basePath,
interceptor,
this,
this.scopeOptions,
this.urlParts.hostname
)
}
this.keyedInterceptors[key].push(interceptor)
globalIntercept(
this.basePath,
interceptor,
this,
this.scopeOptions,
this.urlParts.hostname
)
}

Scope.prototype.remove = function remove(key, interceptor) {
if (this._persist) {
return
}
const arr = this.keyedInterceptors[key]
if (arr) {
arr.splice(arr.indexOf(interceptor), 1)
if (arr.length === 0) {
delete this.keyedInterceptors[key]
remove(key, interceptor) {
if (this._persist) {
return
}
const arr = this.keyedInterceptors[key]
if (arr) {
arr.splice(arr.indexOf(interceptor), 1)
if (arr.length === 0) {
delete this.keyedInterceptors[key]
}
}
}
}

Scope.prototype.intercept = function intercept(
uri,
method,
requestBody,
interceptorOptions
) {
const ic = new Interceptor(this, uri, method, requestBody, interceptorOptions)

this.interceptors.push(ic)
return ic
}
intercept(uri, method, requestBody, interceptorOptions) {
const ic = new Interceptor(
this,
uri,
method,
requestBody,
interceptorOptions
)

Scope.prototype.get = function get(uri, requestBody, options) {
return this.intercept(uri, 'GET', requestBody, options)
}
this.interceptors.push(ic)
return ic
}

Scope.prototype.post = function post(uri, requestBody, options) {
return this.intercept(uri, 'POST', requestBody, options)
}
get(uri, requestBody, options) {
return this.intercept(uri, 'GET', requestBody, options)
}

Scope.prototype.put = function put(uri, requestBody, options) {
return this.intercept(uri, 'PUT', requestBody, options)
}
post(uri, requestBody, options) {
return this.intercept(uri, 'POST', requestBody, options)
}

Scope.prototype.head = function head(uri, requestBody, options) {
return this.intercept(uri, 'HEAD', requestBody, options)
}
put(uri, requestBody, options) {
return this.intercept(uri, 'PUT', requestBody, options)
}

Scope.prototype.patch = function patch(uri, requestBody, options) {
return this.intercept(uri, 'PATCH', requestBody, options)
}
head(uri, requestBody, options) {
return this.intercept(uri, 'HEAD', requestBody, options)
}

Scope.prototype.merge = function merge(uri, requestBody, options) {
return this.intercept(uri, 'MERGE', requestBody, options)
}
patch(uri, requestBody, options) {
return this.intercept(uri, 'PATCH', requestBody, options)
}

Scope.prototype.delete = function _delete(uri, requestBody, options) {
return this.intercept(uri, 'DELETE', requestBody, options)
}
merge(uri, requestBody, options) {
return this.intercept(uri, 'MERGE', requestBody, options)
}

Scope.prototype.options = function _options(uri, requestBody, options) {
return this.intercept(uri, 'OPTIONS', requestBody, options)
}
delete(uri, requestBody, options) {
return this.intercept(uri, 'DELETE', requestBody, options)
}

// Returns the list of keys for non-optional Interceptors that haven't been completed yet.
// TODO: This assumes that completed mocks are removed from the keyedInterceptors list
// (when persistence is off). We should change that (and this) in future.
Scope.prototype.pendingMocks = function pendingMocks() {
return this.activeMocks().filter(key =>
this.keyedInterceptors[key].some(({ interceptionCounter, optional }) => {
const persistedAndUsed = this._persist && interceptionCounter > 0
return !persistedAndUsed && !optional
})
)
}
options(uri, requestBody, options) {
return this.intercept(uri, 'OPTIONS', requestBody, options)
}

// Returns all keyedInterceptors that are active.
// This includes incomplete interceptors, persisted but complete interceptors, and
// optional interceptors, but not non-persisted and completed interceptors.
Scope.prototype.activeMocks = function activeMocks() {
return Object.keys(this.keyedInterceptors)
}
// Returns the list of keys for non-optional Interceptors that haven't been completed yet.
// TODO: This assumes that completed mocks are removed from the keyedInterceptors list
// (when persistence is off). We should change that (and this) in future.
pendingMocks() {
return this.activeMocks().filter(key =>
this.keyedInterceptors[key].some(({ interceptionCounter, optional }) => {
const persistedAndUsed = this._persist && interceptionCounter > 0
return !persistedAndUsed && !optional
})
)
}

Scope.prototype.isDone = function isDone() {
// if nock is turned off, it always says it's done
if (!globalIntercept.isOn()) {
return true
// Returns all keyedInterceptors that are active.
// This includes incomplete interceptors, persisted but complete interceptors, and
// optional interceptors, but not non-persisted and completed interceptors.
activeMocks() {
return Object.keys(this.keyedInterceptors)
}

return this.pendingMocks().length === 0
}
isDone() {
// if nock is turned off, it always says it's done
if (!globalIntercept.isOn()) {
return true
}

Scope.prototype.done = function done() {
assert.ok(
this.isDone(),
`Mocks not yet satisfied:\n${this.pendingMocks().join('\n')}`
)
}
return this.pendingMocks().length === 0
}

Scope.prototype.buildFilter = function buildFilter() {
const filteringArguments = arguments

if (arguments[0] instanceof RegExp) {
return function(candidate) {
/* istanbul ignore if */
if (typeof candidate !== 'string') {
// Given the way nock is written, it seems like `candidate` will always
// be a string, regardless of what options might be passed to it.
// However the code used to contain a truthiness test of `candidate`.
// The check is being preserved for now.
throw Error(
`Nock internal assertion failed: typeof candidate is ${typeof candidate}. If you encounter this error, please report it as a bug.`
)
done() {
assert.ok(
this.isDone(),
`Mocks not yet satisfied:\n${this.pendingMocks().join('\n')}`
)
}

buildFilter() {
const filteringArguments = arguments

if (arguments[0] instanceof RegExp) {
return function(candidate) {
/* istanbul ignore if */
if (typeof candidate !== 'string') {
// Given the way nock is written, it seems like `candidate` will always
// be a string, regardless of what options might be passed to it.
// However the code used to contain a truthiness test of `candidate`.
// The check is being preserved for now.
throw Error(
`Nock internal assertion failed: typeof candidate is ${typeof candidate}. If you encounter this error, please report it as a bug.`
)
}
return candidate.replace(filteringArguments[0], filteringArguments[1])
}
return candidate.replace(filteringArguments[0], filteringArguments[1])
} else if (_.isFunction(arguments[0])) {
return arguments[0]
}
} else if (_.isFunction(arguments[0])) {
return arguments[0]
}
}

Scope.prototype.filteringPath = function filteringPath() {
this.transformPathFunction = this.buildFilter.apply(this, arguments)
if (!this.transformPathFunction) {
throw new Error(
'Invalid arguments: filtering path should be a function or a regular expression'
)
filteringPath() {
this.transformPathFunction = this.buildFilter.apply(this, arguments)
if (!this.transformPathFunction) {
throw new Error(
'Invalid arguments: filtering path should be a function or a regular expression'
)
}
return this
}
return this
}

Scope.prototype.filteringRequestBody = function filteringRequestBody() {
this.transformRequestBodyFunction = this.buildFilter.apply(this, arguments)
if (!this.transformRequestBodyFunction) {
throw new Error(
'Invalid arguments: filtering request body should be a function or a regular expression'
)
filteringRequestBody() {
this.transformRequestBodyFunction = this.buildFilter.apply(this, arguments)
if (!this.transformRequestBodyFunction) {
throw new Error(
'Invalid arguments: filtering request body should be a function or a regular expression'
)
}
return this
}
return this
}

Scope.prototype.matchHeader = function matchHeader(name, value) {
// We use lower-case header field names throughout Nock.
this.matchHeaders.push({ name: name.toLowerCase(), value })
return this
}
matchHeader(name, value) {
// We use lower-case header field names throughout Nock.
this.matchHeaders.push({ name: name.toLowerCase(), value })
return this
}

Scope.prototype.defaultReplyHeaders = function defaultReplyHeaders(headers) {
this._defaultReplyHeaders = common.headersInputToRawArray(headers)
return this
}
defaultReplyHeaders(headers) {
this._defaultReplyHeaders = common.headersInputToRawArray(headers)
return this
}

Scope.prototype.log = function log(newLogger) {
this.logger = newLogger
return this
}
log(newLogger) {
this.logger = newLogger
return this
}

Scope.prototype.persist = function persist(flag) {
this._persist = flag == null ? true : flag
if (typeof this._persist !== 'boolean') {
throw new Error('Invalid arguments: argument should be a boolean')
persist(flag) {
this._persist = flag == null ? true : flag
if (typeof this._persist !== 'boolean') {
throw new Error('Invalid arguments: argument should be a boolean')
}
return this
}
return this
}

Scope.prototype.shouldPersist = function shouldPersist() {
return this._persist
}
shouldPersist() {
return this._persist
}

Scope.prototype.replyContentLength = function replyContentLength() {
this.contentLen = true
return this
}
replyContentLength() {
this.contentLen = true
return this
}

Scope.prototype.replyDate = function replyDate(d) {
this.date = d || new Date()
return this
replyDate(d) {
this.date = d || new Date()
return this
}
}

function cleanAll() {
Expand Down

0 comments on commit df1a5cd

Please sign in to comment.