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.
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