gherkin-lint
Advanced tools
Comparing version 3.1.0 to 3.2.0
@@ -9,2 +9,13 @@ 'use strict'; | ||
function readAndParseFile(fileName) { | ||
var fileContent = fs.readFileSync(fileName, 'utf-8'); | ||
var file = { | ||
name: fileName, | ||
lines: fileContent.split(/\r\n|\r|\n/) | ||
}; | ||
var feature = parser.parse(fileContent).feature || {}; | ||
return { feature, file }; | ||
} | ||
function lint(files, configuration, additionalRulesDirs) { | ||
@@ -14,11 +25,8 @@ var output = []; | ||
files.forEach(function (fileName) { | ||
var fileContent = fs.readFileSync(fileName, 'utf-8'); | ||
var file = { | ||
name: fileName, | ||
lines: fileContent.split(/\r\n|\r|\n/) | ||
}; | ||
var errors = []; | ||
try { | ||
var feature = parser.parse(fileContent).feature || {}; | ||
var _readAndParseFile = readAndParseFile(fileName), | ||
feature = _readAndParseFile.feature, | ||
file = _readAndParseFile.file; | ||
errors = rules.runAllEnabledRules(feature, file, configuration, additionalRulesDirs); | ||
@@ -66,3 +74,3 @@ } catch (e) { | ||
if (errors[i].message.indexOf('expected: #TagLine, #ScenarioLine, #ScenarioOutlineLine, #Comment, #Empty') > -1) { | ||
index = i; | ||
index = i + 1; | ||
} else { | ||
@@ -73,3 +81,3 @@ break; | ||
} | ||
return { errors: errors.slice(index, errors.length), errorMsgs: errorMsgs }; | ||
return { errors: errors.slice(index), errorMsgs: errorMsgs }; | ||
} | ||
@@ -103,3 +111,4 @@ | ||
module.exports = { | ||
lint: lint | ||
lint: lint, | ||
readAndParseFile: readAndParseFile | ||
}; |
@@ -64,6 +64,8 @@ 'use strict'; | ||
test(examples.location, 'Examples'); | ||
test(examples.tableHeader.location, 'example'); | ||
examples.tableBody.forEach(function (row) { | ||
test(row.location, 'example'); | ||
}); | ||
if (examples.tableHeader) { | ||
test(examples.tableHeader.location, 'example'); | ||
examples.tableBody.forEach(function (row) { | ||
test(row.location, 'example'); | ||
}); | ||
} | ||
}); | ||
@@ -110,3 +112,3 @@ } | ||
if (!feature || Object.keys(feature).length === 0) { | ||
return; | ||
return []; | ||
} | ||
@@ -113,0 +115,0 @@ var mergedConfiguration = mergeConfiguration(configuration); |
@@ -23,32 +23,31 @@ 'use strict'; | ||
function nameLength(feature, unused, configuration) { | ||
if (!feature || Object.keys(feature).length === 0) { | ||
return; | ||
} | ||
var mergedConfiguration = _.merge(availableConfigs, configuration); | ||
errors = []; | ||
if (feature && Object.keys(feature).length !== 0) { | ||
var mergedConfiguration = _.merge(availableConfigs, configuration); | ||
// Check Feature name length | ||
test(feature.name, feature.location, mergedConfiguration, 'Feature'); | ||
// Check Feature name length | ||
test(feature.name, feature.location, mergedConfiguration, 'Feature'); | ||
feature.children.forEach(function (child) { | ||
switch (child.type) { | ||
case 'Scenario': | ||
case 'ScenarioOutline': | ||
// Check Scenario name length | ||
test(child.name, child.location, mergedConfiguration, 'Scenario'); | ||
break; | ||
case 'Background': | ||
break; | ||
default: | ||
errors.push({ message: 'Unknown gherkin node type ' + child.type, | ||
rule: rule, | ||
line: child.location.line }); | ||
break; | ||
} | ||
feature.children.forEach(function (child) { | ||
switch (child.type) { | ||
case 'Scenario': | ||
case 'ScenarioOutline': | ||
// Check Scenario name length | ||
test(child.name, child.location, mergedConfiguration, 'Scenario'); | ||
break; | ||
case 'Background': | ||
break; | ||
default: | ||
errors.push({ message: 'Unknown gherkin node type ' + child.type, | ||
rule: rule, | ||
line: child.location.line }); | ||
break; | ||
} | ||
child.steps.forEach(function (step) { | ||
// Check Step name length | ||
test(step.text, step.location, mergedConfiguration, 'Step'); | ||
child.steps.forEach(function (step) { | ||
// Check Step name length | ||
test(step.text, step.location, mergedConfiguration, 'Step'); | ||
}); | ||
}); | ||
}); | ||
} | ||
@@ -55,0 +54,0 @@ return errors; |
@@ -10,2 +10,3 @@ 'use strict'; | ||
function newLineAtEOF(unused, file, configuration) { | ||
var errors = []; | ||
if (_.indexOf(availableConfigs, configuration) === -1) { | ||
@@ -25,6 +26,10 @@ logger.boldError(rule + ' requires an extra configuration value.\nAvailable configurations: ' + availableConfigs.join(', ') + '\nFor syntax please look at the documentation.'); | ||
if (errormsg !== '') { | ||
return { message: errormsg, | ||
errors.push({ | ||
message: errormsg, | ||
rule: rule, | ||
line: file.lines.length }; | ||
line: file.lines.length | ||
}); | ||
} | ||
return errors; | ||
} | ||
@@ -31,0 +36,0 @@ |
@@ -7,9 +7,12 @@ 'use strict'; | ||
function noDuplicateFeatureNames(feature, file) { | ||
if (feature.name) { | ||
var errors = []; | ||
if (feature && feature.name) { | ||
if (feature.name in features) { | ||
var dupes = features[feature.name].files.join(', '); | ||
features[feature.name].files.push(file.name); | ||
return { message: 'Feature name is already used in: ' + dupes, | ||
errors.push({ | ||
message: 'Feature name is already used in: ' + dupes, | ||
rule: rule, | ||
line: feature.location.line }; | ||
line: feature.location.line | ||
}); | ||
} else { | ||
@@ -19,2 +22,3 @@ features[feature.name] = { files: [file.name] }; | ||
} | ||
return errors; | ||
} | ||
@@ -21,0 +25,0 @@ |
@@ -5,6 +5,10 @@ 'use strict'; | ||
var scenarios = []; | ||
var availableConfigs = ['anywhere', 'in-feature']; | ||
function noDuplicateScenarioNames(feature, file) { | ||
if (feature.children) { | ||
var errors = []; | ||
function noDuplicateScenarioNames(feature, file, configuration) { | ||
var errors = []; | ||
if (configuration === 'in-feature') { | ||
scenarios = []; | ||
} | ||
if (feature && feature.children) { | ||
feature.children.forEach(function (scenario) { | ||
@@ -23,4 +27,4 @@ if (scenario.name) { | ||
}); | ||
return errors; | ||
} | ||
return errors; | ||
} | ||
@@ -38,3 +42,4 @@ | ||
name: rule, | ||
run: noDuplicateScenarioNames | ||
run: noDuplicateScenarioNames, | ||
availableConfigs: availableConfigs | ||
}; |
@@ -5,10 +5,13 @@ 'use strict'; | ||
var rule = 'no-empty-file'; | ||
var suppressOtherRules = true; | ||
function noEmptyFiles(feature) { | ||
var errors = []; | ||
if (_.isEmpty(feature)) { | ||
return { message: 'Empty feature files are disallowed', | ||
errors.push({ | ||
message: 'Empty feature files are disallowed', | ||
rule: rule, | ||
line: 1 }; | ||
line: 1 | ||
}); | ||
} | ||
return errors; | ||
} | ||
@@ -18,4 +21,3 @@ | ||
name: rule, | ||
run: noEmptyFiles, | ||
suppressOtherRules: suppressOtherRules | ||
run: noEmptyFiles | ||
}; |
@@ -10,7 +10,11 @@ 'use strict'; | ||
function noFilesWithoutScenarios(feature) { | ||
var errors = []; | ||
if (!feature.children || !feature.children.some(filterScenarios)) { | ||
return { message: 'Feature file does not have any Scenarios', | ||
errors.push({ | ||
message: 'Feature file does not have any Scenarios', | ||
rule: rule, | ||
line: 1 }; | ||
line: 1 | ||
}); | ||
} | ||
return errors; | ||
} | ||
@@ -17,0 +21,0 @@ |
@@ -7,3 +7,3 @@ 'use strict'; | ||
var errors = []; | ||
if (feature.children) { | ||
if (feature && feature.children) { | ||
feature.children.forEach(function (scenario) { | ||
@@ -13,3 +13,3 @@ if (scenario.tags) { | ||
if (tag.name.indexOf('#') > 0) { | ||
errors.push({ message: 'Partially commented tag lines not allowed ', | ||
errors.push({ message: 'Partially commented tag lines not allowed', | ||
rule: rule, | ||
@@ -16,0 +16,0 @@ line: tag.location.line }); |
'use strict'; | ||
var _ = require('lodash'); | ||
var rule = 'no-scenario-outlines-without-examples'; | ||
@@ -8,4 +9,5 @@ | ||
var errors = []; | ||
feature.children.forEach(function (scenario) { | ||
if (scenario.type === 'ScenarioOutline' && !scenario.examples.length) { | ||
if (scenario.type === 'ScenarioOutline' && (!_.find(scenario.examples, 'tableBody') || !_.find(scenario.examples, 'tableBody')['tableBody'].length)) { | ||
errors.push({ message: 'Scenario Outline does not have any Examples', | ||
@@ -12,0 +14,0 @@ rule: rule, |
@@ -6,7 +6,10 @@ 'use strict'; | ||
function noUnNamedFeatures(feature) { | ||
var errors = []; | ||
if (!feature || !feature.name) { | ||
return { message: 'Missing Feature name', | ||
errors.push({ | ||
message: 'Missing Feature name', | ||
rule: rule, | ||
line: feature.location && feature.location.line || 0 }; | ||
line: feature.location && feature.location.line || 0 }); | ||
} | ||
return errors; | ||
} | ||
@@ -13,0 +16,0 @@ |
@@ -6,4 +6,4 @@ 'use strict'; | ||
function noUnNamedScenarios(feature) { | ||
if (feature.children) { | ||
var errors = []; | ||
var errors = []; | ||
if (feature && feature.children) { | ||
feature.children.forEach(function (scenario) { | ||
@@ -16,4 +16,4 @@ if (!scenario.name && scenario.type === 'Scenario') { | ||
}); | ||
return errors; | ||
} | ||
return errors; | ||
} | ||
@@ -20,0 +20,0 @@ |
{ | ||
"name": "gherkin-lint", | ||
"version": "3.1.0", | ||
"version": "3.2.0", | ||
"description": "A Gherkin linter/validator written in javascript", | ||
@@ -82,2 +82,3 @@ "author": "Vasiliki Siakka", | ||
"mocha-sinon": "2.1.0", | ||
"nyc": "14.0.0", | ||
"sinon": "6.2.0" | ||
@@ -93,2 +94,3 @@ }, | ||
"mocha": "mocha --recursive", | ||
"code-coverage": "nyc --reporter=text --include=dist/** mocha --recursive", | ||
"prepublish": "npm run build", | ||
@@ -95,0 +97,0 @@ "test": "npm run lint && npm run build && npm run mocha" |
@@ -12,3 +12,2 @@ # Gherkin lint | ||
npm install gherkin-lint | ||
``` | ||
@@ -44,3 +43,3 @@ | ||
| `no-dupe-feature-names` | Disallows duplicate Feature names | | ||
| `no-dupe-scenario-names` | Disallows duplicate Scenario names | | ||
| [`no-dupe-scenario-names`](#no-dupe-scenario-names)| Disallows duplicate Scenario names | | ||
| `no-duplicate-tags` | Disallows duplicate tags on the same Feature or Scenario | | ||
@@ -60,2 +59,3 @@ | `no-empty-background` | Disallows features with backgrounds without steps | | ||
| `no-unused-variables` | Disallows unused variables in scenario outlines | | ||
| [`scenario-size`](#scenario-size) | Allows restricting the maximum number of steps in a scenario, scenario outline and background | | ||
| `one-space-between-tags` | Tags on the same time must be separated by a single space | | ||
@@ -147,3 +147,3 @@ | `use-and` | Disallows repeated step names requiring use of And instead | | ||
`new-line-at-eof` can also be configured to enforcing or disallowing new lines at EOF. | ||
`new-line-at-eof` can be configured to enforce or disallow new lines at EOF. | ||
- To enforce new lines at EOF: | ||
@@ -163,8 +163,34 @@ ``` | ||
### no-restricted-tags | ||
### no-dupe-scenario-names | ||
`no-restricted-tags` must be configured with list of tags for it to have any effect: | ||
`no-dupe-scenario-names` can be configured to search for duplicates in each individual feature or amongst all feature files. | ||
To enable searching for duplicates in each individual feature (same scenario name in different features won't raise an error) you need to configure the rule like this: | ||
``` | ||
{ | ||
"no-dupe-scenario-names": ["on", "in-feature"] | ||
} | ||
``` | ||
The default case is testing against all the features (same scenario name in different features will raise an error). To get that behavor use the following configuration: | ||
``` | ||
{ | ||
"no-dupe-scenario-names": "on" | ||
} | ||
``` | ||
or | ||
``` | ||
{ | ||
"no-dupe-scenario-names": ["on", "anywhere"] | ||
} | ||
``` | ||
### no-restricted-tags | ||
`no-restricted-tags` should be configured with the list of restricted tags: | ||
``` | ||
{ | ||
"no-restricted-tags": ["on", {"tags": ["@watch", "@wip", "@todo"]}] | ||
@@ -175,2 +201,11 @@ } | ||
### scenario-size | ||
`scenario-size` lets you specify a maximum step length for scenarios and backgrounds. The `Scenario` configuration applies to both scenarios and scenario outlines: | ||
``` | ||
{ | ||
"scenario-size": ["on", { "steps-length": { "Background": 15, "Scenario": 15 }}] | ||
} | ||
``` | ||
## Configuration File | ||
@@ -177,0 +212,0 @@ The default name for the configuration file is `.gherkin-lintrc` and it's expected to be in your working directory. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1303
228
56704
8
43