diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 96f4a799..d2e2defd 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -239,6 +239,8 @@ getJasmineRequireObj().Spec = function() { this.queueRunner = attrs.queueRunner || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + if (!this.fn) { this.pend(); } @@ -274,9 +276,26 @@ getJasmineRequireObj().Spec = function() { return; } + function timeoutable(fn) { + return function(done) { + var timeout = Function.prototype.apply.apply(self.timer.setTimeout, [window, [function() { + onException(new Error('timeout')); + done(); + }, 10000]]); + + var callDone = function() { + Function.prototype.apply.apply(self.timer.clearTimeout, [window, [timeout]]); + done(); + }; + + fn(callDone); //TODO: do we care about more than 1 arg? + }; + } + var befores = this.beforeFns() || [], - afters = this.afterFns() || []; - var allFns = befores.concat(this.fn).concat(afters); + afters = this.afterFns() || [], + thisOne = (this.fn.length) ? timeoutable(this.fn) : this.fn; + var allFns = befores.concat(thisOne).concat(afters); this.queueRunner({ fns: allFns, @@ -360,6 +379,7 @@ getJasmineRequireObj().Env = function(j$) { var catchExceptions = true; var realSetTimeout = j$.getGlobal().setTimeout; + var realClearTimeout = j$.getGlobal().clearTimeout; this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler()); var spies = []; @@ -495,7 +515,8 @@ getJasmineRequireObj().Env = function(j$) { description: description, expectationResultFactory: expectationResultFactory, queueRunner: queueRunnerFactory, - fn: fn + fn: fn, + timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout} }); if (!self.specFilter(spec)) { diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index 9fb5b08e..09d32453 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -292,6 +292,55 @@ describe("Env integration", function() { env.execute(); }); + describe("with a mock clock", function() { + beforeEach(function() { + jasmine.getEnv().clock.install(); + }); + + afterEach(function() { + jasmine.getEnv().clock.uninstall(); + }); + + it("should not hang on async specs that forget to call done()", function(done) { + var env = new j$.Env(), + reporter = jasmine.createSpyObj('fakeReporter', [ + "jasmineStarted", + "jasmineDone", + "suiteStarted", + "suiteDone", + "specStarted", + "specDone" + ]); + + env.addReporter(reporter); + + env.describe("tests", function() { + env.it("async spec that will hang", function(underTestCallback) { + env.expect(true).toBeTruthy(); + }); + + env.it("after async spec", function() { + env.expect(true).toBeTruthy(); + }); + }); + + env.execute(); + + reporter.jasmineDone.and.callFake(function() { + expect(reporter.jasmineStarted).toHaveBeenCalledWith({ + totalSpecsDefined: 2 + }); + + expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({status: 'passed'})); + expect(reporter.specDone).toHaveBeenCalledWith(jasmine.objectContaining({status: 'failed'})); + + done(); + }); + + jasmine.getEnv().clock.tick(60001); + }); + }); + // TODO: something is wrong with this spec it("should report as expected", function(done) { var env = new j$.Env(), diff --git a/src/core/Env.js b/src/core/Env.js index 3294cbcd..20436edc 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -8,6 +8,7 @@ getJasmineRequireObj().Env = function(j$) { var catchExceptions = true; var realSetTimeout = j$.getGlobal().setTimeout; + var realClearTimeout = j$.getGlobal().clearTimeout; this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler()); var spies = []; @@ -143,7 +144,8 @@ getJasmineRequireObj().Env = function(j$) { description: description, expectationResultFactory: expectationResultFactory, queueRunner: queueRunnerFactory, - fn: fn + fn: fn, + timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout} }); if (!self.specFilter(spec)) { diff --git a/src/core/Spec.js b/src/core/Spec.js index d5c539de..27b11087 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -16,6 +16,8 @@ getJasmineRequireObj().Spec = function() { this.queueRunner = attrs.queueRunner || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + if (!this.fn) { this.pend(); } @@ -51,9 +53,26 @@ getJasmineRequireObj().Spec = function() { return; } + function timeoutable(fn) { + return function(done) { + var timeout = Function.prototype.apply.apply(self.timer.setTimeout, [window, [function() { + onException(new Error('timeout')); + done(); + }, 10000]]); + + var callDone = function() { + Function.prototype.apply.apply(self.timer.clearTimeout, [window, [timeout]]); + done(); + }; + + fn(callDone); //TODO: do we care about more than 1 arg? + }; + } + var befores = this.beforeFns() || [], - afters = this.afterFns() || []; - var allFns = befores.concat(this.fn).concat(afters); + afters = this.afterFns() || [], + thisOne = (this.fn.length) ? timeoutable(this.fn) : this.fn; + var allFns = befores.concat(thisOne).concat(afters); this.queueRunner({ fns: allFns,