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
						
					
					
				| 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);
 | |
|   });
 | |
| });
 |