123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- /*global require:true */
- var hooker = require('../lib/hooker');
- exports['hook'] = {
- setUp: function(done) {
- this.order = [];
- this.track = function() {
- [].push.apply(this.order, arguments);
- };
- this.prop = 1;
- this.add = function(a, b) {
- this.track("add", this.prop, a, b);
- return this.prop + a + b;
- };
- this.obj = {
- that: this,
- prop: 1,
- add1: function(a, b) {
- this.that.track("add1", this.prop, a, b);
- return this.prop + a + b;
- },
- add2: function(a, b) {
- this.that.track("add2", this.prop, a, b);
- return this.prop + a + b;
- },
- add3: function(a, b) {
- this.that.track("add3", this.prop, a, b);
- return this.prop + a + b;
- }
- };
- done();
- },
- 'orig': function(test) {
- test.expect(1);
- var orig = this.add;
- hooker.hook(this, "add", function() {});
- test.strictEqual(hooker.orig(this, "add"), orig, "should return a refernce to the original function.");
- test.done();
- },
- 'once': function(test) {
- test.expect(5);
- var orig = this.add;
- hooker.hook(this, "add", {
- once: true,
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- }
- });
- test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
- test.strictEqual(this.add, orig, "should automatically unhook when once is specified.");
- this.order = [];
- test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["add", 1, 2, 3], "only the original function should execute.");
- test.done();
- },
- 'pre-hook (simple syntax)': function(test) {
- test.expect(3);
- // Pre-hook.
- var result = hooker.hook(this, "add", function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- });
- test.deepEqual(result, ["add"], "add should have been hooked.");
- test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
- test.done();
- },
- 'pre-hook': function(test) {
- test.expect(3);
- // Pre-hook.
- var result = hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- }
- });
- test.deepEqual(result, ["add"], "add should have been hooked.");
- test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
- test.done();
- },
- 'post-hook': function(test) {
- test.expect(3);
- // Post-hook.
- var result = hooker.hook(this, "add", {
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- }
- });
- test.deepEqual(result, ["add"], "add should have been hooked.");
- test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook': function(test) {
- test.expect(2);
- // Pre- & post-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- }
- });
- test.strictEqual(this.add(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- test.done();
- },
- 'pre-hook, return value override': function(test) {
- test.expect(2);
- // Pre-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // This return value will override the original function's return value.
- return hooker.override("b" + this.prop + a + b);
- }
- });
- test.strictEqual(this.add(2, 3), "b123", "should return the overridden result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3], "functions should execute in-order.");
- test.done();
- },
- 'post-hook, return value override': function(test) {
- test.expect(2);
- // Post-hook.
- hooker.hook(this, "add", {
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- // This return value will override the original function's return value.
- return hooker.override("a" + this.prop + a + b + result);
- }
- });
- test.strictEqual(this.add(2, 3), "a1236", "should return the post-hook overridden result.");
- test.deepEqual(this.order, ["add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, return value override': function(test) {
- test.expect(2);
- // Pre- & post-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // This return value will override the original function's return value.
- return hooker.override("b" + this.prop + a + b);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- // This return value will override the original function's return value
- // AND the pre-hook's return value.
- return hooker.override("a" + this.prop + a + b + result);
- }
- });
- test.strictEqual(this.add(2, 3), "a1236", "should return the overridden result, and post-hook result should take precedence over pre-hook result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- test.done();
- },
- 'pre-hook, filtering arguments': function(test) {
- test.expect(2);
- // Pre-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Return hooker.filter(context, arguments) and they will be passed into
- // the original function. The "track" and "order" propterites are just
- // set here for the same of this unit test.
- return hooker.filter({prop: "x", track: this.track, order: this.order}, ["y", "z"]);
- }
- });
- test.strictEqual(this.add(2, 3), "xyz", "should return the original function's result, given filtered context and arguments.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", "x", "y", "z"], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, filtering arguments': function(test) {
- test.expect(2);
- // Pre- & post-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Return hooker.filter(context, arguments) and they will be passed into
- // the original function. The "track" and "order" propterites are just
- // set here for the same of this unit test.
- return hooker.filter({prop: "x", track: this.track, order: this.order}, ["y", "z"]);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- }
- });
- test.strictEqual(this.add(2, 3), "xyz", "should return the original function's result, given filtered context and arguments.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", "x", "y", "z", "after", 1, 2, 3, "xyz"], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, filtering arguments, return value override': function(test) {
- test.expect(2);
- // Pre- & post-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Return hooker.filter(context, arguments) and they will be passed into
- // the original function. The "track" and "order" propterites are just
- // set here for the same of this unit test.
- return hooker.filter({prop: "x", track: this.track, order: this.order}, ["y", "z"]);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- // This return value will override the original function's return value
- // AND the pre-hook's return value.
- return hooker.override("a" + this.prop + a + b + result);
- }
- });
- test.strictEqual(this.add(2, 3), "a123xyz", "should return the post-hook overridden result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add", "x", "y", "z", "after", 1, 2, 3, "xyz"], "functions should execute in-order.");
- test.done();
- },
- 'pre-hook, preempt original function': function(test) {
- test.expect(2);
- // Pre-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Returning hooker.preempt will prevent the original function from being
- // invoked and optionally set a return value.
- return hooker.preempt();
- }
- });
- test.strictEqual(this.add(2, 3), undefined, "should return the value passed to preempt.");
- test.deepEqual(this.order, ["before", 1, 2, 3], "functions should execute in-order.");
- test.done();
- },
- 'pre-hook, preempt original function with value': function(test) {
- test.expect(2);
- // Pre-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Returning hooker.preempt will prevent the original function from being
- // invoked and optionally set a return value.
- return hooker.preempt(9000);
- }
- });
- test.strictEqual(this.add(2, 3), 9000, "should return the value passed to preempt.");
- test.deepEqual(this.order, ["before", 1, 2, 3], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, preempt original function with value': function(test) {
- test.expect(2);
- // Pre- & post-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Returning hooker.preempt will prevent the original function from being
- // invoked and optionally set a return value.
- return hooker.preempt(9000);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- }
- });
- test.strictEqual(this.add(2, 3), 9000, "should return the value passed to preempt.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "after", 1, 2, 3, 9000], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, preempt original function with value, return value override': function(test) {
- test.expect(2);
- // Pre- & post-hook.
- hooker.hook(this, "add", {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.track("before", this.prop, a, b);
- // Returning hooker.preempt will prevent the original function from being
- // invoked and optionally set a return value.
- return hooker.preempt(9000);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.track("after", this.prop, a, b, result);
- // This return value will override any preempt value set in pre-hook.
- return hooker.override("a" + this.prop + a + b + result);
- }
- });
- test.strictEqual(this.add(2, 3), "a1239000", "should return the overridden result, and post-hook result should take precedence over preempt value.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "after", 1, 2, 3, 9000], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, some properties': function(test) {
- test.expect(7);
- // Pre- & post-hook.
- var result = hooker.hook(this.obj, ["add1", "add2"], {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.that.track("before", this.prop, a, b);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.that.track("after", this.prop, a, b, result);
- }
- });
- test.deepEqual(result.sort(), ["add1", "add2"], "both functions should have been hooked.");
- test.strictEqual(this.obj.add1(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add1", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- this.order = [];
- test.strictEqual(this.obj.add2(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add2", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- this.order = [];
- test.strictEqual(this.obj.add3(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["add3", 1, 2, 3], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, all properties': function(test) {
- test.expect(7);
- // Pre- & post-hook.
- var result = hooker.hook(this.obj, {
- pre: function(a, b) {
- // Arguments are passed into pre-hook as specified.
- this.that.track("before", this.prop, a, b);
- },
- post: function(result, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.that.track("after", this.prop, a, b, result);
- }
- });
- test.deepEqual(result.sort(), ["add1", "add2", "add3"], "all functions should have been hooked.");
- test.strictEqual(this.obj.add1(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add1", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- this.order = [];
- test.strictEqual(this.obj.add2(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add2", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- this.order = [];
- test.strictEqual(this.obj.add3(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, 2, 3, "add3", 1, 2, 3, "after", 1, 2, 3, 6], "functions should execute in-order.");
- test.done();
- },
- 'pre- & post-hook, all properties, passName': function(test) {
- test.expect(6);
- // Pre- & post-hook.
- hooker.hook(this.obj, {
- passName: true,
- pre: function(name, a, b) {
- // Arguments are passed into pre-hook as specified.
- this.that.track("before", this.prop, name, a, b);
- },
- post: function(result, name, a, b) {
- // Arguments to post-hook are the original function's return value,
- // followed by the specified function arguments.
- this.that.track("after", this.prop, name, a, b, result);
- }
- });
- test.strictEqual(this.obj.add1(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, "add1", 2, 3, "add1", 1, 2, 3, "after", 1, "add1", 2, 3, 6], "functions should execute in-order.");
- this.order = [];
- test.strictEqual(this.obj.add2(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, "add2", 2, 3, "add2", 1, 2, 3, "after", 1, "add2", 2, 3, 6], "functions should execute in-order.");
- this.order = [];
- test.strictEqual(this.obj.add3(2, 3), 6, "should return the original function's result.");
- test.deepEqual(this.order, ["before", 1, "add3", 2, 3, "add3", 1, 2, 3, "after", 1, "add3", 2, 3, 6], "functions should execute in-order.");
- test.done();
- },
- 'unhook one property': function(test) {
- test.expect(5);
- var orig = this.add;
- hooker.hook(this, "add", function() {});
- var result = hooker.unhook(this, "add");
- test.deepEqual(result, ["add"], "one function should have been unhooked.");
- test.strictEqual(this.add, orig, "should have unhooked, restoring the original function");
- result = hooker.unhook(this, "add");
- test.deepEqual(result, [], "nothing should have been unhooked.");
- test.strictEqual(this.add, orig, "shouldn't explode if already unhooked");
- test.strictEqual(this.add.orig, undefined, "original function shouldn't have an orig property");
- test.done();
- },
- 'unhook some properties': function(test) {
- test.expect(6);
- var add1 = this.obj.add1;
- var add2 = this.obj.add2;
- hooker.hook(this.obj, ["add1", "add2"], function() {});
- test.strictEqual(hooker.orig(this.obj, "add1"), add1, "should return a refernce to the original function");
- test.strictEqual(hooker.orig(this.obj, "add2"), add2, "should return a refernce to the original function");
- test.strictEqual(hooker.orig(this.obj, "add3"), undefined, "should not have been hooked, so should not have an original function");
- var result = hooker.unhook(this.obj, ["add1", "add2"]);
- test.deepEqual(result.sort(), ["add1", "add2"], "both functions should have been unhooked.");
- test.strictEqual(this.obj.add1, add1, "should have unhooked, restoring the original function");
- test.strictEqual(this.obj.add2, add2, "should have unhooked, restoring the original function");
- test.done();
- },
- 'unhook all properties': function(test) {
- test.expect(7);
- var add1 = this.obj.add1;
- var add2 = this.obj.add2;
- var add3 = this.obj.add3;
- hooker.hook(this.obj, function() {});
- test.strictEqual(hooker.orig(this.obj, "add1"), add1, "should return a refernce to the original function");
- test.strictEqual(hooker.orig(this.obj, "add2"), add2, "should return a refernce to the original function");
- test.strictEqual(hooker.orig(this.obj, "add3"), add3, "should return a refernce to the original function");
- var result = hooker.unhook(this.obj);
- test.deepEqual(result.sort(), ["add1", "add2", "add3"], "all functions should have been unhooked.");
- test.strictEqual(this.obj.add1, add1, "should have unhooked, restoring the original function");
- test.strictEqual(this.obj.add2, add2, "should have unhooked, restoring the original function");
- test.strictEqual(this.obj.add3, add3, "should have unhooked, restoring the original function");
- test.done();
- }
- };
|