rubocop-rspec 1.37.1 → 1.41.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/README.md +2 -62
  5. data/config/default.yml +155 -15
  6. data/lib/rubocop-rspec.rb +3 -1
  7. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +69 -0
  8. data/lib/rubocop/cop/rspec/context_wording.rb +5 -1
  9. data/lib/rubocop/cop/rspec/cop.rb +9 -29
  10. data/lib/rubocop/cop/rspec/describe_class.rb +20 -5
  11. data/lib/rubocop/cop/rspec/describe_method.rb +0 -1
  12. data/lib/rubocop/cop/rspec/empty_hook.rb +50 -0
  13. data/lib/rubocop/cop/rspec/expect_actual.rb +27 -2
  14. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +5 -2
  15. data/lib/rubocop/cop/rspec/file_path.rb +32 -4
  16. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +3 -21
  17. data/lib/rubocop/cop/rspec/instance_variable.rb +16 -11
  18. data/lib/rubocop/cop/rspec/leading_subject.rb +3 -10
  19. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -4
  20. data/lib/rubocop/cop/rspec/let_before_examples.rb +3 -21
  21. data/lib/rubocop/cop/rspec/let_setup.rb +15 -3
  22. data/lib/rubocop/cop/rspec/named_subject.rb +6 -6
  23. data/lib/rubocop/cop/rspec/nested_groups.rb +9 -10
  24. data/lib/rubocop/cop/rspec/predicate_matcher.rb +2 -2
  25. data/lib/rubocop/cop/rspec/rails/http_status.rb +2 -0
  26. data/lib/rubocop/cop/rspec/repeated_description.rb +17 -2
  27. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +97 -0
  28. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +96 -0
  29. data/lib/rubocop/cop/rspec/return_from_stub.rb +3 -2
  30. data/lib/rubocop/cop/rspec/scattered_let.rb +13 -0
  31. data/lib/rubocop/cop/rspec/scattered_setup.rb +27 -9
  32. data/lib/rubocop/cop/rspec/shared_examples.rb +1 -0
  33. data/lib/rubocop/cop/rspec/subject_stub.rb +23 -51
  34. data/lib/rubocop/cop/rspec/variable_definition.rb +56 -0
  35. data/lib/rubocop/cop/rspec/variable_name.rb +47 -0
  36. data/lib/rubocop/cop/rspec_cops.rb +7 -1
  37. data/lib/rubocop/rspec/corrector/move_node.rb +52 -0
  38. data/lib/rubocop/rspec/description_extractor.rb +2 -6
  39. data/lib/rubocop/rspec/example.rb +1 -1
  40. data/lib/rubocop/rspec/example_group.rb +21 -49
  41. data/lib/rubocop/rspec/factory_bot.rb +7 -1
  42. data/lib/rubocop/rspec/hook.rb +44 -11
  43. data/lib/rubocop/rspec/language.rb +20 -0
  44. data/lib/rubocop/rspec/language/node_pattern.rb +5 -1
  45. data/lib/rubocop/rspec/top_level_group.rb +44 -0
  46. data/lib/rubocop/rspec/variable.rb +16 -0
  47. data/lib/rubocop/rspec/version.rb +1 -1
  48. metadata +20 -11
  49. data/lib/rubocop/rspec/util.rb +0 -19
@@ -14,72 +14,44 @@ module RuboCop
14
14
  ExampleGroups::ALL + SharedGroups::ALL + Includes::ALL
15
15
  ).block_pattern
16
16
 
17
+ def lets
18
+ find_all_in_scope(node, :let?)
19
+ end
20
+
17
21
  def subjects
18
- subjects_in_scope(node)
22
+ find_all_in_scope(node, :subject?)
19
23
  end
20
24
 
21
25
  def examples
22
- examples_in_scope(node).map(&Example.public_method(:new))
26
+ find_all_in_scope(node, :example?).map(&Example.public_method(:new))
23
27
  end
24
28
 
25
29
  def hooks
26
- hooks_in_scope(node).map(&Hook.public_method(:new))
30
+ find_all_in_scope(node, :hook?).map(&Hook.public_method(:new))
27
31
  end
28
32
 
29
33
  private
30
34
 
31
- def subjects_in_scope(node)
32
- node.each_child_node.flat_map do |child|
33
- find_subjects(child)
34
- end
35
- end
36
-
37
- def find_subjects(node)
38
- return [] if scope_change?(node)
39
-
40
- if subject?(node)
41
- [node]
42
- else
43
- subjects_in_scope(node)
44
- end
45
- end
46
-
47
- def hooks_in_scope(node)
48
- node.each_child_node.flat_map do |child|
49
- find_hooks(child)
50
- end
51
- end
52
-
53
- def find_hooks(node)
54
- return [] if scope_change?(node) || example?(node)
55
-
56
- if hook?(node)
57
- [node]
58
- else
59
- hooks_in_scope(node)
60
- end
61
- end
62
-
63
- def examples_in_scope(node, &blk)
64
- node.each_child_node.flat_map do |child|
65
- find_examples(child, &blk)
66
- end
67
- end
68
-
69
- # Recursively search for examples within the current scope
35
+ # Recursively search for predicate within the current scope
70
36
  #
