slush-gladius
Version:
Slush generator for bleeding edge weapo... ehr, applications!
143 lines (122 loc) • 4.24 kB
JavaScript
var RouteRecognizer = require('./route-recognizer.js')['default'];
var FakeXMLHttpRequest = require('./fakexmlhttprequest.js');
function Pretender(maps){
maps = maps || function(){};
// Herein we keep track of RouteRecognizer instances
// keyed by HTTP method. Feel free to add more as needed.
this.registry = {
GET: new RouteRecognizer(),
PUT: new RouteRecognizer(),
POST: new RouteRecognizer(),
DELETE: new RouteRecognizer(),
PATCH: new RouteRecognizer(),
HEAD: new RouteRecognizer()
};
this.handlers = [];
this.handledRequests = [];
this.unhandledRequests = [];
// reference the native XMLHttpRequest object so
// it can be restored later
this._nativeXMLHttpRequest = window.XMLHttpRequest;
// capture xhr requests, channeling them into
// the route map.
window.XMLHttpRequest = this.__fakeXMLHttpRequest = interceptor(this);
// trigger the route map DSL.
maps.call(this);
}
function interceptor(pretender) {
function FakeRequest(){
// super()
FakeXMLHttpRequest.call(this);
}
// extend
var proto = new FakeXMLHttpRequest();
proto.send = function send(){
setTimeout(function() {
FakeXMLHttpRequest.prototype.send.apply(this, arguments);
pretender.handleRequest(this);
}.bind(this), 200);
};
FakeRequest.prototype = proto;
return FakeRequest;
}
function verbify(verb){
return function(path, handler){
this.register(verb, path, handler);
};
}
function throwIfURLDetected(url){
var HTTP_REGEXP = /^https?/;
var message;
if(HTTP_REGEXP.test(url)) {
var parser = window.document.createElement('a');
parser.href = url;
message = "Pretender will not respond to requests for URLs. It is not possible to accurately simluate the browser's CSP. "+
"Remove the " + parser.protocol +"//"+ parser.hostname +" from " + url + " and try again";
throw new Error(message)
}
}
Pretender.prototype = {
get: verbify('GET'),
post: verbify('POST'),
put: verbify('PUT'),
'delete': verbify('DELETE'),
patch: verbify('PATCH'),
head: verbify('HEAD'),
register: function register(verb, path, handler){
handler.numberOfCalls = 0;
this.handlers.push(handler);
var registry = this.registry[verb];
registry.add([{path: path, handler: handler}]);
},
handleRequest: function handleRequest(request){
var verb = request.method.toUpperCase();
var path = request.url;
throwIfURLDetected(path);
var handler = this._handlerFor(verb, path, request);
if (handler) {
handler.handler.numberOfCalls++;
this.handledRequests.push(request);
try {
var statusHeadersAndBody = handler.handler(request),
status = statusHeadersAndBody[0],
headers = this.prepareHeaders(statusHeadersAndBody[1]),
body = this.prepareBody(statusHeadersAndBody[2]);
request.respond(status, headers, body);
this.handledRequest(verb, path, request);
} catch (error) {
this.erroredRequest(verb, path, request, error);
}
} else {
this.unhandledRequests.push(request);
this.unhandledRequest(verb, path, request);
}
},
prepareBody: function(body) { return body; },
prepareHeaders: function(headers) { return headers; },
handledRequest: function(verb, path, request) { /* no-op */},
unhandledRequest: function(verb, path, request) {
throw new Error("Pretender intercepted "+verb+" "+path+" but no handler was defined for this type of request");
},
erroredRequest: function(verb, path, request, error){
error.message = "Pretender intercepted "+verb+" "+path+" but encountered an error: " + error.message;
throw error;
},
_handlerFor: function(verb, path, request){
var registry = this.registry[verb];
var matches = registry.recognize(path);
var match = matches ? matches[0] : null;
if (match) {
request.params = match.params;
request.queryParams = matches.queryParams;
}
return match;
},
shutdown: function shutdown(){
window.XMLHttpRequest = this._nativeXMLHttpRequest;
},
restart: function restart() {
window.XMLHttpRequest = this.__fakeXMLHttpRequest;
}
};
module.exports.Pretender = Pretender;