mappersmith
Version:
It is a lightweight rest client for node.js and the browser
236 lines (206 loc) • 5.72 kB
JavaScript
import { configs } from '../mappersmith'
import MockRequest from './mock-request'
import Request from '../request'
const VALUE_NOT_MATCHED = '<MAPPERSMITH_VALUE_NOT_MATCHED>'
/**
* @param {Integer} id
* @param {Object} client - the client generated by {@link forge}
*/
function MockResource(id, client) {
if (!client || !client._manifest) {
throw new Error('[Mappersmith Test] "mockClient" received an invalid client')
}
this.id = id
this.manifest = client._manifest
this.resourceName = null
this.methodName = null
this.requestParams = {}
this.responseData = null
this.responseHandler = null
this.responseHeaders = {}
this.responseStatus = 200
this.responseStatusHandler = null
this.mockRequest = null
this.asyncFinalRequest = null
this.pendingMiddlewareExecution = true
}
MockResource.prototype = {
/**
* @return {MockResource}
*/
resource(resourceName) {
this.resourceName = resourceName
return this
},
/**
* @return {MockResource}
*/
method(methodName) {
this.methodName = methodName
return this
},
/**
* @return {MockResource}
*/
with(requestParams) {
this.requestParams = requestParams
return this
},
/**
* @return {MockResource}
*/
headers(responseHeaders) {
this.responseHeaders = responseHeaders
return this
},
/**
* @return {MockResource}
*/
status(responder) {
if (typeof responder === 'function') {
this.responseStatusHandler = responder
} else {
this.responseStatus = responder
}
return this
},
/**
* @return {MockResource}
*/
response(responder) {
if (typeof responder === 'function') {
this.responseHandler = responder
} else {
this.responseData = responder
}
return this
},
/**
* @return {Promise<MockAssert>}
*/
assertObjectAsync() {
return this.createAsyncRequest().then((finalRequest) => {
this.asyncFinalRequest = finalRequest
this.pendingMiddlewareExecution = false
return this.toMockRequest().assertObject()
})
},
/**
* @return {MockAssert}
*/
assertObject() {
// The middleware "prepareRequest" phase is always async, so the middleware
// stack will never run when assertObject is used
return this.toMockRequest().assertObject()
},
/**
* @return {MockRequest}
*/
toMockRequest() {
const finalRequest = this.asyncFinalRequest ? this.asyncFinalRequest : this.createRequest()
const responseStatus = this.responseStatusHandler || this.responseStatus
if (!this.mockRequest) {
this.mockRequest = new MockRequest(this.id, {
method: finalRequest.method(),
url: this.generateUrlMatcher(finalRequest),
body: finalRequest.body(),
headers: finalRequest.headers(),
response: {
status: responseStatus,
headers: this.responseHeaders,
body: this.responseData,
handler: this.responseHandler,
},
})
}
return this.mockRequest
},
/**
* @private
*/
generateUrlMatcher(finalRequest) {
const params = finalRequest.params()
const hasParamMatchers = Object.keys(params).find((key) => typeof params[key] === 'function')
if (!hasParamMatchers) {
return finalRequest.url()
}
const urlMatcher = (requestUrl, requestParams) => {
const additionalParams = this.evaluateParamMatchers(params, requestParams)
const testRequest = finalRequest.enhance({ params: additionalParams })
return testRequest.url() === requestUrl
}
return urlMatcher
},
/**
* @private
*/
executeMiddlewareStack() {
return this.createAsyncRequest().then((finalRequest) => {
this.asyncFinalRequest = finalRequest
if (this.mockRequest) {
const urlMatcher = this.generateUrlMatcher(finalRequest)
this.mockRequest.url = urlMatcher
this.mockRequest.body = finalRequest.body()
this.pendingMiddlewareExecution = false
}
})
},
/**
* @private
*/
evaluateParamMatchers(mockParams, requestParams) {
return Object.keys(mockParams).reduce((obj, key) => {
const matcher = mockParams[key]
if (typeof matcher !== 'function') {
return obj
}
const value = requestParams[key]
// Only evaluate if key was provided in request params.
// Otherwise we always consider it not to match.
if (key in requestParams && matcher(value)) {
obj[key] = value
} else {
obj[key] = VALUE_NOT_MATCHED
}
return obj
}, {})
},
/**
* @private
* It never runs the middleware stack
*/
createRequest() {
const methodDescriptor = this.manifest.createMethodDescriptor(
this.resourceName,
this.methodName
)
return new Request(methodDescriptor, this.requestParams)
},
/**
* @private
* Always runs the middleware stack
*/
createAsyncRequest() {
const methodDescriptor = this.manifest.createMethodDescriptor(
this.resourceName,
this.methodName
)
const initialRequest = new Request(methodDescriptor, this.requestParams)
const middleware = this.manifest.createMiddleware({
resourceName: this.resourceName,
resourceMethod: this.methodName,
mockRequest: true,
})
const abort = (error) => {
throw error
}
const getInitialRequest = () => configs.Promise.resolve(initialRequest)
const prepareRequest = middleware.reduce(
(next, middleware) => () =>
configs.Promise.resolve().then(() => middleware.prepareRequest(next, abort)),
getInitialRequest
)
return prepareRequest()
},
}
export default MockResource