middlewarify
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -6,6 +6,6 @@ /** | ||
var __ = require('lodash'); | ||
var Promise = require('bluebird'); | ||
var middlewarify = module.exports = {}; | ||
var noop = function() {}; | ||
var noopMidd = function(cb) {if (__.isFunction(cb)) cb();}; | ||
@@ -27,3 +27,4 @@ | ||
* @param {Object=} optParams Optional parameters. | ||
* @param {boolean=} throwErrors default is true. | ||
* @param {boolean=} beforeAfter set to true to add Before/After hooks | ||
* instead of the single use hook. | ||
*/ | ||
@@ -41,3 +42,2 @@ middlewarify.make = function(obj, prop, optFinalCb, optParams) { | ||
var defaultParams = { | ||
throwErrors: true, | ||
beforeAfter: false, | ||
@@ -82,30 +82,16 @@ }; | ||
var args = Array.prototype.slice.call(arguments, 1); | ||
var doneArgs; | ||
var isDone = false; | ||
var doneActual = noop; | ||
var done = function() { | ||
isDone = true; | ||
doneArgs = arguments; | ||
doneActual.apply(null, arguments); | ||
}; | ||
var midds; | ||
if (middObj.params.beforeAfter) { | ||
midds = Array.prototype.slice.call(middObj.beforeMidds, 0); | ||
midds.push(middObj.mainCallback); | ||
midds = midds.concat(middObj.afterMidds); | ||
} else { | ||
midds = Array.prototype.slice.call(middObj.midds, 0); | ||
midds.push(middObj.mainCallback); | ||
} | ||
middlewarify._fetchAndInvoke(midds, args, middObj.params, done); | ||
return {done: function(fn) { | ||
if (isDone) { | ||
fn.apply(null, doneArgs); | ||
return new Promise(function(resolve, reject) { | ||
var deferred = Promise.defer(); | ||
deferred.promise.then(resolve, reject); | ||
var midds; | ||
if (middObj.params.beforeAfter) { | ||
midds = Array.prototype.slice.call(middObj.beforeMidds); | ||
midds.push(middObj.mainCallback); | ||
midds = midds.concat(middObj.afterMidds); | ||
} else { | ||
doneActual = fn; | ||
midds = Array.prototype.slice.call(middObj.midds); | ||
midds.push(middObj.mainCallback); | ||
} | ||
}}; | ||
middlewarify._fetchAndInvoke(midds, args, deferred); | ||
}); | ||
}; | ||
@@ -118,35 +104,70 @@ | ||
* @param {Array} args An array of arbitrary arguments, can be empty. | ||
* @param {Object} params Parameters passed by the user. | ||
* @param {Function} done Callback. | ||
* @param {...*} optMiddArgs Arguments passed from the last middleware. | ||
* @param {Promise.Defer} deferred Deferred object. | ||
* @private | ||
*/ | ||
middlewarify._fetchAndInvoke = function(midds, args, params, done, optMiddArgs) { | ||
var lastMiddArgs = optMiddArgs || []; | ||
middlewarify._fetchAndInvoke = function(midds, args, deferred) { | ||
if (midds.length === 0) { | ||
return deferred.resolve(); | ||
} | ||
if (0 === midds.length) { | ||
lastMiddArgs.unshift(null); | ||
return done.apply(null, lastMiddArgs); | ||
var midd = midds.shift(); | ||
var arity = midd.length; | ||
var argsLen = args.length; | ||
/** @type {boolean} User defined callback */ | ||
var hasCb = false; | ||
// check if there's a callback defined, be explicit expect arity | ||
// to be args.length + 1 | ||
if (arity - argsLen === 1) { | ||
hasCb = true; | ||
} | ||
var midd = midds.shift(); | ||
try { | ||
midd.apply(null, args.concat(function(err){ | ||
if (err) { | ||
done(err); | ||
} else { | ||
var middArgs = Array.prototype.slice.call(arguments, 1); | ||
middlewarify._fetchAndInvoke(midds, args, params, done, middArgs); | ||
} | ||
})); | ||
} catch(ex) { | ||
if (params.throwErrors) { | ||
throw ex; | ||
function userCb(err) { | ||
if (err) { | ||
deferred.reject(err); | ||
} else { | ||
done(ex); | ||
middlewarify._fetchAndInvoke(midds, args, deferred); | ||
} | ||
} | ||
var invokeArgs = Array.prototype.slice.call(args); | ||
if (hasCb) { | ||
invokeArgs.push(userCb); | ||
} | ||
middlewarify._invoke(midd, invokeArgs, function(err) { | ||
if (err) { | ||
return deferred.reject(err); | ||
} | ||
if (!hasCb) { | ||
middlewarify._fetchAndInvoke(midds, args, deferred); | ||
} | ||
}); | ||
}; | ||
/** | ||
* The actual invocation of the middleware happens here. | ||
* | ||
* @param {Function} midd The middleware to invoke. | ||
* @param {Array} invokeArgs Arguments to invoke middleware with. | ||
* @param {Function(Error=)} cb callback. | ||
* @private | ||
*/ | ||
middlewarify._invoke = function(midd, invokeArgs, cb) { | ||
try { | ||
var maybePromise = midd.apply(null, invokeArgs); | ||
if (Promise.is(maybePromise)) { | ||
return maybePromise.then(cb, function(err) { | ||
cb(err || new Error()); | ||
}); | ||
} | ||
cb(); | ||
} catch(ex) { | ||
cb(ex); | ||
} | ||
}; | ||
/** | ||
* Add middleware. | ||
@@ -153,0 +174,0 @@ * |
{ | ||
"name": "middlewarify", | ||
"description": "Apply the middleware pattern to any function.", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"homepage": "https://github.com/thanpolas/middlewarify", | ||
@@ -34,3 +34,4 @@ "author": { | ||
"lodash": "~1.3.1", | ||
"async": "~0.2.9" | ||
"async": "~0.2.9", | ||
"bluebird": "~1.0.3" | ||
}, | ||
@@ -37,0 +38,0 @@ "devDependencies": { |
203
README.md
# Middlewarify | ||
Apply the middleware pattern, easy. You can add two types of middleware, a single queue type using the keyword `use()` or a Before/After type using `before()` and `after()` hooks. | ||
Middleware pattern implementation, robust, easy, fast. You can add two types of middleware, a single queue type using the keyword `use()` or a Before/After type using `before()` and `after()` hooks. All middleware accept promises or vanilla callbacks and final resolution is done using the Promises/A+ spec. | ||
@@ -47,8 +47,8 @@ [![Build Status](https://travis-ci.org/thanpolas/middlewarify.png)](https://travis-ci.org/thanpolas/middlewarify) | ||
tasks.create.use(function(next){ | ||
tasks.create.use(function(){ | ||
console.log('middleware 1'); | ||
next(); | ||
}); | ||
// add another middleware to the 'create' operation | ||
// this time use a callback to indicate asynchronicity | ||
tasks.create.use(function(next){ | ||
@@ -59,2 +59,12 @@ console.log('middleware 2'); | ||
// add a third middleware to the 'create' operation | ||
// this time use a promise to indicate asynchronicity | ||
tasks.create.use(function(){ | ||
return new Promise(resolve, reject) { | ||
console.log('middleware 3'); | ||
resolve(); | ||
}); | ||
}); | ||
``` | ||
@@ -67,9 +77,16 @@ | ||
tasks.create(); | ||
// prints | ||
// middleware 1 | ||
// middleware 2 | ||
// middleware 3 | ||
// createTask Final Fn to be invoked | ||
``` | ||
Invoking the middleware will return an object with a `done` property which you can use to setup your callbacks: | ||
Invoking the middleware will return a Promise, use the `then` function to determine all middleware including the final function invoked successfully: | ||
```js | ||
tasks.create().done(function(err) { | ||
tasks.create().then(function() { | ||
// all middleware finished. | ||
}, function(err) { | ||
// Middleware failed | ||
}); | ||
@@ -80,2 +97,4 @@ ``` | ||
To use the Before/After hook types all you need to do is pass an option to Middlewarify's `make()` method. | ||
```js | ||
@@ -114,4 +133,6 @@ var midd = require('middlewarify'); | ||
// invoke all middleware | ||
tasks.create().done(function(err){ | ||
// at this point all middleware have finished. | ||
tasks.create().then(function(){ | ||
// at this point all middleware have finished. | ||
}, function(err) { | ||
// handle error | ||
}); | ||
@@ -121,3 +142,2 @@ | ||
## Middlewarify Methods | ||
@@ -135,6 +155,8 @@ | ||
This example has created the Middleware Container `create` in the object `crud`. `create.crud` is a function that will invoke all the middleware. | ||
This example has created the Middleware Container `create` in the object `crud`. `crud.create()` is a function that will invoke all the middleware. | ||
You can add a third argument, the `optMainCallback`, this is the main payload of your middleware. `optOptions` is one more argument you can pass to Middlewarify to define behavior. Both `optOptions` and `optMainCallback` are optional and can be interswitched, i.e. you can pass options as a third argument, read on for examples and what are the available options. | ||
You can pass a third argument, the `optMainCallback`, a Function. This can be considered the main payload of your middleware. | ||
`optOptions` defines behavior. Both `optOptions` and `optMainCallback` are optional and can be interswitched, i.e. you can pass options as a third argument, read on for examples and what are the available options. | ||
#### make() Options | ||
@@ -144,27 +166,7 @@ | ||
* `throwErrors` type: **Boolean**, default: `true` If set to false all thrown errors will be suppressed and available only through the `.done()` method. | ||
* `beforeAfter` type: **Boolean**, default: `false` If set to true the Before/After hooks will be used instead of the single queue `use` hook, which is the default, view the [example displayed above](#using-the-before--after-middleware-type). | ||
##### `throwErrors` Example | ||
```js | ||
// don't throw errors | ||
var crud = {}; | ||
middlewarify.make(crud, 'create', {throwErrors: false}); | ||
crud.create.use(function(){ | ||
throw new Error('an error'); | ||
}); | ||
// executing the middleware will not throw an error, the exception | ||
// will be available only through the .done() callback | ||
crud.create().done(function(err) { | ||
err.message === 'an error'; // true | ||
}); | ||
``` | ||
#### The use(fn) Method | ||
The Middleware Container by default exposes a `use` method so you can add any number of middleware. `use()` accepts any number of parameters as long they are of type Function or Arrays of Functions. When the Before/After flag is enabled `use` is no longer there and instead you get `before` and `after` methods to hook your middleware. All three hook types accept the same argument types and patterns as described bellow. | ||
The Middleware Container by default exposes a `use` hook so you can add any number of middleware. `use()` accepts any number of parameters as long they are of type Function or Array of Functions. When the Before/After flag is enabled `use` is no longer there and instead you get `before` and `after` hooks. All three hook types accept the same argument types and patterns as described bellow. | ||
@@ -193,11 +195,43 @@ ```js | ||
All middleware gets invoked with a callback so it can pass control to the next middleware. | ||
All middleware get invoked with the arguments that the *Middleware Container* was invoked with. The same number or arguments, the exact same references. | ||
following up on the previous examples: | ||
```js | ||
app.connect.use(function(req) { | ||
req.a === 1; // true | ||
req.a++; | ||
}); | ||
app.connect.use(function(req) { | ||
req.a === 2; // true | ||
}); | ||
app.connect({a:1}); | ||
``` | ||
#### Asynchronous Middleware Using Promises | ||
You can return a Promise from your middleware and Middlewarify will wait for its resolution before passing control to the next one. | ||
```js | ||
crud.create.before(function() { | ||
return new Promise(resolve, reject) { | ||
// do something async... | ||
resolve(); | ||
}); | ||
}); | ||
``` | ||
#### Asynchronous Middleware Using Callbacks | ||
Middlewarify determines the arity of your middleware and if it detects that you have one, and only one, more argument that what the *Middleware Container* was invoked with, then it treats it as a callback and you need to invoke it to pass control to the next middleware. | ||
```js | ||
crud.create.use(function(next) { | ||
// do stuff | ||
// Since we expect "create" to be invoked without any arguments | ||
// then Middlewarify assumes this middleware is async and expects | ||
// you to invoked "next" | ||
next(); | ||
}); | ||
crud.create(); // no arguments passed | ||
``` | ||
@@ -244,102 +278,15 @@ | ||
Because any argument passed to the Middleware Container (`crud.create(arg1, arg2, fn1);`) will get piped to the middleware, we cannot add a callback within these arguments. Thus the function `.done()` is provided, so you can check for errors or results. | ||
When invoked, the *Middleware Container* returns a promise, with it you can check for ultimate execution outcome. | ||
```js | ||
crud.create(arg1, arg2, fn1).done(function(err) { | ||
if (err) { | ||
return console.error(err); | ||
} | ||
crud.create(arg1, arg2, fn1).then(function() { | ||
// all cool... | ||
}, function(err) { | ||
// ops, handle error | ||
return console.error(err); | ||
}); | ||
``` | ||
The only way to pass arguments back to the callback of the `.done()` method is through the *Final Callback* that is defined in the `make()` method. | ||
```js | ||
var crud = {}; | ||
var lastMiddlware = function(done) { | ||
// since this is the final middleware, we name the | ||
// callback "done" instead of "next" | ||
// and we invoke it with a null value as the first | ||
// argument to indicate there were no errors. | ||
done(null, 'one', 'two'); | ||
}); | ||
// now create the Middleware Container | ||
middlewarify.make(crud, 'create', lastMiddlware); | ||
// Invoke the Middleware Container | ||
crud.create().done(function(err, arg1, arg2) { | ||
if (err) { /* tough love */ } | ||
arg1 === 'one'; // true | ||
arg2 === 'two'; // true | ||
}); | ||
``` | ||
> **Beware of Error Handling** Middlewarify will catch all thrown errors from your middleware. They will be piped to the `.done()` method. So if any of your middleware functions throws an error, it will not be visible unless you setup the `.done()` callback. | ||
#### Why a .done() function | ||
The trailling `.done()` function will notify you of the ultimate outcome of the middleware execution. The problem for having the callback as an argument when invoking the middleware with `tasks.create()` is that there is no way to determine if that is the callback to call when all middleware are done, or an argument that should be passed to all middleware. | ||
This becomes more aparent when using the Before/After feature of Middlewarify which binds a `before` and `after` functions instead of `use`. | ||
```js | ||
var midd = require('middlewarify'); | ||
var tasks = module.exports = {}; | ||
// this is the main callback of your middleware. | ||
function createTask(cb, done) { | ||
anAsyncOp(function(err, result) { | ||
if (err) { | ||
cb(err); | ||
done(err); | ||
return; | ||
} | ||
// cb is the anon function passed as argument when the middleware will be | ||
// invoked, done will signal to Middlewarify that execution has completed. | ||
cb(null, result); | ||
done(null, result); | ||
}); | ||
} | ||
// Make the'create' Middleware Container using before/after hooks | ||
midd.make(tasks, 'create', createTask, {beforeAfter: true}); | ||
/** ... */ | ||
// add a before hook | ||
tasks.create.before(function(cb, next) { | ||
// ... | ||
next(); | ||
}); | ||
// add an after hook | ||
tasks.create.after(function(cb, result, next) { | ||
// do something with "result" | ||
next(); | ||
}); | ||
/** ... */ | ||
// invoke all middleware | ||
tasks.create(function(err, result, done){ | ||
// this is invoked by the "createTask" function and BEFORE any of the | ||
// "after" middleware are executed. | ||
done(); | ||
}).done(function(err, fn, result){ | ||
// the "fn" is the anon function defined as an argument to tasks.create()! | ||
// at this point all middleware have finished. | ||
}); | ||
``` | ||
## Release History | ||
- **v0.2.0**, *08 Feb 2014* | ||
- Major API change, introduced Promises to API. | ||
- **v0.1.0**, *28 Jan 2014* | ||
@@ -346,0 +293,0 @@ - Added Before/After feature |
@@ -6,6 +6,7 @@ /** | ||
var assert = require('chai').assert; | ||
var Promise = require('bluebird'); | ||
var midd = require('../'); | ||
// var noop = function(){}; | ||
var noop = function(){}; | ||
@@ -18,3 +19,2 @@ suite('6. Before / After middleware', function() { | ||
// The numbering (e.g. 1.1.1) has nothing to do with order | ||
@@ -24,3 +24,2 @@ // The purpose is to provide a unique string so specific tests are | ||
test('6.1 Types Test', function() { | ||
@@ -35,3 +34,4 @@ var obj = Object.create(null); | ||
assert.notProperty(obj.create, 'use', 'obj.create should NOT have a use fn'); | ||
assert.isFunction(obj.create().done, 'obj.create().done should be a function'); | ||
assert.isFunction(obj.create().then, 'obj.create().then should be a Function'); | ||
assert.ok(Promise.is(obj.create()), 'obj.create() is a Promise'); | ||
}); | ||
@@ -52,34 +52,23 @@ }); | ||
fnPayload = sinon.spy(); | ||
midd.make(obj, 'create', fnPayload, { | ||
beforeAfter: true, | ||
}); | ||
midd.make(obj, 'create', fnPayload, {beforeAfter: true}); | ||
}); | ||
teardown(function() { | ||
obj.create(); | ||
teardown(function(done) { | ||
obj.create().then(function() { | ||
assert.ok(midd1.calledOnce, 'midd1 called only once, called: ' + midd1.callCount); | ||
assert.ok(midd2.calledOnce, 'midd2 called only once, called: ' + midd2.callCount); | ||
assert.ok(midd3.calledOnce, 'midd3 called only once, called: ' + midd3.callCount); | ||
assert.ok(midd4.calledOnce, 'midd4 called only once, called: ' + midd4.callCount); | ||
assert.ok(midd5.calledOnce, 'midd5 called only once, called: ' + midd5.callCount); | ||
assert.ok(midd6.calledOnce, 'midd6 called only once, called: ' + midd6.callCount); | ||
assert.ok(fnPayload.calledOnce, 'fnPayload called only once'); | ||
midd1.yield(); | ||
midd2.yield(); | ||
midd3.yield(); | ||
fnPayload.yield(); | ||
midd4.yield(); | ||
midd5.yield(); | ||
midd6.yield(); | ||
assert.ok(midd1.calledOnce, 'midd1 called only once'); | ||
assert.ok(midd2.calledOnce, 'midd2 called only once'); | ||
assert.ok(midd3.calledOnce, 'midd3 called only once'); | ||
assert.ok(midd4.calledOnce, 'midd4 called only once'); | ||
assert.ok(midd5.calledOnce, 'midd5 called only once'); | ||
assert.ok(midd6.calledOnce, 'midd6 called only once'); | ||
assert.ok(fnPayload.calledOnce, 'fnPayload called only once'); | ||
assert.ok(midd1.calledBefore(midd2), '"midd1" called before "midd2"'); | ||
assert.ok(midd2.calledBefore(midd3), '"midd2" called before "midd3"'); | ||
assert.ok(midd3.calledBefore(fnPayload), '"midd3" called before "fnPayload"'); | ||
assert.ok(fnPayload.calledBefore(midd4), '"fnPayload" called before "midd4"'); | ||
assert.ok(midd4.calledBefore(midd5), '"midd4" called before "midd5"'); | ||
assert.ok(midd5.calledBefore(midd6), '"midd5" called before "midd6"'); | ||
assert.ok(midd1.calledBefore(midd2), '"midd1" called before "midd2"'); | ||
assert.ok(midd2.calledBefore(midd3), '"midd2" called before "midd3"'); | ||
assert.ok(midd3.calledBefore(fnPayload), '"midd3" called before "fnPayload"'); | ||
assert.ok(fnPayload.calledBefore(midd4), '"fnPayload" called before "midd4"'); | ||
assert.ok(midd4.calledBefore(midd5), '"midd4" called before "midd5"'); | ||
assert.ok(midd5.calledBefore(midd6), '"midd5" called before "midd6"'); | ||
done(); | ||
}, done).then(null, done); | ||
}); | ||
@@ -107,3 +96,2 @@ | ||
}); | ||
}); | ||
@@ -113,8 +101,2 @@ | ||
var obj, mainMidd, firstMidd, secondMidd, thirdMidd; | ||
function callAll(index) { | ||
firstMidd.callArg(index); | ||
secondMidd.callArg(index); | ||
mainMidd.callArg(index); | ||
thirdMidd.callArg(index); | ||
} | ||
setup(function() { | ||
@@ -137,3 +119,3 @@ obj = Object.create(null); | ||
var bar = {b: 2}; | ||
obj.create(1, foo, bar).done(function(err){ | ||
obj.create(1, foo, bar).then(function(err){ | ||
assert.notOk(err, 'error should not be truthy'); | ||
@@ -145,22 +127,6 @@ assert.ok(firstMidd.alwaysCalledWith(1, foo, bar), 'firstMidd should be invoked with these arguments'); | ||
done(); | ||
}); | ||
callAll(3); | ||
}, done).then(null, done); | ||
}); | ||
}); | ||
suite('6.4. Final middleware arguments', function(){ | ||
test('6.4.1 Last middleware passes arguments to create callback', function(done) { | ||
var obj = Object.create(null); | ||
midd.make(obj, 'create', function(cb){ | ||
cb(null, 1, 2); | ||
}, {beforeAfter: true}); | ||
obj.create().done(function(err, arg1, arg2) { | ||
assert.equal(1, arg1, 'Arg1 should be 1'); | ||
assert.equal(2, arg2, 'Arg2 should be 2'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
suite('6.5. Failing middleware cases', function(){ | ||
@@ -172,22 +138,30 @@ var obj; | ||
}); | ||
test('6.5.1.1 Before middleware throws an error', function(){ | ||
obj.create.before(function(){ | ||
test('6.5.1.1 Before middleware throws an error', function(done){ | ||
obj.create.before(function() { | ||
throw new Error('an error'); | ||
}); | ||
assert.throws(obj.create, Error); | ||
obj.create().then(null, function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, 'an error'); | ||
done(); | ||
}).then(null, done); | ||
}); | ||
test('6.5.1.1.2 After middleware throws an error', function(){ | ||
obj.create.after(function(){ | ||
test('6.5.1.1.2 After middleware throws an error', function(done){ | ||
obj.create.after(function() { | ||
throw new Error('an error'); | ||
}); | ||
assert.throws(obj.create, Error); | ||
obj.create().then(null, function(err) { | ||
assert.instanceOf(err, Error); | ||
assert.equal(err.message, 'an error'); | ||
done(); | ||
}).then(null, done); | ||
}); | ||
test('6.5.1.2 Before middleware throws an error when param is not throw', function(){ | ||
test('6.5.1.2 Before middleware throws an error when param is not throw', function(done){ | ||
var custObj = Object.create(null); | ||
midd.make(custObj, 'create', {throwErrors: false, beforeAfter: true}); | ||
midd.make(custObj, 'create', {beforeAfter: true}); | ||
@@ -198,39 +172,43 @@ custObj.create.before(function() { | ||
custObj.create().done(function(err){ | ||
custObj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}); | ||
done(); | ||
}, done).then(null, done); | ||
}); | ||
test('6.5.1.3 After middleware throws an error when param is not throw', function(){ | ||
test('6.5.1.3 After middleware throws an error when param is not throw', function(done){ | ||
var custObj = Object.create(null); | ||
midd.make(custObj, 'create', {throwErrors: false, beforeAfter: true}); | ||
midd.make(custObj, 'create', {beforeAfter: true}); | ||
custObj.create.after(function(){ | ||
throw new Error('an error'); | ||
}); | ||
custObj.create().done(function(err){ | ||
custObj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}); | ||
done(); | ||
}, done).then(null, done); | ||
}); | ||
test('6.5.2 a Before middleware calls next with an error', function(){ | ||
test('6.5.2 a Before middleware calls next with an error', function(done){ | ||
obj.create.before(function(next){ | ||
next(new Error('an error')); | ||
}); | ||
obj.create().done(function(err){ | ||
obj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}); | ||
done(); | ||
}, done).then(null, done); | ||
}); | ||
test('6.5.2.1 a After middleware calls next with an error', function(){ | ||
test('6.5.2.1 a After middleware calls next with an error', function(done){ | ||
obj.create.after(function(next){ | ||
next(new Error('an error')); | ||
}); | ||
obj.create().done(function(err){ | ||
obj.create().then(noop, function(err) { | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}); | ||
done(); | ||
}, done).then(null, done); | ||
}); | ||
test('6.5.3 a failing Before middleware prevents rest of middleware from executing', function(){ | ||
test('6.5.3 a failing Before middleware prevents rest of middleware from executing', function(done){ | ||
obj.create.before(function(next){ | ||
@@ -245,8 +223,9 @@ next(new Error('an error')); | ||
obj.create().done(function(){ | ||
obj.create().then(noop, function(){ | ||
assert.notOk(middSpy.called, 'second Before middleware should not be called'); | ||
assert.notOk(afterMiddSpy.called, 'After middleware should not be called'); | ||
}); | ||
done(); | ||
}).then(null, done); | ||
}); | ||
test('6.5.4 a failing After middleware prevents rest of middleware from executing', function(){ | ||
test('6.5.4 a failing After middleware prevents rest of middleware from executing', function(done){ | ||
obj.create.after(function(next){ | ||
@@ -259,8 +238,7 @@ next(new Error('an error')); | ||
obj.create().done(function(){ | ||
obj.create().then(noop, function(){ | ||
assert.notOk(middSpy.called, 'second after middleware should not be called'); | ||
}); | ||
done(); | ||
}).then(null, done); | ||
}); | ||
}); | ||
@@ -7,6 +7,6 @@ /** | ||
var assert = require('chai').assert; | ||
var Promise = require('bluebird'); | ||
var midd = require('../'); | ||
// var noop = function(){}; | ||
var noop = function(){}; | ||
@@ -19,3 +19,2 @@ suite('1. Basic Tests', function() { | ||
// The numbering (e.g. 1.1.1) has nothing to do with order | ||
@@ -25,3 +24,2 @@ // The purpose is to provide a unique string so specific tests are | ||
test('1.1 Types Test', function() { | ||
@@ -32,7 +30,8 @@ var obj = Object.create(null); | ||
assert.isFunction(obj.create.use, 'obj.create.use should be a function'); | ||
assert.isFunction(obj.create().done, 'obj.create().done should be a function'); | ||
assert.isFunction(obj.create().then, 'obj.create().then should be a Function'); | ||
assert.ok(Promise.is(obj.create()), 'obj.create().then is a Promise'); | ||
}); | ||
}); | ||
suite('2. middleware.use() Sequence of invocation', function() { | ||
suite('2. middleware.use() Sequence of invocation Synchronous', function() { | ||
var obj, lastMidd, firstMidd, secondMidd, thirdMidd; | ||
@@ -50,6 +49,2 @@ setup(function() { | ||
obj.create(); | ||
firstMidd.yield(); | ||
secondMidd.yield(); | ||
thirdMidd.yield(); | ||
lastMidd.yield(); | ||
assert.ok(firstMidd.calledOnce, 'firstMidd should be called only once'); | ||
@@ -81,23 +76,55 @@ assert.ok(secondMidd.calledOnce, 'secondMidd should be called only once'); | ||
}); | ||
}); | ||
suite('3. middleware() argument piping', function() { | ||
suite('2.10 middleware.use() Sequence of invocation Asynchronous', function() { | ||
var obj, lastMidd, firstMidd, secondMidd, thirdMidd; | ||
function callAll(index) { | ||
firstMidd.callArg(index); | ||
secondMidd.callArg(index); | ||
thirdMidd.callArg(index); | ||
lastMidd.callArg(index); | ||
} | ||
var spyLastMidd, spyFirstMidd, spySecondMidd, spyThirdMidd; | ||
setup(function() { | ||
spyLastMidd = sinon.spy(); | ||
spyFirstMidd = sinon.spy(); | ||
spySecondMidd = sinon.spy(); | ||
spyThirdMidd = sinon.spy(); | ||
obj = Object.create(null); | ||
lastMidd = sinon.spy(); | ||
firstMidd = sinon.spy(); | ||
secondMidd = sinon.spy(); | ||
thirdMidd = sinon.spy(); | ||
lastMidd = function(next) {spyLastMidd();next();}; | ||
firstMidd = function(next) {spyFirstMidd();next();}; | ||
secondMidd = function(next) {spySecondMidd();next();}; | ||
thirdMidd = function(next) {spyThirdMidd();next();}; | ||
midd.make(obj, 'create', lastMidd); | ||
}); | ||
teardown(function(){ | ||
obj.create(); | ||
assert.ok(spyFirstMidd.calledOnce, 'firstMidd should be called only once'); | ||
assert.ok(spySecondMidd.calledOnce, 'secondMidd should be called only once'); | ||
assert.ok(spyThirdMidd.calledOnce, 'thirdMidd should be called only once'); | ||
assert.ok(spyLastMidd.calledOnce, 'lastMidd should be called only once'); | ||
assert.ok(spyFirstMidd.calledBefore(spySecondMidd), 'firstMidd should be called before secondMidd'); | ||
assert.ok(spySecondMidd.calledAfter(spyFirstMidd), 'secondMidd should be called after firstMidd'); | ||
assert.ok(spyThirdMidd.calledAfter(spySecondMidd), 'thirdMidd should be called after secondMidd'); | ||
assert.ok(spyLastMidd.calledAfter(spyThirdMidd), 'lastMidd should be called after thirdMidd'); | ||
}); | ||
test('2.10.1 Multiple arguments', function() { | ||
obj.create.use(firstMidd, secondMidd, thirdMidd); | ||
}); | ||
test('2.10.2 Multiple calls', function() { | ||
obj.create.use(firstMidd); | ||
obj.create.use(secondMidd); | ||
obj.create.use(thirdMidd); | ||
}); | ||
test('2.10.3 An array', function() { | ||
obj.create.use([firstMidd, secondMidd, thirdMidd]); | ||
}); | ||
test('2.10.4 Array mixed with arg', function() { | ||
obj.create.use([firstMidd, secondMidd], thirdMidd); | ||
}); | ||
}); | ||
suite('3. middleware() argument piping', function() { | ||
var obj, lastMidd, firstMidd, secondMidd, thirdMidd; | ||
teardown(function(){ | ||
@@ -107,13 +134,23 @@ }); | ||
test('3.1 Three arguments', function(done) { | ||
function checkMiddlewareArgs(arg1, arg2, arg3) { | ||
assert.equal(arg1, 1); | ||
assert.deepEqual(arg2, {a: 1}); | ||
assert.deepEqual(arg3, {b: 2}); | ||
} | ||
obj = Object.create(null); | ||
lastMidd = checkMiddlewareArgs; | ||
firstMidd = checkMiddlewareArgs; | ||
secondMidd = checkMiddlewareArgs; | ||
thirdMidd = checkMiddlewareArgs; | ||
midd.make(obj, 'create', lastMidd); | ||
obj.create.use(firstMidd, secondMidd, thirdMidd); | ||
var foo = {a: 1}; | ||
var bar = {b: 2}; | ||
obj.create(1, foo, bar).done(function(err){ | ||
assert.notOk(err, 'error should not be truthy'); | ||
assert.ok(firstMidd.alwaysCalledWith(1, foo, bar), 'firstMidd should be invoked with these arguments'); | ||
assert.ok(secondMidd.alwaysCalledWith(1, foo, bar), 'secondMidd should be invoked with these arguments'); | ||
assert.ok(thirdMidd.alwaysCalledWith(1, foo, bar), 'thirdMidd should be invoked with these arguments'); | ||
assert.ok(lastMidd.alwaysCalledWith(1, foo, bar), 'lastMidd should be invoked with these arguments'); | ||
obj.create(1, foo, bar).then(function() { | ||
done(); | ||
}); | ||
callAll(3); | ||
}, done).then(null, done); | ||
}); | ||
@@ -140,17 +177,2 @@ | ||
suite('4. Final middleware arguments', function(){ | ||
test('4.1 Last middleware passes arguments to create callback', function(done) { | ||
var obj = Object.create(null); | ||
midd.make(obj, 'create', function(cb){ | ||
cb(null, 1, 2); | ||
}); | ||
obj.create().done(function(err, arg1, arg2) { | ||
assert.equal(1, arg1, 'Arg1 should be 1'); | ||
assert.equal(2, arg2, 'Arg2 should be 2'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
suite('5. Failing middleware cases', function(){ | ||
@@ -162,33 +184,28 @@ var obj; | ||
}); | ||
test('5.1.1 middleware throws an error', function(){ | ||
obj.create.use(function(){ | ||
throw new Error('an error'); | ||
}); | ||
assert.throws(obj.create, Error); | ||
}); | ||
test('5.1.2 middleware throws an error when param is not throw', function(){ | ||
test('5.1.2 middleware accepts throw error', function(done){ | ||
var custObj = Object.create(null); | ||
midd.make(custObj, 'create', {throwErrors: false}); | ||
midd.make(custObj, 'create'); | ||
custObj.create.use(function(){ | ||
throw new Error('an error'); | ||
}); | ||
custObj.create().done(function(err){ | ||
custObj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}); | ||
done(); | ||
}).then(null, done); | ||
}); | ||
test('5.2 a middleware calls next with an error', function(){ | ||
test('5.2 a middleware calls next with an error', function(done){ | ||
obj.create.use(function(next){ | ||
next(new Error('an error')); | ||
}); | ||
obj.create().done(function(err){ | ||
obj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}); | ||
done(); | ||
}).then(null, done); | ||
}); | ||
test('5.3 a failing middleware prevents rest of middleware from executing', function(){ | ||
test('5.3 a failing middleware prevents rest of middleware from executing', function(done){ | ||
obj.create.use(function(next){ | ||
@@ -201,7 +218,8 @@ next(new Error('an error')); | ||
obj.create().done(function(){ | ||
obj.create().then(null, function() { | ||
assert.notOk(middSpy.called, 'second middleware should not be called'); | ||
}); | ||
done(); | ||
}, done).then(null, done); | ||
}); | ||
}); | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
37486
13
706
0
3
303
+ Addedbluebird@~1.0.3
+ Addedbluebird@1.0.8(transitive)