chutney 2.0.3 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/.rufo +1 -0
  4. data/Gemfile +2 -0
  5. data/README.md +2 -1
  6. data/Rakefile +2 -0
  7. data/chutney.gemspec +10 -5
  8. data/config/chutney.yml +4 -0
  9. data/config/cucumber.yml +1 -0
  10. data/docs/usage/rules.md +7 -1
  11. data/exe/chutney +2 -0
  12. data/lib/chutney.rb +19 -19
  13. data/lib/chutney/configuration.rb +2 -0
  14. data/lib/chutney/formatter.rb +2 -0
  15. data/lib/chutney/formatter/json_formatter.rb +2 -0
  16. data/lib/chutney/formatter/pie_formatter.rb +2 -0
  17. data/lib/chutney/formatter/rainbow_formatter.rb +2 -0
  18. data/lib/chutney/issue.rb +2 -0
  19. data/lib/chutney/linter.rb +65 -61
  20. data/lib/chutney/linter/avoid_full_stop.rb +3 -1
  21. data/lib/chutney/linter/avoid_outline_for_single_example.rb +8 -6
  22. data/lib/chutney/linter/avoid_scripting.rb +6 -4
  23. data/lib/chutney/linter/avoid_typographers_quotes.rb +39 -0
  24. data/lib/chutney/linter/background_does_more_than_setup.rb +8 -6
  25. data/lib/chutney/linter/background_requires_multiple_scenarios.rb +6 -3
  26. data/lib/chutney/linter/bad_scenario_name.rb +4 -2
  27. data/lib/chutney/linter/empty_feature_file.rb +10 -0
  28. data/lib/chutney/linter/file_name_differs_feature_name.rb +4 -2
  29. data/lib/chutney/linter/givens_after_background.rb +5 -6
  30. data/lib/chutney/linter/invalid_file_name.rb +2 -0
  31. data/lib/chutney/linter/invalid_step_flow.rb +7 -7
  32. data/lib/chutney/linter/missing_example_name.rb +7 -5
  33. data/lib/chutney/linter/missing_feature_description.rb +6 -3
  34. data/lib/chutney/linter/missing_feature_name.rb +5 -2
  35. data/lib/chutney/linter/missing_scenario_name.rb +3 -4
  36. data/lib/chutney/linter/missing_test_action.rb +3 -1
  37. data/lib/chutney/linter/missing_verification.rb +3 -1
  38. data/lib/chutney/linter/required_tags_starts_with.rb +2 -0
  39. data/lib/chutney/linter/same_tag_for_all_scenarios.rb +11 -10
  40. data/lib/chutney/linter/scenario_names_match.rb +4 -3
  41. data/lib/chutney/linter/tag_used_multiple_times.rb +2 -0
  42. data/lib/chutney/linter/too_clumsy.rb +3 -1
  43. data/lib/chutney/linter/too_long_step.rb +4 -2
  44. data/lib/chutney/linter/too_many_different_tags.rb +4 -2
  45. data/lib/chutney/linter/too_many_steps.rb +4 -2
  46. data/lib/chutney/linter/too_many_tags.rb +2 -0
  47. data/lib/chutney/linter/unique_scenario_names.rb +3 -3
  48. data/lib/chutney/linter/unknown_variable.rb +13 -13
  49. data/lib/chutney/linter/unused_variable.rb +13 -13
  50. data/lib/chutney/linter/use_background.rb +17 -16
  51. data/lib/chutney/linter/use_outline.rb +8 -7
  52. data/lib/chutney/version.rb +3 -1
  53. data/lib/config/locales/en.yml +6 -0
  54. data/spec/chutney_spec.rb +2 -0
  55. data/spec/spec_helper.rb +2 -0
  56. metadata +47 -15
@@ -1,15 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'chutney/linter'
2
4
 
3
5
  module Chutney
4
6
  # service class to lint for tags used multiple times
5
7
  class ScenarioNamesMatch < Linter
6
- MESSAGE = 'Scenario Name does not match pattern'.freeze
8
+ MESSAGE = 'Scenario Name does not match pattern'
7
9
 
8
10
 
9
11
  def lint
10
12
  scenarios do |feature, scenario|
