"use strict"; var assert = require("chai").assert; var sinon = require("sinon"); var express = require("express"); var GET = require("./util/http-utils").GET; var delay = function (method, payload) { setTimeout(function () { method(payload); }, 10); }; var PromiseRouter = require("../lib/express-promise-router.js"); describe("new Router().route(...)", function () { var app; var serverListening; var server; var router; var bootstrap = function (router) { app = express(); app.use("/", router); if (serverListening) { throw "already bootstrapped"; } serverListening = new Promise(function (resolve, reject) { server = app.listen(12345, function (err) { if (err) { reject(err); } else { resolve(); } }); }); return serverListening; }; beforeEach(function () { router = new PromiseRouter(); }); afterEach(function () { if (serverListening) { return serverListening.then(function () { server.close(); app = undefined; server = undefined; serverListening = undefined; }); } }); it("should call next with an error when a returned promise is rejected", function () { var callback = sinon.spy(); router.route("/foo").get(function () { return new Promise(function (resolve, reject) { delay(reject, "some error"); }); }); router.use(function (err, req, res, next) { assert.equal("some error", err); callback(); res.send(); }); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function () { assert(callback.calledOnce); }); }); it('should call next without an error when a returned promise is resolved with "next"', function () { var errorCallback = sinon.spy(); var nextCallback = sinon.spy(); router .route("/foo") .get(function () { return new Promise(function (resolve) { delay(resolve, "next"); }); }) .all(function (req, res) { nextCallback(); res.send(); }); router.use(function (err, req, res, next) { errorCallback(); next(); }); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function () { assert(errorCallback.notCalled); assert(nextCallback.calledOnce); }); }); it('should not call next when a returned promise is resolved with anything other than "route" or "next"', function () { var callback = sinon.spy(); router.route("/foo").get(function (req, res) { return new Promise(function (resolve) { res.send(); delay(resolve, "something"); }); }); router.route("/bar").get(function (req, res) { return new Promise(function (resolve) { res.send(); delay(resolve, {}); }); }); router.use(function (req, res) { callback(); res.send(500); }); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function () { assert(callback.notCalled); return GET("/bar"); }) .then(function () { assert(callback.notCalled); }); }); it("should move to the next middleware when next is called without an error", function () { var callback = sinon.spy(); router .route("/foo") .get(function (req, res, next) { next(); }) .all(function (req, res, next) { callback(); res.send(); }); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function () { assert(callback.calledOnce); }); }); it("should move to the next error handler when next is called with an error", function () { var callback = sinon.spy(); var errorCallback = sinon.spy(); router .route("/foo") .get(function (req, res, next) { next("an error"); }) .all(function (req, res, next) { callback(); next(); }); router.use(function (err, req, res, next) { assert.equal("an error", err); errorCallback(); res.send(); }); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function () { assert(errorCallback.calledOnce); assert(callback.notCalled); }); }); it("should call chained handlers in the correct order", function () { var fn2 = sinon.spy(function (req, res) { res.send(); }); var fn1 = sinon.spy(function () { assert(fn2.notCalled); return Promise.resolve("next"); }); router.route("/foo").get(fn1, fn2); return bootstrap(router).then(function () { return GET("/foo"); }); }); it("should correctly call an array of handlers", function () { var fn2 = sinon.spy(function (req, res) { res.send(); }); var fn1 = sinon.spy(function () { assert(fn2.notCalled); return Promise.resolve("next"); }); router.route("/foo").get([[fn1], [fn2]]); return bootstrap(router).then(function () { return GET("/foo"); }); }); it('should call next("route") if a returned promise is resolved with "route"', function () { var fn1 = function () { return Promise.resolve("route"); }; var fn2 = function () { assert.fail(); }; router.route("/foo").get(fn1, fn2); router.route("/foo").get(function (req, res) { res.send(); }); return bootstrap(router).then(function () { return GET("/foo"); }); }); it("should bind to RegExp routes", function () { var fn1 = function (req, res) { res.send(); }; router.route(/^\/foo/).get(fn1); return bootstrap(router).then(function () { return GET("/foo"); }); }); it('multiple calls to handlers that have used "next" should not interfere with each other', function () { var fn = sinon.spy(function (req, res, next) { if (fn.calledOnce) { next("error"); } else { setTimeout(function () { res.status(200).send("ok"); }, 15); } }); var errHandler = function (err, req, res, next) { if (err === "error") { res.send("fail"); } else { next(err); } }; router.route("/foo").get(fn, errHandler); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function (res) { assert.equal(res.body, "fail"); return GET("/foo"); }) .then(function (res) { assert.equal(res.body, "ok"); }); }); it("calls next if next is called even if the handler returns a promise", function () { var fn = function (req, res, next) { next(); return new Promise(function (resolve, reject) {}); }; var fn2 = function (req, res) { res.send("ok"); }; var errHandler = function (err, req, res, next) { res.send("error"); }; router.route("/foo").get(fn, fn2, errHandler); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function (res) { assert.equal(res.body, "ok"); }); }); it("calls next with an error if the returned promise is rejected with no reason", function () { var fn = function () { return new Promise(function (resolve, reject) { delay(reject, null); }); }; var errHandler = function (err, req, res, next) { res.send("error"); }; router.route("/foo").get(fn, errHandler); return bootstrap(router) .then(function () { return GET("/foo"); }) .then(function (res) { assert.equal(res.body, "error"); }); }); it("should handle resolved promises returned in req.param() calls", function () { router.param("id", function () { return new Promise(function (resolve) { delay(resolve, "next"); }); }); router.route("/foo/:id").all(function (req, res) { res.send("done"); }); return bootstrap(router) .then(function () { return GET("/foo/1"); }) .then(function (res) { assert.equal(res.body, "done"); }); }); });