/*global describe,it,expect,beforeEach,Scorpion, setTimeout*/ describe("deferred", function() { var deferred, promise; beforeEach(function() { deferred = Scorpion.defer(); promise = deferred.promise; }); this.timeout(100); describe("promise", function() { it("should have a 'then' method", function() { expect(promise.then).not.to.equal(undefined); }); it("should be able to chain 'then' methods", function() { var promise1 = promise; var promise2 = promise1.then(function(x) { return x + 1; }); 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("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(); }); }); 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 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 call handlers, even when added after resolution", function(done) { deferred.resolve(null); promise.then(function() { done(); }); }); it("should not allow multiple resolution", function() { deferred.resolve(1); expect(function() { deferred.resolve(2); }).to.throw(); }); it("should not allow rejecting after resolving", function() { deferred.resolve(1); expect(function() { deferred.reject(2); }).to.throw(); }); it("should handle a returned promise by 'unwrapping' it", function(done) { promise.then(function(value) { var deferred = Scorpion.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("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 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 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(); }); }); it("should not allow multiple rejection", function() { deferred.reject(1); expect(function() { deferred.reject(2); }).to.throw(); }); it("should not allow resolving after rejecting", function() { deferred.reject(1); expect(function() { deferred.resolve(2); }).to.throw(); }); it("should call handlers, even when added after resolution", function(done) { deferred.reject(null); promise.then(null, function() { done(); }); }); it("should handle a returned promise by 'unwrapping' in the error case", function(done) { promise.then(function(value) { var deferred = Scorpion.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); }); }); describe("waitForAll", function() { it("should resolve with an array if all successful", function(done) { var d1 = Scorpion.defer(), d2 = Scorpion.defer(), d3 = Scorpion.defer(); Scorpion .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); }); it("should reject with an object of successes/errors if any fail", function(done) { var d1 = Scorpion.defer(), d2 = Scorpion.defer(), d3 = Scorpion.defer(); Scorpion .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); }); }); }); describe("injector", function() { var injector, valuePlugin; beforeEach(function() { valuePlugin = new Scorpion.ValuePlugin(); injector = new Scorpion([valuePlugin]); }); this.timeout(100); 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); }); 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); }); }); 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("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); }); 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); }); }); 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(); }); }); 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(); }); }); }); });