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.
		
		
		
		
		
			
		
			
				
					427 lines
				
				10 KiB
			
		
		
			
		
	
	
					427 lines
				
				10 KiB
			| 
											3 years ago
										 | var assert = require('assert'); | ||
|  | var Kareem = require('../'); | ||
|  | 
 | ||
|  | /* Much like [hooks](https://npmjs.org/package/hooks), kareem lets you define | ||
|  |  * pre and post hooks: pre hooks are called before a given function executes. | ||
|  |  * Unlike hooks, kareem stores hooks and other internal state in a separate | ||
|  |  * object, rather than relying on inheritance. Furthermore, kareem exposes | ||
|  |  * an `execPre()` function that allows you to execute your pre hooks when | ||
|  |  * appropriate, giving you more fine-grained control over your function hooks. | ||
|  |  */ | ||
|  | describe('pre hooks', function() { | ||
|  |   var hooks; | ||
|  | 
 | ||
|  |   beforeEach(function() { | ||
|  |     hooks = new Kareem(); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('runs without any hooks specified', function(done) { | ||
|  |     hooks.execPre('cook', null, function() { | ||
|  |       // ...
 | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   /* pre hook functions take one parameter, a "done" function that you execute | ||
|  |    * when your pre hook is finished. | ||
|  |    */ | ||
|  |   it('runs basic serial pre hooks', function(done) { | ||
|  |     var count = 0; | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(done) { | ||
|  |       ++count; | ||
|  |       done(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.execPre('cook', null, function() { | ||
|  |       assert.equal(1, count); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('can run multipe pre hooks', function(done) { | ||
|  |     var count1 = 0; | ||
|  |     var count2 = 0; | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(done) { | ||
|  |       ++count1; | ||
|  |       done(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(done) { | ||
|  |       ++count2; | ||
|  |       done(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.execPre('cook', null, function() { | ||
|  |       assert.equal(1, count1); | ||
|  |       assert.equal(1, count2); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   /* If your pre hook function takes no parameters, its assumed to be | ||
|  |    * fully synchronous. | ||
|  |    */ | ||
|  |   it('can run fully synchronous pre hooks', function(done) { | ||
|  |     var count1 = 0; | ||
|  |     var count2 = 0; | ||
|  | 
 | ||
|  |     hooks.pre('cook', function() { | ||
|  |       ++count1; | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', function() { | ||
|  |       ++count2; | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.execPre('cook', null, function(error) { | ||
|  |       assert.equal(null, error); | ||
|  |       assert.equal(1, count1); | ||
|  |       assert.equal(1, count2); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   /* Pre save hook functions are bound to the second parameter to `execPre()` | ||
|  |    */ | ||
|  |   it('properly attaches context to pre hooks', function(done) { | ||
|  |     hooks.pre('cook', function(done) { | ||
|  |       this.bacon = 3; | ||
|  |       done(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(done) { | ||
|  |       this.eggs = 4; | ||
|  |       done(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     var obj = { bacon: 0, eggs: 0 }; | ||
|  | 
 | ||
|  |     // In the pre hooks, `this` will refer to `obj`
 | ||
|  |     hooks.execPre('cook', obj, function(error) { | ||
|  |       assert.equal(null, error); | ||
|  |       assert.equal(3, obj.bacon); | ||
|  |       assert.equal(4, obj.eggs); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   /* Like the hooks module, you can declare "async" pre hooks - these take two | ||
|  |    * parameters, the functions `next()` and `done()`. `next()` passes control to | ||
|  |    * the next pre hook, but the underlying function won't be called until all | ||
|  |    * async pre hooks have called `done()`. | ||
|  |    */ | ||
|  |   it('can execute parallel (async) pre hooks', function(done) { | ||
|  |     hooks.pre('cook', true, function(next, done) { | ||
|  |       this.bacon = 3; | ||
|  |       next(); | ||
|  |       setTimeout(function() { | ||
|  |         done(); | ||
|  |       }, 5); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', true, function(next, done) { | ||
|  |       next(); | ||
|  |       var _this = this; | ||
|  |       setTimeout(function() { | ||
|  |         _this.eggs = 4; | ||
|  |         done(); | ||
|  |       }, 10); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(next) { | ||
|  |       this.waffles = false; | ||
|  |       next(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     var obj = { bacon: 0, eggs: 0 }; | ||
|  | 
 | ||
|  |     hooks.execPre('cook', obj, function() { | ||
|  |       assert.equal(3, obj.bacon); | ||
|  |       assert.equal(4, obj.eggs); | ||
|  |       assert.equal(false, obj.waffles); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   /* You can also return a promise from your pre hooks instead of calling | ||
|  |    * `next()`. When the returned promise resolves, kareem will kick off the | ||
|  |    * next middleware. | ||
|  |    */ | ||
|  |   it('supports returning a promise', function(done) { | ||
|  |     hooks.pre('cook', function() { | ||
|  |       return new Promise(resolve => { | ||
|  |         setTimeout(() => { | ||
|  |           this.bacon = 3; | ||
|  |           resolve(); | ||
|  |         }, 100); | ||
|  |       }); | ||
|  |     }); | ||
|  | 
 | ||
|  |     var obj = { bacon: 0 }; | ||
|  | 
 | ||
|  |     hooks.execPre('cook', obj, function() { | ||
|  |       assert.equal(3, obj.bacon); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | }); | ||
|  | 
 | ||
|  | describe('post hooks', function() { | ||
|  |   var hooks; | ||
|  | 
 | ||
|  |   beforeEach(function() { | ||
|  |     hooks = new Kareem(); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('runs without any hooks specified', function(done) { | ||
|  |     hooks.execPost('cook', null, [1], function(error, eggs) { | ||
|  |       assert.ifError(error); | ||
|  |       assert.equal(1, eggs); | ||
|  |       done(); | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('executes with parameters passed in', function(done) { | ||
|  |     hooks.post('cook', function(eggs, bacon, callback) { | ||
|  |       assert.equal(1, eggs); | ||
|  |       assert.equal(2, bacon); | ||
|  |       callback(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) { | ||
|  |       assert.ifError(error); | ||
|  |       assert.equal(1, eggs); | ||
|  |       assert.equal(2, bacon); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('can use synchronous post hooks', function(done) { | ||
|  |     var execed = {}; | ||
|  | 
 | ||
|  |     hooks.post('cook', function(eggs, bacon) { | ||
|  |       execed.first = true; | ||
|  |       assert.equal(1, eggs); | ||
|  |       assert.equal(2, bacon); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.post('cook', function(eggs, bacon, callback) { | ||
|  |       execed.second = true; | ||
|  |       assert.equal(1, eggs); | ||
|  |       assert.equal(2, bacon); | ||
|  |       callback(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) { | ||
|  |       assert.ifError(error); | ||
|  |       assert.equal(2, Object.keys(execed).length); | ||
|  |       assert.ok(execed.first); | ||
|  |       assert.ok(execed.second); | ||
|  |       assert.equal(1, eggs); | ||
|  |       assert.equal(2, bacon); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | 
 | ||
|  |   /* You can also return a promise from your post hooks instead of calling | ||
|  |    * `next()`. When the returned promise resolves, kareem will kick off the | ||
|  |    * next middleware. | ||
|  |    */ | ||
|  |   it('supports returning a promise', function(done) { | ||
|  |     hooks.post('cook', function(bacon) { | ||
|  |       return new Promise(resolve => { | ||
|  |         setTimeout(() => { | ||
|  |           this.bacon = 3; | ||
|  |           resolve(); | ||
|  |         }, 100); | ||
|  |       }); | ||
|  |     }); | ||
|  | 
 | ||
|  |     var obj = { bacon: 0 }; | ||
|  | 
 | ||
|  |     hooks.execPost('cook', obj, obj, function() { | ||
|  |       assert.equal(obj.bacon, 3); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | }); | ||
|  | 
 | ||
|  | describe('wrap()', function() { | ||
|  |   var hooks; | ||
|  | 
 | ||
|  |   beforeEach(function() { | ||
|  |     hooks = new Kareem(); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('wraps pre and post calls into one call', function(done) { | ||
|  |     hooks.pre('cook', true, function(next, done) { | ||
|  |       this.bacon = 3; | ||
|  |       next(); | ||
|  |       setTimeout(function() { | ||
|  |         done(); | ||
|  |       }, 5); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', true, function(next, done) { | ||
|  |       next(); | ||
|  |       var _this = this; | ||
|  |       setTimeout(function() { | ||
|  |         _this.eggs = 4; | ||
|  |         done(); | ||
|  |       }, 10); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(next) { | ||
|  |       this.waffles = false; | ||
|  |       next(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.post('cook', function(obj) { | ||
|  |       obj.tofu = 'no'; | ||
|  |     }); | ||
|  | 
 | ||
|  |     var obj = { bacon: 0, eggs: 0 }; | ||
|  | 
 | ||
|  |     var args = [obj]; | ||
|  |     args.push(function(error, result) { | ||
|  |       assert.ifError(error); | ||
|  |       assert.equal(null, error); | ||
|  |       assert.equal(3, obj.bacon); | ||
|  |       assert.equal(4, obj.eggs); | ||
|  |       assert.equal(false, obj.waffles); | ||
|  |       assert.equal('no', obj.tofu); | ||
|  | 
 | ||
|  |       assert.equal(obj, result); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.wrap( | ||
|  |       'cook', | ||
|  |       function(o, callback) { | ||
|  |         assert.equal(3, obj.bacon); | ||
|  |         assert.equal(4, obj.eggs); | ||
|  |         assert.equal(false, obj.waffles); | ||
|  |         assert.equal(undefined, obj.tofu); | ||
|  |         callback(null, o); | ||
|  |       }, | ||
|  |       obj, | ||
|  |       args); | ||
|  |   }); | ||
|  | }); | ||
|  | 
 | ||
|  | describe('createWrapper()', function() { | ||
|  |   var hooks; | ||
|  | 
 | ||
|  |   beforeEach(function() { | ||
|  |     hooks = new Kareem(); | ||
|  |   }); | ||
|  | 
 | ||
|  |   it('wraps wrap() into a callable function', function(done) { | ||
|  |     hooks.pre('cook', true, function(next, done) { | ||
|  |       this.bacon = 3; | ||
|  |       next(); | ||
|  |       setTimeout(function() { | ||
|  |         done(); | ||
|  |       }, 5); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', true, function(next, done) { | ||
|  |       next(); | ||
|  |       var _this = this; | ||
|  |       setTimeout(function() { | ||
|  |         _this.eggs = 4; | ||
|  |         done(); | ||
|  |       }, 10); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.pre('cook', function(next) { | ||
|  |       this.waffles = false; | ||
|  |       next(); | ||
|  |     }); | ||
|  | 
 | ||
|  |     hooks.post('cook', function(obj) { | ||
|  |       obj.tofu = 'no'; | ||
|  |     }); | ||
|  | 
 | ||
|  |     var obj = { bacon: 0, eggs: 0 }; | ||
|  | 
 | ||
|  |     var cook = hooks.createWrapper( | ||
|  |       'cook', | ||
|  |       function(o, callback) { | ||
|  |         assert.equal(3, obj.bacon); | ||
|  |         assert.equal(4, obj.eggs); | ||
|  |         assert.equal(false, obj.waffles); | ||
|  |         assert.equal(undefined, obj.tofu); | ||
|  |         callback(null, o); | ||
|  |       }, | ||
|  |       obj); | ||
|  | 
 | ||
|  |     cook(obj, function(error, result) { | ||
|  |       assert.ifError(error); | ||
|  |       assert.equal(3, obj.bacon); | ||
|  |       assert.equal(4, obj.eggs); | ||
|  |       assert.equal(false, obj.waffles); | ||
|  |       assert.equal('no', obj.tofu); | ||
|  | 
 | ||
|  |       assert.equal(obj, result); | ||
|  |       // acquit:ignore:start
 | ||
|  |       done(); | ||
|  |       // acquit:ignore:end
 | ||
|  |     }); | ||
|  |   }); | ||
|  | }); | ||
|  | 
 | ||
|  | describe('clone()', function() { | ||
|  |   it('clones a Kareem object', function() { | ||
|  |     var k1 = new Kareem(); | ||
|  |     k1.pre('cook', function() {}); | ||
|  |     k1.post('cook', function() {}); | ||
|  | 
 | ||
|  |     var k2 = k1.clone(); | ||
|  |     assert.deepEqual(Array.from(k2._pres.keys()), ['cook']); | ||
|  |     assert.deepEqual(Array.from(k2._posts.keys()), ['cook']); | ||
|  |   }); | ||
|  | }); | ||
|  | 
 | ||
|  | describe('merge()', function() { | ||
|  |   it('pulls hooks from another Kareem object', function() { | ||
|  |     var k1 = new Kareem(); | ||
|  |     var test1 = function() {}; | ||
|  |     k1.pre('cook', test1); | ||
|  |     k1.post('cook', function() {}); | ||
|  | 
 | ||
|  |     var k2 = new Kareem(); | ||
|  |     var test2 = function() {}; | ||
|  |     k2.pre('cook', test2); | ||
|  |     var k3 = k2.merge(k1); | ||
|  |     assert.equal(k3._pres.get('cook').length, 2); | ||
|  |     assert.equal(k3._pres.get('cook')[0].fn, test2); | ||
|  |     assert.equal(k3._pres.get('cook')[1].fn, test1); | ||
|  |     assert.equal(k3._posts.get('cook').length, 1); | ||
|  |   }); | ||
|  | }); |