Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Allow for node-like callback errors

From now on, failures can be signaled � la Node.js:

   this.Then(/^I fail$/, function (callback) {
     callback(new Error("failure message"));
   });
  • Loading branch information...
commit 19231ba8e2687e7001be8a2bc1ab4756564ea62e 1 parent b895662
@jbpros jbpros authored
View
10 features/nodejs_style_callback_failure.feature
@@ -0,0 +1,10 @@
+Feature: Node.js-like callback failures
+
+ Scenario: fail with single-parameter error
+ Given a scenario with:
+ """
+ When I divide 10 by 0
+ """
+ And the step "I divide 10 by 0" has a mapping failing via a Node-like error construct
+ When Cucumber executes the scenario
+ Then the scenario fails
View
4 features/step_definitions/cucumber_js_mappings.rb
@@ -65,6 +65,10 @@ def write_asynchronously_failing_mapping_through_exception_with_message(step_nam
append_step_definition(step_name, "setTimeout(function() { throw new Error('#{message}');}, 10);")
end
+ def write_failing_mapping_through_nodejs_callback(step_name)
+ append_step_definition(step_name, "callback(new Error('#fail'));")
+ end
+
def write_mapping_incrementing_world_variable_by_value(step_name, increment_value)
append_step_definition(step_name, "this.variable += #{increment_value}; callback();")
end
View
8 features/step_definitions/cucumber_steps.js
@@ -89,6 +89,14 @@ var cucumberSteps = function() {
callback();
});
+ Given(/^the step "([^"]*)" has a mapping failing via a Node-like error construct$/, function(stepName, callback) {
+ this.stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
+ world.touchStep(\"" + stepName + "\");\
+ callback(new Error('#fail'));\
+});\n";
+ callback();
+ });
+
Given(/^the step "([^"]*)" has a pending mapping$/, function(stepName, callback) {
this.stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
world.touchStep(\"" + stepName + "\");\
View
4 features/step_definitions/cucumber_steps.rb
@@ -25,6 +25,10 @@
write_asynchronously_failing_mapping_through_exception_with_message(step_name, message)
end
+Given /^the step "([^"]*)" has a mapping failing via a Node-like error construct$/ do |step_name|
+ write_failing_mapping_through_nodejs_callback(step_name)
+end
+
Given /^a custom World constructor calling back with an explicit object$/ do
write_custom_world_constructor_calling_back_with_explicit_object
end
View
14 lib/cucumber/support_code/step_definition.js
@@ -1,4 +1,4 @@
-var StepDefinition = function(pattern, code) {
+var StepDefinition = function (pattern, code) {
var Cucumber = require('../../cucumber');
var self = {
@@ -25,10 +25,14 @@ var StepDefinition = function(pattern, code) {
Cucumber.Util.Exception.unregisterUncaughtExceptionHandler(handleException);
};
- var codeCallback = function() {
- var successfulStepResult = Cucumber.Runtime.SuccessfulStepResult({step: step});
- cleanUp();
- callback(successfulStepResult);
+ var codeCallback = function (error) {
+ if (error) {
+ codeCallback.fail(error);
+ } else {
+ var successfulStepResult = Cucumber.Runtime.SuccessfulStepResult({step: step});
+ cleanUp();
+ callback(successfulStepResult);
+ }
};
codeCallback.pending = function pending(reason) {
View
180 spec/cucumber/support_code/step_definition_spec.js
@@ -1,31 +1,31 @@
require('../../support/spec_helper');
-describe("Cucumber.SupportCode.StepDefinition", function() {
+describe("Cucumber.SupportCode.StepDefinition", function () {
var Cucumber = requireLib('cucumber');
var stepDefinition, pattern, stepDefinitionCode;
- beforeEach(function() {
+ beforeEach(function () {
pattern = createSpyWithStubs("pattern", {test: null});
stepDefinitionCode = createSpy("step definition code");
stepDefinition = Cucumber.SupportCode.StepDefinition(pattern, stepDefinitionCode);
spyOn(global, 'RegExp');
});
- describe("getPatternRegexp()", function() {
- describe("when the pattern is a RegExp", function() {
- it("does not instantiate a RegExp", function() {
+ describe("getPatternRegexp()", function () {
+ describe("when the pattern is a RegExp", function () {
+ it("does not instantiate a RegExp", function () {
expect(global.RegExp).not.toHaveBeenCalled();
});
- it("returns the pattern itself", function() {
+ it("returns the pattern itself", function () {
expect(stepDefinition.getPatternRegexp()).toBe(pattern);
});
});
- describe("when the pattern is a String", function() {
+ describe("when the pattern is a String", function () {
var regexp, quotedDollarParameterSubstitutedPattern, regexpString;
- beforeEach(function() {
+ beforeEach(function () {
regexp = createSpy("regexp");
regexpString = createSpy("regexp string");
quotedDollarParameterSubstitutedPattern = createSpyWithStubs("quoted dollar param substituted pattern", {replace: regexpString});
@@ -33,57 +33,57 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
global.RegExp.andReturn(regexp);
});
- it("replaces quoted dollar-prefixed parameters with the regexp equivalent", function() {
+ it("replaces quoted dollar-prefixed parameters with the regexp equivalent", function () {
stepDefinition.getPatternRegexp();
expect(pattern.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_REGEXP, Cucumber.SupportCode.StepDefinition.QUOTED_DOLLAR_PARAMETER_SUBSTITUTION);
});
- it("replaces other dollar-prefixed parameter with the regexp equivalent", function() {
+ it("replaces other dollar-prefixed parameter with the regexp equivalent", function () {
stepDefinition.getPatternRegexp();
expect(quotedDollarParameterSubstitutedPattern.replace).toHaveBeenCalledWith(Cucumber.SupportCode.StepDefinition.DOLLAR_PARAMETER_REGEXP, Cucumber.SupportCode.StepDefinition.DOLLAR_PARAMETER_SUBSTITUTION);
});
- it("instantiates a new RegExp", function() {
+ it("instantiates a new RegExp", function () {
stepDefinition.getPatternRegexp();
expect(global.RegExp).toHaveBeenCalledWith(regexpString);
});
- it("returns the new RegExp", function() {
+ it("returns the new RegExp", function () {
expect(stepDefinition.getPatternRegexp()).toBe(regexp);
});
});
});
- describe("matchesStepName()", function() {
+ describe("matchesStepName()", function () {
var patternRegexp, stepName;
- beforeEach(function() {
+ beforeEach(function () {
stepName = createSpy("step name");
matchResult = createSpy("step match result (boolean)");
patternRegexp = createSpyWithStubs("pattern regexp", {test: matchResult});
spyOn(stepDefinition, 'getPatternRegexp').andReturn(patternRegexp);
});
- it("gets the pattern regexp", function(){
+ it("gets the pattern regexp", function (){
stepDefinition.matchesStepName(stepName);
expect(stepDefinition.getPatternRegexp).toHaveBeenCalled();
});
- it("tests the string against the step name", function() {
+ it("tests the string against the step name", function () {
stepDefinition.matchesStepName(stepName);
expect(patternRegexp.test).toHaveBeenCalledWith(stepName);
});
- it("returns the match result", function() {
+ it("returns the match result", function () {
expect(stepDefinition.matchesStepName(stepName)).toBe(matchResult);
});
});
- describe("invoke()", function() {
+ describe("invoke()", function () {
var step, world, callback;
var parameters, exceptionHandler;
- beforeEach(function() {
+ beforeEach(function () {
step = createSpy("step");
world = createSpy("world");
callback = createSpy("callback");
@@ -95,14 +95,14 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
spyOn(stepDefinitionCode, 'apply');
});
- it("builds the step invocation parameters", function() {
+ it("builds the step invocation parameters", function () {
stepDefinition.invoke(step, world, callback);
expect(stepDefinition.buildInvocationParameters).toHaveBeenCalled();
expect(stepDefinition.buildInvocationParameters).toHaveBeenCalledWithValueAsNthParameter(step, 1);
expect(stepDefinition.buildInvocationParameters).toHaveBeenCalledWithAFunctionAsNthParameter(2);
});
- it("builds an exception handler for the code callback", function() {
+ it("builds an exception handler for the code callback", function () {
stepDefinition.invoke(step, world, callback);
expect(stepDefinition.buildExceptionHandlerToCodeCallback).toHaveBeenCalledWithAFunctionAsNthParameter(1);
@@ -116,16 +116,16 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
expect(Cucumber.Util.Exception.registerUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
});
- it("calls the step definition code with the parameters and World as 'this'", function() {
+ 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);
});
- describe("callback passed to the step definition code", function() {
+ describe("callback passed to the step definition code", function () {
var codeExecutionCallback;
var successfulStepResult;
- beforeEach(function() {
+ beforeEach(function () {
stepDefinition.invoke(step, world, callback);
codeExecutionCallback = stepDefinition.buildInvocationParameters.mostRecentCall.args[1];
successfulStepResult = createSpy("successful step result");
@@ -133,70 +133,104 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
spyOn(Cucumber.Util.Exception, 'unregisterUncaughtExceptionHandler');
});
- it("creates a successful step result", function() {
- codeExecutionCallback();
- expect(Cucumber.Runtime.SuccessfulStepResult).toHaveBeenCalledWith({step: step});
- });
+ describe("when called without an error", function () {
+ beforeEach(function () {
+ spyOn(codeExecutionCallback, 'fail');
+ codeExecutionCallback();
+ });
- it("unregisters the exception handler", function() {
- codeExecutionCallback();
- expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
+ it("creates a successful step result", function () {
+ expect(Cucumber.Runtime.SuccessfulStepResult).toHaveBeenCalledWith({step: step});
+ });
+
+ it("unregisters the exception handler", function () {
+ expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
+ });
+
+ it("calls back", function () {
+ expect(callback).toHaveBeenCalledWith(successfulStepResult);
+ });
+
+ it("does not fail", function () {
+ expect(codeExecutionCallback.fail).not.toHaveBeenCalled();
+ });
});
- it("calls back", function() {
- codeExecutionCallback();
- expect(callback).toHaveBeenCalledWith(successfulStepResult);
+ describe("when called with an error", function () {
+ var error;
+
+ beforeEach(function () {
+ error = createSpy("error");
+ spyOn(codeExecutionCallback, 'fail');
+ codeExecutionCallback(error);
+ });
+
+ it("does not create a successful step result", function () {
+ expect(Cucumber.Runtime.SuccessfulStepResult).not.toHaveBeenCalled();
+ });
+
+ it("does not unregister the exception handler", function () {
+ expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).not.toHaveBeenCalled();
+ });
+
+ it("does not call back", function () {
+ expect(callback).not.toHaveBeenCalled();
+ });
+
+ it("fails", function () {
+ expect(codeExecutionCallback.fail).toHaveBeenCalledWith(error);
+ });
});
- it("supplies a function to the step to let it claim its pendingness", function() {
- expect(codeExecutionCallback.pending).toBeAFunction();
+ it("supplies a function to the step to let it claim its pendingness", function () {
+ expect(codeExecutionCallback.pending).toBeAFunction ();
});
- it("supplies a function to the step to let it fail asynchronously", function() {
- expect(codeExecutionCallback.fail).toBeAFunction();
+ it("supplies a function to the step to let it fail asynchronously", function () {
+ expect(codeExecutionCallback.fail).toBeAFunction ();
});
- describe("pending()", function() {
+ describe("pending()", function () {
var pendingReason, pendingStepResult;
- beforeEach(function() {
+ beforeEach(function () {
pendingReason = createSpy("pending reason");
pendingStepResult = createSpy("pending step result");
spyOn(Cucumber.Runtime, 'PendingStepResult').andReturn(pendingStepResult);
});
- it("creates a pending step result", function() {
+ it("creates a pending step result", function () {
codeExecutionCallback.pending(pendingReason);
expect(Cucumber.Runtime.PendingStepResult).toHaveBeenCalledWith({step: step, pendingReason: pendingReason});
});
- it("unregisters the exception handler", function() {
+ it("unregisters the exception handler", function () {
codeExecutionCallback.pending(pendingReason);
expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
});
- it("calls back", function() {
+ it("calls back", function () {
codeExecutionCallback.pending(pendingReason);
expect(callback).toHaveBeenCalledWith(pendingStepResult);
});
});
- describe("fail()", function() {
+ describe("fail()", function () {
var failureReason, failedStepResult;
- beforeEach(function() {
+ beforeEach(function () {
failureReason = createSpy("failure reason");
failedStepResult = createSpy("failed step result");
spyOn(Cucumber.Runtime, 'FailedStepResult').andReturn(failedStepResult);
});
- it("creates a failing step result", function() {
+ it("creates a failing step result", function () {
codeExecutionCallback.fail(failureReason);
expect(Cucumber.Runtime.FailedStepResult).toHaveBeenCalledWith({step: step, failureException: failureReason});
});
- describe("when no failure reason is given", function() {
- it("creates a failing step result with a generic step failure exception", function() {
+ describe("when no failure reason is given", function () {
+ it("creates a failing step result with a generic step failure exception", function () {
codeExecutionCallback.fail();
var payload = Cucumber.Runtime.FailedStepResult.mostRecentCall.args[0];
expect(payload.step).toBe(step);
@@ -204,38 +238,38 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
});
});
- it("unregisters the exception handler", function() {
+ it("unregisters the exception handler", function () {
codeExecutionCallback.fail(failureReason);
expect(Cucumber.Util.Exception.unregisterUncaughtExceptionHandler).toHaveBeenCalledWith(exceptionHandler);
});
- it("calls back", function() {
+ it("calls back", function () {
codeExecutionCallback.fail(failureReason);
expect(callback).toHaveBeenCalledWith(failedStepResult);
});
});
});
- describe("when the step definition code throws an exception", function() {
+ describe("when the step definition code throws an exception", function () {
var failureException;
- beforeEach(function() {
+ beforeEach(function () {
failureException = createSpy("I am a failing step definition");
stepDefinitionCode.apply.andThrow(failureException);
});
- it("handles the exception with the exception handler", function() {
+ it("handles the exception with the exception handler", function () {
stepDefinition.invoke(step, world, callback);
expect(exceptionHandler).toHaveBeenCalledWith(failureException);
});
});
});
- describe("buildInvocationParameters()", function() {
+ describe("buildInvocationParameters()", function () {
var patternRegexp, step, stepName, stepAttachment, stepAttachmentContents;
var matches, callback;
- beforeEach(function() {
+ beforeEach(function () {
stepName = createSpy("step name to match");
matches = createSpyWithStubs("matches", {shift: null, push: null});
patternRegexp = createSpyWithStubs("pattern regexp", {test: matches});
@@ -246,69 +280,69 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
spyOnStub(patternRegexp, 'exec').andReturn(matches);
});
- it("gets the step name", function() {
+ it("gets the step name", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(step.getName).toHaveBeenCalled();
});
- it("gets the pattern regexp", function() {
+ it("gets the pattern regexp", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(stepDefinition.getPatternRegexp).toHaveBeenCalled();
});
- it("executes the pattern regexp against the step name", function() {
+ it("executes the pattern regexp against the step name", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(patternRegexp.exec).toHaveBeenCalledWith(stepName);
});
- it("removes the whole matched string of the regexp result array (to only keep matching groups)", function() {
+ it("removes the whole matched string of the regexp result array (to only keep matching groups)", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(matches.shift).toHaveBeenCalled();
});
- it("checks whether the step has an attachment or not", function() {
+ it("checks whether the step has an attachment or not", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(step.hasAttachment).toHaveBeenCalled();
});
- describe("when the step has an attachment", function() {
- beforeEach(function() {
+ describe("when the step has an attachment", function () {
+ beforeEach(function () {
step.hasAttachment.andReturn(true);
});
- it("gets the attachment contents", function() {
+ it("gets the attachment contents", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(step.getAttachmentContents).toHaveBeenCalled();
});
- it("adds the attachment contents to the parameter array", function() {
+ it("adds the attachment contents to the parameter array", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(matches.push).toHaveBeenCalledWith(stepAttachmentContents);
});
});
- describe("when the step has no attachment", function() {
- beforeEach(function() {
+ describe("when the step has no attachment", function () {
+ beforeEach(function () {
step.hasAttachment.andReturn(false);
});
- it("does not get the attachment contents", function() {
+ it("does not get the attachment contents", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(step.getAttachmentContents).not.toHaveBeenCalled();
});
- it("does not add the attachement contents to the parameter array", function() {
+ it("does not add the attachement contents to the parameter array", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(matches.push.callCount).toBe(1);
});
});
- it("adds the callback to the parameter array", function() {
+ it("adds the callback to the parameter array", function () {
stepDefinition.buildInvocationParameters(step, callback);
expect(matches.push).toHaveBeenCalledWith(callback);
});
- it("returns the parameters", function() {
+ it("returns the parameters", function () {
expect(stepDefinition.buildInvocationParameters(step, callback)).toBe(matches);
});
});
@@ -316,13 +350,13 @@ describe("Cucumber.SupportCode.StepDefinition", function() {
describe("buildExceptionHandlerToCodeCallback()", function () {
var codeCallback, exceptionHandler;
- beforeEach(function() {
+ beforeEach(function () {
codeCallback = createSpyWithStubs("code callback", {fail: null});
exceptionHandler = stepDefinition.buildExceptionHandlerToCodeCallback(codeCallback);
});
- it("returns an exception handler", function() {
- expect(exceptionHandler).toBeAFunction();
+ it("returns an exception handler", function () {
+ expect(exceptionHandler).toBeAFunction ();
});
describe("returned exception handler", function () {
Please sign in to comment.
Something went wrong with that request. Please try again.