Skip to content

Commit

Permalink
Unify features with cucumber-features (close #21)
Browse files Browse the repository at this point in the history
- In addition to Aruba, Cucumber.js can now run the "common features" against itself. It uses the same inner step definitions as Aruba.
- A few debug methods were added.
  • Loading branch information
jbpros committed Jul 28, 2011
1 parent 216658a commit bcab1b5
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 3 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,15 @@ This is still a work in progress; some step definition mappings are missing to r
You can run the following script which will execute cucumber.js recursively against all known passing features and "core.feature":

$ ./run_all_features.js

### Debug messages

You can display debug messages by setting the DEBUG_LEVEL environment variable. It goes from `1` to `5`. `5` will diplay everything, `1` will only print out the critical things.

$ DEBUG_LEVEL=5 ./run_all_features.js
$ DEBUG_LEVEL=5 ./cucumber.js features/cucumber-features/core.feature

It even works with Aruba:

$ rm -rf doc; DEBUG_LEVEL=5 ARUBA_REPORT_DIR=doc cucumber features/cucumber-features/core.feature -r features
$ open doc/features/cucumber-features/*.html # you'll see debug messages in Aruba-generated docs
1 change: 0 additions & 1 deletion features/step_definitions/cucumber_js_mappings.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# FIXME: Rename to CucumberJsMappings?
module CucumberJsMappings
STEP_DEFINITIONS_FILE = "features/step_definitions/cucumber_steps.js"
FEATURE_FILE = "features/a_feature.feature"
Expand Down
174 changes: 174 additions & 0 deletions features/step_definitions/cucumber_steps.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,178 @@
var cucumberSteps = function() {
var shouldPrepare = true;
var featureSource;
var stepDefinitions;
var touchedSteps;
var lastRunSucceeded;
var lastRunOutput;

Given(/^a scenario "([^"]*)" with:$/, function(scenarioName, steps, callback) {
prepare();
featureSource += "Feature: A feature\n";
featureSource += " Scenario: " + scenarioName + "\n";
featureSource += steps.replace(/^/gm, ' ');
callback();
});

Given(/^the step "([^"]*)" has a passing mapping$/, function(stepName, callback) {
prepare();
stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
touchStep(\"" + stepName + "\");\
callback();\
});\n";
callback();
});

Given(/^the step "([^"]*)" has a failing mapping$/, function(stepName, callback) {
prepare();
stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
touchStep(\"" + stepName + "\");\
throw('I was supposed to fail.');\
});\n";
callback();
});

Given(/^the step "([^"]*)" has a pending mapping$/, function(stepName, callback) {
prepare();
stepDefinitions += "Given(/^" + stepName + "$/, function(callback) {\
touchStep(\"" + stepName + "\");\
callback.pending('I am pending.');\
});\n";
callback();
});

Given(/^the following feature:$/, function(feature, callback) {
prepare();
featureSource = feature;
callback();
});

When(/^Cucumber executes the scenario "([^"]*)"$/, function(scenarioName, callback) {
runFeature(callback);
});

When(/^Cucumber runs the feature$/, function(callback) {
runFeature(callback);
});

When(/^Cucumber runs the scenario with steps for a calculator$/, function(callback) {
RpnCalculator = require('../support/rpn_calculator');
supportCode = function() { require('./calculator_steps')(RpnCalculator) };
runFeatureWithSupportCodeSource(supportCode, callback);
});

Then(/^the scenario passes$/, function(callback) {
if (!lastRunSucceeded)
throw("Expected the scenario to pass but it failed");
callback();
});

Then(/^the scenario fails$/, function(callback) {
assertFailedScenario();
callback();
});

Then(/^the scenario is pending$/, function(callback) {
assertPendingScenario();
callback();
});

Then(/^the scenario is undefined$/, function(callback) {
assertUndefinedScenario();
callback();
});

Then(/^the step "([^"]*)" is skipped$/, function(stepName, callback) {
assertSkippedStep(stepName);
callback();
});

Then(/^the feature passes$/, function(callback) {
assertPassingFeature();
callback();
});

function prepare() {
if (shouldPrepare) {
shouldPrepare = false;
touchedSteps = [];
featureSource = "";
stepDefinitions = "";
}
}

function runFeature(callback) {
var supportCode;
var supportCodeSource = "supportCode = function() {\n" + stepDefinitions + "};\n";
eval(supportCodeSource);
runFeatureWithSupportCodeSource(supportCode, callback);
}

function runFeatureWithSupportCodeSource(supportCode, callback) {
var Cucumber = require('../../lib/cucumber');
var cucumber = Cucumber(featureSource, supportCode);
var formatter = Cucumber.Listener.ProgressFormatter({logToConsole: false});
cucumber.attachListener(formatter);
cucumber.start(function(succeeded) {
lastRunSucceeded = succeeded;
lastRunOutput = formatter.getLogs();
Cucumber.Debug.notice(lastRunOutput, 'cucumber output', 5);
shouldPrepare = true;
callback();
});
}

function touchStep(string) {
touchedSteps.push(string);
}

function isStepTouched(pattern) {
return (touchedSteps.indexOf(pattern) >= 0);
}

function assertPassingFeature() {
assertNoPartialOutput("failed", lastRunOutput);
assertSuccess();
}

function assertFailedScenario() {
assertPartialOutput("1 scenario (1 failed)", lastRunOutput);
assertFailure();
}

function assertPendingScenario() {
assertPartialOutput("1 scenario (1 pending)", lastRunOutput);
assertSuccess();
}

function assertUndefinedScenario() {
assertPartialOutput("1 scenario (1 undefined)", lastRunOutput);
assertSuccess();
}

function assertSkippedStep(stepName) {
if (isStepTouched(stepName))
throw("Expected step \"" + stepName + "\" to have been skipped.");
}

function assertSuccess() {
if (!lastRunSucceeded)
throw("Expected Cucumber to succeed but it failed.");
}

function assertFailure() {
if (lastRunSucceeded)
throw("Expected Cucumber to fail but it succeeded.");
}

function assertPartialOutput(expected, actual) {
if (actual.indexOf(expected) < 0)
throw("Expected:\n\"" + actual + "\"\nto match:\n\"" + expected + "\"");
}

function assertNoPartialOutput(expected, actual) {
if (actual.indexOf(expected) >= 0)
throw("Expected:\n\"" + actual + "\"\nnot to match:\n\"" + expected + "\"");
}
};
module.exports = cucumberSteps;
33 changes: 31 additions & 2 deletions lib/cucumber/debug.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
var Debug = {
TODO: function(description) {
TODO: function TODO(description) {
return function() { throw("IMPLEMENT ME: " + description); };
},

warn: function warn(string, caption, level) {
if (Debug.isMessageLeveltoBeDisplayed(level))
process.stdout.write(Debug.warningString(string, caption));
},

notice: function notice(string, caption, level) {
if (Debug.isMessageLeveltoBeDisplayed(level))
process.stdout.write(Debug.noticeString(string, caption));
},

warningString: function warningString(string, caption) {
caption = caption || 'debug-warning';
return "\033[30;43m" + caption + ":\033[0m \033[33m" + string + "\033[0m"
},

noticeString: function noticeString(string, caption) {
caption = caption || 'debug-notice';
return "\033[30;46m" + caption + ":\033[0m \033[36m" + string + "\033[0m"
},

prefix: function prefix() {
return ;
},

isMessageLeveltoBeDisplayed: function isMessageLeveltoBeDisplayed(level) {
level = level || 3; // default level
return (level <= process.env['DEBUG_LEVEL']);
}
};
Debug.SimpleAstListener = require('./debug/simple_ast_listener');
module.exports = Debug;
module.exports = Debug;
2 changes: 2 additions & 0 deletions lib/cucumber/support_code/step_definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ var StepDefinition = function(regexp, code) {
try {
code.apply(undefined, parameters);
} catch (err) {
if (err)
Cucumber.Debug.warn(err, 'exception inside feature', 3);
var stepResult = (err instanceof Cucumber.Runtime.PendingStepException) ?
Cucumber.Runtime.PendingStepResult() :
Cucumber.Runtime.FailedStepResult();
Expand Down

0 comments on commit bcab1b5

Please sign in to comment.