71
- # Searches node for examples and halts when a scope change is detected
37
+ # Searches node and halts when a scope change is detected
72
38
  #
73
- # @param node [RuboCop::Node] node to recursively search for examples
39
+ # @param node [RuboCop::Node] node to recursively search
74
40
  #
75
- # @return [Array<RuboCop::Node>] discovered example nodes
76
- def find_examples(node)
77
- return [] if scope_change?(node)
41
+ # @return [Array<RuboCop::Node>] discovered nodes
42
+ def find_all_in_scope(node, predicate)
43
+ node.each_child_node.flat_map do |child|
44
+ find_all(child, predicate)
45
+ end
46
+ end
78
47
 
79
- if example?(node)
48
+ def find_all(node, predicate)
49
+ if public_send(predicate, node)
80
50
  [node]
51
+ elsif scope_change?(node) || example?(node)
52
+ []
81
53
  else
82
- examples_in_scope(node)
54
+ find_all_in_scope(node, predicate)
83
55
  end
84
56
  end
85
57
  end
@@ -4,7 +4,13 @@ module RuboCop
4
4
  module RSpec
5
5
  # RuboCop FactoryBot project namespace
6
6
  module FactoryBot
7
- ATTRIBUTE_DEFINING_METHODS = %i[factory trait transient ignore].freeze
7
+ ATTRIBUTE_DEFINING_METHODS = %i[
8
+ factory
9
+ ignore
10
+ trait
11
+ traits_for_enum
12
+ transient
13
+ ].freeze
8
14
 
9
15
  UNPROXIED_METHODS = %i[
10
16
  __send__
@@ -4,21 +4,24 @@ module RuboCop
4
4
  module RSpec
5
5
  # Wrapper for RSpec hook
6
6
  class Hook < Concept
7
- STANDARDIZED_SCOPES = %i[each context suite].freeze
8
- private_constant(:STANDARDIZED_SCOPES)
7
+ def_node_matcher :extract_metadata, <<~PATTERN
8
+ (block
9
+ {
10
+ (send _ _ #valid_scope? $...)
11
+ (send _ _ $...)
12
+ }
13
+ ...
14
+ )
15
+ PATTERN
9
16
 
10
17
  def name
11
18
  node.method_name
12
19
  end
13
20
 
14
21
  def knowable_scope?
15
- return true unless scope_argument
16
-
17
- scope_argument.sym_type?
18
- end
19
-
20
- def valid_scope?
21
- STANDARDIZED_SCOPES.include?(scope)
22
+ scope_argument.nil? ||
23
+ scope_argument.sym_type? ||
24
+ scope_argument.hash_type?
22
25
  end
23
26
 
24
27
  def example?
@@ -26,17 +29,47 @@ module RuboCop
26
29
  end
27
30
 
28
31
  def scope
32
+ return :each if scope_argument&.hash_type?
33
+
29
34
  case scope_name
30
35
  when nil, :each, :example then :each
31
36
  when :context, :all then :context
32
37
  when :suite then :suite
33
- else
34
- scope_name
35
38
  end
36
39
  end
37
40
 
41
+ def metadata
42
+ (extract_metadata(node) || [])
43
+ .map { |meta| transform_metadata(meta) }
44
+ .flatten
45
+ .inject(&:merge)
46
+ end
47
+
38
48
  private
39
49
 
50
+ def valid_scope?(node)
51
+ node&.sym_type? && Hooks::Scopes::ALL.include?(node.value)
52
+ end
53
+
54
+ def transform_metadata(meta)
55
+ if meta.sym_type?
56
+ { meta => true }
57
+ else
58
+ # This check is to be able to compare those two hooks:
59
+ #
60
+ # before(:example, :special) { ... }
61
+ # before(:example, special: true) { ... }
62
+ #
63
+ # In the second case it's a node with a pair that has a value
64
+ # of a `true_type?`.
65
+ meta.pairs.map { |pair| { pair.key => transform_true(pair.value) } }
66
+ end
67
+ end
68
+
69
+ def transform_true(node)
70
+ node.true_type? ? true : node
71
+ end
72
+
40
73
  def scope_name
41
74
  scope_argument.to_a.first
42
75
  end
@@ -28,6 +28,14 @@ module RuboCop
28
28
  "(block #{send_pattern} ...)"
29
29
  end
30
30
 
31
+ def block_pass_pattern
32
+ "(send #{RSPEC} #{node_pattern_union} _ block_pass)"
33
+ end
34
+
35
+ def block_or_block_pass_pattern
36
+ "{#{block_pattern} #{block_pass_pattern}}"
37
+ end
38
+
31
39
  def send_pattern
32
40
  "(send #{RSPEC} #{node_pattern_union} ...)"
33
41
  end
@@ -94,6 +102,18 @@ module RuboCop
94
102
  append_after
95
103
  ]
96
104
  )
