Skip to content

Commit

Permalink
Check the indentation of tags (gherkin-lint#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonStJG authored and vsiakka committed Jan 4, 2018
1 parent edd70df commit f5da1e7
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 62 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,13 @@ You can override the defaults for `indentation` like this:
"when": 2,
"then": 2,
"and": 2,
"but": 2
"but": 2,
"feature tag": 0,
"scenario tag": 0
}
]
}
There is no need to override all the defaults, as is done above, instead they can be overriden only where required. `Step` will be used as a fallback if the keyword of the step, eg. 'given', is not specified.
There is no need to override all the defaults, as is done above, instead they can be overriden only where required. `Step` will be used as a fallback if the keyword of the step, eg. 'given', is not specified. If `feature tag` is not set then `Feature` is used as a fallback, and if `scenario tag` is not set then `Scenario` is used as a fallback.
This feature is able to handle all localizations of the gherkin steps.
```
Expand Down
118 changes: 75 additions & 43 deletions src/rules/indentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var _ = require('lodash');
var languageMapping = require('gherkin').DIALECTS;
var rule = 'indentation';

var availableConfigs = {
var defaultConfig = {
'Feature': 0,
'Background': 0,
'Scenario': 0,
Expand All @@ -16,60 +16,86 @@ var availableConfigs = {
'but': 2
};

var errors = [];
var availableConfigs = _.merge({}, defaultConfig, {
// The values here are unused by the config parsing logic.
'feature tag': -1,
'scenario tag': -1
});

function test(parsedLocation, configuration, type) {
// location.column is 1 index based so, when we compare with the expected indentation we need to subtract 1
if (--parsedLocation.column !== configuration[type]) {
errors.push({message: 'Wrong indentation for "' + type +
'", expected indentation level of ' + configuration[type] +
', but got ' + parsedLocation.column,
rule : rule,
line : parsedLocation.line});
function mergeConfiguration(configuration) {
var mergedConfiguration = _.merge({}, defaultConfig, configuration);
if (!Object.prototype.hasOwnProperty.call(mergedConfiguration, 'feature tag')) {
mergedConfiguration['feature tag'] = mergedConfiguration['Feature'];
}
if (!Object.prototype.hasOwnProperty.call(mergedConfiguration, 'scenario tag')) {
mergedConfiguration['scenario tag'] = mergedConfiguration['Scenario'];
}
return mergedConfiguration;
}

function testStep(step, language, configuration, mergedConfiguration) {
var keyword = step.keyword;
var stepType = _.findKey(language, function(values) {
return values instanceof Array && values.indexOf(keyword) !== -1;
});
stepType = stepType in configuration ? stepType : 'Step';
test(step.location, mergedConfiguration, stepType);
}
function testFeature(feature, configuration, mergedConfiguration) {
var errors = [];

function test(parsedLocation, type) {
// location.column is 1 index based so, when we compare with the expected
// indentation we need to subtract 1
if (--parsedLocation.column !== mergedConfiguration[type]) {
errors.push({message: 'Wrong indentation for "' + type +
'", expected indentation level of ' + mergedConfiguration[type] +
', but got ' + parsedLocation.column,
rule : rule,
line : parsedLocation.line});
}
}

function testScenarioOutline(scenarioOutline, mergedConfiguration) {
test(scenarioOutline.location, mergedConfiguration, 'Scenario');
scenarioOutline.examples.forEach(function(examples) {
test(examples.location, mergedConfiguration, 'Examples');
test(examples.tableHeader.location, mergedConfiguration, 'example');
examples.tableBody.forEach(function(row) {
test(row.location, mergedConfiguration, 'example');
function testStep(step) {
var keyword = step.keyword;
var stepType = _.findKey(languageMapping[feature.language], function(values) {
return values instanceof Array && values.indexOf(keyword) !== -1;
});
});
}
stepType = stepType in configuration ? stepType : 'Step';
test(step.location, stepType);
}

function indentation(feature, unused, configuration) {
if (!feature || Object.keys(feature).length === 0) {
return;
function testScenarioOutline(scenarioOutline) {
test(scenarioOutline.location, 'Scenario');
scenarioOutline.examples.forEach(function(examples) {
test(examples.location, 'Examples');
test(examples.tableHeader.location, 'example');
examples.tableBody.forEach(function(row) {
test(row.location, 'example');
});
});
}

function testTags(tags, type) {
_(tags).map(function(tag) {
return tag.location;
}).groupBy(function(tagLocation) {
return tagLocation.line;
}).forEach(function(locationsPerLine) {
var firstLocation = locationsPerLine.sort(function(location) {
return -location.column;
})[0];
test(firstLocation, type);
});
}
var language = languageMapping[feature.language];
var mergedConfiguration = _.merge(availableConfigs, configuration);
errors = [];

// Check Feature indentation
test(feature.location, mergedConfiguration, 'Feature');
test(feature.location, 'Feature');
testTags(feature.tags, 'feature tag');

feature.children.forEach(function(child) {
switch(child.type) {
case 'Background':
test(child.location, mergedConfiguration, 'Background');
test(child.location, 'Background');
break;
case 'Scenario':
test(child.location, mergedConfiguration, 'Scenario');
test(child.location, 'Scenario');
testTags(child.tags, 'scenario tag');
break;
case 'ScenarioOutline':
testScenarioOutline(child, mergedConfiguration);
testScenarioOutline(child);
testTags(child.tags, 'scenario tag');
break;
default:
errors.push({message: 'Unknown gherkin node type ' + child.type,
Expand All @@ -78,17 +104,23 @@ function indentation(feature, unused, configuration) {
break;
}

child.steps.forEach(function(step) {
// Check Step indentation
testStep(step, language, configuration, mergedConfiguration);
});
child.steps.forEach(testStep);
});

return errors;
}

function run(feature, unused, configuration) {
if (!feature || Object.keys(feature).length === 0) {
return;
}
var mergedConfiguration = mergeConfiguration(configuration);

return testFeature(feature, configuration, mergedConfiguration);
}

module.exports = {
name: rule,
run: indentation,
run: run,
availableConfigs: availableConfigs
};
5 changes: 5 additions & 0 deletions test/rules/indentation/CorrectIndentationSpaces.feature
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
@featureTag1 @featureTag2 @featureTag3
Feature: Test for indentation - spaces

Background:
Given I have a Feature file with great indentation

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario: This is a Scenario with correct indentation - spaces
Then I should not see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario Outline: This is a Scenario Outline with correct indentation - spaces
Then I should not see an indentation error
Examples:
Expand Down
5 changes: 5 additions & 0 deletions test/rules/indentation/CorrectIndentationTabs.feature
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
@featureTag1 @featureTag2 @featuretag3
Feature: Test for correct indentation - tabs

Background:
Given I have a Feature file with great indentation

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario: This is a Scenario with correct indentation - tabs
Then I should not see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario Outline: This is a Scenario Outline with correct indentation - tabs
Then I should not see an indentation error
Examples:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@featureTag1 @featureTag2
Feature: Test for indentation

Background:
Given I have a Feature file with great indentation

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario: This is a Scenario with correct indentation
Then I should not see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario Outline: This is a Scenario Outline with correct indentation
Then I should not see an indentation error
Examples:
| foo | bar |
| bar | foo |
| har | har |
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@featureTag1 @featureTag2
Feature: Test for indentation

Background:
Given I have a Feature file with great indentation

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario: This is a Scenario with correct indentation
Then I should not see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario Outline: This is a Scenario Outline with correct indentation
Then I should not see an indentation error
Examples:
| foo | bar |
| bar | foo |
| har | har |
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
#language: de
@featureTag1 @featureTag2
Funktionalität: Test for indentation - with German language and spaces

Grundlage:
Angenommen I have a Feature file with indentation all over the place

@scenarioTag1 @scenarioTag2
@scenarioTag3
Szenario: This is a Scenario for indentation - German + spaces
Dann I should see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Szenariogrundriss: This is a Scenario Outline for indentation - German + spaces
Dann I should see an indentation error
Beispiele:
Expand Down
5 changes: 5 additions & 0 deletions test/rules/indentation/WrongIndentationSpaces.feature
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
@featureTag1 @featureTag2
Feature: Test for indentation - spaces

Background:
Given I have a Feature file with indentation all over the place

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario: This is a Scenario for indentation - spaces
Then I should see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario Outline: This is a Scenario Outline for indentation - spaces
Then I should see an indentation error
Examples:
Expand Down
5 changes: 5 additions & 0 deletions test/rules/indentation/WrongIndentationTabs.feature
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
@featureTag1 @featureTag2
Feature: Test for indentation - tabs

Background:
Given I have a Feature file with indentation all over the place

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario: This is a Scenario for indentation - tabs
Then I should see an indentation error

@scenarioTag1 @scenarioTag2
@scenarioTag3
Scenario Outline: This is a Scenario Outline for indentation - tabs
Then I should see an indentation error
Examples:
Expand Down
Loading

0 comments on commit f5da1e7

Please sign in to comment.