examples.test.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. var assert = require('assert');
  2. var Kareem = require('../');
  3. /* Much like [hooks](https://npmjs.org/package/hooks), kareem lets you define
  4. * pre and post hooks: pre hooks are called before a given function executes.
  5. * Unlike hooks, kareem stores hooks and other internal state in a separate
  6. * object, rather than relying on inheritance. Furthermore, kareem exposes
  7. * an `execPre()` function that allows you to execute your pre hooks when
  8. * appropriate, giving you more fine-grained control over your function hooks.
  9. */
  10. describe('pre hooks', function() {
  11. var hooks;
  12. beforeEach(function() {
  13. hooks = new Kareem();
  14. });
  15. it('runs without any hooks specified', function(done) {
  16. hooks.execPre('cook', null, function() {
  17. // ...
  18. // acquit:ignore:start
  19. done();
  20. // acquit:ignore:end
  21. });
  22. });
  23. /* pre hook functions take one parameter, a "done" function that you execute
  24. * when your pre hook is finished.
  25. */
  26. it('runs basic serial pre hooks', function(done) {
  27. var count = 0;
  28. hooks.pre('cook', function(done) {
  29. ++count;
  30. done();
  31. });
  32. hooks.execPre('cook', null, function() {
  33. assert.equal(1, count);
  34. // acquit:ignore:start
  35. done();
  36. // acquit:ignore:end
  37. });
  38. });
  39. it('can run multipe pre hooks', function(done) {
  40. var count1 = 0;
  41. var count2 = 0;
  42. hooks.pre('cook', function(done) {
  43. ++count1;
  44. done();
  45. });
  46. hooks.pre('cook', function(done) {
  47. ++count2;
  48. done();
  49. });
  50. hooks.execPre('cook', null, function() {
  51. assert.equal(1, count1);
  52. assert.equal(1, count2);
  53. // acquit:ignore:start
  54. done();
  55. // acquit:ignore:end
  56. });
  57. });
  58. /* If your pre hook function takes no parameters, its assumed to be
  59. * fully synchronous.
  60. */
  61. it('can run fully synchronous pre hooks', function(done) {
  62. var count1 = 0;
  63. var count2 = 0;
  64. hooks.pre('cook', function() {
  65. ++count1;
  66. });
  67. hooks.pre('cook', function() {
  68. ++count2;
  69. });
  70. hooks.execPre('cook', null, function(error) {
  71. assert.equal(null, error);
  72. assert.equal(1, count1);
  73. assert.equal(1, count2);
  74. // acquit:ignore:start
  75. done();
  76. // acquit:ignore:end
  77. });
  78. });
  79. /* Pre save hook functions are bound to the second parameter to `execPre()`
  80. */
  81. it('properly attaches context to pre hooks', function(done) {
  82. hooks.pre('cook', function(done) {
  83. this.bacon = 3;
  84. done();
  85. });
  86. hooks.pre('cook', function(done) {
  87. this.eggs = 4;
  88. done();
  89. });
  90. var obj = { bacon: 0, eggs: 0 };
  91. // In the pre hooks, `this` will refer to `obj`
  92. hooks.execPre('cook', obj, function(error) {
  93. assert.equal(null, error);
  94. assert.equal(3, obj.bacon);
  95. assert.equal(4, obj.eggs);
  96. // acquit:ignore:start
  97. done();
  98. // acquit:ignore:end
  99. });
  100. });
  101. /* Like the hooks module, you can declare "async" pre hooks - these take two
  102. * parameters, the functions `next()` and `done()`. `next()` passes control to
  103. * the next pre hook, but the underlying function won't be called until all
  104. * async pre hooks have called `done()`.
  105. */
  106. it('can execute parallel (async) pre hooks', function(done) {
  107. hooks.pre('cook', true, function(next, done) {
  108. this.bacon = 3;
  109. next();
  110. setTimeout(function() {
  111. done();
  112. }, 5);
  113. });
  114. hooks.pre('cook', true, function(next, done) {
  115. next();
  116. var _this = this;
  117. setTimeout(function() {
  118. _this.eggs = 4;
  119. done();
  120. }, 10);
  121. });
  122. hooks.pre('cook', function(next) {
  123. this.waffles = false;
  124. next();
  125. });
  126. var obj = { bacon: 0, eggs: 0 };
  127. hooks.execPre('cook', obj, function() {
  128. assert.equal(3, obj.bacon);
  129. assert.equal(4, obj.eggs);
  130. assert.equal(false, obj.waffles);
  131. // acquit:ignore:start
  132. done();
  133. // acquit:ignore:end
  134. });
  135. });
  136. /* You can also return a promise from your pre hooks instead of calling
  137. * `next()`. When the returned promise resolves, kareem will kick off the
  138. * next middleware.
  139. */
  140. it('supports returning a promise', function(done) {
  141. hooks.pre('cook', function() {
  142. return new Promise(resolve => {
  143. setTimeout(() => {
  144. this.bacon = 3;
  145. resolve();
  146. }, 100);
  147. });
  148. });
  149. var obj = { bacon: 0 };
  150. hooks.execPre('cook', obj, function() {
  151. assert.equal(3, obj.bacon);
  152. // acquit:ignore:start
  153. done();
  154. // acquit:ignore:end
  155. });
  156. });
  157. });
  158. describe('post hooks', function() {
  159. var hooks;
  160. beforeEach(function() {
  161. hooks = new Kareem();
  162. });
  163. it('runs without any hooks specified', function(done) {
  164. hooks.execPost('cook', null, [1], function(error, eggs) {
  165. assert.ifError(error);
  166. assert.equal(1, eggs);
  167. done();
  168. });
  169. });
  170. it('executes with parameters passed in', function(done) {
  171. hooks.post('cook', function(eggs, bacon, callback) {
  172. assert.equal(1, eggs);
  173. assert.equal(2, bacon);
  174. callback();
  175. });
  176. hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) {
  177. assert.ifError(error);
  178. assert.equal(1, eggs);
  179. assert.equal(2, bacon);
  180. // acquit:ignore:start
  181. done();
  182. // acquit:ignore:end
  183. });
  184. });
  185. it('can use synchronous post hooks', function(done) {
  186. var execed = {};
  187. hooks.post('cook', function(eggs, bacon) {
  188. execed.first = true;
  189. assert.equal(1, eggs);
  190. assert.equal(2, bacon);
  191. });
  192. hooks.post('cook', function(eggs, bacon, callback) {
  193. execed.second = true;
  194. assert.equal(1, eggs);
  195. assert.equal(2, bacon);
  196. callback();
  197. });
  198. hooks.execPost('cook', null, [1, 2], function(error, eggs, bacon) {
  199. assert.ifError(error);
  200. assert.equal(2, Object.keys(execed).length);
  201. assert.ok(execed.first);
  202. assert.ok(execed.second);
  203. assert.equal(1, eggs);
  204. assert.equal(2, bacon);
  205. // acquit:ignore:start
  206. done();
  207. // acquit:ignore:end
  208. });
  209. });
  210. /* You can also return a promise from your post hooks instead of calling
  211. * `next()`. When the returned promise resolves, kareem will kick off the
  212. * next middleware.
  213. */
  214. it('supports returning a promise', function(done) {
  215. hooks.post('cook', function(bacon) {
  216. return new Promise(resolve => {
  217. setTimeout(() => {
  218. this.bacon = 3;
  219. resolve();
  220. }, 100);
  221. });
  222. });
  223. var obj = { bacon: 0 };
  224. hooks.execPost('cook', obj, obj, function() {
  225. assert.equal(obj.bacon, 3);
  226. // acquit:ignore:start
  227. done();
  228. // acquit:ignore:end
  229. });
  230. });
  231. });
  232. describe('wrap()', function() {
  233. var hooks;
  234. beforeEach(function() {
  235. hooks = new Kareem();
  236. });
  237. it('wraps pre and post calls into one call', function(done) {
  238. hooks.pre('cook', true, function(next, done) {
  239. this.bacon = 3;
  240. next();
  241. setTimeout(function() {
  242. done();
  243. }, 5);
  244. });
  245. hooks.pre('cook', true, function(next, done) {
  246. next();
  247. var _this = this;
  248. setTimeout(function() {
  249. _this.eggs = 4;
  250. done();
  251. }, 10);
  252. });
  253. hooks.pre('cook', function(next) {
  254. this.waffles = false;
  255. next();
  256. });
  257. hooks.post('cook', function(obj) {
  258. obj.tofu = 'no';
  259. });
  260. var obj = { bacon: 0, eggs: 0 };
  261. var args = [obj];
  262. args.push(function(error, result) {
  263. assert.ifError(error);
  264. assert.equal(null, error);
  265. assert.equal(3, obj.bacon);
  266. assert.equal(4, obj.eggs);
  267. assert.equal(false, obj.waffles);
  268. assert.equal('no', obj.tofu);
  269. assert.equal(obj, result);
  270. // acquit:ignore:start
  271. done();
  272. // acquit:ignore:end
  273. });
  274. hooks.wrap(
  275. 'cook',
  276. function(o, callback) {
  277. assert.equal(3, obj.bacon);
  278. assert.equal(4, obj.eggs);
  279. assert.equal(false, obj.waffles);
  280. assert.equal(undefined, obj.tofu);
  281. callback(null, o);
  282. },
  283. obj,
  284. args);
  285. });
  286. });
  287. describe('createWrapper()', function() {
  288. var hooks;
  289. beforeEach(function() {
  290. hooks = new Kareem();
  291. });
  292. it('wraps wrap() into a callable function', function(done) {
  293. hooks.pre('cook', true, function(next, done) {
  294. this.bacon = 3;
  295. next();
  296. setTimeout(function() {
  297. done();
  298. }, 5);
  299. });
  300. hooks.pre('cook', true, function(next, done) {
  301. next();
  302. var _this = this;
  303. setTimeout(function() {
  304. _this.eggs = 4;
  305. done();
  306. }, 10);
  307. });
  308. hooks.pre('cook', function(next) {
  309. this.waffles = false;
  310. next();
  311. });
  312. hooks.post('cook', function(obj) {
  313. obj.tofu = 'no';
  314. });
  315. var obj = { bacon: 0, eggs: 0 };
  316. var cook = hooks.createWrapper(
  317. 'cook',
  318. function(o, callback) {
  319. assert.equal(3, obj.bacon);
  320. assert.equal(4, obj.eggs);
  321. assert.equal(false, obj.waffles);
  322. assert.equal(undefined, obj.tofu);
  323. callback(null, o);
  324. },
  325. obj);
  326. cook(obj, function(error, result) {
  327. assert.ifError(error);
  328. assert.equal(3, obj.bacon);
  329. assert.equal(4, obj.eggs);
  330. assert.equal(false, obj.waffles);
  331. assert.equal('no', obj.tofu);
  332. assert.equal(obj, result);
  333. // acquit:ignore:start
  334. done();
  335. // acquit:ignore:end
  336. });
  337. });
  338. });
  339. describe('clone()', function() {
  340. it('clones a Kareem object', function() {
  341. var k1 = new Kareem();
  342. k1.pre('cook', function() {});
  343. k1.post('cook', function() {});
  344. var k2 = k1.clone();
  345. assert.deepEqual(Array.from(k2._pres.keys()), ['cook']);
  346. assert.deepEqual(Array.from(k2._posts.keys()), ['cook']);
  347. });
  348. });
  349. describe('merge()', function() {
  350. it('pulls hooks from another Kareem object', function() {
  351. var k1 = new Kareem();
  352. var test1 = function() {};
  353. k1.pre('cook', test1);
  354. k1.post('cook', function() {});
  355. var k2 = new Kareem();
  356. var test2 = function() {};
  357. k2.pre('cook', test2);
  358. var k3 = k2.merge(k1);
  359. assert.equal(k3._pres.get('cook').length, 2);
  360. assert.equal(k3._pres.get('cook')[0].fn, test2);
  361. assert.equal(k3._pres.get('cook')[1].fn, test1);
  362. assert.equal(k3._posts.get('cook').length, 1);
  363. });
  364. });