Repository: vsiakka/gherkin-lint
Pull request #88 (2017-10-11 18:16:12)

Add one-space-between-tags rule
One last rule that I thought others might need.


Patch:
diff --git a/README.md b/README.md
index da1a6c2..bd0dda9 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@ Or check this:
 | `no-trailing-spaces`                        | Disallows trailing spaces                                                                |
 | `no-unnamed-features`                       | Disallows empty Feature name                                                             |
 | `no-unnamed-scenarios`                      | Disallows empty Scenario name                                                            |
+| `one-space-between-tags`                    | Tags on the same time must be separated by a single space                                |
 | `use-and`                                   | Disallows repeated step names requiring use of And instead                               |
 
 \* These rules cannot be turned off because they detect undocumented cucumber functionality that causes the [gherkin](https://github.com/cucumber/gherkin-javascript) parser to crash.
diff --git a/src/rules/one-space-between-tags.js b/src/rules/one-space-between-tags.js
new file mode 100644
index 0000000..0770160
--- /dev/null
+++ b/src/rules/one-space-between-tags.js
@@ -0,0 +1,43 @@
+const _ = require('lodash');
+
+var rule = 'one-space-between-tags';
+
+function run(feature) {
+  if (!feature || Object.keys(feature).length === 0) {
+    return [];
+  }
+  var errors = [];
+
+  function testTags(allTags) {
+    _(allTags).sort(function(tag) {
+      return -tag.location.column;
+    }).groupBy(function(tag) {
+      return tag.location.line;
+    }).forEach(function(tags) {
+      _.range(tags.length - 1).map(function(i) {
+        if (tags[i].location.column + tags[i].name.length < tags[i + 1].location.column - 1) {
+          errors.push({
+            line: tags[i].location.line,
+            rule: rule,
+            message: 'There is more than one space between the tags ' +
+                      tags[i].name + ' and ' + tags[i + 1].name
+          });
+        }
+      });
+    });
+  }
+
+  testTags(feature.tags);
+  feature.children.forEach(function(child) {
+    if (child.type === 'Scenario' || child.type === 'ScenarioOutline') {
+      testTags(child.tags);
+    }
+  });
+
+  return errors;
+}
+
+module.exports = {
+  run: run,
+  name: rule
+};


Test patch:
diff --git a/test-data-wip/.gherkin-lintrc b/test-data-wip/.gherkin-lintrc
index 5260b96..64f24da 100644
--- a/test-data-wip/.gherkin-lintrc
+++ b/test-data-wip/.gherkin-lintrc
@@ -16,5 +16,6 @@
   "use-and": "on",
   "no-duplicate-tags": "on",
   "no-superfluous-tags": "on",
-  "no-homogenous-tags": "on"
+  "no-homogenous-tags": "on",
+  "one-space-between-tags": "on"
 }
diff --git a/test/rules/one-space-between-tags/NoViolations.feature b/test/rules/one-space-between-tags/NoViolations.feature
new file mode 100644
index 0000000..6db0e8c
--- /dev/null
+++ b/test/rules/one-space-between-tags/NoViolations.feature
@@ -0,0 +1,18 @@
+@featuretag1 @featuretag2
+@featuretag3
+Feature: Feature with good tag spacing
+
+Background:
+  Given I have a Background
+
+@scenariotag1 @scenariotag2
+@scenariotag3 @scenariotag4 @scenariotag5
+Scenario: This is a Scenario with good tag spacing
+  Then this is a then step
+
+@scenariotag5 @scenariotag6
+Scenario Outline: This is a Scenario Outline with good tag spacing
+  Then this is a then step
+Examples:
+  | foo |
+  | bar |
diff --git a/test/rules/one-space-between-tags/Violations.feature b/test/rules/one-space-between-tags/Violations.feature
new file mode 100644
index 0000000..83c33a3
--- /dev/null
+++ b/test/rules/one-space-between-tags/Violations.feature
@@ -0,0 +1,18 @@
+@featuretag1  @featuretag2
+@featuretag3
+Feature: Feature with good tag spacing
+
+Background:
+  Given I have a Background
+
+@scenariotag1 @scenariotag2
+@scenariotag3   @scenariotag4  @scenariotag5
+Scenario: This is a Scenario with bad tag spacing
+  Then this is a then step
+
+@scenariotag5   @scenariotag6
+Scenario Outline: This is a Scenario Outline with bad tag spacing
+  Then this is a then step
+Examples:
+  | foo |
+  | bar |
diff --git a/test/rules/one-space-between-tags/one-space-between-tags.js b/test/rules/one-space-between-tags/one-space-between-tags.js
new file mode 100644
index 0000000..aa512a7
--- /dev/null
+++ b/test/rules/one-space-between-tags/one-space-between-tags.js
@@ -0,0 +1,26 @@
+var ruleTestBase = require('../rule-test-base');
+var rule = require('../../../src/rules/one-space-between-tags.js');
+var runTest = ruleTestBase.createRuleTest(rule,
+  'There is more than one space between the tags <%= left %> and <%= right %>');
+
+describe('One space between tags rule', function() {
+  it('doesn\'t raise errors when there are no violations', function() {
+    runTest('one-space-between-tags/NoViolations.feature', {}, []);
+  });
+
+  it('detects errors for tags on features, scenarios, and scenario outlines', function() {
+    runTest('one-space-between-tags/Violations.feature', {}, [{
+      line: 1,
+      messageElements: {left: '@featuretag1', right: '@featuretag2'}
+    }, {
+      line: 9,
+      messageElements: {left: '@scenariotag3', right: '@scenariotag4'}
+    }, {
+      line: 9,
+      messageElements: {left: '@scenariotag4', right: '@scenariotag5'}
+    }, {
+      line: 13,
+      messageElements: {left: '@scenariotag5', right: '@scenariotag6'}
+    }]);
+  });
+});