diff --git a/features/hooks.feature b/features/hooks.feature index 494eeaf9f..ac790a6ca 100644 --- a/features/hooks.feature +++ b/features/hooks.feature @@ -253,6 +253,53 @@ Feature: Environment Hooks ] """ + Scenario: Failing the BeforeFeatures hook exists immediately with code = 1 + Given a file named "features/a.feature" with: + """ + Feature: some feature + + Scenario: I've declared one step and it is passing + Given This step is passing + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + var cucumberSteps = function() { + this.Given(/^This step is passing$/, function(callback) { console.log('is passing'); callback(); }); + }; + module.exports = cucumberSteps; + """ + And a file named "features/support/hooks.js" with: + """ + var hooks = function () { + this.registerHandler('BeforeFeatures', function(){ + return { + then: function(resolve, reject){ + setTimeout(function(){ + reject(new Error('Something bad')) + }); + } + }; + }); + }; + + module.exports = hooks; + """ + When I run cucumber.js with `-f json` + Then the exit status should be non-zero + And the error output contains the text: + """ + features/support/hooks.js:2 Something bad + """ + And the output does not contain the text: + """ + is passing + """ + And the error output does not contain the text: + """ + is passing + """ + + Scenario: Hooks still execute after a failure Given a file named "features/a.feature" with: """ diff --git a/features/step_definitions/cli_steps.js b/features/step_definitions/cli_steps.js index d719da185..209e98b94 100644 --- a/features/step_definitions/cli_steps.js +++ b/features/step_definitions/cli_steps.js @@ -63,16 +63,21 @@ var cliSteps = function cliSteps() { getAdditionalErrorText(this.lastRun)); }); - this.Then(/^the (error )?output contains the text:$/, function(error, expectedOutput) { + this.Then(/^the (error )?output (does not)? ?contains? the text:$/, function(error, shouldFind, expectedOutput) { + var expectedToFind = !(shouldFind); var actualOutput = error ? this.lastRun.stderr : this.lastRun.stdout; actualOutput = normalizeText(actualOutput); expectedOutput = normalizeText(expectedOutput); - if (actualOutput.indexOf(expectedOutput) === -1) - throw new Error('Expected output to contain the following:\n' + expectedOutput + '\n' + - 'Got:\n' + actualOutput + '\n' + - getAdditionalErrorText(this.lastRun)); + if ((actualOutput.indexOf(expectedOutput) !== -1) !== expectedToFind) { + var errorPhrase = (expectedToFind) ? 'Expected output to contain the following:' : 'Expected output not to contain the following:'; + + throw new Error(errorPhrase + '\n' + expectedOutput + '\n' + + 'Got:\n' + actualOutput + '\n' + + getAdditionalErrorText(this.lastRun)); + } + }); this.Then(/^I see the version of Cucumber$/, function() { diff --git a/lib/cucumber/runtime/event_broadcaster.js b/lib/cucumber/runtime/event_broadcaster.js index 2061dba37..ff5057ee3 100644 --- a/lib/cucumber/runtime/event_broadcaster.js +++ b/lib/cucumber/runtime/event_broadcaster.js @@ -27,7 +27,7 @@ function EventBroadcaster(listeners, listenerDefaultTimeout) { Cucumber.Util.asyncForEach(listeners, function (listener, callback) { listener.hear(event, listenerDefaultTimeout, function(error) { if (error) { - throw error; + process.nextTick(function(){ throw error; }); // prevent swallow by unhandled rejection } callback(); });