Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test and implementation for parsing steps. #78

Merged
merged 1 commit into from
Aug 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 72 additions & 21 deletions features-support/step_definitions/parsing.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,74 @@ function unwrapSingleColumnTable(singleColumnTable) {
return (singleColumnTable.raw()).map(function (valueWrappedInArray) {return valueWrappedInArray[0]});
}

var scenarioNumberToIndex = {
"background": 0,
"default": 1,
"first": 1,
"second": 2,
"third": 3,
"fourth": 4
};

module.exports = function() {
// Shared variables
var featureText;
var features;

function compareFeatureValues(key) {
return function compare(table) {
var featureValues = features[0][key];
var expectedValues = unwrapSingleColumnTable(table);
featureValues.should.containDeepOrdered(expectedValues);
}
}

// TODO: FAR TOO COMPLICATED! Maybe remove the conditionals
// TODO: by having two different functions.
// key1 is a key on the scenario
// key2 is an optional key on a sub-object.
function compareScenarioValues(key1, key2) {
return function compare(scenarioNumber, table) {
var done = undefined;

/**
* Sometimes just a table is passed in the first argument slot,
* in that case adjust the parameters.
*
* CucumberJS determines if the step definition completion
* should be dependent on a callback by counting the number
* of arguments, so we have to cope with that.
**/
if (typeof scenarioNumber === "object") {
done = table;
table = scenarioNumber;
scenarioNumber = "default";
}

var scenarioIndex = scenarioNumberToIndex[scenarioNumber];
var scenario = features[0].scenarios[scenarioIndex];
var scenarioValues;

// If the suboject key is specified dig the values out of the objects.
// e.g. get the names of steps out of an array of steps.
// c.f. https://lodash.com/docs#pluck .
if (key2) {
scenarioValues = scenario[key1].map(function(subObject) {return subObject[key2]; });
} else {
scenarioValues = scenario[key1];
}

var expectedValues = unwrapSingleColumnTable(table);

// Do the test.
scenarioValues.should.containDeepOrdered(expectedValues);

if (typeof done === "function") {
done();
}
}
}

this.Given(/^the feature file\.?$/, function (string) {
featureText = string;
});
Expand All @@ -29,39 +92,27 @@ module.exports = function() {
features[0].name.should.be.exactly(featureTitle);
});

this.Then(/^a background with the title "([^"]*)"\.?$/, function (backgroundTitle) {
features[0].backgrounds[0].name.should.be.exactly(backgroundTitle);
this.Then(/^I get a background with the title "([^"]*)"\.?$/, function (backgroundTitle) {
features[0].scenarios[0].name.should.be.exactly(backgroundTitle);
});

