diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 171890a3..3b968686 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -266,7 +266,8 @@ getJasmineRequireObj().Spec = function(j$) { }; Spec.prototype.execute = function(onComplete) { - var self = this; + var self = this, + timeout; this.onStart(this); @@ -277,26 +278,25 @@ getJasmineRequireObj().Spec = function(j$) { function timeoutable(fn) { return function(done) { - var timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { + timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.')); done(); }, j$.DEFAULT_TIMEOUT_INTERVAL]]); var callDone = function() { - Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); + clearTimeoutable(); done(); }; - try { - fn.call(this, callDone); //TODO: do we care about more than 1 arg? - } - catch (e) { - onException(e); - callDone(); - } + fn.call(this, callDone); //TODO: do we care about more than 1 arg? }; } + function clearTimeoutable() { + Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); + timeout = void 0; + } + var befores = this.beforeFns() || [], afters = this.afterFns() || [], thisOne = (this.fn.length) ? timeoutable(this.fn) : this.fn; @@ -309,6 +309,7 @@ getJasmineRequireObj().Spec = function(j$) { }); function onException(e) { + clearTimeoutable(); if (Spec.isPendingSpecException(e)) { self.pend(); return; diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index 6f7378f9..c89a0cae 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -218,42 +218,24 @@ describe("Spec", function() { expect(specNameSpy.calls.mostRecent().args[0].id).toEqual(spec.id); }); - it("resets the timeout timer when an async spec throws an exception", function(done) { - var handleException = jasmine.createSpy('exception handler'), + it("resets the timeout timer when an async spec throws an exception", function() { + var queueRunnerSpy = jasmine.createSpy('queueRunner'), + clearTimeoutSpy = jasmine.createSpy('clear timeout'), spec = new j$.Spec({ - fn: function(done) { throw new Error('test'); }, + fn: function(done) { }, catchExceptions: function() { return true; }, - expectationResultFactory: handleException, timer: { - // Force timeout to 0 to postpone execution to next tick - // without using the default 5s interval - setTimeout: function (fn) { return setTimeout(fn, 0); }, - clearTimeout: clearTimeout + setTimeout: function () { return 920; }, + clearTimeout: clearTimeoutSpy }, - queueRunnerFactory: function (attrs) { - // Fake the "run" method of a regular queue runner - // for an async spec. - try { - attrs.fns[0].call({}, function () {}); - } catch (e) { - handleException(e); - } - } + queueRunnerFactory: queueRunnerSpy }); - // Spec execution will create the timeout timer that would report - // a failure on next tick unless it gets properly cleared before - // the end of this tick. spec.execute(); + queueRunnerSpy.calls.mostRecent().args[0].fns[0](); + queueRunnerSpy.calls.mostRecent().args[0].onException(new Error()); - // Run the expect clause on next tick to detect the case when the - // timeout timer was not properly reset. The exception handler is - // called once when the error is thrown. It is called twice when - // the timeout timer is not properly reset. - setTimeout(function () { - expect(handleException.calls.count()).toEqual(1); - done(); - }, 0); + expect(clearTimeoutSpy).toHaveBeenCalledWith(920); }); describe("when a spec is marked pending during execution", function() { diff --git a/src/core/Spec.js b/src/core/Spec.js index afdebd41..a75f0c1c 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -41,7 +41,8 @@ getJasmineRequireObj().Spec = function(j$) { }; Spec.prototype.execute = function(onComplete) { - var self = this; + var self = this, + timeout; this.onStart(this); @@ -52,26 +53,25 @@ getJasmineRequireObj().Spec = function(j$) { function timeoutable(fn) { return function(done) { - var timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { + timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.')); done(); }, j$.DEFAULT_TIMEOUT_INTERVAL]]); var callDone = function() { - Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); + clearTimeoutable(); done(); }; - try { - fn.call(this, callDone); //TODO: do we care about more than 1 arg? - } - catch (e) { - onException(e); - callDone(); - } + fn.call(this, callDone); //TODO: do we care about more than 1 arg? }; } + function clearTimeoutable() { + Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); + timeout = void 0; + } + var befores = this.beforeFns() || [], afters = this.afterFns() || [], thisOne = (this.fn.length) ? timeoutable(this.fn) : this.fn; @@ -84,6 +84,7 @@ getJasmineRequireObj().Spec = function(j$) { }); function onException(e) { + clearTimeoutable(); if (Spec.isPendingSpecException(e)) { self.pend(); return;