gherkin_lint 0.1.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|