Skip to content

Commit

Permalink
Output step definition snippets in CoffeeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
johngeorgewright authored and jbpros committed Nov 27, 2013
1 parent 41e0895 commit 336ca0f
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 52 deletions.
2 changes: 2 additions & 0 deletions lib/cucumber/cli.js
Expand Up @@ -65,6 +65,8 @@ var Cli = function(argv) {
summary : prints a summary only, after all\n\
scenarios were executed\n\
\n\
--coffee Display step definitions in CoffeeScript.\n\
\n\
-v, --version Display Cucumber.js's version.\n\
\n\
-h, --help You're looking at it.\n");
Expand Down
8 changes: 8 additions & 0 deletions lib/cucumber/cli/argument_parser.js
Expand Up @@ -69,6 +69,7 @@ var ArgumentParser = function(argv) {
definitions[ArgumentParser.FORMAT_OPTION_NAME] = String;
definitions[ArgumentParser.HELP_FLAG_NAME] = Boolean;
definitions[ArgumentParser.VERSION_FLAG_NAME] = Boolean;
definitions[ArgumentParser.COFFEE_SNIPPETS] = Boolean;
return definitions;
},

Expand All @@ -90,6 +91,11 @@ var ArgumentParser = function(argv) {
return isVersionRequested;
},

shouldSnippetsBeInCoffee: function shouldSnippetsBeInCoffee() {
var coffeeDisplay = self.getOptionOrDefault(ArgumentParser.COFFEE_SNIPPETS, ArgumentParser.DEFAULT_COFFEE_SNIPPETS);
return coffeeDisplay;
},

storeOptions: function storeOptions(newOptions) {
options = newOptions;
},
Expand Down Expand Up @@ -122,6 +128,8 @@ ArgumentParser.HELP_FLAG_SHORT_NAME = "h";
ArgumentParser.DEFAULT_HELP_FLAG_VALUE = false;
ArgumentParser.VERSION_FLAG_NAME = "version";
ArgumentParser.DEFAULT_VERSION_FLAG_VALUE = false;
ArgumentParser.COFFEE_SNIPPETS = "coffee";
ArgumentParser.DEFAULT_COFFEE_SNIPPETS = false;
ArgumentParser.FeaturePathExpander = require('./argument_parser/feature_path_expander');
ArgumentParser.PathExpander = require('./argument_parser/path_expander');
ArgumentParser.SupportCodePathExpander = require('./argument_parser/support_code_path_expander');
Expand Down
14 changes: 10 additions & 4 deletions lib/cucumber/cli/configuration.js
Expand Up @@ -8,18 +8,19 @@ var Configuration = function(argv) {
getFormatter: function getFormatter() {
var formatter;
var format = argumentParser.getFormat();
var options = {coffee: self.shouldSnippetsBeInCoffee()};
switch(format) {
case Configuration.JSON_FORMAT_NAME:
formatter = Cucumber.Listener.JsonFormatter();
formatter = Cucumber.Listener.JsonFormatter(options);
break;
case Configuration.PROGRESS_FORMAT_NAME:
formatter = Cucumber.Listener.ProgressFormatter();
formatter = Cucumber.Listener.ProgressFormatter(options);
break;
case Configuration.PRETTY_FORMAT_NAME:
formatter = Cucumber.Listener.PrettyFormatter();
formatter = Cucumber.Listener.PrettyFormatter(options);
break;
case Configuration.SUMMARY_FORMAT_NAME:
formatter = Cucumber.Listener.SummaryFormatter();
formatter = Cucumber.Listener.SummaryFormatter(options);
break;
default:
throw new Error("Unknown formatter name \"" + format + "\".");
Expand Down Expand Up @@ -65,6 +66,11 @@ var Configuration = function(argv) {
isVersionRequested: function isVersionRequested() {
var isVersionRequested = argumentParser.isVersionRequested();
return isVersionRequested;
},

shouldSnippetsBeInCoffee: function shouldSnippetsBeInCoffee() {
var coffeeDisplay = argumentParser.shouldSnippetsBeInCoffee();
return coffeeDisplay;
}

};
Expand Down
2 changes: 1 addition & 1 deletion lib/cucumber/listener/progress_formatter.js
Expand Up @@ -5,7 +5,7 @@ var ProgressFormatter = function(options) {
options = {};

var self = Cucumber.Listener.Formatter(options);
var summaryFormatter = Cucumber.Listener.SummaryFormatter({logToConsole: false});
var summaryFormatter = Cucumber.Listener.SummaryFormatter({coffee: options.coffee, logToConsole: false});

var parentHear = self.hear;
self.hear = function hear(event, callback) {
Expand Down
7 changes: 6 additions & 1 deletion lib/cucumber/listener/summary_formatter.js
Expand Up @@ -59,11 +59,16 @@ var SummaryFormatter = function (options) {
};

self.storeUndefinedStep = function storeUndefinedStep(step) {
var snippetBuilder = Cucumber.SupportCode.StepDefinitionSnippetBuilder(step);
var snippetBuilder = Cucumber.SupportCode.StepDefinitionSnippetBuilder(step, self.getStepDefinitionSyntax());
var snippet = snippetBuilder.buildSnippet();
self.appendStringToUndefinedStepLogBuffer(snippet);
};

self.getStepDefinitionSyntax = function getStepDefinitionSyntax() {
var syntax = options.coffee ? 'CoffeeScript' : 'JavaScript';
return new Cucumber.SupportCode.StepDefinitionSnippetBuilderSyntax[syntax]();
};

self.appendStringToFailedScenarioLogBuffer = function appendStringToFailedScenarioLogBuffer(string) {
failedScenarioLogBuffer += string + "\n";
};
Expand Down
1 change: 1 addition & 0 deletions lib/cucumber/support_code.js
Expand Up @@ -3,5 +3,6 @@ SupportCode.Hook = require('./support_code/hook');
SupportCode.Library = require('./support_code/library');
SupportCode.StepDefinition = require('./support_code/step_definition');
SupportCode.StepDefinitionSnippetBuilder = require('./support_code/step_definition_snippet_builder');
SupportCode.StepDefinitionSnippetBuilderSyntax = require('./support_code/step_definition_snippet_builder_syntax');
SupportCode.WorldConstructor = require('./support_code/world_constructor');
module.exports = SupportCode;
62 changes: 24 additions & 38 deletions lib/cucumber/support_code/step_definition_snippet_builder.js
@@ -1,6 +1,7 @@
var _ = require('underscore');
var _ = require('underscore'),
stepDefinitionSnippetBuilderSyntax = require ('./step_definition_snippet_builder_syntax');

var StepDefinitionSnippetBuilder = function(step) {
var StepDefinitionSnippetBuilder = function(step, syntax) {
var Cucumber = require('../../cucumber');

var self = {
Expand All @@ -9,24 +10,24 @@ var StepDefinitionSnippetBuilder = function(step) {
var pattern = self.buildStepDefinitionPattern();
var parameters = self.buildStepDefinitionParameters();
var snippet =
StepDefinitionSnippetBuilder.STEP_DEFINITION_START +
functionName +
StepDefinitionSnippetBuilder.STEP_DEFINITION_INNER1 +
pattern +
StepDefinitionSnippetBuilder.STEP_DEFINITION_INNER2 +
parameters +
StepDefinitionSnippetBuilder.STEP_DEFINITION_END;
syntax.getStepDefinitionStart() +
functionName +
syntax.getStepDefinitionInner1() +
pattern +
syntax.getStepDefinitionInner2() +
parameters +
syntax.getStepDefinitionEnd();
return snippet;
},

buildStepDefinitionFunctionName: function buildStepDefinitionFunctionName() {
var functionName;
if (step.isOutcomeStep())
functionName = StepDefinitionSnippetBuilder.OUTCOME_STEP_DEFINITION_FUNCTION_NAME;
functionName = syntax.getOutcomeStepDefinitionFunctionName();
else if (step.isEventStep())
functionName = StepDefinitionSnippetBuilder.EVENT_STEP_DEFINITION_FUNCTION_NAME;
functionName = syntax.getEventStepDefinitionFunctionName();
else
functionName = StepDefinitionSnippetBuilder.CONTEXT_STEP_DEFINITION_FUNCTION_NAME;
functionName = syntax.getContextStepDefinitionFunctionName();
return functionName;
},

Expand All @@ -35,21 +36,21 @@ var StepDefinitionSnippetBuilder = function(step) {
var escapedStepName = Cucumber.Util.RegExp.escapeString(stepName);
var parameterizedStepName = self.parameterizeStepName(escapedStepName);
var pattern =
StepDefinitionSnippetBuilder.PATTERN_START +
parameterizedStepName +
StepDefinitionSnippetBuilder.PATTERN_END
syntax.getPatternStart() +
parameterizedStepName +
syntax.getPatternEnd();
return pattern;
},

buildStepDefinitionParameters: function buildStepDefinitionParameters() {
var parameters = self.getStepDefinitionPatternMatchingGroupParameters();
if (step.hasDocString())
parameters = parameters.concat([StepDefinitionSnippetBuilder.STEP_DEFINITION_DOC_STRING]);
parameters = parameters.concat([syntax.getStepDefinitionDocString()]);
else if (step.hasDataTable())
parameters = parameters.concat([StepDefinitionSnippetBuilder.STEP_DEFINITION_DATA_TABLE]);
parameters = parameters.concat([syntax.getStepDefinitionDataTable()]);
var parametersAndCallback =
parameters.concat([StepDefinitionSnippetBuilder.STEP_DEFINITION_CALLBACK]);
var parameterString = parametersAndCallback.join(StepDefinitionSnippetBuilder.FUNCTION_PARAMETER_SEPARATOR);
parameters.concat([syntax.getStepDefinitionCallback()]);
var parameterString = parametersAndCallback.join(syntax.getFunctionParameterSeparator());
return parameterString;
},

Expand All @@ -66,39 +67,24 @@ var StepDefinitionSnippetBuilder = function(step) {
countStepDefinitionPatternMatchingGroups: function countStepDefinitionPatternMatchingGroups() {
var stepDefinitionPattern = self.buildStepDefinitionPattern();
var numberMatchingGroupCount =
Cucumber.Util.String.count(stepDefinitionPattern, StepDefinitionSnippetBuilder.NUMBER_MATCHING_GROUP);
Cucumber.Util.String.count(stepDefinitionPattern, syntax.getNumberMatchingGroup());
var quotedStringMatchingGroupCount =
Cucumber.Util.String.count(stepDefinitionPattern, StepDefinitionSnippetBuilder.QUOTED_STRING_MATCHING_GROUP);
Cucumber.Util.String.count(stepDefinitionPattern, syntax.getQuotedStringMatchingGroup());
var count = numberMatchingGroupCount + quotedStringMatchingGroupCount;
return count;
},

parameterizeStepName: function parameterizeStepName(stepName) {
var parameterizedStepName =
stepName
.replace(StepDefinitionSnippetBuilder.NUMBER_PATTERN, StepDefinitionSnippetBuilder.NUMBER_MATCHING_GROUP)
.replace(StepDefinitionSnippetBuilder.QUOTED_STRING_PATTERN, StepDefinitionSnippetBuilder.QUOTED_STRING_MATCHING_GROUP);
.replace(StepDefinitionSnippetBuilder.NUMBER_PATTERN, syntax.getNumberMatchingGroup())
.replace(StepDefinitionSnippetBuilder.QUOTED_STRING_PATTERN, syntax.getQuotedStringMatchingGroup());
return parameterizedStepName;
}
};
return self;
};

StepDefinitionSnippetBuilder.STEP_DEFINITION_START = 'this.';
StepDefinitionSnippetBuilder.STEP_DEFINITION_INNER1 = '(';
StepDefinitionSnippetBuilder.STEP_DEFINITION_INNER2 = ', function(';
StepDefinitionSnippetBuilder.STEP_DEFINITION_END = ") {\n // express the regexp above with the code you wish you had\n callback.pending();\n});\n";
StepDefinitionSnippetBuilder.STEP_DEFINITION_DOC_STRING = 'string';
StepDefinitionSnippetBuilder.STEP_DEFINITION_DATA_TABLE = 'table';
StepDefinitionSnippetBuilder.STEP_DEFINITION_CALLBACK = 'callback';
StepDefinitionSnippetBuilder.PATTERN_START = '/^';
StepDefinitionSnippetBuilder.PATTERN_END = '$/';
StepDefinitionSnippetBuilder.CONTEXT_STEP_DEFINITION_FUNCTION_NAME = 'Given';
StepDefinitionSnippetBuilder.EVENT_STEP_DEFINITION_FUNCTION_NAME = 'When';
StepDefinitionSnippetBuilder.OUTCOME_STEP_DEFINITION_FUNCTION_NAME = 'Then';
StepDefinitionSnippetBuilder.NUMBER_PATTERN = /\d+/gi;
StepDefinitionSnippetBuilder.NUMBER_MATCHING_GROUP = '(\\d+)';
StepDefinitionSnippetBuilder.QUOTED_STRING_PATTERN = /"[^"]*"/gi;
StepDefinitionSnippetBuilder.QUOTED_STRING_MATCHING_GROUP = '"([^"]*)"';
StepDefinitionSnippetBuilder.FUNCTION_PARAMETER_SEPARATOR = ', ';
module.exports = StepDefinitionSnippetBuilder;
@@ -0,0 +1,98 @@
var _ = require('underscore'),
Syntax = function() {},
JavaScriptSyntax = function() {},
CoffeeScriptSyntax = function() {};

Syntax.prototype = {
getStepDefinitionDocString: function() {
return 'string';
},

getStepDefinitionDataTable: function() {
return 'table';
},

getStepDefinitionCallback: function() {
return 'callback';
},

getPatternStart: function() {
return '/^';
},

getPatternEnd: function() {
return '$/';
},

getContextStepDefinitionFunctionName: function() {
return 'Given';
},

getEventStepDefinitionFunctionName: function() {
return 'When';
},

getOutcomeStepDefinitionFunctionName: function() {
return 'Then';
},

getNumberMatchingGroup: function() {
return '(\\d+)';
},

getQuotedStringMatchingGroup: function() {
return '"([^"]*)"';
},

getFunctionParameterSeparator: function() {
return ', ';
},

getStepDefinitionEndComment: function() {
return 'express the regexp above with the code you wish you had';
}
};

JavaScriptSyntax.prototype = {
getStepDefinitionStart: function() {
return 'this.';
},

getStepDefinitionInner1: function() {
return '(';
},

getStepDefinitionInner2: function() {
return ', function(';
},

getStepDefinitionEnd: function() {
return ") {\n // " + this.getStepDefinitionEndComment() + "\n callback.pending();\n});\n";
},
};

_.extend(JavaScriptSyntax.prototype, Syntax.prototype);

CoffeeScriptSyntax.prototype = {
getStepDefinitionStart: function() {
return '@';
},

getStepDefinitionInner1: function() {
return ' ';
},

getStepDefinitionInner2: function() {
return ', (';
},

getStepDefinitionEnd: function() {
return ") ->\n # " + this.getStepDefinitionEndComment() + "\n callback.pending()\n";
}
};

_.extend(CoffeeScriptSyntax.prototype, Syntax.prototype);

exports.JavaScript = JavaScriptSyntax;
exports.CoffeeScript = CoffeeScriptSyntax;

1 change: 1 addition & 0 deletions spec/cucumber/cli/configuration_spec.js
Expand Up @@ -31,6 +31,7 @@ describe("Cucumber.Cli.Configuration", function () {
describe("getFormatter()", function () {
beforeEach(function () {
spyOnStub(argumentParser, 'getFormat').andReturn("progress");
spyOnStub(argumentParser, 'shouldSnippetsBeInCoffee').andReturn(false);
spyOn(Cucumber.Listener, 'JsonFormatter');
spyOn(Cucumber.Listener, 'ProgressFormatter');
spyOn(Cucumber.Listener, 'PrettyFormatter');
Expand Down
18 changes: 11 additions & 7 deletions spec/cucumber/listener/summary_formatter_spec.js
Expand Up @@ -239,10 +239,11 @@ describe("Cucumber.Listener.SummaryFormatter", function () {
});

describe("handleAfterFeaturesEvent()", function () {
var callback;
var callback, event;

beforeEach(function () {
callback = createSpy("callback");
event = createSpy("event");
spyOn(summaryFormatter, 'logSummary');
});

Expand Down Expand Up @@ -306,28 +307,31 @@ describe("Cucumber.Listener.SummaryFormatter", function () {
});

describe("storeUndefinedStep()", function () {
var snippetBuilder, snippet, step;
var snippetBuilderSyntax, numberMatchingGroup, snippetBuilder, snippet, step;

beforeEach(function () {
stpe = createSpy("step");
numberMatchingGroup = createSpy("snippet number matching group");
snippetBuilderSyntax = createSpyWithStubs("snippet builder syntax", {getNumberMatchingGroup: numberMatchingGroup});
step = createSpy("step");
snippet = createSpy("step definition snippet");
snippetBuilder = createSpyWithStubs("snippet builder", {buildSnippet: snippet});
spyOn(Cucumber.SupportCode, 'StepDefinitionSnippetBuilder').andReturn(snippetBuilder);
spyOn(summaryFormatter, 'appendStringToUndefinedStepLogBuffer');
spyOn(summaryFormatter, 'getStepDefinitionSyntax').andReturn(snippetBuilderSyntax);
});

it("creates a new step definition snippet builder", function () {
summaryFormatter.storeUndefinedStep(step);
expect(Cucumber.SupportCode.StepDefinitionSnippetBuilder).toHaveBeenCalledWith(step);
summaryFormatter.storeUndefinedStep(step, snippetBuilderSyntax);
expect(Cucumber.SupportCode.StepDefinitionSnippetBuilder).toHaveBeenCalledWith(step, snippetBuilderSyntax);
});

it("builds the step definition", function () {
summaryFormatter.storeUndefinedStep(step);
summaryFormatter.storeUndefinedStep(step, snippetBuilderSyntax);
expect(snippetBuilder.buildSnippet).toHaveBeenCalled();
});

it("appends the snippet to the undefined step log buffer", function () {
summaryFormatter.storeUndefinedStep(step);
summaryFormatter.storeUndefinedStep(step, snippetBuilderSyntax);
expect(summaryFormatter.appendStringToUndefinedStepLogBuffer).toHaveBeenCalledWith(snippet);
});
});
Expand Down

0 comments on commit 336ca0f

Please sign in to comment.