this.Then(/^scenarios with titles$/, function (table) {
for(var i = 0; i < table.raw().length; i++) {
var row = table.raw()[i];
// TODO: Make more expressive.
this.Then(/^I get scenarios with titles\.?$/, function (table) {
var expectedValues = unwrapSingleColumnTable(table);
for(var i = 1; i < table.raw().length; i++) {
var row = table.raw()[i-1];
var scenario = features[0].scenarios[i];
scenario.name.should.be.exactly(row[0]);
}
});

function compareFeatureValues(key) {
return function compare(table) {
var featureValues = features[0][key];
var expectedValues = unwrapSingleColumnTable(table);
featureValues.should.containDeepOrdered(expectedValues);
}
}

function compareScenarioValues(key) {
return function compare(table) {
var featureValues = features[0].scenarios[0][key];
var expectedValues = unwrapSingleColumnTable(table);
featureValues.should.containDeepOrdered(expectedValues);
}
}

this.Then(/^feature tags are associated with features\.?$/, compareFeatureValues('tags'));

this.Then(/^scenario tags are associated with scenarios\.?$/, compareScenarioValues('tags'));

this.Then(/^feature comments are associated with features\.?$/, compareFeatureValues('comments'));

this.Then(/^scenario comments are associated with scenarios\.?$/, compareScenarioValues('comments'));

this.Then(/^the "([^"]*)" scenario has steps with the names\.?$/, compareScenarioValues('steps', 'name'));;
};
20 changes: 15 additions & 5 deletions features/feature-parsing/parsing-specifications.feature
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ Feature: Parsing specifications
Feature: coping with multiple features in a file.
"""

@parsing @dev
@parsing
Scenario: Parse titles
When I parse this specification
Then I get a feature with title "Feature title"
And a background with the title "Backgrounds exist"
And scenarios with titles
| Scenario 1 |
| Scenario 2 |
And I get a background with the title "Backgrounds exist"
And I get scenarios with titles
| Scenario 1 |
| Scenario 2 |

@parsing
Scenario: Parse tags
Expand All @@ -77,3 +77,13 @@ Feature: Parsing specifications
| # A feature comment. |
And scenario comments are associated with scenarios
| # A scenario comment. |

@parsing
Scenario: Parse steps
When I parse this specification
Then the "first" scenario has steps with the names
| something is true |
| there is an outcome |
And the "second" scenario has steps with the names
| I have an "argument" |
| I expect |
25 changes: 12 additions & 13 deletions lib/parser/featureModels/featureFactory.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
'use strict';
/* eslint camelcase: 0 */

var scenarioOrBackgroundFactory = require('./scenarioOrBackgroundFactory');

function Feature(config) {
this.token = 'feature';
this.backgrounds = [];

// Contains backgrounds and scenarios.
this.scenarios = [];
this.keyword = config.keyword;
this.name = config.name;
this.description = config.description;
this.line = config.line;
this.tags = config.tags;
this.comments = config.comments;
};
this.scenarios.getLatest = function() { return this[this.length - 1]; }

function ScenarioOrBackground(config) {
this.token = config.keyword.toLowerCase();
this.keyword = config.keyword;
this.name = config.name;
this.description = config.description;
this.line = config.line;
this.tags = config.tags;
this.comments = config.comments;
}
};

Feature.prototype.addScenarioOrBackground = function(config) {
var key = config.keyword.toLowerCase() + 's';
this[key].push(new ScenarioOrBackground(config));
this.scenarios.push(scenarioOrBackgroundFactory(config));
};

Feature.prototype.addStep = function(config) {
var currentScenario = this.scenarios.getLatest();
currentScenario.addStep(config);
};

// Export the feature factory.
Expand Down
28 changes: 28 additions & 0 deletions lib/parser/featureModels/scenarioOrBackgroundFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';
/* eslint camelcase: 0 */

function Step(config) {
this.token = 'step';
this.keyword = config.keyword;
this.name = config.name;
this.line = config.line;
}

function ScenarioOrBackground(config) {
this.steps = [];
this.token = config.keyword.toLowerCase();
this.keyword = config.keyword;
this.name = config.name;
this.description = config.description;
this.line = config.line;
this.tags = config.tags;
this.comments = config.comments;
}

ScenarioOrBackground.prototype.addStep = function(config) {
this.steps.push(new Step(config));
}

module.exports = function(config) {
return new ScenarioOrBackground(config);
}
15 changes: 14 additions & 1 deletion lib/parser/visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ var commentsFactory = require('./featureModels/commentsFactory.js');

module.exports = function Visitor() {
var features = [];
features.getLatest = function() { return this[this.length - 1]; };

var tags = tagsFactory();
var comments = commentsFactory();

function backgroundOrScenarioParser(keyword, name, description, line) {
var localTags = tags.flush();
var localComments = comments.flush();

var feature = features[features.length - 1];
var feature = features.getLatest();
if (feature === undefined) {
throw new TypeError(keyword + ' found outside of feature, giving up parsing.');
}
Expand Down Expand Up @@ -57,6 +59,17 @@ module.exports = function Visitor() {
examples: function(keyword, name, description, line) {
},
step: function(keyword, name, line) {
var feature = features.getLatest();

if (feature === undefined) {
throw new TypeError(keyword + ' found outside of feature, giving up parsing.');
}

feature.addStep({
keyword: keyword,
name: name,
line: line
});
},
doc_string: function(content_type, string, line) {
},
Expand Down