gherkin_lint 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rubocop.yml +1 -1
- data/Gemfile +1 -0
- data/README.md +42 -631
- data/Rakefile +6 -2
- data/features/avoid_scripting.feature +2 -0
- data/features/be_declarative.feature +53 -0
- data/features/missing_example_name.feature +24 -0
- data/features/same_tag_for_all_scenarios.feature +41 -1
- data/gherkin_lint.gemspec +3 -2
- data/lib/gherkin_lint.rb +10 -4
- data/lib/gherkin_lint/issue.rb +13 -0
- data/lib/gherkin_lint/linter.rb +15 -11
- data/lib/gherkin_lint/linter/avoid_outline_for_single_example.rb +1 -1
- data/lib/gherkin_lint/linter/avoid_period.rb +1 -1
- data/lib/gherkin_lint/linter/avoid_scripting.rb +1 -1
- data/lib/gherkin_lint/linter/background_does_more_than_setup.rb +1 -1
- data/lib/gherkin_lint/linter/background_requires_multiple_scenarios.rb +1 -1
- data/lib/gherkin_lint/linter/bad_scenario_name.rb +1 -1
- data/lib/gherkin_lint/linter/be_declarative.rb +45 -0
- data/lib/gherkin_lint/linter/file_name_differs_feature_name.rb +1 -1
- data/lib/gherkin_lint/linter/invalid_file_name.rb +1 -1
- data/lib/gherkin_lint/linter/invalid_step_flow.rb +3 -3
- data/lib/gherkin_lint/linter/missing_example_name.rb +2 -1
- data/lib/gherkin_lint/linter/missing_feature_description.rb +1 -1
- data/lib/gherkin_lint/linter/missing_feature_name.rb +1 -1
- data/lib/gherkin_lint/linter/missing_scenario_name.rb +1 -1
- data/lib/gherkin_lint/linter/missing_test_action.rb +1 -1
- data/lib/gherkin_lint/linter/missing_verification.rb +1 -1
- data/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb +39 -6
- data/lib/gherkin_lint/linter/too_clumsy.rb +1 -1
- data/lib/gherkin_lint/linter/too_long_step.rb +1 -1
- data/lib/gherkin_lint/linter/too_many_different_tags.rb +2 -2
- data/lib/gherkin_lint/linter/too_many_steps.rb +1 -1
- data/lib/gherkin_lint/linter/too_many_tags.rb +1 -1
- data/lib/gherkin_lint/linter/unique_scenario_names.rb +1 -1
- data/lib/gherkin_lint/linter/unknown_variable.rb +1 -1
- data/lib/gherkin_lint/linter/unused_variable.rb +1 -1
- data/lib/gherkin_lint/linter/use_background.rb +1 -1
- data/lib/gherkin_lint/linter/use_outline.rb +1 -1
- metadata +43 -15
- checksums.yaml +0 -7
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ end
|
|
11
11
|
|
12
12
|
desc 'Publishes the Gem'
|
13
13
|
task :push do
|
14
|
-
sh 'gem push gherkin_lint-0.
|
14
|
+
sh 'gem push gherkin_lint-0.3.0.gem'
|
15
15
|
end
|
16
16
|
|
17
17
|
desc 'Checks ruby style'
|
@@ -36,5 +36,9 @@ task :language do
|
|
36
36
|
end
|
37
37
|
|
38
38
|
task :self_check do
|
39
|
-
|
39
|
+
disabled_checks = %w(
|
40
|
+
UnknownVariable
|
41
|
+
BadScenarioName
|
42
|
+
)
|
43
|
+
sh "RUBYLIB=lib ./bin/gherkin_lint --disable #{disabled_checks.join ','} features/*.feature"
|
40
44
|
end
|
@@ -31,6 +31,7 @@ Feature: Avoid Scripting
|
|
31
31
|
"""
|
32
32
|
AvoidScripting - Multiple Actions
|
33
33
|
lint.feature (2): Test.A
|
34
|
+
|
34
35
|
"""
|
35
36
|
|
36
37
|
Scenario: Repeat Action-Verfication Steps
|
@@ -49,6 +50,7 @@ Feature: Avoid Scripting
|
|
49
50
|
"""
|
50
51
|
AvoidScripting - Multiple Actions
|
51
52
|
lint.feature (2): Test.A
|
53
|
+
|
52
54
|
"""
|
53
55
|
|
54
56
|
Scenario: Valid Example
|
@@ -0,0 +1,53 @@
|
|
1
|
+
Feature: Be Declarative
|
2
|
+
As a Customer
|
3
|
+
I want to read declarative scenarios
|
4
|
+
so that I'm able to understand how to set the system in the state and how to prove the state
|
5
|
+
|
6
|
+
Background: Prepare Testee
|
7
|
+
Given a file named "lint.rb" with:
|
8
|
+
"""
|
9
|
+
$LOAD_PATH << '../../lib'
|
10
|
+
require 'gherkin_lint'
|
11
|
+
|
12
|
+
linter = GherkinLint::GherkinLint.new
|
13
|
+
linter.enable %w(BeDeclarative)
|
14
|
+
linter.analyze 'lint.feature'
|
15
|
+
exit linter.report
|
16
|
+
|
17
|
+
"""
|
18
|
+
|
19
|
+
Scenario: Non Declarative Scenario
|
20
|
+
Given a file named "lint.feature" with:
|
21
|
+
"""
|
22
|
+
Feature: Test
|
23
|
+
Scenario: A
|
24
|
+
Given a number 1
|
25
|
+
And another number 2
|
26
|
+
When add these numbers
|
27
|
+
Then the result is 3
|
28
|
+
"""
|
29
|
+
When I run `ruby lint.rb`
|
30
|
+
Then it should pass with exactly:
|
31
|
+
"""
|
32
|
+
BeDeclarative (Warning) - no verb
|
33
|
+
lint.feature (3): Test.A step: a number 1
|
34
|
+
BeDeclarative (Warning) - no verb
|
35
|
+
lint.feature (4): Test.A step: another number 2
|
36
|
+
|
37
|
+
"""
|
38
|
+
|
39
|
+
Scenario: Valid Example
|
40
|
+
Given a file named "lint.feature" with:
|
41
|
+
"""
|
42
|
+
Feature: Test
|
43
|
+
Scenario: A
|
44
|
+
Given a number is set to 1
|
45
|
+
And another number is set to 2
|
46
|
+
When add these numbers
|
47
|
+
Then the result is 3
|
48
|
+
"""
|
49
|
+
When I run `ruby lint.rb`
|
50
|
+
Then it should pass with exactly:
|
51
|
+
"""
|
52
|
+
|
53
|
+
"""
|
@@ -27,12 +27,36 @@ Feature: Missing Example Name
|
|
27
27
|
Examples:
|
28
28
|
| value |
|
29
29
|
| test |
|
30
|
+
|
31
|
+
Examples:
|
32
|
+
| value |
|
33
|
+
| test |
|
30
34
|
"""
|
31
35
|
When I run `ruby lint.rb`
|
32
36
|
Then it should fail with exactly:
|
33
37
|
"""
|
34
38
|
MissingExampleName - No Example Name
|
35
39
|
lint.feature (2): Test.A
|
40
|
+
MissingExampleName - No Example Name
|
41
|
+
lint.feature (2): Test.A
|
42
|
+
|
43
|
+
"""
|
44
|
+
|
45
|
+
Scenario: Names could be omitted for scenarios with a single example
|
46
|
+
Given a file named "lint.feature" with:
|
47
|
+
"""
|
48
|
+
Feature: Test
|
49
|
+
Scenario Outline: A
|
50
|
+
When test
|
51
|
+
Then <value>
|
52
|
+
|
53
|
+
Examples:
|
54
|
+
| value |
|
55
|
+
| test |
|
56
|
+
"""
|
57
|
+
When I run `ruby lint.rb`
|
58
|
+
Then it should pass with exactly:
|
59
|
+
"""
|
36
60
|
|
37
61
|
"""
|
38
62
|
|
@@ -16,7 +16,7 @@ Feature: Same Tag For All Scenarios
|
|
16
16
|
|
17
17
|
"""
|
18
18
|
|
19
|
-
Scenario:
|
19
|
+
Scenario: Tags used multiple times for scenario
|
20
20
|
Given a file named "lint.feature" with:
|
21
21
|
"""
|
22
22
|
Feature: Test
|
@@ -33,6 +33,46 @@ Feature: Same Tag For All Scenarios
|
|
33
33
|
|
34
34
|
"""
|
35
35
|
|
36
|
+
Scenario: Tags used multiple times for example
|
37
|
+
Given a file named "lint.feature" with:
|
38
|
+
"""
|
39
|
+
Feature: Test
|
40
|
+
Scenario Outline: A
|
41
|
+
When test
|
42
|
+
|
43
|
+
@A
|
44
|
+
Examples: First
|
45
|
+
| field |
|
46
|
+
| value |
|
47
|
+
|
48
|
+
@A
|
49
|
+
Examples: Second
|
50
|
+
| field |
|
51
|
+
| value |
|
52
|
+
"""
|
53
|
+
When I run `ruby lint.rb`
|
54
|
+
Then it should fail with exactly:
|
55
|
+
"""
|
56
|
+
SameTagForAllScenarios - Tag '@A' should be used at Scenario Outline level
|
57
|
+
lint.feature (2): Test.A
|
58
|
+
|
59
|
+
"""
|
60
|
+
|
61
|
+
Scenario: @skip is an exception
|
62
|
+
Given a file named "lint.feature" with:
|
63
|
+
"""
|
64
|
+
Feature: Test
|
65
|
+
@skip
|
66
|
+
Scenario: A
|
67
|
+
@skip
|
68
|
+
Scenario: B
|
69
|
+
"""
|
70
|
+
When I run `ruby lint.rb`
|
71
|
+
Then it should pass with exactly:
|
72
|
+
"""
|
73
|
+
|
74
|
+
"""
|
75
|
+
|
36
76
|
Scenario: Valid Example with different Tags
|
37
77
|
Given a file named "lint.feature" with:
|
38
78
|
"""
|
data/gherkin_lint.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'gherkin_lint'
|
3
|
-
s.version = '0.
|
4
|
-
s.date = '2016-
|
3
|
+
s.version = '0.3.0'
|
4
|
+
s.date = '2016-05-01'
|
5
5
|
s.summary = 'Gherkin Lint'
|
6
6
|
s.description = 'Lint Gherkin Files'
|
7
7
|
s.authors = ['Stefan Rohe']
|
@@ -11,5 +11,6 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.add_runtime_dependency 'gherkin', ['= 2.12.2']
|
12
12
|
s.add_runtime_dependency 'term-ansicolor', ['>= 1.3.2']
|
13
13
|
s.add_runtime_dependency 'amatch', ['>= 0.3.0']
|
14
|
+
s.add_runtime_dependency 'engtagger', ['>=0.2.0']
|
14
15
|
s.add_development_dependency 'aruba', ['>= 0.6.2']
|
15
16
|
end
|
data/lib/gherkin_lint.rb
CHANGED
@@ -8,6 +8,7 @@ require 'gherkin_lint/linter/avoid_scripting'
|
|
8
8
|
require 'gherkin_lint/linter/background_does_more_than_setup'
|
9
9
|
require 'gherkin_lint/linter/background_requires_multiple_scenarios'
|
10
10
|
require 'gherkin_lint/linter/bad_scenario_name'
|
11
|
+
require 'gherkin_lint/linter/be_declarative'
|
11
12
|
require 'gherkin_lint/linter/file_name_differs_feature_name'
|
12
13
|
require 'gherkin_lint/linter/invalid_file_name'
|
13
14
|
require 'gherkin_lint/linter/invalid_step_flow'
|
@@ -36,12 +37,13 @@ module GherkinLint
|
|
36
37
|
# gherkin linter
|
37
38
|
class GherkinLint
|
38
39
|
LINTER = [
|
40
|
+
AvoidOutlineForSingleExample,
|
39
41
|
AvoidPeriod,
|
40
42
|
AvoidScripting,
|
41
|
-
AvoidOutlineForSingleExample,
|
42
43
|
BackgroundDoesMoreThanSetup,
|
43
44
|
BackgroundRequiresMultipleScenarios,
|
44
45
|
BadScenarioName,
|
46
|
+
BeDeclarative,
|
45
47
|
FileNameDiffersFeatureName,
|
46
48
|
MissingExampleName,
|
47
49
|
MissingFeatureDescription,
|
@@ -106,14 +108,18 @@ module GherkinLint
|
|
106
108
|
|
107
109
|
def report
|
108
110
|
issues = @linter.map do |linter|
|
109
|
-
|
110
|
-
linter.lint_files(@files, tags_to_suppress)
|
111
|
+
linter.lint_files(@files, disable_tags)
|
111
112
|
linter.issues
|
112
113
|
end.flatten
|
113
114
|
|
114
115
|
issues.each { |issue| puts issue.render }
|
115
116
|
|
116
|
-
issues.
|
117
|
+
return 0 if issues.select { |issue| issue.class == Error }.empty?
|
118
|
+
-1
|
119
|
+
end
|
120
|
+
|
121
|
+
def disable_tags
|
122
|
+
LINTER.map { |lint| "disable#{lint.new.class.name.split('::').last}" }
|
117
123
|
end
|
118
124
|
|
119
125
|
def to_json(input, file = 'generated.feature')
|
data/lib/gherkin_lint/issue.rb
CHANGED
@@ -11,7 +11,10 @@ module GherkinLint
|
|
11
11
|
@references = references
|
12
12
|
@description = description
|
13
13
|
end
|
14
|
+
end
|
14
15
|
|
16
|
+
# entity value class for errors
|
17
|
+
class Error < Issue
|
15
18
|
def render
|
16
19
|
result = red(@name)
|
17
20
|
result += " - #{@description}" unless @description.nil?
|
@@ -19,4 +22,14 @@ module GherkinLint
|
|
19
22
|
result
|
20
23
|
end
|
21
24
|
end
|
25
|
+
|
26
|
+
# entity value class for warnings
|
27
|
+
class Warning < Issue
|
28
|
+
def render
|
29
|
+
result = "#{yellow(@name)} (Warning)"
|
30
|
+
result += " - #{@description}" unless @description.nil?
|
31
|
+
result += "\n " + green(@references.uniq * "\n ")
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
22
35
|
end
|
data/lib/gherkin_lint/linter.rb
CHANGED
@@ -73,12 +73,6 @@ module GherkinLint
|
|
73
73
|
lint
|
74
74
|
end
|
75
75
|
|
76
|
-
def tag?(data, tag)
|
77
|
-
return false if data.class != Hash
|
78
|
-
return false unless data.key? 'tags'
|
79
|
-
data['tags'].map { |item| item['name'] }.include? "@#{tag}"
|
80
|
-
end
|
81
|
-
|
82
76
|
def filter_tag(data, tag)
|
83
77
|
return data.select { |item| !tag?(item, tag) }.map { |item| filter_tag(item, tag) } if data.class == Array
|
84
78
|
return data unless data.class == Hash
|
@@ -88,8 +82,10 @@ module GherkinLint
|
|
88
82
|
result
|
89
83
|
end
|
90
84
|
|
91
|
-
def
|
92
|
-
|
85
|
+
def tag?(data, tag)
|
86
|
+
return false if data.class != Hash
|
87
|
+
return false unless data.key? 'tags'
|
88
|
+
data['tags'].map { |item| item['name'] }.include? "@#{tag}"
|
93
89
|
end
|
94
90
|
|
95
91
|
def suppress_tags(data, tags)
|
@@ -105,8 +101,12 @@ module GherkinLint
|
|
105
101
|
result
|
106
102
|
end
|
107
103
|
|
104
|
+
def suppress(data, tags)
|
105
|
+
data.select { |item| !tags.map { |tag| "@#{tag}" }.include? item['name'] }
|
106
|
+
end
|
107
|
+
|
108
108
|
def lint
|
109
|
-
|
109
|
+
fail 'not implemented'
|
110
110
|
end
|
111
111
|
|
112
112
|
def reference(file, feature = nil, scenario = nil, step = nil)
|
@@ -124,8 +124,12 @@ module GherkinLint
|
|
124
124
|
line
|
125
125
|
end
|
126
126
|
|
127
|
-
def
|
128
|
-
@issues.push
|
127
|
+
def add_error(references, description = nil)
|
128
|
+
@issues.push Error.new(name, references, description)
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_warning(references, description = nil)
|
132
|
+
@issues.push Warning.new(name, references, description)
|
129
133
|
end
|
130
134
|
|
131
135
|
def gather_tags(element)
|
@@ -9,7 +9,7 @@ module GherkinLint
|
|
9
9
|
invalid_steps = background['steps'].select { |step| step['keyword'] == 'When ' || step['keyword'] == 'Then ' }
|
10
10
|
next if invalid_steps.empty?
|
11
11
|
references = [reference(file, feature, background, invalid_steps[0])]
|
12
|
-
|
12
|
+
add_error(references, 'Just Given Steps allowed')
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -9,7 +9,7 @@ module GherkinLint
|
|
9
9
|
next if scenarios.length >= 2
|
10
10
|
|
11
11
|
references = [reference(file, feature, background)]
|
12
|
-
|
12
|
+
add_error(references, "There are just #{scenarios.length} scenarios")
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -10,7 +10,7 @@ module GherkinLint
|
|
10
10
|
description = 'Prefer to rely just on Given and When steps when name your scenario to keep it stable'
|
11
11
|
bad_words = %w(test verif check)
|
12
12
|
bad_words.each do |bad_word|
|
13
|
-
|
13
|
+
add_error(references, description) if scenario['name'].downcase.include? bad_word
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'gherkin_lint/linter'
|
2
|
+
require 'engtagger'
|
3
|
+
|
4
|
+
module GherkinLint
|
5
|
+
# service class to lint for avoiding periods
|
6
|
+
class BeDeclarative < Linter
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def lint
|
12
|
+
filled_scenarios do |file, feature, scenario|
|
13
|
+
scenario['steps'].each do |step|
|
14
|
+
references = [reference(file, feature, scenario, step)]
|
15
|
+
add_warning(references, 'no verb') unless verb? step
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def verb?(step)
|
21
|
+
tagged = tagger.add_tags step['name']
|
22
|
+
step_verbs = verbs tagged
|
23
|
+
|
24
|
+
!step_verbs.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def verbs(tagged_text)
|
28
|
+
verbs = [
|
29
|
+
:get_infinitive_verbs,
|
30
|
+
:get_past_tense_verbs,
|
31
|
+
:get_gerund_verbs,
|
32
|
+
:get_passive_verbs,
|
33
|
+
:get_present_verbs,
|
34
|
+
:get_base_present_verbs
|
35
|
+
]
|
36
|
+
verbs.map { |verb| tagger.send(verb, tagged_text).keys }.flatten
|
37
|
+
end
|
38
|
+
|
39
|
+
def tagger
|
40
|
+
@tagger = EngTagger.new unless instance_variable_defined? :@tagger
|
41
|
+
|
42
|
+
@tagger
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -9,7 +9,7 @@ module GherkinLint
|
|
9
9
|
expected_feature_name = title_case file
|
10
10
|
next if feature['name'].casecmp(expected_feature_name) == 0
|
11
11
|
references = [reference(file, feature)]
|
12
|
-
|
12
|
+
add_error(references, "Feature name should be '#{expected_feature_name}'")
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|