1 /*global require:true */
2 var hooker = require('../lib/hooker');
5 setUp: function(done) {
7 this.track = function() {
8 [].push.apply(this.order, arguments);
12 this.add = function(a, b) {
13 this.track("add", this.prop, a, b);
14 return this.prop + a + b;
20 add1: function(a, b) {
21 this.that.track("add1", this.prop, a, b);
22 return this.prop + a + b;
24 add2: function(a, b) {
25 this.that.track("add2", this.prop, a, b);
26 return this.prop + a + b;
28 add3: function(a, b) {
29 this.that.track("add3", this.prop, a, b);
30 return this.prop + a + b;
36 'orig': function(test) {
39 hooker.hook(this, "add", function() {});
40 test.strictEqual(hooker.orig(this, "add"), orig, "should return a refernce to the original function.");
43 'once': function(test) {
46 hooker.hook(this, "add", {
49 // Arguments are passed into pre-hook as specified.
50 this.track("before", this.prop, a, b);
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.");
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.");
61 'pre-hook (simple syntax)': function(test) {
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);
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.");
73 'pre-hook': function(test) {
76 var result = hooker.hook(this, "add", {
78 // Arguments are passed into pre-hook as specified.
79 this.track("before", this.prop, a, b);
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.");
87 'post-hook': function(test) {
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);
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.");
102 'pre- & post-hook': function(test) {
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);
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);
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.");
121 'pre-hook, return value override': function(test) {
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);
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.");
136 'post-hook, return value override': function(test) {
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);
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.");
152 'pre- & post-hook, return value override': function(test) {
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);
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);
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.");
176 'pre-hook, filtering arguments': function(test) {
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"]);
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.");
193 'pre- & post-hook, filtering arguments': function(test) {
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"]);
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);
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.");
215 'pre- & post-hook, filtering arguments, return value override': function(test) {
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"]);
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);
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.");
241 'pre-hook, preempt original function': function(test) {
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();
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.");
257 'pre-hook, preempt original function with value': function(test) {
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);
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.");
273 'pre- & post-hook, preempt original function with value': function(test) {
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);
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);
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.");
294 'pre- & post-hook, preempt original function with value, return value override': function(test) {
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);
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);
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.");
317 'pre- & post-hook, some properties': function(test) {
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);
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);
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.");
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.");
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.");
342 'pre- & post-hook, all properties': function(test) {
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);
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);
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.");
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.");
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.");
367 'pre- & post-hook, all properties, passName': function(test) {
370 hooker.hook(this.obj, {
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);
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);
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.");
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.");
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.");
392 'unhook one property': function(test) {
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");
405 'unhook some properties': function(test) {
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");
419 'unhook all properties': function(test) {
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");