You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
9.3 KiB
401 lines
9.3 KiB
"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("express-promise-router", 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.use("/foo", 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.use("/foo", function () {
|
|
return new Promise(function (resolve) {
|
|
delay(resolve, "next");
|
|
});
|
|
});
|
|
router.use("/foo", 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.get("/foo", function (req, res) {
|
|
return new Promise(function (resolve) {
|
|
res.send();
|
|
delay(resolve, "something");
|
|
});
|
|
});
|
|
router.get("/bar", 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.use("/foo", function (req, res, next) {
|
|
next();
|
|
});
|
|
router.use("/foo", 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.use("/foo", function (req, res, next) {
|
|
next("an error");
|
|
});
|
|
router.use("/foo", 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.get("/foo", 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.get("/foo", [[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.get("/foo", fn1, fn2);
|
|
router.get("/foo", 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.get(/^\/foo/, 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.get("/foo", 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.get("/foo", 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.get("/foo", 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.use("/foo/:id", function (req, res) {
|
|
res.send("done");
|
|
});
|
|
|
|
return bootstrap(router)
|
|
.then(function () {
|
|
return GET("/foo/1");
|
|
})
|
|
.then(function (res) {
|
|
assert.equal(res.body, "done");
|
|
});
|
|
});
|
|
|
|
it("should call next with unresolved promises returned in req.param() calls", function () {
|
|
var assertOutput = "error in param";
|
|
|
|
router.param("id", function (req, res, next, id) {
|
|
return new Promise(function (resolve, reject) {
|
|
delay(reject, assertOutput);
|
|
});
|
|
});
|
|
|
|
var fn = function (req, res) {
|
|
res.send("done");
|
|
};
|
|
|
|
var errHandler = function (err, req, res, next) {
|
|
res.send(err);
|
|
};
|
|
|
|
router.use("/foo/:id", fn);
|
|
|
|
router.use(errHandler);
|
|
|
|
return bootstrap(router)
|
|
.then(function () {
|
|
return GET("/foo/1");
|
|
})
|
|
.then(function (res) {
|
|
assert.equal(res.body, assertOutput);
|
|
});
|
|
});
|
|
|
|
it("support array in routes values", function () {
|
|
router.use(["/", "/foo/:bar"], function (req, res) {
|
|
res.send("done");
|
|
});
|
|
|
|
return bootstrap(router)
|
|
.then(function () {
|
|
return GET("/");
|
|
})
|
|
.then(function (res) {
|
|
assert.equal(res.body, "done");
|
|
|
|
return GET("/foo/1");
|
|
})
|
|
.then(function (res) {
|
|
assert.equal(res.body, "done");
|
|
});
|
|
});
|
|
|
|
it("should throw sensible errors when handler is not a function", function () {
|
|
assert.throws(function () {
|
|
router.use("/foo/:id", null);
|
|
}, /callback/);
|
|
});
|
|
});
|
|
|