UNPKG

discord-bot-cdk-construct

Version:

A quick CDK Construct for creating a serverless Discord bot in AWS!

1,533 lines (1,204 loc) 53.7 kB
"use strict"; var referee = require("@sinonjs/referee"); var setupDOM = require("jsdom-global"); var JSDOM = require("jsdom").JSDOM; var sinon = require("sinon"); var sinonFakeServer = require("./index"); var fakeXhr = require("../fake-xhr/"); var FakeXMLHttpRequest = fakeXhr.FakeXMLHttpRequest; var JSDOMParser; if (JSDOM) { JSDOMParser = new JSDOM().window.DOMParser; } var assert = referee.assert; var refute = referee.refute; var supportsArrayBuffer = typeof ArrayBuffer !== "undefined"; describe("sinonFakeServer", function() { beforeEach(function() { if (JSDOMParser) { global.DOMParser = JSDOMParser; this.cleanupDOM = setupDOM(); } }); afterEach(function() { if (this.server) { this.server.restore(); } if (JSDOMParser) { delete global.DOMParser; this.cleanupDOM(); } }); it("provides restore method", function() { this.server = sinonFakeServer.create(); assert.isFunction(this.server.restore); }); describe(".create", function() { it("allows the 'autoRespond' setting", function() { var server = sinonFakeServer.create({ autoRespond: true }); assert( server.autoRespond, "fakeServer.create should accept 'autoRespond' setting" ); }); it("allows the 'autoRespondAfter' setting", function() { var server = sinonFakeServer.create({ autoRespondAfter: 500 }); assert.equals( server.autoRespondAfter, 500, "fakeServer.create should accept 'autoRespondAfter' setting" ); }); it("allows the 'respondImmediately' setting", function() { var server = sinonFakeServer.create({ respondImmediately: true }); assert( server.respondImmediately, "fakeServer.create should accept 'respondImmediately' setting" ); }); it("allows the 'fakeHTTPMethods' setting", function() { var server = sinonFakeServer.create({ fakeHTTPMethods: true }); assert( server.fakeHTTPMethods, "fakeServer.create should accept 'fakeHTTPMethods' setting" ); }); it("allows the 'unsafeHeadersEnabled' setting", function() { var server = sinon.fakeServer.create({ unsafeHeadersEnabled: false }); refute.isUndefined( server.unsafeHeadersEnabled, "'unsafeHeadersEnabled' expected to be defined at server level" ); assert( !server.unsafeHeadersEnabled, "fakeServer.create should accept 'unsafeHeadersEnabled' setting" ); }); it("does not assign a non-allowlisted setting", function() { var server = sinonFakeServer.create({ foo: true }); refute( server.foo, "fakeServer.create should not accept 'foo' settings" ); }); }); it("fakes XMLHttpRequest", function() { var sandbox = sinon.createSandbox(); sandbox.stub(fakeXhr, "useFakeXMLHttpRequest").returns({ restore: sinon.stub() }); this.server = sinonFakeServer.create(); assert(fakeXhr.useFakeXMLHttpRequest.called); sandbox.restore(); }); it("mirrors FakeXMLHttpRequest restore method", function() { var sandbox = sinon.createSandbox(); this.server = sinonFakeServer.create(); var restore = sandbox.stub(FakeXMLHttpRequest, "restore"); this.server.restore(); assert(restore.called); sandbox.restore(); }); describe(".requests", function() { beforeEach(function() { this.server = sinonFakeServer.create(); }); afterEach(function() { this.server.restore(); }); it("collects objects created with fake XHR", function() { var xhrs = [new FakeXMLHttpRequest(), new FakeXMLHttpRequest()]; assert.equals(this.server.requests, xhrs); }); it("collects xhr objects through addRequest", function() { this.server.addRequest = sinon.spy(); var xhr = new FakeXMLHttpRequest(); assert(this.server.addRequest.calledWith(xhr)); }); it("observes onSend on requests", function() { var xhrs = [new FakeXMLHttpRequest(), new FakeXMLHttpRequest()]; assert.isFunction(xhrs[0].onSend); assert.isFunction(xhrs[1].onSend); }); it("onSend should call handleRequest with request object", function() { var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/"); sinon.spy(this.server, "handleRequest"); xhr.send(); assert(this.server.handleRequest.called); assert(this.server.handleRequest.calledWith(xhr)); }); }); describe(".handleRequest", function() { beforeEach(function() { this.server = sinonFakeServer.create(); }); afterEach(function() { this.server.restore(); }); it("responds to synchronous requests", function() { var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/", false); sinon.spy(xhr, "respond"); xhr.send(); assert(xhr.respond.called); }); it("does not respond to async requests", function() { var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/", true); sinon.spy(xhr, "respond"); xhr.send(); assert.isFalse(xhr.respond.called); }); }); describe(".respondWith", function() { beforeEach(function() { this.sandbox = sinon.createSandbox(); this.server = sinonFakeServer.create({ setTimeout: this.sandbox.spy(), useImmediateExceptions: false }); this.getRootAsync = new FakeXMLHttpRequest(); this.getRootAsync.open("GET", "/", true); this.getRootAsync.send(); sinon.spy(this.getRootAsync, "respond"); this.getRootAsyncArrayBuffer = new FakeXMLHttpRequest(); this.getRootAsyncArrayBuffer.responseType = "arraybuffer"; this.getRootAsyncArrayBuffer.open("GET", "/", true); this.getRootAsyncArrayBuffer.send(); sinon.spy(this.getRootAsyncArrayBuffer, "respond"); this.postRootAsync = new FakeXMLHttpRequest(); this.postRootAsync.open("POST", "/", true); this.postRootAsync.send(); sinon.spy(this.postRootAsync, "respond"); this.getRootSync = new FakeXMLHttpRequest(); this.getRootSync.open("GET", "/", false); this.getPathAsync = new FakeXMLHttpRequest(); this.getPathAsync.open("GET", "/path", true); this.getPathAsync.send(); sinon.spy(this.getPathAsync, "respond"); this.postPathAsync = new FakeXMLHttpRequest(); this.postPathAsync.open("POST", "/path", true); this.postPathAsync.send(); sinon.spy(this.postPathAsync, "respond"); }); afterEach(function() { this.server.restore(); this.sandbox.restore(); }); it("responds to queued async text requests", function() { this.server.respondWith("Oh yeah! Duffman!"); this.server.respond(); assert(this.getRootAsync.respond.called); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "Oh yeah! Duffman!" ]); assert.equals( this.getRootAsync.readyState, FakeXMLHttpRequest.DONE ); }); it("responds to all queued async requests", function() { this.server.respondWith("Oh yeah! Duffman!"); this.server.respond(); assert(this.getRootAsync.respond.called); assert(this.getPathAsync.respond.called); }); it("does not respond to requests queued after respond() (eg from callbacks)", function() { var xhr; this.getRootAsync.addEventListener("load", function() { xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/", true); xhr.send(); sinon.spy(xhr, "respond"); }); this.server.respondWith("Oh yeah! Duffman!"); this.server.respond(); assert(this.getRootAsync.respond.called); assert(this.getPathAsync.respond.called); assert(!xhr.respond.called); this.server.respond(); assert(xhr.respond.called); }); it("responds with status, headers, and text body", function() { var headers = { "Content-Type": "X-test" }; this.server.respondWith([201, headers, "Oh yeah!"]); this.server.respond(); assert(this.getRootAsync.respond.called); assert.equals(this.getRootAsync.respond.args[0], [ 201, headers, "Oh yeah!" ]); assert.equals( this.getRootAsync.readyState, FakeXMLHttpRequest.DONE ); }); it("handles responding with empty queue", function() { delete this.server.queue; var server = this.server; refute.exception(function() { server.respond(); }); }); it("responds to sync request with canned answers", function() { this.server.respondWith([210, { "X-Ops": "Yeah" }, "Body, man"]); this.getRootSync.send(); assert.equals(this.getRootSync.status, 210); assert.equals( this.getRootSync.getAllResponseHeaders(), "X-Ops: Yeah\r\n" ); assert.equals(this.getRootSync.responseText, "Body, man"); }); it("responds to sync request with 404 if no response is set", function() { this.getRootSync.send(); assert.equals(this.getRootSync.status, 404); assert.equals(this.getRootSync.getAllResponseHeaders(), ""); assert.equals(this.getRootSync.responseText, ""); }); it("responds to async request with 404 if no response is set", function() { this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); }); it("responds to specific URL", function() { this.server.respondWith("/path", "Duffman likes Duff beer"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "Duffman likes Duff beer" ]); }); it("responds to URL matched by regexp", function() { this.server.respondWith(/^\/p.*/, "Regexp"); this.server.respond(); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "Regexp" ]); }); it("responds to URL matched by url matcher function", function() { this.server.respondWith(function() { return true; }, "FuncMatcher"); this.server.respond(); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "FuncMatcher" ]); }); it("does not respond to URL not matched by regexp", function() { this.server.respondWith(/^\/p.*/, "No regexp match"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); }); it("does not respond to URL not matched by function url matcher", function() { this.server.respondWith(function() { return false; }, "No function match"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); }); it("responds to all URLs matched by regexp", function() { this.server.respondWith(/^\/.*/, "Match all URLs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "Match all URLs" ]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "Match all URLs" ]); }); it("responds to all URLs matched by function matcher", function() { this.server.respondWith(function() { return true; }, "Match all URLs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "Match all URLs" ]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "Match all URLs" ]); }); it("responds to all requests when match URL is falsy", function() { this.server.respondWith("", "Falsy URL"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "Falsy URL" ]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "Falsy URL" ]); }); it("responds to no requests when function matcher is falsy", function() { this.server.respondWith(function() { return false; }, "Falsy URL"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.getPathAsync.respond.args[0], [404, {}, ""]); }); it("responds to all GET requests", function() { this.server.respondWith("GET", "", "All GETs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "All GETs" ]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "All GETs" ]); assert.equals(this.postRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postPathAsync.respond.args[0], [404, {}, ""]); }); it("responds to all 'get' requests (case-insensitivity)", function() { this.server.respondWith("get", "", "All GETs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "All GETs" ]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "All GETs" ]); assert.equals(this.postRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postPathAsync.respond.args[0], [404, {}, ""]); }); it("responds to all PUT requests", function() { this.server.respondWith("PUT", "", "All PUTs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.getPathAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postPathAsync.respond.args[0], [404, {}, ""]); }); it("responds to all POST requests", function() { this.server.respondWith("POST", "", "All POSTs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.getPathAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postRootAsync.respond.args[0], [ 200, {}, "All POSTs" ]); assert.equals(this.postPathAsync.respond.args[0], [ 200, {}, "All POSTs" ]); }); it("responds to all POST requests to /path", function() { this.server.respondWith("POST", "/path", "All POSTs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.getPathAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postPathAsync.respond.args[0], [ 200, {}, "All POSTs" ]); }); it("responds to all POST requests matching regexp", function() { this.server.respondWith("POST", /^\/path(\?.*)?/, "All POSTs"); this.server.respond(); assert.equals(this.getRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.getPathAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postRootAsync.respond.args[0], [404, {}, ""]); assert.equals(this.postPathAsync.respond.args[0], [ 200, {}, "All POSTs" ]); }); it("does not respond to aborted requests", function() { this.server.respondWith("/", "That's my homepage!"); this.getRootAsync.aborted = true; this.server.respond(); assert.isFalse(this.getRootAsync.respond.called); }); it("resets requests", function() { this.server.respondWith("/", "That's my homepage!"); this.server.respond(); assert.equals(this.server.queue, []); }); it("notifies all requests when some throw", function() { this.getRootAsync.respond = function() { throw new Error("Oops!"); }; this.server.respondWith(""); this.server.respond(); assert.equals(this.getPathAsync.respond.args[0], [200, {}, ""]); assert.equals(this.postRootAsync.respond.args[0], [200, {}, ""]); assert.equals(this.postPathAsync.respond.args[0], [200, {}, ""]); }); it("recognizes request with hostname", function() { // set the host value, as jsdom default is 'about:blank' setupDOM("", { url: "http://localhost/" }); this.server.respondWith("/", [200, {}, "Yep"]); var xhr = new FakeXMLHttpRequest(); var loc = window.location; xhr.open("GET", `${loc.protocol}//${loc.host}/`, true); xhr.send(); sinon.spy(xhr, "respond"); this.server.respond(); assert.equals(xhr.respond.args[0], [200, {}, "Yep"]); }); it("responds to matching paths with port number in external URL", function() { // setup server & client setupDOM("", { url: "http://localhost/" }); var localAPI = "http://localhost:5000/ping"; this.server.respondWith("GET", localAPI, "Pong"); // Create fake client request var xhr = new FakeXMLHttpRequest(); xhr.open("GET", localAPI, true); xhr.send(); sinon.spy(xhr, "respond"); this.server.respond(); assert.equals(xhr.respond.args[0], [200, {}, "Pong"]); }); it("responds to matching paths when port numbers are different", function() { // setup server & client setupDOM("", { url: "http://localhost:8080/" }); var localAPI = "http://localhost:5000/ping"; this.server.respondWith("GET", localAPI, "Pong"); this.server.respondWith( "GET", "http://localhost:8080/ping", "Ding" ); // Create fake client request var xhr = new FakeXMLHttpRequest(); xhr.open("GET", localAPI, true); xhr.send(); sinon.spy(xhr, "respond"); this.server.respond(); assert.equals(xhr.respond.args[0], [200, {}, "Pong"]); }); it("responds although window.location is undefined", function() { // setup server & client this.cleanupDOM(); // remove window, Document, etc. var origin = "http://localhost"; this.server.respondWith("GET", "/ping", "Pong"); // Create fake client request var xhr = new FakeXMLHttpRequest(); xhr.open("GET", `${origin}/ping`, true); xhr.send(); sinon.spy(xhr, "respond"); this.server.respond(); assert.equals(xhr.respond.args[0], [200, {}, "Pong"]); }); // React Native on Android places location on window.window it("responds as expected for React Native on Android with window.window.location", function() { this.cleanupDOM(); // remove default window, Document, etc. // setup client // Build Android like format (manual jsdom, with window.window inset) var html = // eslint-disable-next-line quotes '<!doctype html><html><head><meta charset="utf-8">' + "</head><body></body></html>"; var options = {}; var document = new JSDOM(html, options); var window = document.window; global.document = window.document; global.window = { window: window }; window.console = global.console; // setup server var url = "http://localhost"; this.server.respondWith("GET", url, "Pong"); // Create fake client request var xhr = new FakeXMLHttpRequest(); xhr.open("GET", url, true); xhr.send(); sinon.spy(xhr, "respond"); this.server.respond(); assert.equals(xhr.respond.args[0], [200, {}, "Pong"]); }); it("accepts URLS which are common route DSLs", function() { this.server.respondWith("/foo/*", [200, {}, "Yep"]); var xhr = new FakeXMLHttpRequest(); xhr.respond = sinon.spy(); xhr.open("GET", "/foo/bla/boo", true); xhr.send(); this.server.respond(); assert.equals(xhr.respond.args[0], [200, {}, "Yep"]); }); it("yields URL capture groups to response handler when using DSLs", function() { var handler = sinon.spy(); this.server.respondWith("GET", "/thing/:id", handler); var xhr = new FakeXMLHttpRequest(); xhr.respond = sinon.spy(); xhr.open("GET", "/thing/1337", true); xhr.send(); this.server.respond(); assert(handler.called); assert.equals(handler.args[0], [xhr, "1337"]); }); it("throws understandable error if response is not a string or ArrayBuffer", function() { var server = this.server; assert.exception( function() { server.respondWith("/", {}); }, { message: "Fake server response body should be a string or ArrayBuffer, but was object" } ); }); it("throws understandable error if response in array is not a string or ArrayBuffer", function() { var server = this.server; assert.exception( function() { server.respondWith("/", [200, {}]); }, { message: "Fake server response body should be a string or ArrayBuffer, but was undefined" } ); }); it("is able to pass the same args to respond directly", function() { this.server.respond("Oh yeah! Duffman!"); assert.equals(this.getRootAsync.respond.args[0], [ 200, {}, "Oh yeah! Duffman!" ]); assert.equals(this.getPathAsync.respond.args[0], [ 200, {}, "Oh yeah! Duffman!" ]); assert.equals(this.postRootAsync.respond.args[0], [ 200, {}, "Oh yeah! Duffman!" ]); assert.equals(this.postPathAsync.respond.args[0], [ 200, {}, "Oh yeah! Duffman!" ]); }); it("responds to most recently defined match", function() { this.server.respondWith("POST", "", "All POSTs"); this.server.respondWith("POST", "/path", "Particular POST"); this.server.respond(); assert.equals(this.postRootAsync.respond.args[0], [ 200, {}, "All POSTs" ]); assert.equals(this.postPathAsync.respond.args[0], [ 200, {}, "Particular POST" ]); }); if (supportsArrayBuffer) { it("responds to queued async arraybuffer requests", function() { var buffer = new Uint8Array([160, 64, 0, 0, 32, 193]).buffer; this.server.respondWith(buffer); this.server.respond(); assert(this.getRootAsyncArrayBuffer.respond.called); assert.equals(this.getRootAsyncArrayBuffer.respond.args[0], [ 200, {}, buffer ]); assert.equals( this.getRootAsyncArrayBuffer.readyState, FakeXMLHttpRequest.DONE ); }); it("responds with status, headers, and arraybuffer body", function() { var buffer = new Uint8Array([160, 64, 0, 0, 32, 193]).buffer; var headers = { "Content-Type": "X-test" }; this.server.respondWith([201, headers, buffer]); this.server.respond(); assert(this.getRootAsyncArrayBuffer.respond.called); assert.equals(this.getRootAsyncArrayBuffer.respond.args[0], [ 201, headers, buffer ]); assert.equals( this.getRootAsyncArrayBuffer.readyState, FakeXMLHttpRequest.DONE ); }); } }); describe(".respondWith (FunctionHandler)", function() { beforeEach(function() { this.server = sinonFakeServer.create(); }); afterEach(function() { this.server.restore(); }); it("yields response to request function handler", function() { var handler = sinon.spy(); this.server.respondWith("/hello", handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(handler.calledOnce); assert(handler.calledWith(xhr)); }); it("responds to request from function handler", function() { this.server.respondWith("/hello", function(xhr) { xhr.respond( 200, { "Content-Type": "application/json" }, // eslint-disable-next-line quotes '{"id":42}' ); }); var request = new FakeXMLHttpRequest(); request.open("GET", "/hello"); request.send(); this.server.respond(); assert.equals(request.status, 200); assert.equals(request.responseHeaders, { "Content-Type": "application/json" }); assert.equals( request.responseText, // eslint-disable-next-line quotes '{"id":42}' ); }); it("yields response to request function handler when method matches", function() { var handler = sinon.spy(); this.server.respondWith("GET", "/hello", handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(handler.calledOnce); }); it("yields response to request function handler when url contains RegExp characters", function() { var handler = sinon.spy(); this.server.respondWith("GET", "/hello?world", handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello?world"); xhr.send(); this.server.respond(); assert(handler.calledOnce); }); function equalMatcher(expected) { return function(test) { return expected === test; }; } it("yields response to request function handler when url is a function that returns true", function() { var handler = sinon.spy(); this.server.respondWith( "GET", equalMatcher("/hello?world"), handler ); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello?world"); xhr.send(); this.server.respond(); assert(handler.calledOnce); }); // eslint-disable-next-line max-len it("yields response to request function handler when url is a function that returns true with no Http Method specified", function() { var handler = sinon.spy(); this.server.respondWith(equalMatcher("/hello?world"), handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello?world"); xhr.send(); this.server.respond(); assert(handler.calledOnce); }); it("does not yield response to request function handler when method does not match", function() { var handler = sinon.spy(); this.server.respondWith("GET", "/hello", handler); var xhr = new FakeXMLHttpRequest(); xhr.open("POST", "/hello"); xhr.send(); this.server.respond(); assert(!handler.called); }); // eslint-disable-next-line max-len it("does not yield response to request function handler when method does not match (using url mather function)", function() { var handler = sinon.spy(); this.server.respondWith("GET", equalMatcher("/hello"), handler); var xhr = new FakeXMLHttpRequest(); xhr.open("POST", "/hello"); xhr.send(); this.server.respond(); assert(!handler.called); }); it("yields response to request function handler when regexp url matches", function() { var handler = sinon.spy(); this.server.respondWith("GET", /\/.*/, handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(handler.calledOnce); }); it("does not yield response to request function handler when regexp url does not match", function() { var handler = sinon.spy(); this.server.respondWith("GET", /\/a.*/, handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(!handler.called); }); it("does not yield response to request function handler when urlMatcher function returns false", function() { var handler = sinon.spy(); this.server.respondWith("GET", equalMatcher("/goodbye"), handler); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(!handler.called); }); // eslint-disable-next-line max-len it("does not yield response to request function handler when urlMatcher function returns non Boolean truthy value", function() { var handler = sinon.spy(); this.server.respondWith( "GET", function() { return "truthy"; }, handler ); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(!handler.called); }); // eslint-disable-next-line max-len it("does not yield response to request function handler when urlMatcher function returns non Boolean falsey value", function() { var handler = sinon.spy(); this.server.respondWith( "GET", function() { return undefined; }, handler ); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); this.server.respond(); assert(!handler.called); }); it("adds function handler without method or url filter", function() { this.server.respondWith(function(xhr) { xhr.respond( 200, { "Content-Type": "application/json" }, // eslint-disable-next-line quotes '{"id":42}' ); }); var request = new FakeXMLHttpRequest(); request.open("GET", "/whatever"); request.send(); this.server.respond(); assert.equals(request.status, 200); assert.equals(request.responseHeaders, { "Content-Type": "application/json" }); assert.equals( request.responseText, // eslint-disable-next-line quotes '{"id":42}' ); }); it("does not process request further if processed by function", function() { var handler = sinon.spy(); this.server.respondWith("GET", "/aloha", [200, {}, "Oh hi"]); this.server.respondWith("GET", /\/a.*/, handler); var xhr = new FakeXMLHttpRequest(); xhr.respond = sinon.spy(); xhr.open("GET", "/aloha"); xhr.send(); this.server.respond(); assert(handler.called); assert(xhr.respond.calledOnce); }); it("yields URL capture groups to response handler", function() { var handler = sinon.spy(); this.server.respondWith("GET", /\/people\/(\d+)/, handler); var xhr = new FakeXMLHttpRequest(); xhr.respond = sinon.spy(); xhr.open("GET", "/people/3"); xhr.send(); this.server.respond(); assert(handler.called); assert.equals(handler.args[0], [xhr, "3"]); }); }); describe("respond with fake HTTP Verb", function() { beforeEach(function() { this.server = sinonFakeServer.create(); this.request = new FakeXMLHttpRequest(); this.request.open("post", "/path", true); this.request.send("_method=delete"); sinon.spy(this.request, "respond"); }); afterEach(function() { this.server.restore(); }); it("does not respond to DELETE request with _method parameter", function() { this.server.respondWith("DELETE", "", ""); this.server.respond(); assert.equals(this.request.respond.args[0], [404, {}, ""]); }); it("responds to 'fake' DELETE request", function() { this.server.fakeHTTPMethods = true; this.server.respondWith("DELETE", "", "OK"); this.server.respond(); assert.equals(this.request.respond.args[0], [200, {}, "OK"]); }); it("does not respond to POST when faking DELETE", function() { this.server.fakeHTTPMethods = true; this.server.respondWith("POST", "", "OK"); this.server.respond(); assert.equals(this.request.respond.args[0], [404, {}, ""]); }); it("does not fake method when not POSTing", function() { this.server.fakeHTTPMethods = true; this.server.respondWith("DELETE", "", "OK"); var request = new FakeXMLHttpRequest(); request.open("GET", "/"); request.send(); request.respond = sinon.spy(); this.server.respond(); assert.equals(request.respond.args[0], [404, {}, ""]); }); it("customizes HTTP method extraction", function() { this.server.getHTTPMethod = function() { return "PUT"; }; this.server.respondWith("PUT", "", "OK"); this.server.respond(); assert.equals(this.request.respond.args[0], [200, {}, "OK"]); }); it("does not fail when getting the HTTP method from a request with no body", function() { var server = this.server; server.fakeHTTPMethods = true; assert.equals(server.getHTTPMethod({ method: "POST" }), "POST"); }); }); describe(".respondAll", function() { beforeEach(function() { this.server = sinonFakeServer.create(); }); afterEach(function() { this.server.restore(); }); it("performs all the pending requests", function() { var server = this.server; server.respondWith("GET", "/first/url", "first body"); server.respondWith("GET", "/second/url", "second body"); server.respondWith("GET", "/third/url", "third body"); var request1 = new FakeXMLHttpRequest(); request1.open("GET", "/first/url"); request1.send(); request1.respond = sinon.spy(); var request2 = new FakeXMLHttpRequest(); request2.open("GET", "/second/url"); request2.send(); request2.respond = sinon.spy(); var request3 = new FakeXMLHttpRequest(); request3.open("GET", "/third/url"); request3.send(); request3.respond = sinon.spy(); assert.equals(server.responses.length, 3); assert.equals(server.requests.length, 3); assert.equals(server.queue.length, 3); assert.equals(request1.respond.args.length, 0); assert.equals(request2.respond.args.length, 0); assert.equals(request3.respond.args.length, 0); server.respondAll(); assert.equals(server.queue.length, 0); assert.equals(server.responses.length, 3); assert.equals(request1.respond.args[0], [200, {}, "first body"]); assert.equals(request2.respond.args[0], [200, {}, "second body"]); assert.equals(request3.respond.args[0], [200, {}, "third body"]); }); }); describe(".autoResponse", function() { beforeEach(function() { this.get = function get(url) { var request = new FakeXMLHttpRequest(); sinon.spy(request, "respond"); request.open("get", url, true); request.send(); return request; }; this.server = sinonFakeServer.create(); this.clock = sinon.useFakeTimers(); }); afterEach(function() { this.server.restore(); this.clock.uninstall(); }); it("responds async automatically after 10ms", function() { this.server.autoRespond = true; var request = this.get("/path"); this.clock.tick(10); assert.isTrue(request.respond.calledOnce); }); it("normal server does not respond automatically", function() { var request = this.get("/path"); this.clock.tick(100); assert.isTrue(!request.respond.called); }); it("auto-responds only once", function() { this.server.autoRespond = true; var requests = [this.get("/path")]; this.clock.tick(5); requests.push(this.get("/other")); this.clock.tick(5); assert.isTrue(requests[0].respond.calledOnce); assert.isTrue(requests[1].respond.calledOnce); }); it("auto-responds after having already responded", function() { this.server.autoRespond = true; var requests = [this.get("/path")]; this.clock.tick(10); requests.push(this.get("/other")); this.clock.tick(10); assert.isTrue(requests[0].respond.calledOnce); assert.isTrue(requests[1].respond.calledOnce); }); it("sets auto-respond timeout to 50ms", function() { this.server.autoRespond = true; this.server.autoRespondAfter = 50; var request = this.get("/path"); this.clock.tick(49); assert.isFalse(request.respond.called); this.clock.tick(1); assert.isTrue(request.respond.calledOnce); }); it("auto-responds if two successive requests are made with a single XHR", function() { this.server.autoRespond = true; var request = this.get("/path"); this.clock.tick(10); assert.isTrue(request.respond.calledOnce); request.open("get", "/other", true); request.send(); this.clock.tick(10); assert.isTrue(request.respond.calledTwice); }); it("auto-responds if timeout elapses between creating XHR object and sending request with it", function() { this.server.autoRespond = true; var request = new FakeXMLHttpRequest(); sinon.spy(request, "respond"); this.clock.tick(100); request.open("get", "/path", true); request.send(); this.clock.tick(10); assert.isTrue(request.respond.calledOnce); }); }); describe(".respondImmediately", function() { beforeEach(function() { this.get = function get(url) { var request = new FakeXMLHttpRequest(); sinon.spy(request, "respond"); request.open("get", url, true); request.send(); return request; }; this.server = sinonFakeServer.create(); this.server.respondImmediately = true; }); afterEach(function() { this.server.restore(); }); it("responds synchronously", function() { var request = this.get("/path"); assert.isTrue(request.respond.calledOnce); }); it("doesn't rely on a clock", function() { this.clock = sinon.useFakeTimers(); var request = this.get("/path"); assert.isTrue(request.respond.calledOnce); this.clock.uninstall(); }); }); describe(".log", function() { beforeEach(function() { this.server = sinonFakeServer.create(); }); afterEach(function() { this.server.restore(); }); it("logs response and request", function() { sinon.spy(this.server, "log"); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); var response = [200, {}, "Hello!"]; this.server.respond("GET", /.*/, response); assert(this.server.log.calledOnce); assert(this.server.log.calledWithExactly(response, xhr)); }); it("can be overridden", function() { this.server.log = sinon.spy(); var xhr = new FakeXMLHttpRequest(); xhr.open("GET", "/hello"); xhr.send(); var response = [200, {}, "Hello!"]; this.server.respond("GET", /.*/, response); assert(this.server.log.calledOnce); assert(this.server.log.calledWithExactly(response, xhr)); }); }); describe(".reset", function() { beforeEach(function() { this.server = sinonFakeServer.create(); this.resetBehaviorStub = sinon.stub(this.server, "resetBehavior"); this.resetHistoryStub = sinon.stub(this.server, "resetHistory"); }); afterEach(function() { this.server.restore(); this.resetBehaviorStub.restore(); this.resetHistoryStub.restore(); }); it("should call resetBehavior and resetHistory", function() { assert(this.resetBehaviorStub.notCalled); assert(this.resetHistoryStub.notCalled); this.server.reset(); assert(this.resetBehaviorStub.calledOnce); assert(this.resetBehaviorStub.calledWithExactly()); assert(this.resetHistoryStub.calledOnce); assert(this.resetHistoryStub.calledWithExactly()); assert(this.resetBehaviorStub.calledBefore(this.resetHistoryStub)); }); }); describe(".resetBehavior", function() { before(function() { // capture default response var self = this; sinonFakeServer.processRequest.call( { log: function(response) { self.defaultResponse = response; } }, // eslint-disable-next-line no-empty-function { respond: function() {} } ); }); function makeRequest(context) { context.request = new FakeXMLHttpRequest(); context.request.open("get", "url", true); context.request.send(null); sinon.spy(context.request, "respond"); } beforeEach(function() { this.server = sinonFakeServer.create(); this.testResponse = [200, {}, "OK"]; this.server.respondWith("GET", "url", this.testResponse); makeRequest(this); }); it("should reset behavior", function() { this.server.resetBehavior(); assert.equals(this.server.queue.length, 0); assert.equals(this.server.responses.length, 0); }); it("should work as expected", function() { this.server.respond(); assert.equals(this.request.respond.args[0], this.testResponse); this.server.resetBehavior(); makeRequest(this); this.server.respond(); assert.equals(this.request.respond.args[0], this.defaultResponse); }); it("should be idempotent", function() { this.server.respond(); assert.equals(this.request.respond.args[0], this.testResponse); // calling N times should have the same effect as calling once this.server.resetBehavior(); this.server.resetBehavior(); this.server.resetBehavior(); makeRequest(this); this.server.respond(); assert.equals(this.request.respond.args[0], this.defaultResponse); }); }); describe("history", function() { function assertDefaultServerState(server) { refute(server.requestedOnce); refute(server.requestedTwice); refute(server.requestedThrice); refute(server.requested); refute(server.firstRequest); refute(server.secondRequest); refute(server.thirdRequest); refute(server.lastRequest); } function makeRequest() { var request = new FakeXMLHttpRequest(); request.open("get", "url", true); request.send(null); } beforeEach(function() { this.server = sinonFakeServer.create(); }); describe(".getRequest", function() { it("should handle invalid indexes", function() { assert.isNull(this.server.getRequest(1e3)); assert.isNull(this.server.getRequest(0)); assert.isNull(this.server.getRequest(-2)); assert.isNull(this.server.getRequest("catpants")); }); it("should return expected requests", function() { makeRequest(); assert.equals( this.se