11
- name = scenario.key?(:name) ? scenario[:name].strip : ''
12
- next unless (name =~ /#{configuration['Matcher']}/).nil?
13
+ next unless (scenario.name =~ /#{configuration['Matcher']}/).nil?
13
14
 
14
15
  add_issue(
15
16
  I18n.t('linters.scenario_names_match',
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for tags used multiple times
3
5
  class TagUsedMultipleTimes < Linter
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'chutney/linter'
2
4
 
3
5
  module Chutney
@@ -5,7 +7,7 @@ module Chutney
5
7
  class TooClumsy < Linter
6
8
  def lint
7
9
  filled_scenarios do |feature, scenario|
8
- characters = scenario[:steps].map { |step| step[:text].length }.inject(0, :+)
10
+ characters = scenario.steps.map { |step| step.text.length }.inject(0, :+)
9
11
  next if characters < 400
10
12
 
11
13
  add_issue(
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for too long steps
3
5
  class TooLongStep < Linter
4
6
  def lint
5
7
  steps do |feature, scenario, step|
6
- next if step[:text].length <= maxlength
8
+ next if step.text.length <= maxlength
7
9
 
8
10
  add_issue(
9
- I18n.t('linters.too_long_step', length: step[:text].length, max: maxlength),
11
+ I18n.t('linters.too_long_step', length: step.text.length, max: maxlength),
10
12
  feature, scenario
11
13
  )
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for too many different tags
3
5
  class TooManyDifferentTags < Linter
@@ -16,9 +18,9 @@ module Chutney
16
18
  end
17
19
 
18
20
  def all_tags
19
- return [] unless feature.include?(:children)
21
+ return [] unless feature&.scenarios
20
22
 
21
- tags_for(feature) + feature[:children].map { |scenario| tags_for(scenario) }.flatten
23
+ tags_for(feature) + feature.scenarios.map { |scenario| tags_for(scenario) }.flatten
22
24
  end
23
25
  end
24
26
  end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for too many steps
3
5
  class TooManySteps < Linter
4
6
  def lint
5
7
  filled_scenarios do |feature, scenario|
6
- next if scenario[:steps].length <= maxcount
8
+ next if scenario.steps.length <= maxcount
7
9
 
8
10
  add_issue(
9
- I18n.t('linters.too_many_steps', count: scenario[:steps].length, max: maxcount),
11
+ I18n.t('linters.too_many_steps', count: scenario.steps.length, max: maxcount),
10
12
  feature
11
13
  )
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for too many tags
3
5
  class TooManyTags < Linter
@@ -1,12 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for unique scenario names
3
5
  class UniqueScenarioNames < Linter
4
6
  def lint
5
7
  references_by_name = {}
6
8
  scenarios do |feature, scenario|
7
- next unless scenario.key? :name
8
-
9
- name = scenario[:name]
9
+ name = scenario.name
10
10
  if references_by_name[name]
11
11
  issue(name, references_by_name[name], scenario)
12
12
  else
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for unknown variables
3
5
  class UnknownVariable < Linter
4
6
  def lint
5
7
  filled_scenarios do |feature, scenario|
6
8
  known_vars = Set.new(known_variables(scenario))
7
- scenario[:steps].each do |step|
9
+ scenario.steps.each do |step|
8
10
  step_vars(step).each do |used_var|
9
11
  next if known_vars.include? used_var
10
12
 
@@ -17,17 +19,17 @@ module Chutney
17
19
  end
18
20
 
19
21
  def step_vars(step)
20
- vars = gather_vars step[:text]
21
- return vars unless step.include? :argument
22
+ vars = gather_vars step.text
23
+ return vars unless step.block
22
24
 
23
- vars + gather_vars_from_argument(step[:argument])
25
+ vars + gather_vars_from_argument(step.block)
24
26
  end
25
27
 
26
28
  def gather_vars_from_argument(argument)
27
- return gather_vars argument[:content] if argument[:type] == :DocString
28
-
29
- (argument[:rows] || []).map do |row|
30
- row[:cells].map { |value| gather_vars value[:value] }.flatten
29
+ return gather_vars argument.content if argument.is_a? CukeModeler::DocString
30
+
31
+ argument.rows.map do |row|
32
+ row.cells.map { |cell| gather_vars cell.value }.flatten
31
33
  end.flatten
32
34
  end
33
35
 
@@ -36,11 +38,9 @@ module Chutney
36
38
  end
37
39
 
38
40
  def known_variables(scenario)
39
- (scenario[:examples] || []).map do |example|
40
- next unless example.key? :tableHeader
41
-
42
- example[:tableHeader][:cells].map { |cell| cell[:value].strip }
43
- end.flatten
41
+ return [] unless scenario.is_a? CukeModeler::Outline
42
+
43
+ scenario.examples.map { |ex| ex.rows.first.cells.map(&:value) }.flatten
44
44
  end
45
45
  end
46
46
  end
@@ -1,14 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for unused variables
3
5
  class UnusedVariable < Linter
4
6
  def lint
5
7
  scenarios do |feature, scenario|
6
- next unless scenario.key?(:examples)
7
-
8
- scenario[:examples].each do |example|
9
- next unless example.key?(:tableHeader)
8
+ next unless scenario.is_a? CukeModeler::Outline
9
+
10
+ scenario.examples.each do |example|
10
11
 
11
- example[:tableHeader][:cells].map { |cell| cell[:value] }.each do |variable|
12
+ example.rows.first.cells.map(&:value).each do |variable|
12
13
  next if used?(variable, scenario)
13
14
 
14
15
  add_issue(I18n.t('linters.unused_variable', variable: variable), feature, scenario, example)
@@ -19,11 +20,10 @@ module Chutney
19
20
 
20
21
  def used?(variable, scenario)
21
22
  variable = "<#{variable}>"
22
- return false unless scenario.key? :steps
23
23
 
24
- scenario[:steps].each do |step|
25
- return true if step[:text].include? variable
26
- next unless step.include? :argument
24
+ scenario.steps.each do |step|
25
+ return true if step.text.include? variable
26
+ next unless step.block
27
27
  return true if used_in_docstring?(variable, step)
28
28
  return true if used_in_table?(variable, step)
29
29
  end
@@ -31,14 +31,14 @@ module Chutney
31
31
  end
32
32
 
33
33
  def used_in_docstring?(variable, step)
34
- step[:argument][:type] == :DocString && step[:argument][:content].include?(variable)
34
+ step.block.is_a?(CukeModeler::DocString) && step.block.content.include?(variable)
35
35
  end
36
36
 
37
37
  def used_in_table?(variable, step)
38
- return false unless step[:argument][:type] == :DataTable
38
+ return false unless step.block.is_a?(CukeModeler::Table)
39
39
 
40
- step[:argument][:rows].each do |row|
41
- row[:cells].each { |value| return true if value[:value].include?(variable) }
40
+ step.block.rows.each do |row|
41
+ row.cells.each { |cell| return true if cell.value.include?(variable) }
42
42
  end
43
43
  false
44
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for using background
3
5
  class UseBackground < Linter
@@ -13,14 +15,13 @@ module Chutney
13
15
  end
14
16
 
15
17
  def gather_givens
16
- return unless feature.include? :children
18
+ return unless feature.children
17
19
 
18
20
  has_non_given_step = false
19
- feature[:children].each do |scenario|
20
- next unless scenario.include? :steps
21
- next if scenario[:steps].empty?
21
+ scenarios do |_feature, scenario|
22
+ next unless scenario.steps
22
23
 
23
- has_non_given_step = true unless given_word?(scenario[:steps].first[:keyword])
24
+ has_non_given_step = true unless given_word?(scenario.steps.first.keyword)
24
25
  end
25
26
  return if has_non_given_step
26
27
 
@@ -29,15 +30,13 @@ module Chutney
29
30
  result
30
31
  end
31
32
 
32
- def expanded_steps
33
- feature[:children].each do |scenario|
34
- next unless scenario[:type] != :Background
35
- next unless scenario.include? :steps
36
- next if scenario[:steps].empty?
33
+ def expanded_steps(&block)
34
+ scenarios do |_feature, scenario|
35
+ next unless scenario.steps
37
36
 
38
- prototypes = [render_step(scenario[:steps].first)]
39
- prototypes = expand_examples(scenario[:examples], prototypes) if scenario.key? :examples
40
- prototypes.each { |prototype| yield prototype }
37
+ prototypes = [render_step(scenario.steps.first)]
38
+ prototypes = expand_examples(scenario.examples, prototypes) if scenario.is_a? CukeModeler::Outline
39
+ prototypes.each(&block)
41
40
  end
42
41
  end
43
42
 
@@ -50,10 +49,12 @@ module Chutney
50
49
 
51
50
  def expand_outlines(sentence, example)
52
51
  result = []
53
- headers = example[:tableHeader][:cells].map { |cell| cell[:value] }
54
- example[:tableBody].each do |row| # .slice(1, example[:tableBody].length).each do |row|
52
+ headers = example.rows.first.cells.map(&:value)
53
+ example.rows.each_with_index do |row, idx|
54
+ next if idx.zero? # skip the header
55
+
55
56
  modified_sentence = sentence.dup
56
- headers.zip(row[:cells].map { |cell| cell[:value] }).map do |key, value|
57
+ headers.zip(row.cells.map(&:value)).map do |key, value|
57
58
  modified_sentence.gsub!("<#{key}>", value)
58
59
  end
59
60
  result.push modified_sentence
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
4
  # service class to lint for using outline
3
5
  class UseOutline < Linter
@@ -36,12 +38,11 @@ module Chutney
36
38
 
37
39
  def gather_scenarios(feature)
38
40
  scenarios = []
39
- return scenarios unless feature.include? :children
41
+ return scenarios if feature.nil? || !feature.tests
40
42
 
41
- feature[:children].each do |scenario|
42
- next unless scenario[:type] == :Scenario
43
- next unless scenario.include? :steps
44
- next if scenario[:steps].empty?
43
+ scenarios do |_feature, scenario|
44
+ next unless scenario.steps
45
+ next if scenario.steps.empty?
45
46
 
46
47
  scenarios.push generate_reference(feature, scenario)
47
48
  end
@@ -51,8 +52,8 @@ module Chutney
51
52
  def generate_reference(feature, scenario)
52
53
  reference = {}
53
54
  reference[:reference] = location(feature, scenario, nil)
54
- reference[:name] = "#{scenario[:keyword]}: #{scenario[:name]}"
55
- reference[:text] = scenario[:steps].map { |step| render_step(step) }.join ' '
55
+ reference[:name] = "#{scenario.keyword}: #{scenario.name}"
56
+ reference[:text] = scenario.steps.map { |step| render_step(step) }.join ' '
56
57
  reference
57
58
  end
58
59
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Chutney
2
- VERSION = '2.0.3'.freeze
4
+ VERSION = '3.0.0.beta.1'
3
5
  end
@@ -11,12 +11,18 @@ en:
11
11
  avoid_scripting: >-
12
12
  You have %{count} When steps when you should only have one.
13
13
  Be careful not to add And steps following a When.
14
+ avoid_typographers_quotes: >-
15
+ You are using typographers quotation marks (curly quotes).
16
+ Make sure you intend to use this character and not a neutral quote
17
+ (straight quote) instead.
14
18
  background_does_more_than_setup: >-
15
19
  The Background does more than setup. It should only contain Given steps.
16
20
  background_requires_multiple_scenarios: >-
17
21
  Avoid using Background if you only have a single scenario.
18
22
  bad_scenario_name: >-
19
23
  You should avoid using words like '%{word}' in your scenario names.
24
+ empty_feature_file: >-
25
+ The feature file is empty
20
26
  file_name_differs_feature_name: >-
21
27
  The name of the feature should reflect the file name. Consider renaming this feature
22
28
  to '%{expected}'.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'chutney'
2
4
 
3
5
  describe Chutney::ChutneyLint do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'simplecov'
2
4
  require 'coveralls'
3
5
  # Coveralls.wear!
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chutney
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 3.0.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nigel Brookes-Thomas
8
8
  - Stefan Rohe
9
9
  - Nishtha Argawal
10
10
  - John Gluck
11
- autorequire:
11
+ autorequire:
12
12
  bindir: exe
13
13
  cert_chain: []
14
- date: 2020-04-28 00:00:00.000000000 Z
14
+ date: 2020-09-02 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: amatch
@@ -27,13 +27,27 @@ dependencies:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
29
  version: 0.4.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: cuke_modeler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '3.3'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '3.3'
30
44
  - !ruby/object:Gem::Dependency
31
45
  name: gherkin
32
46
  requirement: !ruby/object:Gem::Requirement
33
47
  requirements:
34
48
  - - ">="
35
49
  - !ruby/object:Gem::Version
36
- version: '5.1'
50
+ version: 5.1.0
37
51
  - - "<"
38
52
  - !ruby/object:Gem::Version
39
53
  version: '9.1'
@@ -43,7 +57,7 @@ dependencies:
43
57
  requirements:
44
58
  - - ">="
45
59
  - !ruby/object:Gem::Version
46
- version: '5.1'
60
+ version: 5.1.0
47
61
  - - "<"
48
62
  - !ruby/object:Gem::Version
49
63
  version: '9.1'
@@ -105,6 +119,20 @@ dependencies:
105
119
  version: '0.8'
106
120
  - !ruby/object:Gem::Dependency
107
121
  name: cucumber
122
+ requirement: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - "~>"
125
+ - !ruby/object:Gem::Version
126
+ version: '5.1'
127
+ type: :development
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '5.1'
134
+ - !ruby/object:Gem::Dependency
135
+ name: pry-byebug
108
136
  requirement: !ruby/object:Gem::Requirement
109
137
  requirements:
110
138
  - - "~>"
@@ -165,14 +193,14 @@ dependencies:
165
193
  requirements:
166
194
  - - "~>"
167
195
  - !ruby/object:Gem::Version
168
- version: 0.82.0
196
+ version: 0.89.0
169
197
  type: :development
170
198
  prerelease: false
171
199
  version_requirements: !ruby/object:Gem::Requirement
172
200
  requirements:
173
201
  - - "~>"
174
202
  - !ruby/object:Gem::Version
175
- version: 0.82.0
203
+ version: 0.89.0
176
204
  - !ruby/object:Gem::Dependency
177
205
  name: rspec
178
206
  requirement: !ruby/object:Gem::Requirement
@@ -188,7 +216,7 @@ dependencies:
188
216
  - !ruby/object:Gem::Version
189
217
  version: '3.8'
190
218
  description: A linter for your Cucumber features. It supports any spoken language
191
- Cucumber v3 supports.
219
+ Cucumber supports.
192
220
  email:
193
221
  - nigel@brookes-thomas.co.uk
194
222
  executables:
@@ -201,12 +229,14 @@ files:
201
229
  - ".gitignore"
202
230
  - ".rspec"
203
231
  - ".rubocop.yml"
232
+ - ".rufo"
204
233
  - Gemfile
205
234
  - LICENSE.txt
206
235
  - README.md
207
236
  - Rakefile
208
237
  - chutney.gemspec
209
238
  - config/chutney.yml
239
+ - config/cucumber.yml
210
240
  - docs/.keep
211
241
  - docs/_config.yml
212
242
  - docs/credits.md
@@ -234,9 +264,11 @@ files:
234
264
  - lib/chutney/linter/avoid_full_stop.rb
235
265
  - lib/chutney/linter/avoid_outline_for_single_example.rb
236
266
  - lib/chutney/linter/avoid_scripting.rb
267
+ - lib/chutney/linter/avoid_typographers_quotes.rb
237
268
  - lib/chutney/linter/background_does_more_than_setup.rb
238
269
  - lib/chutney/linter/background_requires_multiple_scenarios.rb
239
270
  - lib/chutney/linter/bad_scenario_name.rb
271
+ - lib/chutney/linter/empty_feature_file.rb
240
272
  - lib/chutney/linter/file_name_differs_feature_name.rb
241
273
  - lib/chutney/linter/givens_after_background.rb
242
274
  - lib/chutney/linter/invalid_file_name.rb
@@ -272,23 +304,23 @@ metadata:
272
304
  homepage_uri: https://billyruffian.github.io/chutney/
273
305
  source_code_uri: https://github.com/BillyRuffian/chutney
274
306
  changelog_uri: https://github.com/BillyRuffian/chutney/releases
275
- post_install_message:
307
+ post_install_message:
276
308
  rdoc_options: []
277
309
  require_paths:
278
310
  - lib
279
311
  required_ruby_version: !ruby/object:Gem::Requirement
280
312
  requirements:
281
- - - ">="
313
+ - - "~>"
282
314
  - !ruby/object:Gem::Version
283
- version: '0'
315
+ version: '2.6'
284
316
  required_rubygems_version: !ruby/object:Gem::Requirement
285
317
  requirements:
286
- - - ">="
318
+ - - ">"
287
319
  - !ruby/object:Gem::Version
288
- version: '0'
320
+ version: 1.3.1
289
321
  requirements: []
290
322
  rubygems_version: 3.1.2
291
- signing_key:
323
+ signing_key:
292
324
  specification_version: 4
293
- summary: A linter for English language Gherkin
325
+ summary: A linter for multi-lingual Gherkin
294
326
  test_files: []