summaryrefslogtreecommitdiff
path: root/injector-tests.js
diff options
context:
space:
mode:
Diffstat (limited to 'injector-tests.js')
-rw-r--r--injector-tests.js445
1 files changed, 302 insertions, 143 deletions
diff --git a/injector-tests.js b/injector-tests.js
index 339034f..e94126f 100644
--- a/injector-tests.js
+++ b/injector-tests.js
@@ -1,200 +1,359 @@
-/*global describe,it,expect,beforeEach*/
-describe("injector", function() {
- var injector;
+/*global describe,it,expect,beforeEach,Injector, setTimeout*/
+
+describe("deferred", function() {
+ var deferred, promise;
beforeEach(function() {
- injector = new Injector();
+ deferred = Injector.defer();
+ promise = deferred.promise;
});
+ this.timeout(100);
- describe("#register()", function() {
- it("should return the injector", function() {
- var result = injector.register("A", function(){ return "A"; });
- expect(result).toBe(injector);
- });
-
- it("should use a function's name if no name provided", function() {
- injector.register(function A(){ return "A"; });
- expect(injector.specs.A).not.toBe(undefined); // IMPLEMENTATION DETAIL
+ describe("promise", function() {
+ it("should have a 'then' method", function() {
+ expect(promise.then).not.to.equal(undefined);
});
- it("should register an object as a series of dependencies", function() {
- injector.register({
- A: function(){ return "A"; },
- B: function(){ return "B"; }
+ it("should be able to chain 'then' methods", function() {
+ var promise1 = promise;
+ var promise2 = promise1.then(function(x) {
+ return x + 1;
});
- expect(injector.specs.A).not.toBe(undefined); // IMPLEMENTATION DETAIL
- expect(injector.specs.B).not.toBe(undefined); // IMPLEMENTATION DETAIL
+ var promise3 = promise1.then(function(x) {
+ return x + 1;
+ });
+ expect(promise1.then).not.to.equal(undefined);
+ expect(promise2.then).not.to.equal(undefined);
+ expect(promise3.then).not.to.equal(undefined);
});
});
- describe("#get()", function() {
- it("should throw if the dependency can't be found", function() {
- expect(function() {
- injector.get("A");
- }).toThrow();
+ describe("resolution", function() {
+
+ it("should resolve asynchronously", function(done) {
+ var hasRun = false;
+ promise.then(function(val) {
+ hasRun = true;
+ return val;
+ });
+ deferred.resolve("value");
+ expect(hasRun).to.equal(false);
+ setTimeout(function() {
+ expect(hasRun).to.equal(true);
+ done();
+ });
});
- });
- describe("#invoke()", function() {
- it("should be able to invoke a function with no dependencies", function() {
- var A = injector.invoke(function() {return "functionResult";});
- expect(A).toBe("functionResult");
+ it("should propagate through multiple promises", function(done) {
+ var promise2 = promise.then(function(value) {
+ return value + 1;
+ });
+ var promise3 = promise2.then(function(value) {
+ return value + 1;
+ });
+ promise3.then(function(value) {
+ expect(value).to.equal(3);
+ done();
+ });
+ deferred.resolve(1);
});
- it("should be able to invoke a function with a dependency in an array", function() {
- injector.register("B", function() {return "constructorB";});
- var A = injector.invoke(["B", function(B) {return B + "A";}]);
- expect(A).toBe("constructorBA");
+ it("should call all registered handlers", function(done) {
+ var called = 0;
+ promise.then(function() {called++;});
+ promise.then(function() {called++;});
+ promise.then(function() {called++;});
+ expect(called).to.equal(0);
+ deferred.resolve(null);
+ setTimeout(function() {
+ expect(called).to.equal(3);
+ done();
+ });
});
- it("should be able to invoke a function with an implicit dependency", function() {
- injector.register("B", function() {return "constructorB";});
- var A = injector.invoke(function(B) {return B + "A";});
- expect(A).toBe("constructorBA");
+ it("should call handlers, even when added after resolution", function(done) {
+ deferred.resolve(null);
+ promise.then(function() {
+ done();
+ });
});
- it("should not touch any exceptions thrown by the function", function() {
+ it("should not allow multiple resolution", function() {
+ deferred.resolve(1);
expect(function() {
- throw new Error("An error");
- }).toThrow(new Error("An error"));
+ deferred.resolve(2);
+ }).to.throw();
});
- it("should invoke the function with 'this' set to the injector", function() {
- var hasRun = false;
- injector.invoke(function() {
- hasRun = true;
- expect(this).toBe(injector);
- });
- expect(hasRun).toBe(true);
+ it("should not allow rejecting after resolving", function() {
+ deferred.resolve(1);
+ expect(function() {
+ deferred.reject(2);
+ }).to.throw();
});
- it("should not provide 'this.requestor' to the invoked function", function() {
- var caught = {};
- try {
- injector.invoke(function() {
- this.requestor();
- });
- } catch (e) {
- caught = e;
- }
- expect(caught.name).toBe("InjectorError");
+ it("should handle a returned promise by 'unwrapping' it", function(done) {
+ promise.then(function(value) {
+ var deferred = Injector.defer();
+ deferred.resolve(value + 1);
+ return deferred.promise;
+ }).then(function(value) {
+ try {
+ expect(value).to.equal(2);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ deferred.resolve(1);
});
});
- describe("constructor dependency injection", function() {
- it("should be able to register a constructor with no dependencies, and get the value for it", function() {
- injector.register("A", function() {return "constructorA";});
-
- var A = injector.get("A");
- expect(A).toBe("constructorA");
+ describe("rejection", function() {
+ it("should reject asynchronously", function(done) {
+ var hasRun = false;
+ promise.then(null, function(val) {
+ hasRun = true;
+ return val;
+ });
+ deferred.reject("value");
+ expect(hasRun).to.equal(false);
+ setTimeout(function() {
+ expect(hasRun).to.equal(true);
+ done();
+ });
});
- it("should be able to register a constructor with a dependency in an array, and get the value for it", function() {
- injector.register("B", function() {return "constructorB";});
- injector.register("A", ["B", function(B) {return B;}]);
- var A = injector.get("A");
- expect(A).toBe("constructorB");
+ it("should be turned into resolution by a handler", function(done) {
+ var promise2 = promise.then(function(value) {
+ return value + 1;
+ }, function(value) {
+ return value + 100;
+ });
+ promise2.then(function(value) {
+ try {
+ expect(value).to.equal(101);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ deferred.reject(1);
});
- it("should be able to register a constructor with an implicit dependency, and get the value for it", function() {
- injector.register("B", function() {return "constructorB";});
- injector.register("A", function(B) {return B;});
+ it("should call all registered handlers", function(done) {
+ var called = 0;
+ promise.then(null, function() {called++;});
+ promise.then(null, function() {called++;});
+ promise.then(null, function() {called++;});
+ expect(called).to.equal(0);
+ deferred.reject(null);
+ setTimeout(function() {
+ expect(called).to.equal(3);
+ done();
+ });
+ });
- var A = injector.get("A");
- expect(A).toBe("constructorB");
+ it("should not allow multiple rejection", function() {
+ deferred.reject(1);
+ expect(function() {
+ deferred.reject(2);
+ }).to.throw();
});
- it("should be able to register a constructor with an implicit name", function() {
- injector.register(function A() {return "constructorA";});
- var A = injector.get("A");
- expect(A).toBe("constructorA");
+ it("should not allow resolving after rejecting", function() {
+ deferred.reject(1);
+ expect(function() {
+ deferred.resolve(2);
+ }).to.throw();
});
- it("should be able to register constructors with an object", function() {
- injector.register({
- A: ["B", function (B) {return "constructorA" + B;}],
- B: function(C) { return "B" + C; },
- C: function() { return "C"; }
+ it("should call handlers, even when added after resolution", function(done) {
+ deferred.reject(null);
+ promise.then(null, function() {
+ done();
});
- var A = injector.get("A");
- expect(A).toBe("constructorABC");
});
- it("should wrap any exceptions thrown by constructors", function() {
- var error = new Error("constructorA");
- injector.register("A", function() {throw error;});
- var caught = {};
- try {
- injector.get("A");
- } catch (e) {
- caught = e;
- }
- expect(caught.name).toBe("InjectorError");
- expect(caught.cause).toBe(error);
+ it("should handle a returned promise by 'unwrapping' in the error case", function(done) {
+ promise.then(function(value) {
+ var deferred = Injector.defer();
+ deferred.reject(value + 1);
+ return deferred.promise;
+ }).then(null, function(value) {
+ try {
+ expect(value).to.equal(2);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ deferred.resolve(1);
});
+ });
- it("should detect circular dependencies", function() {
- injector.register("A", ["B", function(B) {return null;}]);
- injector.register("B", ["A", function(A) {return null;}]);
+ describe("waitForAll", function() {
+ it("should resolve with an array if all successful", function(done) {
+ var d1 = Injector.defer(),
+ d2 = Injector.defer(),
+ d3 = Injector.defer();
+ Injector
+ .waitForAll([d1.promise, d2.promise, d3.promise])
+ .then(function(values) {
+ if (values.length == 3 &&
+ values[0] === 0 &&
+ values[1] === 1 &&
+ values[2] === 2) {
+ done();
+ } else {
+ done(new Error("Error in resolved result: " + values));
+ }
+ }, done);
+ d1.resolve(0);
+ d2.resolve(1);
+ d3.resolve(2);
+ });
- var caught = {message: ''};
- try {
- injector.get("A");
- } catch (e) {
- caught = e;
- }
- expect(caught.message.indexOf("Cyclic dependency detected")).toBe(0);
+ it("should reject with an object of successes/errors if any fail", function(done) {
+ var d1 = Injector.defer(),
+ d2 = Injector.defer(),
+ d3 = Injector.defer();
+ Injector
+ .waitForAll([d1.promise, d2.promise, d3.promise])
+ .then(function(values) {
+ done(new Error("incorrectly resolved promise"));
+ }, function(e) {
+ if (e.errors && e.values &&
+ e.values[0] === 0 &&
+ e.values[1] === 1 &&
+ e.errors[2] === 2) {
+ done();
+ } else {
+ done(new Error("incorrect reject value"));
+ }
+ });
+ d1.resolve(0);
+ d2.resolve(1);
+ d3.reject(2);
});
+ });
+});
- it("should provide access to the requestor through 'this.requestor()'", function() {
- injector.register("B", function() {
- expect(this.requestor()).toBe("A");
- return "returnValueB";
- });
- injector.register("A", ["B", function(B) {
- return B + "A";
- }]);
+describe("injector", function() {
+ var injector, valuePlugin;
+ beforeEach(function() {
+ valuePlugin = new Injector.ValuePlugin();
+ injector = new Injector([valuePlugin]);
+ });
+ this.timeout(100);
- // make sure the function actually ran
- expect(injector.get("A")).toBe("returnValueBA");
+ describe("value registration and retrieval", function() {
+ it("works", function(done) {
+ injector.register("a", function(){return "the value of a";});
+ injector.get("a").then(function(value){
+ if (value == "the value of a")
+ done();
+ else
+ done(new Error("incorrect value for a (" + value + ")"));
+ }, done);
});
- });
- describe("caching", function() {
- it("should return the same exact value for multiple requests of the same dependency", function() {
- injector.register("A", function() {return {an: 'object'};});
+ it("with dependencies works", function(done) {
+ injector
+ .register("a", function(){return "the value of a";})
+ .register("b", ["a", function(a){return a.replace(/a/g, "b");}]);
+ injector.get("b").then(function(value){
+ if (value == "the vblue of b")
+ done();
+ else
+ done(new Error("incorrect value for b (" + value + ")"));
+ }, done);
+ });
+ });
- var first = injector.get("A");
- var second = injector.get("A");
- expect(first).toBe(second);
+ describe("destroy handlers", function() {
+ it("work with single gets", function(done) {
+ injector.register("a", function(){return "a";});
+ expect("a" in valuePlugin.values).to.equal(false);
+ var result = injector.get("a");
+ result.then(function() {
+ if ("a" in valuePlugin.values) {
+ result.destroy().then(function() {
+ if ("a" in valuePlugin.values) {
+ done(new Error("Value found for a after destruction"));
+ } else {
+ done();
+ }
+ });
+ } else {
+ done(new Error("No value found for a"));
+ }
+ }, done);
});
- it("shouldn't re-run constructors for cached values", function() {
- var numTimes = 0;
- injector.register("A", function() {
- numTimes++;
- if (numTimes > 1)
- throw new Error("Shouldn't be run more than once");
- return {an: 'object'};
- });
+ it("works with multiple gets", function(done) {
+ injector.register("a", function(){return "a";});
+ expect("a" in valuePlugin.values).to.equal(false);
+ var result = [injector.get("a"), injector.get("a"), injector.get("a")];
+ result[1].destroy();
+ result[2].destroy();
+ result[0].then(function() {
+ if ("a" in valuePlugin.values) {
+ result[0].destroy().then(function() {
+ if ("a" in valuePlugin.values) {
+ done(new Error("Value found for a after destruction"));
+ } else {
+ done();
+ }
+ });
+ } else {
+ done(new Error("No value found for a"));
+ }
+ }, done);
+ });
- var first = injector.get("A");
- var second = injector.get("A");
- expect(first).toBe(second);
+ it("works through dependencies", function(done) {
+ injector
+ .register("a", function(){return "a";})
+ .register("b", ["a", function(a){return a + "b";}]);
+ expect("a" in valuePlugin.values).to.equal(false);
+ var result = injector.get("b");
+ result.then(function() {
+ if ("a" in valuePlugin.values) {
+ result.destroy().then(function() {
+ if ("a" in valuePlugin.values) {
+ done(new Error("Value found for a after destruction"));
+ } else {
+ done();
+ }
+ });
+ } else {
+ done(new Error("No value found for a"));
+ }
+ }, done);
});
+ });
- it("shouldn't cache values which depend on their requestor", function() {
- var numTimes = 0;
- injector.register("A", function() {
- numTimes++;
- return "A" + this.requestor();
+ describe("cyclic dependency detection", function() {
+ it("detects simple cycles", function(done) {
+ injector.register("a", ["b", function(b) {return b;}]);
+ injector.register("b", ["a", function(a) {return a;}]);
+ injector.get("a").then(function() {
+ done(new Error("Promise should have been rejected"));
+ }, function(e) {
+ done();
});
- injector.register("B", ["A", function(A) {return "B" + A;}]);
- injector.register("C", ["A", function(A) {return "C" + A;}]);
+ });
- expect(injector.get("B")).toBe("BAB");
- expect(injector.get("C")).toBe("CAC");
- expect(numTimes).toBe(2);
+ it("detects cycles with intermediate nodes", function(done) {
+ injector.register("a", ["c", function(c) {return c;}]);
+ injector.register("b", ["a", function(a) {return a;}]);
+ injector.register("c", ["b", function(b) {return b;}]);
+ injector.get("a").then(function() {
+ done(new Error("Promise should have been rejected"));
+ }, function(e) {
+ done();
+ });
});
});
});