diff --git a/gulpfile.js b/gulpfile.js index 29cec64..a0d08c9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -20,12 +20,12 @@ gulp.task('lint', function () { return gulp.src(['./gulpfile.js', './lib/**/*.js', './tests/**/*.spec.js']) .pipe(linter()) .pipe(linter.reporter('default', { - breakOnError: true + breakOnError: false })) }) gulp.task('test', ['lint'], function () { - return gulp.src('./tests/connect-sequence.spec.js') + return gulp.src('./tests/**/*.spec.js') .pipe(mocha()) }) diff --git a/lib/ConnectSequence.js b/lib/ConnectSequence.js new file mode 100644 index 0000000..0175af5 --- /dev/null +++ b/lib/ConnectSequence.js @@ -0,0 +1,129 @@ +'use strict' + +var MissingArgumentError = require('./errors/MissingArgumentError') + +module.exports = ConnectSequence + +/** + * @class + * @param {Array} [middlewareList] A list of middlewares to run + */ +function ConnectSequence () { + this.middlewares = [] +} + +ConnectSequence.run = deprecatedRun + +ConnectSequence.prototype = { + append: append, + appendList: appendList, + run: run +} + +/** + * Run sequencially each appended middleware, using the next argument as the final callback. + * @method + * @param {object} req The request object + * @param {object} res The response object + * @param {function} next The next middleware + * @returns {undefined} + */ +function run (req, res, next) { + var errorMsg + if (arguments.length < 3) { + errorMsg = 'ConnectSequence#run() takes 3 arguments. ' + errorMsg += arguments.length + ' given.' + throw new MissingArgumentError(errorMsg) + } + if (typeof req !== 'object') { + errorMsg = 'The first argument must be a request object. ' + errorMsg += typeof req + ' given.' + throw new TypeError(errorMsg) + } + if (typeof res !== 'object') { + errorMsg = 'The second argument must be a request object. ' + errorMsg += typeof req + ' given.' + throw new TypeError(errorMsg) + } + if (typeof next !== 'function') { + errorMsg = 'The third argument must be a middleware function. ' + errorMsg += typeof req + ' given.' + throw new TypeError(errorMsg) + } + + var midSequence = this.middlewares.reverse() + var initialNext = next.bind(null, req, res, next) + var nestedCallSequence = midSequence.reduce(middlewareReducer, initialNext) + nestedCallSequence.call() + + function middlewareReducer (prev, current) { + return current.bind(null, req, res, prev) + } +} + +/** + * Run sequentially each middleware in the given array, in the array order, throwing req and res object into. + * @function + * @static + * @deprecated + * @param {Object} req The request object + * @param {Object} res The response object + * @param {Function} initialNext The initial next middleware given at start, which should be run at last + * @param {Array} middlewares The given array of middlewares + * @example + ```js + var connectSequence = require('connect-sequence') + var mids = [] + mids.push(function (req, res, next) { ... }) + ... + mids.push(function (req, res, next) { ... }) + connectSequence.run(req, res, next, mids) + ``` + */ +function deprecatedRun (req, res, initialNext, middlewares) { + if (process.env.NODE_ENV !== 'test') { + var deprecatedMessage = 'WARNING: The static method ConnectSequence.run() is deprecated.' + deprecatedMessage += ' The method will be removed in a near release.' + deprecatedMessage += ' You should use the instance version of ConnectSequence.run().' + deprecatedMessage += ' See https://github.com/sirap-group/connect-sequence for a better documentation about the ConnectSequence instance' + console.log(deprecatedMessage) + } + middlewares.reverse().reduce(middlewareReducer, initialNext.bind(null, req, res, initialNext)).call() + + function middlewareReducer (prev, current) { + return current.bind(null, req, res, prev) + } +} + +/** + * Append an arbitrary number of middlewares as argument list or as an array + * @method + * @throws TypeError if one of the given middlewares is not a function. All the given middlewares would be rejected. + */ +function append () { + var i + for (i = 0; i < arguments.length; i++) { + if (typeof arguments[i] !== 'function') { + var type = typeof arguments[i] + var errMsg = 'Given middlewares must be functions. "' + type + '" given.' + throw new TypeError(errMsg) + } + } + for (i = 0; i < arguments.length; i++) { + this.middlewares.push(arguments[i]) + } +} + +/** + * Append many middlewares in an array + * @method + * @param {array} + */ +function appendList (middlewares) { + if (!Array.isArray(middlewares)) { + var errorMsg = 'First argument must be an array of middlewares. ' + errorMsg += typeof middlewares + ' given.' + throw new TypeError(errorMsg) + } + return this.append.apply(this, middlewares) +} diff --git a/lib/connect-sequence.js b/lib/connect-sequence.js deleted file mode 100644 index e96e929..0000000 --- a/lib/connect-sequence.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict' - -var connectSequence = {} -connectSequence.run = run - -module.exports = connectSequence - -/** - * Run sequentially each middleware in the given array, in the array order, throwing req and res object into. - * @function - * @static - * @param {Object} req The request object - * @param {Object} res The response object - * @param {Function} initialNext The initial next middleware given at start, which should be run at last - * @param {Array} middlewares The given array of middlewares - */ -function run (req, res, initialNext, middlewares) { - middlewares.reverse().reduce(middlewareReducer, initialNext.bind(null, req, res, initialNext)).call() - - function middlewareReducer (prev, current) { - return current.bind(null, req, res, prev) - } -} diff --git a/lib/errors/MissingArgumentError.js b/lib/errors/MissingArgumentError.js new file mode 100644 index 0000000..22c66fc --- /dev/null +++ b/lib/errors/MissingArgumentError.js @@ -0,0 +1,80 @@ +'use strict' + +var PrivateMethodError = require('./PrivateMethodError') + +module.exports = MissingArgumentError + +/** + * MissingArgumentError + * @class + * @property {String} message The error message + * @property {String} stack The error stack trace + * + * @constructor + * @param {String} [msg] The error message + */ +function MissingArgumentError (msg) { + // Error.call(this, msg) + if (msg && typeof msg === 'string') { + this.message = msg + } else { + this.message = MissingArgumentError.DEFAULT_ERROR_MESSAGE + } + this.createStackTrace() +} + +MissingArgumentError.DEFAULT_ERROR_MESSAGE = 'One or more argument are missing' + +MissingArgumentError.prototype = Object.create(Error.prototype) +MissingArgumentError.prototype.constructor = MissingArgumentError +MissingArgumentError.prototype.createStackTrace = createStackTrace + +/** + * Set the the correct stack trace for this error + * @method + * @private + * @returns {undefined} + * @throws {PrivateMethodError} + */ +function createStackTrace () { + privatize() + var stack = new Error().stack + var splited = stack.split('\n') + var modifiedStack = splited[0].concat('\n', splited.splice(3).join('\n')) + this.stack = modifiedStack +} + +/** + * Throws a PrivateMethodError if the third level of a new stack trace differs of the first + * @function + * @inner + * @returns {undefined} + * @throws {PrivateMethodError} + */ +function privatize () { + var trace = new Error().stack + var here = getFileCall(trace, 1) + var caller = getFileCall(trace, 3) + if (here !== caller) { + throw new PrivateMethodError() + } +} + +/** + * Get the file path of the caller at a given stack level + * @function + * @inner + * @param {String} trace A given stack trace + * @param {Number} level A given stack level to get the file path at this level + * @returns {String} The file path for this level + */ +function getFileCall (trace, level) { + var firstLineOfStack = trace.split('\n')[level] + var splitted = firstLineOfStack.split('(') + firstLineOfStack = splitted.splice(1).join('') + splitted = firstLineOfStack.split(')') + firstLineOfStack = splitted.join('') + splitted = firstLineOfStack.split(':') + firstLineOfStack = splitted.slice(0, -2).join('') + return firstLineOfStack +} diff --git a/lib/errors/PrivateMethodError.js b/lib/errors/PrivateMethodError.js new file mode 100644 index 0000000..28c85ef --- /dev/null +++ b/lib/errors/PrivateMethodError.js @@ -0,0 +1,77 @@ +'use strict' + +module.exports = PrivateMethodError + +PrivateMethodError.DEFAULT_ERROR_MESSAGE = 'This is a private method' + +PrivateMethodError.prototype = Object.create(Error.prototype) +PrivateMethodError.prototype.constructor = PrivateMethodError +PrivateMethodError.prototype.createStackTrace = createStackTrace + +/** + * PrivateMethodError + * @class + * @property {String} message The error message + * @property {String} stack The error stack trace + * + * @constructor + * @param {String} msg The error message + */ +function PrivateMethodError (msg) { + if (msg && typeof msg === 'string') { + this.message = msg + } else { + this.message = PrivateMethodError.DEFAULT_ERROR_MESSAGE + } + this.createStackTrace() +} + +/** + * Set the the correct stack trace for this error + * @method + * @private + * @returns {undefined} + * @throws {PrivateMethodError} + */ +function createStackTrace () { + privatize() + var stack = new Error().stack + var splited = stack.split('\n') + var modifiedStack = splited[0].concat('\n', splited.splice(3).join('\n')) + this.stack = modifiedStack +} + +/** + * Throws a PrivateMethodError if the third level of a new stack trace differs of the first + * @function + * @inner + * @returns {undefined} + * @throws {PrivateMethodError} + */ +function privatize () { + var trace = new Error().stack + var here = getFileCall(trace, 1) + var caller = getFileCall(trace, 3) + if (here !== caller) { + throw new PrivateMethodError() + } +} + +/** + * Get the file path of the caller at a given stack level + * @function + * @inner + * @param {String} trace A given stack trace + * @param {Number} level A given stack level to get the file path at this level + * @returns {String} The file path for this level + */ +function getFileCall (trace, level) { + var firstLineOfStack = trace.split('\n')[level] + var splitted = firstLineOfStack.split('(') + firstLineOfStack = splitted.splice(1).join('') + splitted = firstLineOfStack.split(')') + firstLineOfStack = splitted.join('') + splitted = firstLineOfStack.split(':') + firstLineOfStack = splitted.slice(0, -2).join('') + return firstLineOfStack +} diff --git a/package.json b/package.json index a1ac134..6b809b4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "test": "gulp test", + "TDD": "gulp test 2> /dev/null; gulp watch", "coverage": "gulp coverage" }, "repository": { diff --git a/tests/ConnectSequence.spec.js b/tests/ConnectSequence.spec.js new file mode 100644 index 0000000..4567307 --- /dev/null +++ b/tests/ConnectSequence.spec.js @@ -0,0 +1,466 @@ +'use strict' + +var path = require('path') +var chai = require('chai') +var ConnectSequence = require(path.resolve('./lib/ConnectSequence')) +var MissingArgumentError = require(path.resolve('./lib/errors/MissingArgumentError')) + +var describe = global.describe +var beforeEach = global.beforeEach +var it = global.it +var expect = chai.expect + +process.env.NODE_ENV = 'test' + +describe('ConnectSequence', function () { + it('should be a function', function () { + expect(ConnectSequence).to.be.a('function') + }) + + it('should have a "run" property', function () { + expect(ConnectSequence).to.have.property('run') + }) + + it('should have a prototype', function () { + expect(ConnectSequence).to.have.property('prototype') + }) + + describe('.run()', function () { + it('should be a function', function () { + expect(ConnectSequence.run).to.be.a('function') + }) + + it('should throw an Error if one argument is missing', function () { + var initMid = function (req, res) {} + var noopMid = function (req, res, next) { next() } + var run0 = function () { + ConnectSequence.run() + } + var run1 = function () { + ConnectSequence.run({}) + } + var run2 = function () { + ConnectSequence.run({}, {}) + } + var run3 = function () { + ConnectSequence.run({}, {}, function (next) { next() }) + } + var run4 = function () { + ConnectSequence.run({}, {}, noopMid) + } + var run5 = function () { + ConnectSequence.run({}, {}, initMid, [noopMid, noopMid, noopMid]) + } + expect(run0).to.throw(Error) + expect(run1).to.throw(Error) + expect(run2).to.throw(Error) + expect(run3).to.throw(Error) + expect(run4).to.throw(Error) + expect(run5).to.not.throw(Error) + }) + + it('should run the initial next middleware at last', function () { + var _req = {} + var _res = {} + var _next = function (req, res) { + req.output = 'initialNext' + expect(req.output).to.equal('initialNext') + } + var first = function (req, res, next) { + req.output = 'first' + } + var second = first + var third = first + var fourth = first + var mids = [first, second, third, fourth] + + ConnectSequence.run(_req, _res, _next, mids) + }) + + it('should run all the middlewares in the passed array of middlewares', function () { + var _req = { + ids: [] + } + var _res = {} + var _next = function (req, res) { + req.ids.push('initial') + expect(req.ids).to.contain('initial') + expect(req.ids).to.contain('first') + expect(req.ids).to.contain('second') + expect(req.ids).to.contain('third') + expect(req.ids).to.contain('fourth') + } + var first = function (req, res, next) { + req.ids.push('first') + } + var second = function (req, res, next) { + req.ids.push('second') + } + var third = function (req, res, next) { + req.ids.push('third') + } + var fourth = function (req, res, next) { + req.ids.push('fourth') + } + var mids = [first, second, third, fourth] + + ConnectSequence.run(_req, _res, _next, mids) + }) + + it('should run all the middlewares in the same order than the given array', function () { + var _req = { + ids: [] + } + var _res = {} + var _next = function (req, res) { + req.ids.push('last') + expect(req.ids.join()).to.equal('first,second,third,fourth,last') + } + var first = function (req, res, next) { + req.ids.push('first') + } + var second = function (req, res, next) { + req.ids.push('second') + } + var third = function (req, res, next) { + req.ids.push('third') + } + var fourth = function (req, res, next) { + req.ids.push('fourth') + } + var mids = [first, second, third, fourth] + + ConnectSequence.run(_req, _res, _next, mids) + }) + + it('should run each middleware as a callback of the previous', function () { + var _req = { + ids: [] + } + var _res = {} + var _next = function (req, res) { + if (req && req.ids) { + req.ids.push('last') + expect(req.ids.join()).to.equal('first,second,third,fourth,last') + } + } + var first = function (req, res, next) { + setTimeout(function () { + req.ids.push('first') + next() + }, 150) + } + var second = function (req, res, next) { + setTimeout(function () { + req.ids.push('second') + next() + }, 50) + } + var third = function (req, res, next) { + setTimeout(function () { + req.ids.push('third') + next() + }, 100) + } + var fourth = function (req, res, next) { + setTimeout(function () { + req.ids.push('fourth') + next() + }, 150) + } + var mids = [first, second, third, fourth] + + ConnectSequence.run(_req, _res, _next, mids) + }) + }) + + describe('.prototype', function () { + it('should be an object', function () { + expect(ConnectSequence.prototype).to.be.a('object') + }) + + it('should have a "append" method', function () { + expect(ConnectSequence.prototype).to.have.property('append') + expect(ConnectSequence.prototype.append).to.be.a('function') + }) + + it('should have a "appendList" method', function () { + expect(ConnectSequence.prototype).to.have.property('appendList') + expect(ConnectSequence.prototype.appendList).to.be.a('function') + }) + + it('should have a "run" method', function () { + expect(ConnectSequence.prototype).to.have.property('run') + expect(ConnectSequence.prototype.appendList).to.be.a('function') + }) + + describe('.constructor', function () { + it('should be an object of type "ConnectSequence"', function () { + var seq = new ConnectSequence() + expect(seq).to.be.a('object') + expect(seq).to.be.an.instanceof(ConnectSequence) + }) + + it('should init the "middleware" instance property as an empty Array', function () { + var seq = new ConnectSequence() + expect(seq).to.has.a.property('middlewares') + expect(seq.middlewares).to.be.a('array') + expect(seq.middlewares.length).to.be.equal(0) + }) + }) + }) + + describe('#append()', function () { + it('should augments the length of the middlewares array by the number of given middlewares', function () { + var seq = new ConnectSequence() + var mid = function (req, res, next) { next() } + seq.append(mid) + expect(seq.middlewares.length).to.equal(1) + seq.append(mid) + expect(seq.middlewares.length).to.equal(2) + seq.append(mid, mid) + expect(seq.middlewares.length).to.equal(4) + seq.append() + expect(seq.middlewares.length).to.equal(4) + seq.append(mid, mid, mid) + expect(seq.middlewares.length).to.equal(7) + }) + + it('should keep the same order of the given middlewares', function () { + var seq = new ConnectSequence() + var mid0 = function (req, res, next) { next() } + var mid1 = function (req, res, next) { next() } + var mid2 = function (req, res, next) { next() } + var mid3 = function (req, res, next) { next() } + var mid4 = function (req, res, next) { next() } + var mid5 = function (req, res, next) { next() } + var mid6 = function (req, res, next) { next() } + seq.append(mid0) + seq.append(mid1, mid2) + seq.append() + seq.append(mid3, mid4, mid5) + seq.append(mid6) + var mids = [mid0, mid1, mid2, mid3, mid4, mid5, mid6] + for (var i = 0; i < mids.length; i++) { + var mid = mids[i] + expect(seq.middlewares[i]).to.equal(mid) + } + }) + + it('should throw a TypeError and reject all middlewares if one is not a function', function () { + var seq = new ConnectSequence() + var mid1 = function (req, res, next) { next() } + var mid2 = 'not a function' + var mid3 = function (req, res, next) { next() } + var cantAppend = function () { + seq.append(mid1, mid2, mid3) + } + var shouldAppend = function () { + seq.append(mid1, mid3) + } + expect(cantAppend).to.throw(TypeError) + expect(seq.middlewares.length).to.be.equal(0) + expect(shouldAppend).to.not.throw(Error) + expect(seq.middlewares.length).to.be.equal(2) + }) + }) + + describe('#appendList()', function () { + it('should throw TypeError if the first argument is not an array', function () { + var funcs = [ + function () { + var seq = new ConnectSequence() + seq.appendList('not an array') + }, + function () { + var seq = new ConnectSequence() + seq.appendList({foo: 'not an array'}) + }, + function () { + var seq = new ConnectSequence() + seq.appendList(function () { return 'not an array' }) + } + ] + for (var i = 0; i < funcs.length; i++) { + expect(funcs[i]).to.throw(TypeError) + } + }) + + it('should augments the length of the middlewares array by the number of given middlewares', function () { + var seq = new ConnectSequence() + var mid = function (req, res, next) { next() } + seq.appendList([mid]) + expect(seq.middlewares.length).to.equal(1) + seq.appendList([mid]) + expect(seq.middlewares.length).to.equal(2) + seq.appendList([mid, mid]) + expect(seq.middlewares.length).to.equal(4) + seq.appendList([]) + expect(seq.middlewares.length).to.equal(4) + seq.appendList([mid, mid, mid]) + expect(seq.middlewares.length).to.equal(7) + }) + + it('should keep the same order of the given middlewares', function () { + var seq = new ConnectSequence() + var mid0 = function (req, res, next) { next() } + var mid1 = function (req, res, next) { next() } + var mid2 = function (req, res, next) { next() } + var mid3 = function (req, res, next) { next() } + var mid4 = function (req, res, next) { next() } + var mid5 = function (req, res, next) { next() } + var mid6 = function (req, res, next) { next() } + seq.appendList([mid0]) + seq.appendList([mid1, mid2]) + seq.appendList([]) + seq.appendList([mid3, mid4, mid5]) + seq.appendList([mid6]) + var mids = [mid0, mid1, mid2, mid3, mid4, mid5, mid6] + for (var i = 0; i < mids.length; i++) { + var mid = mids[i] + expect(seq.middlewares[i]).to.equal(mid) + } + }) + + it('should throw a TypeError and reject all middlewares if one is not a function', function () { + var seq = new ConnectSequence() + var mid1 = function (req, res, next) { next() } + var mid2 = 'not a function' + var mid3 = function (req, res, next) { next() } + var cantAppend = function () { + seq.appendList([mid1, mid2, mid3]) + } + var shouldAppend = function () { + seq.appendList([mid1, mid3]) + } + expect(cantAppend).to.throw(TypeError) + expect(seq.middlewares.length).to.be.equal(0) + expect(shouldAppend).to.not.throw(Error) + expect(seq.middlewares.length).to.be.equal(2) + }) + }) + + describe('#run()', function () { + var seq, req, res, next + var mid0, mid1, mid2, mid3 + var func0, func1, func2 + + beforeEach(function () { + seq = new ConnectSequence() + req = {} + res = {} + mid0 = function mid0 (req, res, next) { + ensureReqIdsDefined(req) + req.ids.push('mid0') + next() + } + mid1 = function mid1 (req, res, next) { + ensureReqIdsDefined(req) + req.ids.push('mid1') + next() + } + mid2 = function mid2 (req, res, next) { + ensureReqIdsDefined(req) + req.ids.push('mid2') + next() + } + mid3 = function mid3 (req, res, next) { + ensureReqIdsDefined(req) + req.ids.push('mid3') + next() + } + }) + + it('should be a function', function () { + expect(seq.run).to.be.a('function') + }) + + it('should throw MissingArgumentError if called with lower than 3 arguments', function () { + func0 = function () { + seq.appendList([mid0, mid1, mid2, mid3]) + seq.run(req, res) + } + expect(func0).to.throw(MissingArgumentError) + }) + + it('should throw TypeError if the given arguments have a bad type', function () { + next = function (req, res) { return true } + seq.append(mid0, mid1, mid2, mid3) + + func0 = function () { seq.run('not an object', {}, next) } + func1 = function () { seq.run({}, 'not an object', next) } + func2 = function () { seq.run({}, {}, 'not a function') } + + var funcs = [func0, func1, func2] + for (var i = 0; i < funcs.length; i++) { + expect(funcs[i]).to.throw(TypeError) + } + }) + + it('should run the initial next middleware at last', function (done) { + next = function (req, res) { + req.ids = 'initialNext' + done() + } + seq.append(mid0, mid0, mid0, mid0) + seq.run(req, res, next) + expect(req.ids).to.be.a(String) + expect(req.ids).to.equal('initialNext') + }) + + it('should run all the middlewares in the passed array of middlewares', function (done) { + next = function (req, res) { + req.ids.push('initial') + expect(req.ids).to.contain('initial') + expect(req.ids).to.contain('mid0') + expect(req.ids).to.contain('mid1') + expect(req.ids).to.contain('mid2') + expect(req.ids).to.contain('mid3') + setTimeout(done, 20) + } + seq.append(mid0, mid1, mid2, mid3) + seq.run(req, res, next) + }) + + it('should run all the middlewares in the same order than the given array', function (done) { + next = function (req, res) { + req.ids.push('initial') + expect(req.ids.join()).to.equal('mid0,mid1,mid2,mid3,initial') + setTimeout(done, 20) + } + seq.append(mid0, mid1, mid2, mid3) + seq.run(req, res, next) + }) + + it('should run each middleware as a callback of the previous', function (done) { + this.slow(500) + next = function (req, res) { + if (req && req.ids) { + req.ids.push('initial') + expect(req.ids.join()).to.equal('mid0,mid1,mid2,mid3,initial') + done() + } + } + var _mid0 = function (req, res, next) { + setTimeout(mid0.bind(null, req, res, next), 150) + } + var _mid1 = function (req, res, next) { + setTimeout(mid1.bind(null, req, res, next), 50) + } + var _mid2 = function (req, res, next) { + setTimeout(mid2.bind(null, req, res, next), 100) + } + var _mid3 = function (req, res, next) { + setTimeout(mid3.bind(null, req, res, next), 150) + } + seq.append(_mid0, _mid1, _mid2, _mid3) + seq.run(req, res, next) + }) + }) +}) + +function ensureReqIdsDefined (req) { + if (!req.ids) { + req.ids = [] + } +} diff --git a/tests/connect-sequence.spec.js b/tests/connect-sequence.spec.js deleted file mode 100644 index 8366dd3..0000000 --- a/tests/connect-sequence.spec.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict' - -var path = require('path') -var chai = require('chai') -var connectSequence = require(path.resolve('./lib/connect-sequence')) - -var describe = global.describe -var it = global.it -var expect = chai.expect - -describe('connectSequence.run()', function () { - it('should run the initial next middleware at last', function () { - var _req = {} - var _res = {} - var _next = function (req, res) { - req.output = 'initialNext' - expect(req.output).to.equal('initialNext') - } - var first = function (req, res, next) { - req.output = 'first' - } - var second = first - var third = first - var fourth = first - var mids = [first, second, third, fourth] - - connectSequence.run(_req, _res, _next, mids) - }) - - it('should run all the middlewares in the passed array of middlewares', function () { - var _req = { - ids: [] - } - var _res = {} - var _next = function (req, res) { - req.ids.push('initial') - expect(req.ids).to.contain('initial') - expect(req.ids).to.contain('first') - expect(req.ids).to.contain('second') - expect(req.ids).to.contain('third') - expect(req.ids).to.contain('fourth') - } - var first = function (req, res, next) { - req.ids.push('first') - } - var second = function (req, res, next) { - req.ids.push('second') - } - var third = function (req, res, next) { - req.ids.push('third') - } - var fourth = function (req, res, next) { - req.ids.push('fourth') - } - var mids = [first, second, third, fourth] - - connectSequence.run(_req, _res, _next, mids) - }) - - it('should run all the middlewares in the same order than the given array', function () { - var _req = { - ids: [] - } - var _res = {} - var _next = function (req, res) { - req.ids.push('last') - expect(req.ids.join()).to.equal('first,second,third,fourth,last') - } - var first = function (req, res, next) { - req.ids.push('first') - } - var second = function (req, res, next) { - req.ids.push('second') - } - var third = function (req, res, next) { - req.ids.push('third') - } - var fourth = function (req, res, next) { - req.ids.push('fourth') - } - var mids = [first, second, third, fourth] - - connectSequence.run(_req, _res, _next, mids) - }) - - it('should run each middleware as a callback of the previous', function () { - var _req = { - ids: [] - } - var _res = {} - var _next = function (req, res) { - if (req && req.ids) { - req.ids.push('last') - expect(req.ids.join()).to.equal('first,second,third,fourth,last') - } - } - var first = function (req, res, next) { - setTimeout(function () { - req.ids.push('first') - next() - }, 150) - } - var second = function (req, res, next) { - setTimeout(function () { - req.ids.push('second') - next() - }, 50) - } - var third = function (req, res, next) { - setTimeout(function () { - req.ids.push('third') - next() - }, 100) - } - var fourth = function (req, res, next) { - setTimeout(function () { - req.ids.push('fourth') - next() - }, 150) - } - var mids = [first, second, third, fourth] - - connectSequence.run(_req, _res, _next, mids) - }) -}) diff --git a/tests/errors/MissingArgumentError.spec.js b/tests/errors/MissingArgumentError.spec.js new file mode 100644 index 0000000..4bf76cd --- /dev/null +++ b/tests/errors/MissingArgumentError.spec.js @@ -0,0 +1,81 @@ +'use strict' + +var path = require('path') +var chai = require('chai') +var MissingArgumentError = require(path.resolve('./lib/errors/MissingArgumentError')) + +var describe = global.describe +var it = global.it +var expect = chai.expect + +describe('MissingArgumentError', function () { + it('should be a function', function () { + expect(MissingArgumentError).to.be.a('function') + }) + it("should has a 'DEFAULT_ERROR_MESSAGE' static constant property", function () { + expect(MissingArgumentError).to.be.a.property('DEFAULT_ERROR_MESSAGE') + }) + + describe('.prototype', function () { + it('should be a function', function () { + expect(MissingArgumentError.prototype).to.be.a('OBJECT') + }) + it('should be an instance of Error', function () { + expect(MissingArgumentError.prototype).to.be.an.instanceof(Error) + }) + + describe('.constructor()', function () { + it('should be an instance of Error', function () { + var err = new MissingArgumentError() + expect(MissingArgumentError.prototype).to.be.an.instanceof(Error) + expect(err).to.be.an.instanceof(MissingArgumentError) + }) + it("should has a 'name' property of type 'String'", function () { + var msg = "'arg' is missing" + var err = new MissingArgumentError(msg) + expect(err).to.be.a.property('name') + expect(err.message).to.be.a('String') + }) + it("should has a 'message' property of type 'String'", function () { + var msg = "'arg' is missing" + var err = new MissingArgumentError(msg) + expect(err).to.be.a.property('message') + expect(err.message).to.be.a('String') + }) + it("should has a 'stack' property of type 'String'", function () { + var msg = "'arg' is missing" + var err = new MissingArgumentError(msg) + expect(err).to.be.a.property('stack') + expect(err.stack).to.be.a('String') + }) + it('should not fail if an bad type is given instead of a string as first argument', function () { + var err + var func = function () { err = new MissingArgumentError(func) } + var obj = function () { err = new MissingArgumentError({ foo: 'bar' }) } + var arr = function () { err = new MissingArgumentError([ 'foo', 'baz' ]) } + + expect(func).to.not.throw('TypeError') + expect(err.message).to.be.a('String') + + expect(obj).to.not.throw('TypeError') + expect(err.message).to.be.a('String') + + expect(arr).to.not.throw('TypeError') + expect(err.message).to.be.a('String') + }) + it('should fails if we call the private createStackTrace method', function () { + var func = function () { + var err = new MissingArgumentError(func) + var t = err.createStackTrace() + console.log(t) + } + expect(func).to.throw(Error) + }) + it('should has a default error message if no argument is given', function () { + var err = new MissingArgumentError() + expect(err.message).to.be.a('String') + expect(err.message).to.be.equal(MissingArgumentError.DEFAULT_ERROR_MESSAGE) + }) + }) + }) +}) diff --git a/tests/errors/PrivateMethodError.spec.js b/tests/errors/PrivateMethodError.spec.js new file mode 100644 index 0000000..7f07e3a --- /dev/null +++ b/tests/errors/PrivateMethodError.spec.js @@ -0,0 +1,80 @@ +'use strict' + +var path = require('path') +var chai = require('chai') +var PrivateMethodError = require(path.resolve('./lib/errors/PrivateMethodError')) + +var describe = global.describe +var it = global.it +var expect = chai.expect + +describe('PrivateMethodError', function () { + it('should be a function', function () { + expect(PrivateMethodError).to.be.a('function') + }) + it("should has a 'DEFAULT_ERROR_MESSAGE' static constant property", function () { + expect(PrivateMethodError).to.be.a.property('DEFAULT_ERROR_MESSAGE') + }) + + describe('.prototype', function () { + it('should be an object', function () { + expect(PrivateMethodError.prototype).to.be.a('object') + }) + it('should be an instance of Error', function () { + expect(PrivateMethodError.prototype).to.be.an.instanceof(Error) + }) + + describe('.constructor()', function () { + it('should be an instance of PrivateMethodError', function () { + var err = new PrivateMethodError() + expect(err).to.be.an.instanceof(PrivateMethodError) + }) + it("should has a 'name' property of type 'String'", function () { + var msg = 'i am private' + var err = new PrivateMethodError(msg) + expect(err).to.be.a.property('name') + expect(err.message).to.be.a('String') + }) + it("should has a 'message' property of type 'String'", function () { + var msg = 'i am private' + var err = new PrivateMethodError(msg) + expect(err).to.be.a.property('message') + expect(err.message).to.be.a('String') + }) + it("should has a 'stack' property of type 'String'", function () { + var msg = 'i am private' + var err = new PrivateMethodError(msg) + expect(err).to.be.a.property('stack') + expect(err.stack).to.be.a('String') + }) + it('should not fail if an bad type is given instead of a string as first argument', function () { + var err + var func = function () { err = new PrivateMethodError(func) } + var obj = function () { err = new PrivateMethodError({ foo: 'bar' }) } + var arr = function () { err = new PrivateMethodError([ 'foo', 'baz' ]) } + + expect(func).to.not.throw('TypeError') + expect(err.message).to.be.a('String') + + expect(obj).to.not.throw('TypeError') + expect(err.message).to.be.a('String') + + expect(arr).to.not.throw('TypeError') + expect(err.message).to.be.a('String') + }) + it('should fails if we call the private createStackTrace method', function () { + var func = function () { + var err = new PrivateMethodError(func) + var t = err.createStackTrace() + console.log(t) + } + expect(func).to.throw(Error) + }) + it('should has a default error message if no argument is given', function () { + var err = new PrivateMethodError() + expect(err.message).to.be.a('String') + expect(err.message).to.be.equal(PrivateMethodError.DEFAULT_ERROR_MESSAGE) + }) + }) + }) +})