diff --git a/lib/jasmine-core/boot.js b/lib/jasmine-core/boot.js index 4655042c..74dfd70d 100644 --- a/lib/jasmine-core/boot.js +++ b/lib/jasmine-core/boot.js @@ -73,8 +73,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var filterSpecs = !!queryString.getParam("spec"); - var catchingExceptions = queryString.getParam("catch"); - env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); + var stoppingOnSpecFailure = queryString.getParam("failFast"); + env.stopOnSpecFailure(typeof stoppingOnSpecFailure === "undefined" ? true : stoppingOnSpecFailure); var throwingExpectationFailures = queryString.getParam("throwFailures"); env.throwOnExpectationFailure(throwingExpectationFailures); @@ -96,7 +96,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var htmlReporter = new jasmine.HtmlReporter({ env: env, - onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); }, + onStopExecutionClick: function() { queryString.navigateWithNewParam("failFast", !env.stoppingOnSpecFailure()); }, onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); }, onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); }, addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, diff --git a/lib/jasmine-core/boot/boot.js b/lib/jasmine-core/boot/boot.js index 8e12623d..00a65830 100644 --- a/lib/jasmine-core/boot/boot.js +++ b/lib/jasmine-core/boot/boot.js @@ -51,8 +51,8 @@ var filterSpecs = !!queryString.getParam("spec"); - var catchingExceptions = queryString.getParam("catch"); - env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); + var stoppingOnSpecFailure = queryString.getParam("failFast"); + env.stopOnSpecFailure(typeof stoppingOnSpecFailure === "undefined" ? true : stoppingOnSpecFailure); var throwingExpectationFailures = queryString.getParam("throwFailures"); env.throwOnExpectationFailure(throwingExpectationFailures); @@ -74,7 +74,7 @@ */ var htmlReporter = new jasmine.HtmlReporter({ env: env, - onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); }, + onStopExecutionClick: function() { queryString.navigateWithNewParam("failFast", !env.stoppingOnSpecFailure()); }, onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); }, onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); }, addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 4585a6f9..b01d0a22 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -85,7 +85,7 @@ jasmineRequire.HtmlReporter = function(j$) { getContainer = options.getContainer, createElement = options.createElement, createTextNode = options.createTextNode, - onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, + onStopExecutionClick = options.onStopExecutionClick || function() {}, onThrowExpectationsClick = options.onThrowExpectationsClick || function() {}, onRandomClick = options.onRandomClick || function() {}, addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, @@ -322,13 +322,13 @@ jasmineRequire.HtmlReporter = function(j$) { var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom('div', { className: 'jasmine-payload' }, - createDom('div', { className: 'jasmine-exceptions' }, + createDom('div', { className: 'jasmine-stop-on-failure' }, createDom('input', { - className: 'jasmine-raise', - id: 'jasmine-raise-exceptions', + className: 'jasmine-fail-fast', + id: 'jasmine-fail-fast', type: 'checkbox' }), - createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-fail-fast' }, 'stop execution on spec failure')), createDom('div', { className: 'jasmine-throw-failures' }, createDom('input', { className: 'jasmine-throw', @@ -346,10 +346,9 @@ jasmineRequire.HtmlReporter = function(j$) { ) ); - var raiseCheckbox = optionsMenuDom.querySelector('#jasmine-raise-exceptions'); - - raiseCheckbox.checked = !env.catchingExceptions(); - raiseCheckbox.onclick = onRaiseExceptionsClick; + var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast'); + failFastCheckbox.checked = env.stoppingOnSpecFailure(); + failFastCheckbox.onclick = onStopExecutionClick; var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); throwCheckbox.checked = env.throwingExpectationFailures(); diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index e90947ab..0224b5e5 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -563,7 +563,9 @@ getJasmineRequireObj().Spec = function(j$) { onException: function () { self.onException.apply(self, arguments); }, - onComplete: onComplete, + onComplete: function() { + onComplete(self.result.status === 'failed' && new j$.StopExecutionError('spec failed')); + }, userContext: this.userContext() }; @@ -712,8 +714,6 @@ getJasmineRequireObj().Env = function(j$) { var totalSpecsDefined = 0; - var catchExceptions = true; - var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; var clearStack = j$.getClearStack(j$.getGlobal()); @@ -725,6 +725,7 @@ getJasmineRequireObj().Env = function(j$) { var currentlyExecutingSuites = []; var currentDeclarationSuite = null; var throwOnExpectationFailure = false; + var stopOnSpecFailure = false; var random = true; var seed = null; var handlingLoadErrors = true; @@ -859,23 +860,9 @@ getJasmineRequireObj().Env = function(j$) { return buildExpectationResult(attrs); }; - // TODO: fix this naming, and here's where the value comes in - this.catchExceptions = function(value) { - catchExceptions = !!value; - return catchExceptions; - }; - - this.catchingExceptions = function() { - return catchExceptions; - }; - var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; - var catchException = function(e) { - return j$.Spec.isPendingSpecException(e) || catchExceptions; - }; - this.throwOnExpectationFailure = function(value) { throwOnExpectationFailure = !!value; }; @@ -884,6 +871,14 @@ getJasmineRequireObj().Env = function(j$) { return throwOnExpectationFailure; }; + this.stopOnSpecFailure = function(value) { + stopOnSpecFailure = !!value; + }; + + this.stoppingOnSpecFailure = function() { + return stopOnSpecFailure; + }; + this.randomizeTests = function(value) { random = !!value; }; @@ -907,12 +902,17 @@ getJasmineRequireObj().Env = function(j$) { }; var queueRunnerFactory = function(options, args) { - options.catchException = catchException; + var failFast = false; + if (options.isLeaf) { + failFast = throwOnExpectationFailure; + } else if (!options.isReporter) { + failFast = stopOnSpecFailure; + } options.clearStack = options.clearStack || clearStack; options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; options.fail = self.fail; options.globalErrors = globalErrors; - options.completeOnFirstError = throwOnExpectationFailure && options.isLeaf; + options.completeOnFirstError = failFast; options.onException = options.onException || function(e) { (currentRunnable() || topSuite).onException(e); }; @@ -4447,6 +4447,9 @@ getJasmineRequireObj().pp = function(j$) { }; getJasmineRequireObj().QueueRunner = function(j$) { + function StopExecutionError() {} + StopExecutionError.prototype = new Error(); + j$.StopExecutionError = StopExecutionError; function once(fn) { var called = false; @@ -4466,12 +4469,12 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.onComplete = attrs.onComplete || function() {}; this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; - this.catchException = attrs.catchException || function() { return true; }; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; this.fail = attrs.fail || function() {}; this.globalErrors = attrs.globalErrors || { pushListener: function() {}, popListener: function() {} }; this.completeOnFirstError = !!attrs.completeOnFirstError; + this.errored = false; if (typeof(this.onComplete) !== 'function') { throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); @@ -4517,8 +4520,10 @@ getJasmineRequireObj().QueueRunner = function(j$) { cleanup(); if (j$.isError_(err)) { - self.fail(err); - errored = true; + if (!(err instanceof StopExecutionError)) { + self.fail(err); + } + self.errored = errored = true; } function runNext() { @@ -4541,7 +4546,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { next.fail = function nextFail() { self.fail.apply(null, arguments); - errored = true; + self.errored = errored = true; next(); }; @@ -4570,8 +4575,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { return { completedSynchronously: false }; } } catch (e) { - handleException(e, queueableFn); - errored = true; + onException(e); + self.errored = errored = true; } cleanup(); @@ -4579,22 +4584,13 @@ getJasmineRequireObj().QueueRunner = function(j$) { function onException(e) { self.onException(e); - errored = true; + self.errored = errored = true; } function onPromiseRejection(e) { onException(e); next(); } - - function handleException(e, queueableFn) { - onException(e); - if (!self.catchException(e)) { - //TODO: set a var when we catch an exception and - //use a finally block to close the loop in a nice way.. - throw e; - } - } }; QueueRunner.prototype.run = function(recursiveIndex) { @@ -4610,6 +4606,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { return; } + self.errored = result.errored; + if (this.completeOnFirstError && result.errored) { this.skipToCleanup(iterativeIndex); return; @@ -4618,7 +4616,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.clearStack(function() { self.globalErrors.popListener(self.handleFinalError); - self.onComplete(); + self.onComplete(self.errored && new StopExecutionError()); }); }; @@ -4671,7 +4669,8 @@ getJasmineRequireObj().ReportDispatcher = function(j$) { queueRunnerFactory({ queueableFns: fns, - onComplete: onComplete + onComplete: onComplete, + isReporter: true }); } diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index 15483cfe..0e57ae96 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -158,6 +158,29 @@ describe("QueueRunner", function() { expect(queueableFn2.fn).toHaveBeenCalled(); }); + it("does not cause an explicit fail if execution is being stopped", function() { + var err = new jasmineUnderTest.StopExecutionError('foo'), + queueableFn1 = { fn: function(done) { + setTimeout(function() { done(err); }, 100); + } }, + queueableFn2 = { fn: jasmine.createSpy('fn2') }, + failFn = jasmine.createSpy('fail'), + queueRunner = new jasmineUnderTest.QueueRunner({ + queueableFns: [queueableFn1, queueableFn2], + fail: failFn + }); + + queueRunner.execute(); + + expect(failFn).not.toHaveBeenCalled(); + expect(queueableFn2.fn).not.toHaveBeenCalled(); + + jasmine.clock().tick(100); + + expect(failFn).not.toHaveBeenCalled(); + expect(queueableFn2.fn).toHaveBeenCalled(); + }); + it("sets a timeout if requested for asynchronous functions so they don't go on forever", function() { var timeout = 3, beforeFn = { fn: function(done) { }, type: 'before', timeout: function() { return timeout; } }, @@ -442,20 +465,6 @@ describe("QueueRunner", function() { expect(onExceptionCallback).toHaveBeenCalledWith(jasmine.any(Error)); }); - it("rethrows an exception if told to", function() { - var queueableFn = { fn: function() { - throw new Error('fake error'); - } }, - queueRunner = new jasmineUnderTest.QueueRunner({ - queueableFns: [queueableFn], - catchException: function(e) { return false; } - }); - - expect(function() { - queueRunner.execute(); - }).toThrowError('fake error'); - }); - it("continues running the functions even after an exception is thrown in an async spec", function() { var queueableFn = { fn: function(done) { throw new Error("error"); } }, nextQueueableFn = { fn: jasmine.createSpy("nextFunction") }, diff --git a/spec/core/ReportDispatcherSpec.js b/spec/core/ReportDispatcherSpec.js index 43db5ffa..87511ab7 100644 --- a/spec/core/ReportDispatcherSpec.js +++ b/spec/core/ReportDispatcherSpec.js @@ -21,7 +21,8 @@ describe("ReportDispatcher", function() { dispatcher.foo(123, 456, completeCallback); expect(queueRunnerFactory).toHaveBeenCalledWith(jasmine.objectContaining({ - queueableFns: [{fn: jasmine.any(Function)}, {fn: jasmine.any(Function)}] + queueableFns: [{fn: jasmine.any(Function)}, {fn: jasmine.any(Function)}], + isReporter: true })); var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; @@ -38,7 +39,8 @@ describe("ReportDispatcher", function() { dispatcher.bar('a', 'b', completeCallback); expect(queueRunnerFactory).toHaveBeenCalledWith(jasmine.objectContaining({ - queueableFns: [{fn: jasmine.any(Function)}, {fn: jasmine.any(Function)}] + queueableFns: [{fn: jasmine.any(Function)}, {fn: jasmine.any(Function)}], + isReporter: true })); fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; @@ -72,7 +74,8 @@ describe("ReportDispatcher", function() { dispatcher.foo(123, 456, completeCallback); expect(queueRunnerFactory).toHaveBeenCalledWith(jasmine.objectContaining({ - queueableFns: [{fn: jasmine.any(Function)}] + queueableFns: [{fn: jasmine.any(Function)}], + isReporter: true })); var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; @@ -92,7 +95,8 @@ describe("ReportDispatcher", function() { dispatcher.foo(123, 456, completeCallback); expect(queueRunnerFactory).toHaveBeenCalledWith(jasmine.objectContaining({ - queueableFns: [{fn: jasmine.any(Function)}] + queueableFns: [{fn: jasmine.any(Function)}], + isReporter: true })); var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; @@ -111,7 +115,8 @@ describe("ReportDispatcher", function() { dispatcher.addReporter(reporter1); dispatcher.foo(123, completeCallback); expect(queueRunnerFactory).toHaveBeenCalledWith(jasmine.objectContaining({ - queueableFns: [{fn: jasmine.any(Function)}] + queueableFns: [{fn: jasmine.any(Function)}], + isReporter: true })); var fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; @@ -123,7 +128,8 @@ describe("ReportDispatcher", function() { dispatcher.bar(456, completeCallback); expect(queueRunnerFactory).toHaveBeenCalledWith(jasmine.objectContaining({ - queueableFns: [{fn: jasmine.any(Function)}] + queueableFns: [{fn: jasmine.any(Function)}], + isReporter: true })); fns = queueRunnerFactory.calls.mostRecent().args[0].queueableFns; diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index d0b8d169..e20ad2bb 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -157,7 +157,7 @@ describe("Spec", function() { spec.execute('cally-back', true); expect(fakeQueueRunner).toHaveBeenCalledWith(jasmine.objectContaining({ - onComplete: 'cally-back', + onComplete: jasmine.any(Function), queueableFns: [{fn: jasmine.any(Function)}], cleanupFns: [{fn: jasmine.any(Function)}] })); @@ -224,6 +224,23 @@ describe("Spec", function() { expect(done).toHaveBeenCalled(); }); + it("should call the done callback with an error if the spec is failed", function() { + var done = jasmine.createSpy('done callback'), + spec = new jasmineUnderTest.Spec({ + queueableFn: { fn: function() {} }, + catchExceptions: function() { return false; }, + resultCallback: function() {}, + queueRunnerFactory: function(attrs) { + spec.result.status = 'failed'; + attrs.onComplete(); + } + }); + + spec.execute(done); + + expect(done).toHaveBeenCalledWith(jasmine.any(jasmineUnderTest.StopExecutionError)); + }); + it("#status returns passing by default", function() { var spec = new jasmineUnderTest.Spec({queueableFn: { fn: jasmine.createSpy("spec body")} }); expect(spec.status()).toBe('passed'); diff --git a/spec/core/integration/SpecRunningSpec.js b/spec/core/integration/SpecRunningSpec.js index 464bab60..e0d8b2ed 100644 --- a/spec/core/integration/SpecRunningSpec.js +++ b/spec/core/integration/SpecRunningSpec.js @@ -947,4 +947,30 @@ describe("spec running", function () { env.execute(); }); }); + + describe("when stopOnSpecFailure is on", function() { + it("does not run further specs when one fails", function(done) { + var actions = []; + + env.it('fails', function() { + actions.push('fails'); + env.expect(1).toBe(2); + }); + + env.it('does not run', function() { + actions.push('does not run'); + }); + + env.randomizeTests(false); + env.stopOnSpecFailure(true); + + var assertions = function() { + expect(actions).toEqual(['fails']); + done(); + }; + + env.addReporter({ jasmineDone: assertions }); + env.execute(); + }); + }); }); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index f35985d7..7614f6e1 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -436,8 +436,8 @@ describe("HtmlReporter", function() { }); }); - describe("UI for raising/catching exceptions", function() { - it("should be unchecked if the env is catching", function() { + describe("UI for stop on spec failure", function() { + it("should be unchecked for full execution", function() { var env = new jasmineUnderTest.Env(), container = document.createElement("div"), getContainer = function() { @@ -457,11 +457,11 @@ describe("HtmlReporter", function() { reporter.initialize(); reporter.jasmineDone({}); - var raisingExceptionsUI = container.querySelector(".jasmine-raise"); - expect(raisingExceptionsUI.checked).toBe(false); + var stopOnFailureUI = container.querySelector(".jasmine-fail-fast"); + expect(stopOnFailureUI.checked).toBe(false); }); - it("should be checked if the env is not catching", function() { + it("should be checked if stopping short", function() { var env = new jasmineUnderTest.Env(), container = document.createElement("div"), getContainer = function() { @@ -478,25 +478,26 @@ describe("HtmlReporter", function() { } }); + env.stopOnSpecFailure(true); + reporter.initialize(); - env.catchExceptions(false); reporter.jasmineDone({}); - var raisingExceptionsUI = container.querySelector(".jasmine-raise"); - expect(raisingExceptionsUI.checked).toBe(true); + var stopOnFailureUI = container.querySelector(".jasmine-fail-fast"); + expect(stopOnFailureUI.checked).toBe(true); }); - it("should affect the query param for catching exceptions", function() { + it("should trigger the callback when changed", function() { var env = new jasmineUnderTest.Env(), container = document.createElement("div"), - exceptionsClickHandler = jasmine.createSpy("raise exceptions checked"), + failFastHandler = jasmine.createSpy('failFast'), getContainer = function() { return container; }, reporter = new jasmineUnderTest.HtmlReporter({ env: env, + onStopExecutionClick: failFastHandler, getContainer: getContainer, - onRaiseExceptionsClick: exceptionsClickHandler, createElement: function() { return document.createElement.apply(document, arguments); }, @@ -505,12 +506,15 @@ describe("HtmlReporter", function() { } }); + env.stopOnSpecFailure(true); + reporter.initialize(); reporter.jasmineDone({}); - var input = container.querySelector(".jasmine-raise"); - input.click(); - expect(exceptionsClickHandler).toHaveBeenCalled(); + var stopOnFailureUI = container.querySelector(".jasmine-fail-fast"); + stopOnFailureUI.click(); + + expect(failFastHandler).toHaveBeenCalled(); }); }); diff --git a/src/core/Env.js b/src/core/Env.js index 21f7d760..74d18a6d 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -13,8 +13,6 @@ getJasmineRequireObj().Env = function(j$) { var totalSpecsDefined = 0; - var catchExceptions = true; - var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; var clearStack = j$.getClearStack(j$.getGlobal()); @@ -26,6 +24,7 @@ getJasmineRequireObj().Env = function(j$) { var currentlyExecutingSuites = []; var currentDeclarationSuite = null; var throwOnExpectationFailure = false; + var stopOnSpecFailure = false; var random = true; var seed = null; var handlingLoadErrors = true; @@ -160,23 +159,9 @@ getJasmineRequireObj().Env = function(j$) { return buildExpectationResult(attrs); }; - // TODO: fix this naming, and here's where the value comes in - this.catchExceptions = function(value) { - catchExceptions = !!value; - return catchExceptions; - }; - - this.catchingExceptions = function() { - return catchExceptions; - }; - var maximumSpecCallbackDepth = 20; var currentSpecCallbackDepth = 0; - var catchException = function(e) { - return j$.Spec.isPendingSpecException(e) || catchExceptions; - }; - this.throwOnExpectationFailure = function(value) { throwOnExpectationFailure = !!value; }; @@ -185,6 +170,14 @@ getJasmineRequireObj().Env = function(j$) { return throwOnExpectationFailure; }; + this.stopOnSpecFailure = function(value) { + stopOnSpecFailure = !!value; + }; + + this.stoppingOnSpecFailure = function() { + return stopOnSpecFailure; + }; + this.randomizeTests = function(value) { random = !!value; }; @@ -208,12 +201,17 @@ getJasmineRequireObj().Env = function(j$) { }; var queueRunnerFactory = function(options, args) { - options.catchException = catchException; + var failFast = false; + if (options.isLeaf) { + failFast = throwOnExpectationFailure; + } else if (!options.isReporter) { + failFast = stopOnSpecFailure; + } options.clearStack = options.clearStack || clearStack; options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; options.fail = self.fail; options.globalErrors = globalErrors; - options.completeOnFirstError = throwOnExpectationFailure && options.isLeaf; + options.completeOnFirstError = failFast; options.onException = options.onException || function(e) { (currentRunnable() || topSuite).onException(e); }; diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index 10a88f10..71082efb 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -1,4 +1,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { + function StopExecutionError() {} + StopExecutionError.prototype = new Error(); + j$.StopExecutionError = StopExecutionError; function once(fn) { var called = false; @@ -18,12 +21,12 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.onComplete = attrs.onComplete || function() {}; this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; - this.catchException = attrs.catchException || function() { return true; }; this.userContext = attrs.userContext || new j$.UserContext(); this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; this.fail = attrs.fail || function() {}; this.globalErrors = attrs.globalErrors || { pushListener: function() {}, popListener: function() {} }; this.completeOnFirstError = !!attrs.completeOnFirstError; + this.errored = false; if (typeof(this.onComplete) !== 'function') { throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); @@ -69,8 +72,10 @@ getJasmineRequireObj().QueueRunner = function(j$) { cleanup(); if (j$.isError_(err)) { - self.fail(err); - errored = true; + if (!(err instanceof StopExecutionError)) { + self.fail(err); + } + self.errored = errored = true; } function runNext() { @@ -93,7 +98,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { next.fail = function nextFail() { self.fail.apply(null, arguments); - errored = true; + self.errored = errored = true; next(); }; @@ -122,8 +127,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { return { completedSynchronously: false }; } } catch (e) { - handleException(e, queueableFn); - errored = true; + onException(e); + self.errored = errored = true; } cleanup(); @@ -131,22 +136,13 @@ getJasmineRequireObj().QueueRunner = function(j$) { function onException(e) { self.onException(e); - errored = true; + self.errored = errored = true; } function onPromiseRejection(e) { onException(e); next(); } - - function handleException(e, queueableFn) { - onException(e); - if (!self.catchException(e)) { - //TODO: set a var when we catch an exception and - //use a finally block to close the loop in a nice way.. - throw e; - } - } }; QueueRunner.prototype.run = function(recursiveIndex) { @@ -162,6 +158,8 @@ getJasmineRequireObj().QueueRunner = function(j$) { return; } + self.errored = result.errored; + if (this.completeOnFirstError && result.errored) { this.skipToCleanup(iterativeIndex); return; @@ -170,7 +168,7 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.clearStack(function() { self.globalErrors.popListener(self.handleFinalError); - self.onComplete(); + self.onComplete(self.errored && new StopExecutionError()); }); }; diff --git a/src/core/ReportDispatcher.js b/src/core/ReportDispatcher.js index a8d0d576..5b2fad25 100644 --- a/src/core/ReportDispatcher.js +++ b/src/core/ReportDispatcher.js @@ -43,7 +43,8 @@ getJasmineRequireObj().ReportDispatcher = function(j$) { queueRunnerFactory({ queueableFns: fns, - onComplete: onComplete + onComplete: onComplete, + isReporter: true }); } diff --git a/src/core/Spec.js b/src/core/Spec.js index b9c9b7e8..5732b2aa 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -82,7 +82,9 @@ getJasmineRequireObj().Spec = function(j$) { onException: function () { self.onException.apply(self, arguments); }, - onComplete: onComplete, + onComplete: function() { + onComplete(self.result.status === 'failed' && new j$.StopExecutionError('spec failed')); + }, userContext: this.userContext() }; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 5ca871b2..9bb94ed2 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -55,7 +55,7 @@ jasmineRequire.HtmlReporter = function(j$) { getContainer = options.getContainer, createElement = options.createElement, createTextNode = options.createTextNode, - onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, + onStopExecutionClick = options.onStopExecutionClick || function() {}, onThrowExpectationsClick = options.onThrowExpectationsClick || function() {}, onRandomClick = options.onRandomClick || function() {}, addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, @@ -292,13 +292,13 @@ jasmineRequire.HtmlReporter = function(j$) { var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom('div', { className: 'jasmine-payload' }, - createDom('div', { className: 'jasmine-exceptions' }, + createDom('div', { className: 'jasmine-stop-on-failure' }, createDom('input', { - className: 'jasmine-raise', - id: 'jasmine-raise-exceptions', + className: 'jasmine-fail-fast', + id: 'jasmine-fail-fast', type: 'checkbox' }), - createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-fail-fast' }, 'stop execution on spec failure')), createDom('div', { className: 'jasmine-throw-failures' }, createDom('input', { className: 'jasmine-throw', @@ -316,10 +316,9 @@ jasmineRequire.HtmlReporter = function(j$) { ) ); - var raiseCheckbox = optionsMenuDom.querySelector('#jasmine-raise-exceptions'); - - raiseCheckbox.checked = !env.catchingExceptions(); - raiseCheckbox.onclick = onRaiseExceptionsClick; + var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast'); + failFastCheckbox.checked = env.stoppingOnSpecFailure(); + failFastCheckbox.onclick = onStopExecutionClick; var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); throwCheckbox.checked = env.throwingExpectationFailures();