examples.test.js 10 KB

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