Version 1
[yaffs-website] / node_modules / hooker / test / hooker_test.js
1 /*global require:true */
2 var hooker = require('../lib/hooker');
3
4 exports['hook'] = {
5   setUp: function(done) {
6     this.order = [];
7     this.track = function() {
8       [].push.apply(this.order, arguments);
9     };
10
11     this.prop = 1;
12     this.add = function(a, b) {
13       this.track("add", this.prop, a, b);
14       return this.prop + a + b;
15     };
16
17     this.obj = {
18       that: this,
19       prop: 1,
20       add1: function(a, b) {
21         this.that.track("add1", this.prop, a, b);
22         return this.prop + a + b;
23       },
24       add2: function(a, b) {
25         this.that.track("add2", this.prop, a, b);
26         return this.prop + a + b;
27       },
28       add3: function(a, b) {
29         this.that.track("add3", this.prop, a, b);
30         return this.prop + a + b;
31       }
32     };
33
34     done();
35   },
36   'orig': function(test) {
37     test.expect(1);
38     var orig = this.add;
39     hooker.hook(this, "add", function() {});
40     test.strictEqual(hooker.orig(this, "add"), orig, "should return a refernce to the original function.");
41     test.done();
42   },
43   'once': function(test) {
44     test.expect(5);
45     var orig = this.add;
46     hooker.hook(this, "add", {
47       once: true,
48       pre: function(a, b) {
49         // Arguments are passed into pre-hook as specified.
50         this.track("before", this.prop, a, b);
51       }
52     });
53     test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
54     test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
55     test.strictEqual(this.add, orig, "should automatically unhook when once is specified.");
56     this.order = [];
57     test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
58     test.deepEqual(this.order, ["add", 1, 2, 3], "only the original function should execute.");
59     test.done();
60   },
61   'pre-hook (simple syntax)': function(test) {
62     test.expect(3);
63     // Pre-hook.
64     var result = hooker.hook(this, "add", function(a, b) {
65       // Arguments are passed into pre-hook as specified.
66       this.track("before", this.prop, a, b);
67     });
68     test.deepEqual(result, ["add"], "add should have been hooked.");
69     test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
70     test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
71     test.done();
72   },
73   'pre-hook': function(test) {
74     test.expect(3);
75     // Pre-hook.
76     var result = hooker.hook(this, "add", {
77       pre: function(a, b) {
78         // Arguments are passed into pre-hook as specified.
79         this.track("before", this.prop, a, b);
80       }
81     });
82     test.deepEqual(result, ["add"], "add should have been hooked.");
83     test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
84     test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
85     test.done();
86   },
87   'post-hook': function(test) {
88     test.expect(3);
89     // Post-hook.
90     var result = hooker.hook(this, "add", {
91       post: function(result, a, b) {
92         // Arguments to post-hook are the original function's return value,
93         // followed by the specified function arguments.
94         this.track("after", this.prop, a, b, result);
95       }
96     });
97     test.deepEqual(result, ["add"], "add should have been hooked.");
98     test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
99     test.deepEqual(this.order, ["add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
100     test.done();
101   },
102   'pre- & post-hook': function(test) {
103     test.expect(2);
104     // Pre- & post-hook.
105     hooker.hook(this, "add", {
106       pre: function(a, b) {
107         // Arguments are passed into pre-hook as specified.
108         this.track("before", this.prop, a, b);
109       },
110       post: function(result, a, b) {
111         // Arguments to post-hook are the original function's return value,
112         // followed by the specified function arguments.
113         this.track("after", this.prop, a, b, result);
114       }
115     });
116     test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
117     test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
118     test.done();
119   },
120
121   'pre-hook, return value override': function(test) {
122     test.expect(2);
123     // Pre-hook.
124     hooker.hook(this, "add", {
125       pre: function(a, b) {
126         // Arguments are passed into pre-hook as specified.
127         this.track("before", this.prop, a, b);
128         // This return value will override the original function's return value.
129         return hooker.override("b" + this.prop + a + b);
130       }
131     });
132     test.strictEqual(this.add(2, 3), "b123", "should return the overridden result.");
133     test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
134     test.done();
135   },
136   'post-hook, return value override': function(test) {
137     test.expect(2);
138     // Post-hook.
139     hooker.hook(this, "add", {
140       post: function(result, a, b) {
141         // Arguments to post-hook are the original function's return value,
142         // followed by the specified function arguments.
143         this.track("after", this.prop, a, b, result);
144         // This return value will override the original function's return value.
145         return hooker.override("a" + this.prop + a + b + result);
146       }
147     });
148     test.strictEqual(this.add(2, 3), "a1236", "should return the post-hook overridden result.");
149     test.deepEqual(this.order, ["add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
150     test.done();
151   },
152   'pre- & post-hook, return value override': function(test) {
153     test.expect(2);
154     // Pre- & post-hook.
155     hooker.hook(this, "add", {
156       pre: function(a, b) {
157         // Arguments are passed into pre-hook as specified.
158         this.track("before", this.prop, a, b);
159         // This return value will override the original function's return value.
160         return hooker.override("b" + this.prop + a + b);
161       },
162       post: function(result, a, b) {
163         // Arguments to post-hook are the original function's return value,
164         // followed by the specified function arguments.
165         this.track("after", this.prop, a, b, result);
166         // This return value will override the original function's return value
167         // AND the pre-hook's return value.
168         return hooker.override("a" + this.prop + a + b + result);
169       }
170     });
171     test.strictEqual(this.add(2, 3), "a1236", "should return the overridden result, and post-hook result should take precedence over pre-hook result.");
172     test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
173     test.done();
174   },
175
176   'pre-hook, filtering arguments': function(test) {
177     test.expect(2);
178     // Pre-hook.
179     hooker.hook(this, "add", {
180       pre: function(a, b) {
181         // Arguments are passed into pre-hook as specified.
182         this.track("before", this.prop, a, b);
183         // Return hooker.filter(context, arguments) and they will be passed into
184         // the original function. The "track" and "order" propterites are just
185         // set here for the same of this unit test.
186         return hooker.filter({prop: "x", track: this.track, order: this.order}, ["y", "z"]);
187       }
188     });
189     test.strictEqual(this.add(2, 3), "xyz", "should return the original function's result, given filtered context and arguments.");
190     test.deepEqual(this.order, ["before", 1, 2, 3, "add", "x", "y", "z"], "functions should execute in-order.");
191     test.done();
192   },
193   'pre- & post-hook, filtering arguments': function(test) {
194     test.expect(2);
195     // Pre- & post-hook.
196     hooker.hook(this, "add", {
197       pre: function(a, b) {
198         // Arguments are passed into pre-hook as specified.
199         this.track("before", this.prop, a, b);
200         // Return hooker.filter(context, arguments) and they will be passed into
201         // the original function. The "track" and "order" propterites are just
202         // set here for the same of this unit test.
203         return hooker.filter({prop: "x", track: this.track, order: this.order}, ["y", "z"]);
204       },
205       post: function(result, a, b) {
206         // Arguments to post-hook are the original function's return value,
207         // followed by the specified function arguments.
208         this.track("after", this.prop, a, b, result);
209       }
210     });
211     test.strictEqual(this.add(2, 3), "xyz", "should return the original function's result, given filtered context and arguments.");
212     test.deepEqual(this.order, ["before", 1, 2, 3, "add", "x", "y", "z", "after", 1, 2, 3, "xyz"], "functions should execute in-order.");
213     test.done();
214   },
215   'pre- & post-hook, filtering arguments, return value override': function(test) {
216     test.expect(2);
217     // Pre- & post-hook.
218     hooker.hook(this, "add", {
219       pre: function(a, b) {
220         // Arguments are passed into pre-hook as specified.
221         this.track("before", this.prop, a, b);
222         // Return hooker.filter(context, arguments) and they will be passed into
223         // the original function. The "track" and "order" propterites are just
224         // set here for the same of this unit test.
225         return hooker.filter({prop: "x", track: this.track, order: this.order}, ["y", "z"]);
226       },
227       post: function(result, a, b) {
228         // Arguments to post-hook are the original function's return value,
229         // followed by the specified function arguments.
230         this.track("after", this.prop, a, b, result);
231         // This return value will override the original function's return value
232         // AND the pre-hook's return value.
233         return hooker.override("a" + this.prop + a + b + result);
234       }
235     });
236     test.strictEqual(this.add(2, 3), "a123xyz", "should return the post-hook overridden result.");
237     test.deepEqual(this.order, ["before", 1, 2, 3, "add", "x", "y", "z", "after", 1, 2, 3, "xyz"], "functions should execute in-order.");
238     test.done();
239   },
240
241   'pre-hook, preempt original function': function(test) {
242     test.expect(2);
243     // Pre-hook.
244     hooker.hook(this, "add", {
245       pre: function(a, b) {
246         // Arguments are passed into pre-hook as specified.
247         this.track("before", this.prop, a, b);
248         // Returning hooker.preempt will prevent the original function from being
249         // invoked and optionally set a return value.
250         return hooker.preempt();
251       }
252     });
253     test.strictEqual(this.add(2, 3), undefined, "should return the value passed to preempt.");
254     test.deepEqual(this.order, ["before", 1, 2, 3], "functions should execute in-order.");
255     test.done();
256   },
257   'pre-hook, preempt original function with value': function(test) {
258     test.expect(2);
259     // Pre-hook.
260     hooker.hook(this, "add", {
261       pre: function(a, b) {
262         // Arguments are passed into pre-hook as specified.
263         this.track("before", this.prop, a, b);
264         // Returning hooker.preempt will prevent the original function from being
265         // invoked and optionally set a return value.
266         return hooker.preempt(9000);
267       }
268     });
269     test.strictEqual(this.add(2, 3), 9000, "should return the value passed to preempt.");
270     test.deepEqual(this.order, ["before", 1, 2, 3], "functions should execute in-order.");
271     test.done();
272   },
273   'pre- & post-hook, preempt original function with value': function(test) {
274     test.expect(2);
275     // Pre- & post-hook.
276     hooker.hook(this, "add", {
277       pre: function(a, b) {
278         // Arguments are passed into pre-hook as specified.
279         this.track("before", this.prop, a, b);
280         // Returning hooker.preempt will prevent the original function from being
281         // invoked and optionally set a return value.
282         return hooker.preempt(9000);
283       },
284       post: function(result, a, b) {
285         // Arguments to post-hook are the original function's return value,
286         // followed by the specified function arguments.
287         this.track("after", this.prop, a, b, result);
288       }
289     });
290     test.strictEqual(this.add(2, 3), 9000, "should return the value passed to preempt.");
291     test.deepEqual(this.order, ["before", 1, 2, 3, "after", 1, 2, 3, 9000], "functions should execute in-order.");
292     test.done();
293   },
294   'pre- & post-hook, preempt original function with value, return value override': function(test) {
295     test.expect(2);
296     // Pre- & post-hook.
297     hooker.hook(this, "add", {
298       pre: function(a, b) {
299         // Arguments are passed into pre-hook as specified.
300         this.track("before", this.prop, a, b);
301         // Returning hooker.preempt will prevent the original function from being
302         // invoked and optionally set a return value.
303         return hooker.preempt(9000);
304       },
305       post: function(result, a, b) {
306         // Arguments to post-hook are the original function's return value,
307         // followed by the specified function arguments.
308         this.track("after", this.prop, a, b, result);
309         // This return value will override any preempt value set in pre-hook.
310         return hooker.override("a" + this.prop + a + b + result);
311       }
312     });
313     test.strictEqual(this.add(2, 3), "a1239000", "should return the overridden result, and post-hook result should take precedence over preempt value.");
314     test.deepEqual(this.order, ["before", 1, 2, 3, "after", 1, 2, 3, 9000], "functions should execute in-order.");
315     test.done();
316   },
317   'pre- & post-hook, some properties': function(test) {
318     test.expect(7);
319     // Pre- & post-hook.
320     var result = hooker.hook(this.obj, ["add1", "add2"], {
321       pre: function(a, b) {
322         // Arguments are passed into pre-hook as specified.
323         this.that.track("before", this.prop, a, b);
324       },
325       post: function(result, a, b) {
326         // Arguments to post-hook are the original function's return value,
327         // followed by the specified function arguments.
328         this.that.track("after", this.prop, a, b, result);
329       }
330     });
331     test.deepEqual(result.sort(), ["add1", "add2"], "both functions should have been hooked.");
332     test.strictEqual(this.obj.add1(2, 3), 6, "should return the original function's result.");
333     test.deepEqual(this.order, ["before", 1, 2, 3, "add1", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
334     this.order = [];
335     test.strictEqual(this.obj.add2(2, 3), 6, "should return the original function's result.");
336     test.deepEqual(this.order, ["before", 1, 2, 3, "add2", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
337     this.order = [];
338     test.strictEqual(this.obj.add3(2, 3), 6, "should return the original function's result.");
339     test.deepEqual(this.order, ["add3", 1, 2, 3], "functions should execute in-order.");
340     test.done();
341   },
342   'pre- & post-hook, all properties': function(test) {
343     test.expect(7);
344     // Pre- & post-hook.
345     var result = hooker.hook(this.obj, {
346       pre: function(a, b) {
347         // Arguments are passed into pre-hook as specified.
348         this.that.track("before", this.prop, a, b);
349       },
350       post: function(result, a, b) {
351         // Arguments to post-hook are the original function's return value,
352         // followed by the specified function arguments.
353         this.that.track("after", this.prop, a, b, result);
354       }
355     });
356     test.deepEqual(result.sort(), ["add1", "add2", "add3"], "all functions should have been hooked.");
357     test.strictEqual(this.obj.add1(2, 3), 6, "should return the original function's result.");
358     test.deepEqual(this.order, ["before", 1, 2, 3, "add1", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
359     this.order = [];
360     test.strictEqual(this.obj.add2(2, 3), 6, "should return the original function's result.");
361     test.deepEqual(this.order, ["before", 1, 2, 3, "add2", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
362     this.order = [];
363     test.strictEqual(this.obj.add3(2, 3), 6, "should return the original function's result.");
364     test.deepEqual(this.order, ["before", 1, 2, 3, "add3", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
365     test.done();
366   },
367   'pre- & post-hook, all properties, passName': function(test) {
368     test.expect(6);
369     // Pre- & post-hook.
370     hooker.hook(this.obj, {
371       passName: true,
372       pre: function(name, a, b) {
373         // Arguments are passed into pre-hook as specified.
374         this.that.track("before", this.prop, name, a, b);
375       },
376       post: function(result, name, a, b) {
377         // Arguments to post-hook are the original function's return value,
378         // followed by the specified function arguments.
379         this.that.track("after", this.prop, name, a, b, result);
380       }
381     });
382     test.strictEqual(this.obj.add1(2, 3), 6, "should return the original function's result.");
383     test.deepEqual(this.order, ["before", 1, "add1", 2, 3, "add1", 1, 2, 3, "after", 1, "add1", 2, 3, 6], "functions should execute in-order.");
384     this.order = [];
385     test.strictEqual(this.obj.add2(2, 3), 6, "should return the original function's result.");
386     test.deepEqual(this.order, ["before", 1, "add2", 2, 3, "add2", 1, 2, 3, "after", 1, "add2", 2, 3, 6], "functions should execute in-order.");
387     this.order = [];
388     test.strictEqual(this.obj.add3(2, 3), 6, "should return the original function's result.");
389     test.deepEqual(this.order, ["before", 1, "add3", 2, 3, "add3", 1, 2, 3, "after", 1, "add3", 2, 3, 6], "functions should execute in-order.");
390     test.done();
391   },
392   'unhook one property': function(test) {
393     test.expect(5);
394     var orig = this.add;
395     hooker.hook(this, "add", function() {});
396     var result = hooker.unhook(this, "add");
397     test.deepEqual(result, ["add"], "one function should have been unhooked.");
398     test.strictEqual(this.add, orig, "should have unhooked, restoring the original function");
399     result = hooker.unhook(this, "add");
400     test.deepEqual(result, [], "nothing should have been unhooked.");
401     test.strictEqual(this.add, orig, "shouldn't explode if already unhooked");
402     test.strictEqual(this.add.orig, undefined, "original function shouldn't have an orig property");
403     test.done();
404   },
405   'unhook some properties': function(test) {
406     test.expect(6);
407     var add1 = this.obj.add1;
408     var add2 = this.obj.add2;
409     hooker.hook(this.obj, ["add1", "add2"], function() {});
410     test.strictEqual(hooker.orig(this.obj, "add1"), add1, "should return a refernce to the original function");
411     test.strictEqual(hooker.orig(this.obj, "add2"), add2, "should return a refernce to the original function");
412     test.strictEqual(hooker.orig(this.obj, "add3"), undefined, "should not have been hooked, so should not have an original function");
413     var result = hooker.unhook(this.obj, ["add1", "add2"]);
414     test.deepEqual(result.sort(), ["add1", "add2"], "both functions should have been unhooked.");
415     test.strictEqual(this.obj.add1, add1, "should have unhooked, restoring the original function");
416     test.strictEqual(this.obj.add2, add2, "should have unhooked, restoring the original function");
417     test.done();
418   },
419   'unhook all properties': function(test) {
420     test.expect(7);
421     var add1 = this.obj.add1;
422     var add2 = this.obj.add2;
423     var add3 = this.obj.add3;
424     hooker.hook(this.obj, function() {});
425     test.strictEqual(hooker.orig(this.obj, "add1"), add1, "should return a refernce to the original function");
426     test.strictEqual(hooker.orig(this.obj, "add2"), add2, "should return a refernce to the original function");
427     test.strictEqual(hooker.orig(this.obj, "add3"), add3, "should return a refernce to the original function");
428     var result = hooker.unhook(this.obj);
429     test.deepEqual(result.sort(), ["add1", "add2", "add3"], "all functions should have been unhooked.");
430     test.strictEqual(this.obj.add1, add1, "should have unhooked, restoring the original function");
431     test.strictEqual(this.obj.add2, add2, "should have unhooked, restoring the original function");
432     test.strictEqual(this.obj.add3, add3, "should have unhooked, restoring the original function");
433     test.done();
434   }
435 };