forked from Mirrors/doipjs
334 lines
9.2 KiB
JavaScript
334 lines
9.2 KiB
JavaScript
"use strict";
|
|
|
|
var fakeXhr = require("../fake-xhr");
|
|
var push = [].push;
|
|
var log = require("./log");
|
|
var configureLogError = require("../configure-logger");
|
|
var pathToRegexp = require("path-to-regexp");
|
|
|
|
var supportsArrayBuffer = typeof ArrayBuffer !== "undefined";
|
|
|
|
function responseArray(handler) {
|
|
var response = handler;
|
|
|
|
if (Object.prototype.toString.call(handler) !== "[object Array]") {
|
|
response = [200, {}, handler];
|
|
}
|
|
|
|
if (typeof response[2] !== "string") {
|
|
if (!supportsArrayBuffer) {
|
|
throw new TypeError(
|
|
"Fake server response body should be a string, but was " +
|
|
typeof response[2]
|
|
);
|
|
} else if (!(response[2] instanceof ArrayBuffer)) {
|
|
throw new TypeError(
|
|
"Fake server response body should be a string or ArrayBuffer, but was " +
|
|
typeof response[2]
|
|
);
|
|
}
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
function getDefaultWindowLocation() {
|
|
var winloc = {
|
|
hostname: "localhost",
|
|
port: process.env.PORT || 80,
|
|
protocol: "http:"
|
|
};
|
|
winloc.host =
|
|
winloc.hostname +
|
|
(String(winloc.port) === "80" ? "" : ":" + winloc.port);
|
|
return winloc;
|
|
}
|
|
|
|
function getWindowLocation() {
|
|
if (typeof window === "undefined") {
|
|
// Fallback
|
|
return getDefaultWindowLocation();
|
|
}
|
|
|
|
if (typeof window.location !== "undefined") {
|
|
// Browsers place location on window
|
|
return window.location;
|
|
}
|
|
|
|
if (
|
|
typeof window.window !== "undefined" &&
|
|
typeof window.window.location !== "undefined"
|
|
) {
|
|
// React Native on Android places location on window.window
|
|
return window.window.location;
|
|
}
|
|
|
|
return getDefaultWindowLocation();
|
|
}
|
|
|
|
function matchOne(response, reqMethod, reqUrl) {
|
|
var rmeth = response.method;
|
|
var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase();
|
|
var url = response.url;
|
|
var matchUrl =
|
|
!url ||
|
|
url === reqUrl ||
|
|
(typeof url.test === "function" && url.test(reqUrl));
|
|
|
|
return matchMethod && matchUrl;
|
|
}
|
|
|
|
function match(response, request) {
|
|
var wloc = getWindowLocation();
|
|
|
|
var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host + "/");
|
|
|
|
var requestUrl = request.url;
|
|
|
|
if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
|
|
requestUrl = requestUrl.replace(rCurrLoc, "/");
|
|
}
|
|
|
|
if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
|
|
if (typeof response.response === "function") {
|
|
var ru = response.url;
|
|
var args = [request].concat(
|
|
ru && typeof ru.exec === "function"
|
|
? ru.exec(requestUrl).slice(1)
|
|
: []
|
|
);
|
|
return response.response.apply(response, args);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function incrementRequestCount() {
|
|
var count = ++this.requestCount;
|
|
|
|
this.requested = true;
|
|
|
|
this.requestedOnce = count === 1;
|
|
this.requestedTwice = count === 2;
|
|
this.requestedThrice = count === 3;
|
|
|
|
this.firstRequest = this.getRequest(0);
|
|
this.secondRequest = this.getRequest(1);
|
|
this.thirdRequest = this.getRequest(2);
|
|
|
|
this.lastRequest = this.getRequest(count - 1);
|
|
}
|
|
|
|
var fakeServer = {
|
|
create: function(config) {
|
|
var server = Object.create(this);
|
|
server.configure(config);
|
|
this.xhr = fakeXhr.useFakeXMLHttpRequest();
|
|
server.requests = [];
|
|
server.requestCount = 0;
|
|
server.queue = [];
|
|
server.responses = [];
|
|
|
|
this.xhr.onCreate = function(xhrObj) {
|
|
xhrObj.unsafeHeadersEnabled = function() {
|
|
return !(server.unsafeHeadersEnabled === false);
|
|
};
|
|
server.addRequest(xhrObj);
|
|
};
|
|
|
|
return server;
|
|
},
|
|
|
|
configure: function(config) {
|
|
var self = this;
|
|
var whitelist = {
|
|
autoRespond: true,
|
|
autoRespondAfter: true,
|
|
respondImmediately: true,
|
|
fakeHTTPMethods: true,
|
|
logger: true,
|
|
unsafeHeadersEnabled: true
|
|
};
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
config = config || {};
|
|
|
|
Object.keys(config).forEach(function(setting) {
|
|
if (setting in whitelist) {
|
|
self[setting] = config[setting];
|
|
}
|
|
});
|
|
|
|
self.logError = configureLogError(config);
|
|
},
|
|
|
|
addRequest: function addRequest(xhrObj) {
|
|
var server = this;
|
|
push.call(this.requests, xhrObj);
|
|
|
|
incrementRequestCount.call(this);
|
|
|
|
xhrObj.onSend = function() {
|
|
server.handleRequest(this);
|
|
|
|
if (server.respondImmediately) {
|
|
server.respond();
|
|
} else if (server.autoRespond && !server.responding) {
|
|
setTimeout(function() {
|
|
server.responding = false;
|
|
server.respond();
|
|
}, server.autoRespondAfter || 10);
|
|
|
|
server.responding = true;
|
|
}
|
|
};
|
|
},
|
|
|
|
getHTTPMethod: function getHTTPMethod(request) {
|
|
if (this.fakeHTTPMethods && /post/i.test(request.method)) {
|
|
var matches = (request.requestBody || "").match(
|
|
/_method=([^\b;]+)/
|
|
);
|
|
return matches ? matches[1] : request.method;
|
|
}
|
|
|
|
return request.method;
|
|
},
|
|
|
|
handleRequest: function handleRequest(xhr) {
|
|
if (xhr.async) {
|
|
push.call(this.queue, xhr);
|
|
} else {
|
|
this.processRequest(xhr);
|
|
}
|
|
},
|
|
|
|
logger: function() {
|
|
// no-op; override via configure()
|
|
},
|
|
|
|
logError: configureLogError({}),
|
|
|
|
log: log,
|
|
|
|
respondWith: function respondWith(method, url, body) {
|
|
if (arguments.length === 1 && typeof method !== "function") {
|
|
this.response = responseArray(method);
|
|
return;
|
|
}
|
|
|
|
if (arguments.length === 1) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
body = method;
|
|
// eslint-disable-next-line no-param-reassign
|
|
url = method = null;
|
|
}
|
|
|
|
if (arguments.length === 2) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
body = url;
|
|
// eslint-disable-next-line no-param-reassign
|
|
url = method;
|
|
// eslint-disable-next-line no-param-reassign
|
|
method = null;
|
|
}
|
|
|
|
// Escape port number to prevent "named" parameters in 'path-to-regexp' module
|
|
if (typeof url === "string" && url !== "" && /:[0-9]+\//.test(url)) {
|
|
var m = url.match(/^(https?:\/\/.*?):([0-9]+\/.*)$/);
|
|
// eslint-disable-next-line no-param-reassign
|
|
url = m[1] + "\\:" + m[2];
|
|
}
|
|
|
|
push.call(this.responses, {
|
|
method: method,
|
|
url:
|
|
typeof url === "string" && url !== "" ? pathToRegexp(url) : url,
|
|
response: typeof body === "function" ? body : responseArray(body)
|
|
});
|
|
},
|
|
|
|
respond: function respond() {
|
|
if (arguments.length > 0) {
|
|
this.respondWith.apply(this, arguments);
|
|
}
|
|
|
|
var queue = this.queue || [];
|
|
var requests = queue.splice(0, queue.length);
|
|
var self = this;
|
|
|
|
requests.forEach(function(request) {
|
|
self.processRequest(request);
|
|
});
|
|
},
|
|
|
|
respondAll: function respondAll() {
|
|
if (this.respondImmediately) {
|
|
return;
|
|
}
|
|
|
|
this.queue = this.requests.slice(0);
|
|
|
|
var request;
|
|
while ((request = this.queue.shift())) {
|
|
this.processRequest(request);
|
|
}
|
|
},
|
|
|
|
processRequest: function processRequest(request) {
|
|
try {
|
|
if (request.aborted) {
|
|
return;
|
|
}
|
|
|
|
var response = this.response || [404, {}, ""];
|
|
|
|
if (this.responses) {
|
|
for (var l = this.responses.length, i = l - 1; i >= 0; i--) {
|
|
if (match.call(this, this.responses[i], request)) {
|
|
response = this.responses[i].response;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (request.readyState !== 4) {
|
|
this.log(response, request);
|
|
|
|
request.respond(response[0], response[1], response[2]);
|
|
}
|
|
} catch (e) {
|
|
this.logError("Fake server request processing", e);
|
|
}
|
|
},
|
|
|
|
restore: function restore() {
|
|
return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
|
|
},
|
|
|
|
getRequest: function getRequest(index) {
|
|
return this.requests[index] || null;
|
|
},
|
|
|
|
reset: function reset() {
|
|
this.resetBehavior();
|
|
this.resetHistory();
|
|
},
|
|
|
|
resetBehavior: function resetBehavior() {
|
|
this.responses.length = this.queue.length = 0;
|
|
},
|
|
|
|
resetHistory: function resetHistory() {
|
|
this.requests.length = this.requestCount = 0;
|
|
|
|
this.requestedOnce = this.requestedTwice = this.requestedThrice = this.requested = false;
|
|
|
|
this.firstRequest = this.secondRequest = this.thirdRequest = this.lastRequest = null;
|
|
}
|
|
};
|
|
|
|
module.exports = fakeServer;
|