Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: cucumber/cucumber-js
...
head fork: cucumber/cucumber-js
  • 3 commits
  • 12 files changed
  • 0 commit comments
  • 1 contributor
43 History.md
View
@@ -2,23 +2,36 @@
## [v0.2](https://github.com/cucumber/cucumber-js/compare/v0.1.5...master)
-### [master (unreleased)](https://github.com/cucumber/cucumber-js/compare/v0.2.14...master)
+### [master (unreleased)](https://github.com/cucumber/cucumber-js/compare/v0.2.15...master)
**TBD**
-### [v0.2.14](https://github.com/cucumber/cucumber-js/compare/v0.2.13...v0.2.14)
+### [v0.2.15](https://github.com/cucumber/cucumber-js/compare/v0.2.14...v0.2.15)
-#### Fixes
+#### New features
+
+* Handle asynchronous exceptions (#51) (Julien Biezemans)
+
+#### Documentation, internals and tests
+
+* Remove commented code (Julien Biezemans)
+
+
+
+### [v0.2.14](https://github.com/cucumber/cucumber-js/compare/v0.2.13...v0.2.14)
-* Add alternate binary script for Windows (close #60) (Julien Biezemans)
#### New features
* Mention CS support in README (Julien Biezemans)
* Update command-line documentation in README (Julien Biezemans)
+#### Fixes
+
+* Add alternate binary script for Windows (#60) (Julien Biezemans)
+
### [v0.2.13](https://github.com/cucumber/cucumber-js/compare/v0.2.12...v0.2.13)
@@ -42,7 +55,7 @@
#### Changed features
-* Allow World constructor to set explicit World object (close #50) (Julien Biezemans)
+* Allow World constructor to set explicit World object (#50) (Julien Biezemans)
#### Documentation, internals and tests
@@ -55,7 +68,7 @@
#### Changed features
-* Simplify World callbacks (close #49) (Julien Biezemans)
+* Simplify World callbacks (#49) (Julien Biezemans)
#### Fixes
@@ -73,7 +86,7 @@
#### Fixes
-* Fix path handling on Windows platforms (close #47) (Julien Biezemans)
+* Fix path handling on Windows platforms (#47) (Julien Biezemans)
#### Documentation, internals and tests
@@ -89,7 +102,7 @@
#### New features
-* Add support for tagged hooks (close #32) (Julien Biezemans)
+* Add support for tagged hooks (#32) (Julien Biezemans)
#### Changed features
@@ -119,7 +132,7 @@
* Remove unused parameter in parser spec (Julien Biezemans)
* Add JS stepdef for async failing steps scenario (Julien Biezemans)
-* Assign zombie in README example (close #44) (Julien Biezemans)
+* Assign zombie in README example (#44) (Julien Biezemans)
* Remove trailing spaces (Julien Biezemans)
* Get rid of obsolete PendingStepException (Julien Biezemans)
* Refactor SupportCode.Library spec (Julien Biezemans)
@@ -136,7 +149,7 @@
#### Fixes
-* Fix matching groups in step definition snippets (close #42) (Julien Biezemans)
+* Fix matching groups in step definition snippets (#42) (Julien Biezemans)
* Remove obsolete dependency from snippet builder spec (Julien Biezemans)
#### Documentation, internals and tests
@@ -152,11 +165,11 @@
#### New features
* Add tags support (#7) (Julien Biezemans)
-* Add support for tags on features (close #7) (Julien Biezemans)
+* Add support for tags on features (#7) (Julien Biezemans)
#### Changed features
-* Handle missing instance in World constructor callback (close #40) (Julien Biezemans)
+* Handle missing instance in World constructor callback (#40) (Julien Biezemans)
#### Documentation, internals and tests
@@ -174,7 +187,7 @@
#### New features
-* Add Before/After hooks (#32, close #31) (Tristan Dunn)
+* Add Before/After hooks (#32, #31) (Tristan Dunn)
#### Changed features
@@ -199,7 +212,7 @@
#### Changed features
* Add styles for reported errors on web example (Julien Biezemans)
-* Make and expect World constructors to be asynchronous (close #39) (Julien Biezemans)
+* Make and expect World constructors to be asynchronous (#39) (Julien Biezemans)
#### Documentation, internals and tests
@@ -207,7 +220,7 @@
* Add development status to README (Julien Biezemans)
* Add link to demo at cucumber.no.de (Julien Biezemans)
* Add link to example app to README (Julien Biezemans)
-* Add usage documentation to README (close #23) (Olivier Melcher)
+* Add usage documentation to README (#23) (Olivier Melcher)
* Add examples to run features with the CLI (Olivier Melcher)
* Fix header levels and whitespaces in README (Julien Biezemans)
* Add Opera to supported browsers in README (Julien Biezemans)
15 features/asynchronous_failing_steps.feature
View
@@ -1,6 +1,6 @@
Feature: Asynchronous failing steps
- Scenario: see failing asynchronous scenarios
+ Scenario: see asynchronously failing scenarios
Given the following feature:
"""
Feature: a feature
@@ -11,3 +11,16 @@ Feature: Asynchronous failing steps
And the step "I divide 10 by 0" has a mapping asynchronously failing with the message "Divide by 0, uh?"
When Cucumber runs the feature
Then the scenario called "a failing scenario" is reported as failing
+
+ @untestable-on-self
+ Scenario: see asynchronously failing scenarios with exception
+ Given the following feature:
+ """
+ Feature: a feature
+ Scenario: a failing scenario
+ When I divide 10 by 0
+ Then the result is 9
+ """
+ And the step "I divide 10 by 0" has a mapping asynchronously failing through an exception with the message "Divide by 0, uh?"
+ When Cucumber runs the feature
+ Then the scenario called "a failing scenario" is reported as failing
4 features/step_definitions/cucumber_js_mappings.rb
View
@@ -61,6 +61,10 @@ def write_asynchronously_failing_mapping_with_message(step_name, message)
append_step_definition(step_name, "setTimeout(function() { callback.fail('#{message}');}, 10);")
end
+ def write_asynchronously_failing_mapping_through_exception_with_message(step_name, message)
+ append_step_definition(step_name, "setTimeout(function() { throw new Error('#{message}');}, 10);")
+ end
+
def write_mapping_incrementing_world_variable_by_value(step_name, increment_value)
append_step_definition(step_name, "this.variable += #{increment_value}; callback();")
end
4 features/step_definitions/cucumber_steps.js
View
@@ -86,8 +86,8 @@ var cucumberSteps = function() {
world.touchStep(\"" + stepName + "\");\
setTimeout(function() {callback.fail(new Error('" + message + "'));}, 10);\
});\n";
- callback();
-});
+ callback();
+ });
Given(/^the step "([^"]*)" has a pending mapping$/, function(stepName, callback) {
this.stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
4 features/step_definitions/cucumber_steps.rb
View
@@ -21,6 +21,10 @@
write_asynchronously_failing_mapping_with_message(step_name, message)
end
+Given /^the step "([^"]*)" has a mapping asynchronously failing through an exception with the message "([^"]*)"$/ do |step_name, message|
+ write_asynchronously_failing_mapping_through_exception_with_message(step_name, message)
+end
+
Given /^a custom World constructor calling back with an explicit object$/ do
write_custom_world_constructor_calling_back_with_explicit_object
end
2  lib/cucumber.js
View
@@ -16,6 +16,6 @@ Cucumber.Type = require('./cucumber/type');
Cucumber.Util = require('./cucumber/util');
Cucumber.VolatileConfiguration = require('./cucumber/volatile_configuration');
-Cucumber.VERSION = "0.2.14";
+Cucumber.VERSION = "0.2.15";
module.exports = Cucumber;
42 lib/cucumber/support_code/step_definition.js
View
@@ -1,23 +1,6 @@
var StepDefinition = function(pattern, code) {
var Cucumber = require('../../cucumber');
- // var constructor = function() {
- // // Converts a string to a proper regular expression
- // var parseRegexpString = function( regexpString ) {
-
- // // Replace the $VARIABLES with the correct regex.
- // regexpString = regexpString.replace(/\$[a-zA-Z0-9]+/g, '([^"]*)');
- // return new RegExp( regexpString );
- // }
-
- // if( typeof(regexp)=='string' ) {
- // // regexp is a string, convert it to a regexp.
- // regexp = parseRegexpString(regexp);
- // }
- // }
-
- // constructor();
-
var self = {
getPatternRegexp: function getPatternRegexp() {
var regexp;
@@ -38,29 +21,37 @@ var StepDefinition = function(pattern, code) {
},
invoke: function invoke(step, world, callback) {
+ var cleanUp = function cleanUp() {
+ Cucumber.Util.Exception.unregisterUncaughtExceptionHandler(handleException);
+ };
+
var codeCallback = function() {
var successfulStepResult = Cucumber.Runtime.SuccessfulStepResult({step: step});
+ cleanUp();
callback(successfulStepResult);
};
codeCallback.pending = function pending(reason) {
var pendingStepResult = Cucumber.Runtime.PendingStepResult({step: step, pendingReason: reason});
+ cleanUp();
callback(pendingStepResult);
};
codeCallback.fail = function fail(failureReason) {
var failureException = failureReason || new Error(StepDefinition.UNKNOWN_STEP_FAILURE_MESSAGE);
var failedStepResult = Cucumber.Runtime.FailedStepResult({step: step, failureException: failureException});
+ cleanUp();
callback(failedStepResult);
};
- var parameters = self.buildInvocationParameters(step, codeCallback);
+ var parameters = self.buildInvocationParameters(step, codeCallback);
+ var handleException = self.buildExceptionHandlerToCodeCallback(codeCallback);
+ Cucumber.Util.Exception.registerUncaughtExceptionHandler(handleException);
+
try {
code.apply(world, parameters);
} catch (exception) {
- if (exception)
- Cucumber.Debug.warn(exception.stack || exception, 'exception inside feature', 3);
- codeCallback.fail(exception);
+ handleException(exception);
}
},
@@ -75,6 +66,15 @@ var StepDefinition = function(pattern, code) {
}
parameters.push(callback);
return parameters;
+ },
+
+ buildExceptionHandlerToCodeCallback: function buildExceptionHandlerToCodeCallback(codeCallback) {
+ var exceptionHandler = function handleScenarioException(exception) {
+ if (exception)
+ Cucumber.Debug.warn(exception.stack || exception, 'exception inside feature', 3);
+ codeCallback.fail(exception);
+ };
+ return exceptionHandler;
}
};
return self;
1  lib/cucumber/util.js
View
@@ -1,5 +1,6 @@
var Util = {};
Util.Arguments = require('./util/arguments');
+Util.Exception = require('./util/exception');
Util.RegExp = require('./util/reg_exp');
Util.String = require('./util/string');
module.exports = Util;
17 lib/cucumber/util/exception.js
View
@@ -0,0 +1,17 @@
+var Exception = {
+ registerUncaughtExceptionHandler: function registerUncaughtExceptionHandler(exceptionHandler) {
+ if (process.on)
+ process.on('uncaughtException', exceptionHandler);
+ else
+ window.onerror = exceptionHandler;
+ },
+
+ unregisterUncaughtExceptionHandler: function unregisterUncaughtExceptionHandler(exceptionHandler) {
+ if (process.removeListener)
+ process.removeListener('uncaughtException', exceptionHandler);
+ else
+ window.onerror = void(0);
+ }
+};
+
+module.exports = Exception;
2  package.json
View
@@ -1,7 +1,7 @@
{ "name" : "cucumber"
, "description" : "The official JavaScript implementation of Cucumber."
, "keywords" : [ "testing", "bdd", "cucumber", "gherkin", "tests" ]
-, "version" : "0.2.14"
+, "version" : "0.2.15"
, "homepage" : "http://github.com/cucumber/cucumber-js"
, "author" : "Julien Biezemans <jb@jbpros.com> (http://jbpros.net)"
, "contributors" : [
82 spec/cucumber/support_code/step_definition_spec.js
View
@@ -81,14 +81,17 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
describe("invoke()", function() {
var step, world, callback;
- var parameters;
+ var parameters, exceptionHandler;
beforeEach(function() {
- step = createSpy("step");
- world = createSpy("world");
- callback = createSpy("callback");
- parameters = createSpy("code execution parameters");
+ step = createSpy("step");
+ world = createSpy("world");
+ callback = createSpy("callback");
+ parameters = createSpy("code execution parameters");
+ exceptionHandler = createSpy("exception handler");
+ spyOn(Cucumber.Util.Exception, 'registerUncaughtExceptionHandler');
spyOn(stepDefinition, 'buildInvocationParameters').andReturn(parameters);
+ spyOn(stepDefinition, 'buildExceptionHandlerToCodeCallback').andReturn(exceptionHandler);
spyOn(stepDefinitionCode, 'apply');
});
@@ -99,6 +102,20 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
expect(stepDefinition.buildInvocationParameters).toHaveBeenCalledWithAFunctionAsNthParameter(2);
});
+ it("builds an exception handler for the code callback", function() {
+ stepDefinition.invoke(step, world, callback);
+ expect(stepDefinition.buildExceptionHandlerToCodeCallback).toHaveBeenCalledWithAFunctionAsNthParameter(1);
+
+ var codeExecutionCallbackPassedToParameterBuilder = stepDefinition.buildInvocationParameters.mostRecentCall.args[1];
+ var codeExecutionCallbackPassedToExceptionHandlerBuilder = stepDefinition.buildExceptionHandlerToCodeCallback.mostRecentCall.args[0];
+ expect(codeExecutionCallbackPassedToExceptionHandlerBuilder).toBe(codeExecutionCallbackPassedToParameterBuilder);
+ });
+
+ it("registers the exception handler for uncaught exceptions", function () {
+ stepDefinition.invoke(step, world, callback);
+ expect(Cucumber.Util.Exception.registerUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
+ });
+
it("calls the step definition code with the parameters and World as 'this'", function() {
stepDefinition.invoke(step, world, callback);
expect(stepDefinitionCode.apply).toHaveBeenCalledWith(world, parameters);
@@ -113,6 +130,7 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
codeExecutionCallback = stepDefinition.buildInvocationParameters.mostRecentCall.args[1];
successfulStepResult = createSpy("successful step result");
spyOn(Cucumber.Runtime, 'SuccessfulStepResult').andReturn(successfulStepResult);
+ spyOn(Cucumber.Util.Exception, 'unregisterUncaughtExceptionHandler');
});
it("creates a successful step result", function() {
@@ -120,6 +138,11 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
expect(Cucumber.Runtime.SuccessfulStepResult).toHaveBeenCalledWith({step: step});
});
+ it("unregisters the exception handler", function() {
+ codeExecutionCallback();
+ expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
+ });
+
it("calls back", function() {
codeExecutionCallback();
expect(callback).toHaveBeenCalledWith(successfulStepResult);
@@ -147,6 +170,11 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
expect(Cucumber.Runtime.PendingStepResult).toHaveBeenCalledWith({step: step, pendingReason: pendingReason});
});
+ it("unregisters the exception handler", function() {
+ codeExecutionCallback.pending(pendingReason);
+ expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
+ });
+
it("calls back", function() {
codeExecutionCallback.pending(pendingReason);
expect(callback).toHaveBeenCalledWith(pendingStepResult);
@@ -176,6 +204,11 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
});
});
+ it("unregisters the exception handler", function() {
+ codeExecutionCallback.fail(failureReason);
+ expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
+ });
+
it("calls back", function() {
codeExecutionCallback.fail(failureReason);
expect(callback).toHaveBeenCalledWith(failedStepResult);
@@ -184,23 +217,16 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
});
describe("when the step definition code throws an exception", function() {
- var failedStepResult, failureException;
+ var failureException;
beforeEach(function() {
failureException = createSpy("I am a failing step definition");
- failedStepResult = createSpy("failed step result");
stepDefinitionCode.apply.andThrow(failureException);
- spyOn(Cucumber.Runtime, 'FailedStepResult').andReturn(failedStepResult);
});
- it("creates a new failed step result", function() {
+ it("handles the exception with the exception handler", function() {
stepDefinition.invoke(step, world, callback);
- expect(Cucumber.Runtime.FailedStepResult).toHaveBeenCalledWith({step: step, failureException: failureException});
- });
-
- it("calls back with the step result", function() {
- stepDefinition.invoke(step, world, callback);
- expect(callback).toHaveBeenCalledWith(failedStepResult);
+ expect(exceptionHandler).toHaveBeenCalledWith(failureException);
});
});
});
@@ -286,4 +312,30 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
expect(stepDefinition.buildInvocationParameters(step, callback)).toBe(matches);
});
});
+
+ describe("buildExceptionHandlerToCodeCallback()", function () {
+ var codeCallback, exceptionHandler;
+
+ beforeEach(function() {
+ codeCallback = createSpyWithStubs("code callback", {fail: null});
+ exceptionHandler = stepDefinition.buildExceptionHandlerToCodeCallback(codeCallback);
+ });
+
+ it("returns an exception handler", function() {
+ expect(exceptionHandler).toBeAFunction();
+ });
+
+ describe("returned exception handler", function () {
+ var exception;
+
+ beforeEach(function () {
+ exception = createSpy("exception");
+ });
+
+ it("calls back as a failure with the exception", function () {
+ exceptionHandler(exception);
+ expect(codeCallback.fail).toHaveBeenCalledWith(exception);
+ });
+ });
+ });
});
79 spec/cucumber/util/exception_spec.js
View
@@ -0,0 +1,79 @@
+require('../../support/spec_helper');
+
+describe("Cucumber.Util.Arguments", function() {
+ var UNCAUGHT_EXCEPTION_EVENT = 'uncaughtException';
+
+ var Cucumber = requireLib('cucumber');
+
+ describe(".registerUncaughtExceptionHandler()", function () {
+ var exceptionHandler;
+
+ describe("in a Node.js environment", function() {
+ beforeEach(function () {
+ exceptionHandler = createSpy("exception handler");
+ spyOn(process, 'on');
+ });
+
+ it("registers the exception handler to the process's 'uncaughtException' event", function () {
+ Cucumber.Util.Exception.registerUncaughtExceptionHandler(exceptionHandler);
+ expect(process.on).toHaveBeenCalledWith(UNCAUGHT_EXCEPTION_EVENT, exceptionHandler);
+ });
+ });
+
+ describe("in a browser environment", function() {
+ var previousOn;
+
+ beforeEach(function () {
+ previousOn = process.on;
+ process.on = void(0);
+ exceptionHandler = createSpy("exception handler");
+ global.window = createSpy("window");
+ });
+
+ afterEach(function () {
+ process.on = previousOn;
+ });
+
+ it("registers the exception handler to the windows's 'onerror' event handler", function () {
+ Cucumber.Util.Exception.registerUncaughtExceptionHandler(exceptionHandler);
+ expect(window.onerror).toBe(exceptionHandler);
+ });
+ });
+ });
+
+ describe(".unregisterUncaughtExceptionHandler()", function () {
+ var exceptionHandler;
+
+ describe("in a Node.js environment", function() {
+ beforeEach(function () {
+ exceptionHandler = createSpy("exception handler");
+ spyOn(process, 'removeListener');
+ });
+
+ it("registers the exception handler to the process's 'uncaughtException' event", function () {
+ Cucumber.Util.Exception.unregisterUncaughtExceptionHandler(exceptionHandler);
+ expect(process.removeListener).toHaveBeenCalledWith(UNCAUGHT_EXCEPTION_EVENT, exceptionHandler);
+ });
+ });
+
+ describe("in a browser environment", function() {
+ var previousRemoveListener;
+
+ beforeEach(function () {
+ previousRemoveListener = process.removeListener;
+ process.removeListener = void(0);
+ exceptionHandler = createSpy("exception handler");
+ global.window = createSpyWithStubs("window", {onerror: exceptionHandler});
+ });
+
+ afterEach(function () {
+ process.removeListener = previousRemoveListener;
+ });
+
+ it("registers the exception handler to the windows's 'onerror' event handler", function () {
+ Cucumber.Util.Exception.unregisterUncaughtExceptionHandler(exceptionHandler);
+ expect(window.onerror).toBeUndefined();
+ });
+ });
+ });
+});

No commit comments for this range

Something went wrong with that request. Please try again.