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.
Files changed (41) hide show
  1. data/.rubocop.yml +1 -1
  2. data/Gemfile +1 -0
  3. data/README.md +42 -631
  4. data/Rakefile +6 -2
  5. data/features/avoid_scripting.feature +2 -0
  6. data/features/be_declarative.feature +53 -0
  7. data/features/missing_example_name.feature +24 -0
  8. data/features/same_tag_for_all_scenarios.feature +41 -1
  9. data/gherkin_lint.gemspec +3 -2
  10. data/lib/gherkin_lint.rb +10 -4
  11. data/lib/gherkin_lint/issue.rb +13 -0
  12. data/lib/gherkin_lint/linter.rb +15 -11
  13. data/lib/gherkin_lint/linter/avoid_outline_for_single_example.rb +1 -1
  14. data/lib/gherkin_lint/linter/avoid_period.rb +1 -1
  15. data/lib/gherkin_lint/linter/avoid_scripting.rb +1 -1
  16. data/lib/gherkin_lint/linter/background_does_more_than_setup.rb +1 -1
  17. data/lib/gherkin_lint/linter/background_requires_multiple_scenarios.rb +1 -1
  18. data/lib/gherkin_lint/linter/bad_scenario_name.rb +1 -1
  19. data/lib/gherkin_lint/linter/be_declarative.rb +45 -0
  20. data/lib/gherkin_lint/linter/file_name_differs_feature_name.rb +1 -1
  21. data/lib/gherkin_lint/linter/invalid_file_name.rb +1 -1
  22. data/lib/gherkin_lint/linter/invalid_step_flow.rb +3 -3
  23. data/lib/gherkin_lint/linter/missing_example_name.rb +2 -1
  24. data/lib/gherkin_lint/linter/missing_feature_description.rb +1 -1
  25. data/lib/gherkin_lint/linter/missing_feature_name.rb +1 -1
  26. data/lib/gherkin_lint/linter/missing_scenario_name.rb +1 -1
  27. data/lib/gherkin_lint/linter/missing_test_action.rb +1 -1
  28. data/lib/gherkin_lint/linter/missing_verification.rb +1 -1
  29. data/lib/gherkin_lint/linter/same_tag_for_all_scenarios.rb +39 -6
  30. data/lib/gherkin_lint/linter/too_clumsy.rb +1 -1
  31. data/lib/gherkin_lint/linter/too_long_step.rb +1 -1
  32. data/lib/gherkin_lint/linter/too_many_different_tags.rb +2 -2
  33. data/lib/gherkin_lint/linter/too_many_steps.rb +1 -1
  34. data/lib/gherkin_lint/linter/too_many_tags.rb +1 -1
  35. data/lib/gherkin_lint/linter/unique_scenario_names.rb +1 -1
  36. data/lib/gherkin_lint/linter/unknown_variable.rb +1 -1
  37. data/lib/gherkin_lint/linter/unused_variable.rb +1 -1
  38. data/lib/gherkin_lint/linter/use_background.rb +1 -1
  39. data/lib/gherkin_lint/linter/use_outline.rb +1 -1
  40. metadata +43 -15
  41. 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.1.2.gem'
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
- sh 'RUBYLIB=lib ./bin/gherkin_lint --disable UnknownVariable,BadScenarioName features/*.feature'
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: Many Tags
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.1.2'
4
- s.date = '2016-04-17'
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
- tags_to_suppress = LINTER.map { |lint| "disable#{lint.new.class.name.split('::').last}" }
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.empty? ? 0 : -1
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')
@@ -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
@@ -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 suppress(data, tags)
92
- data.select { |item| !tags.map { |tag| "@#{tag}" }.include? item['name'] }
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
- raise 'not implemented'
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 add_issue(references, description = nil)
128
- @issues.push Issue.new(name, references, description)
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)
@@ -12,7 +12,7 @@ module GherkinLint
12
12
  next if scenario['examples'].first['rows'].length > 2
13
13
 
14
14
  references = [reference(file, feature, scenario)]
15
- add_issue(references, 'Better write a scenario')
15
+ add_error(references, 'Better write a scenario')
16
16
  end
17
17
  end
18
18
  end
@@ -9,7 +9,7 @@ module GherkinLint
9
9
 
10
10
  scenario['steps'].each do |step|
11
11
  references = [reference(file, feature, scenario, step)]
12
- add_issue(references) if step['name'].strip.end_with? '.'
12
+ add_error(references) if step['name'].strip.end_with? '.'
13
13
  end
14
14
  end
15
15
  end
@@ -9,7 +9,7 @@ module GherkinLint
9
9
 
10
10
  next if steps.length <= 1
11
11
  references = [reference(file, feature, scenario)]
12
- add_issue(references, 'Multiple Actions')
12
+ add_error(references, 'Multiple Actions')
13
13
  end
14
14
  end
15
15
 
@@ -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
- add_issue(references, 'Just Given Steps allowed')
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
- add_issue(references, "There are just #{scenarios.length} scenarios")
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
- add_issue(references, description) if scenario['name'].downcase.include? bad_word
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
- add_issue(references, "Feature name should be '#{expected_feature_name}'")
12
+ add_error(references, "Feature name should be '#{expected_feature_name}'")
13
13
  end
14
14
  end
15
15