105
+
106
+ module Scopes
107
+ ALL = SelectorSet.new(
108
+ %i[
109
+ each
110
+ example
111
+ context
112
+ all
113
+ suite
114
+ ]
115
+ )
116
+ end
97
117
  end
98
118
 
99
119
  module Helpers
@@ -8,6 +8,10 @@ module RuboCop
8
8
  extend RuboCop::NodePattern::Macros
9
9
 
10
10
  def_node_matcher :example_group?, ExampleGroups::ALL.block_pattern
11
+ def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern
12
+
13
+ spec_groups = ExampleGroups::ALL + SharedGroups::ALL
14
+ def_node_matcher :spec_group?, spec_groups.block_pattern
11
15
 
12
16
  def_node_matcher :example_group_with_body?, <<-PATTERN
13
17
  (block #{ExampleGroups::ALL.send_pattern} args [!nil?])
@@ -17,7 +21,7 @@ module RuboCop
17
21
 
18
22
  def_node_matcher :hook?, Hooks::ALL.block_pattern
19
23
 
20
- def_node_matcher :let?, Helpers::ALL.block_pattern
24
+ def_node_matcher :let?, Helpers::ALL.block_or_block_pass_pattern
21
25
 
22
26
  def_node_matcher :subject?, Subject::ALL.block_pattern
23
27
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Helper methods for top level example group cops
6
+ module TopLevelGroup
7
+ extend RuboCop::NodePattern::Macros
8
+ include RuboCop::RSpec::Language
9
+
10
+ def_node_matcher :example_or_shared_group?,
11
+ (ExampleGroups::ALL + SharedGroups::ALL).block_pattern
12
+
13
+ def on_block(node)
14
+ return unless respond_to?(:on_top_level_group)
15
+ return unless top_level_group?(node)
16
+
17
+ on_top_level_group(node)
18
+ end
19
+
20
+ private
21
+
22
+ def top_level_group?(node)
23
+ top_level_groups.include?(node)
24
+ end
25
+
26
+ def top_level_groups
27
+ @top_level_groups ||=
28
+ top_level_nodes.select { |n| example_or_shared_group?(n) }
29
+ end
30
+
31
+ def top_level_nodes
32
+ if root_node.begin_type?
33
+ root_node.children
34
+ else
35
+ [root_node]
36
+ end
37
+ end
38
+
39
+ def root_node
40
+ processed_source.ast
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Helps check offenses with variable definitions
6
+ module Variable
7
+ include Language
8
+ extend RuboCop::NodePattern::Macros
9
+
10
+ def_node_matcher :variable_definition?, <<~PATTERN
11
+ (send #{RSPEC} #{(Helpers::ALL + Subject::ALL).node_pattern_union}
12
+ $({sym str dsym dstr} ...) ...)
13
+ PATTERN
14
+ end
15
+ end
16
+ end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '1.37.1'
7
+ STRING = '1.41.0'
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.37.1
4
+ version: 1.41.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Backus
8
8
  - Ian MacLeod
9
9
  - Nils Gemeinhardt
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-12-16 00:00:00.000000000 Z
13
+ date: 2020-07-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -72,16 +72,16 @@ dependencies:
72
72
  name: simplecov
73
73
  requirement: !ruby/object:Gem::Requirement
74
74
  requirements:
75
- - - ">="
75
+ - - "<"
76
76
  - !ruby/object:Gem::Version
77
- version: '0'
77
+ version: '0.18'
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
- - - ">="
82
+ - - "<"
83
83
  - !ruby/object:Gem::Version
84
- version: '0'
84
+ version: '0.18'
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: yard
87
87
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +110,7 @@ extra_rdoc_files:
110
110
  - README.md
111
111
  files:
112
112
  - CHANGELOG.md
113
+ - CODE_OF_CONDUCT.md
113
114
  - MIT-LICENSE.md
114
115
  - README.md
115
116
  - config/default.yml
@@ -123,6 +124,7 @@ files:
123
124
  - lib/rubocop/cop/rspec/before_after_all.rb
124
125
  - lib/rubocop/cop/rspec/capybara/current_path_expectation.rb
125
126
  - lib/rubocop/cop/rspec/capybara/feature_methods.rb
127
+ - lib/rubocop/cop/rspec/capybara/visibility_matcher.rb
126
128
  - lib/rubocop/cop/rspec/context_method.rb
127
129
  - lib/rubocop/cop/rspec/context_wording.rb
128
130
  - lib/rubocop/cop/rspec/cop.rb
@@ -133,6 +135,7 @@ files:
133
135
  - lib/rubocop/cop/rspec/described_class_module_wrapping.rb
134
136
  - lib/rubocop/cop/rspec/dialect.rb
135
137
  - lib/rubocop/cop/rspec/empty_example_group.rb
138
+ - lib/rubocop/cop/rspec/empty_hook.rb
136
139
  - lib/rubocop/cop/rspec/empty_line_after_example.rb
137
140
  - lib/rubocop/cop/rspec/empty_line_after_example_group.rb
138
141
  - lib/rubocop/cop/rspec/empty_line_after_final_let.rb
@@ -182,6 +185,8 @@ files:
182
185
  - lib/rubocop/cop/rspec/receive_never.rb
183
186
  - lib/rubocop/cop/rspec/repeated_description.rb
184
187
  - lib/rubocop/cop/rspec/repeated_example.rb
188
+ - lib/rubocop/cop/rspec/repeated_example_group_body.rb
189
+ - lib/rubocop/cop/rspec/repeated_example_group_description.rb
185
190
  - lib/rubocop/cop/rspec/return_from_stub.rb
186
191
  - lib/rubocop/cop/rspec/scattered_let.rb
187
192
  - lib/rubocop/cop/rspec/scattered_setup.rb
@@ -190,6 +195,8 @@ files:
190
195
  - lib/rubocop/cop/rspec/single_argument_message_chain.rb
191
196
  - lib/rubocop/cop/rspec/subject_stub.rb
192
197
  - lib/rubocop/cop/rspec/unspecified_exception.rb
198
+ - lib/rubocop/cop/rspec/variable_definition.rb
199
+ - lib/rubocop/cop/rspec/variable_name.rb
193
200
  - lib/rubocop/cop/rspec/verified_doubles.rb
194
201
  - lib/rubocop/cop/rspec/void_expect.rb
195
202
  - lib/rubocop/cop/rspec/yield.rb
@@ -199,6 +206,7 @@ files:
199
206
  - lib/rubocop/rspec/blank_line_separation.rb
200
207
  - lib/rubocop/rspec/concept.rb
201
208
  - lib/rubocop/rspec/config_formatter.rb
209
+ - lib/rubocop/rspec/corrector/move_node.rb
202
210
  - lib/rubocop/rspec/description_extractor.rb
203
211
  - lib/rubocop/rspec/example.rb
204
212
  - lib/rubocop/rspec/example_group.rb
@@ -210,7 +218,8 @@ files:
210
218
  - lib/rubocop/rspec/language/node_pattern.rb
211
219
  - lib/rubocop/rspec/node.rb
212
220
  - lib/rubocop/rspec/top_level_describe.rb
213
- - lib/rubocop/rspec/util.rb
221
+ - lib/rubocop/rspec/top_level_group.rb
222
+ - lib/rubocop/rspec/variable.rb
214
223
  - lib/rubocop/rspec/version.rb
215
224
  - lib/rubocop/rspec/wording.rb
216
225
  homepage: https://github.com/rubocop-hq/rubocop-rspec
@@ -219,7 +228,7 @@ licenses:
219
228
  metadata:
220
229
  changelog_uri: https://github.com/rubocop-hq/rubocop-rspec/blob/master/CHANGELOG.md
221
230
  documentation_uri: https://rubocop-rspec.readthedocs.io/
222
- post_install_message:
231
+ post_install_message:
223
232
  rdoc_options: []
224
233
  require_paths:
225
234
  - lib
@@ -227,7 +236,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
227
236
  requirements:
228
237
  - - ">="
229
238
  - !ruby/object:Gem::Version
230
- version: 2.3.0
239
+ version: 2.4.0
231
240
  required_rubygems_version: !ruby/object:Gem::Requirement
232
241
  requirements:
233
242
  - - ">="
@@ -235,7 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
235
244
  version: '0'
236
245
  requirements: []
237
246
  rubygems_version: 3.0.3
238
- signing_key:
247
+ signing_key:
239
248
  specification_version: 4
240
249
  summary: Code style checking for RSpec files
241
250
  test_files: []
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module RSpec
5
- # Utility methods
6
- module Util
7
- # Error raised by `Util.one` if size is less than zero or greater than one
8
- SizeError = Class.new(IndexError)
9
-
10
- # Return only element in array if it contains exactly one member
11
- def one(array)
12
- return array.first if array.one?
13
-
14
- raise SizeError,
15
- "expected size to be exactly 1 but size was #{array.size}"
16
- end
17
- end
18
- end
19
- end