rubocop-rspec 1.33.0 → 1.34.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -2
  3. data/README.md +10 -2
  4. data/config/default.yml +6 -1
  5. data/lib/rubocop/cop/rspec/any_instance.rb +0 -1
  6. data/lib/rubocop/cop/rspec/around_block.rb +1 -2
  7. data/lib/rubocop/cop/rspec/before_after_all.rb +0 -1
  8. data/lib/rubocop/cop/rspec/context_wording.rb +18 -17
  9. data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
  10. data/lib/rubocop/cop/rspec/describe_method.rb +1 -1
  11. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  12. data/lib/rubocop/cop/rspec/described_class.rb +79 -13
  13. data/lib/rubocop/cop/rspec/empty_example_group.rb +1 -1
  14. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
  15. data/lib/rubocop/cop/rspec/example_length.rb +1 -1
  16. data/lib/rubocop/cop/rspec/example_wording.rb +6 -4
  17. data/lib/rubocop/cop/rspec/expect_actual.rb +1 -1
  18. data/lib/rubocop/cop/rspec/expect_output.rb +2 -0
  19. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +1 -1
  20. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +1 -2
  21. data/lib/rubocop/cop/rspec/file_path.rb +0 -1
  22. data/lib/rubocop/cop/rspec/focus.rb +1 -1
  23. data/lib/rubocop/cop/rspec/hook_argument.rb +0 -1
  24. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  25. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  26. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +1 -1
  27. data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -1
  28. data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
  29. data/lib/rubocop/cop/rspec/leading_subject.rb +0 -1
  30. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +128 -0
  31. data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
  32. data/lib/rubocop/cop/rspec/let_setup.rb +2 -4
  33. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -2
  34. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -1
  35. data/lib/rubocop/cop/rspec/multiple_expectations.rb +32 -16
  36. data/lib/rubocop/cop/rspec/multiple_subjects.rb +1 -1
  37. data/lib/rubocop/cop/rspec/nested_groups.rb +0 -1
  38. data/lib/rubocop/cop/rspec/overwriting_setup.rb +0 -1
  39. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  40. data/lib/rubocop/cop/rspec/predicate_matcher.rb +0 -3
  41. data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
  42. data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
  43. data/lib/rubocop/cop/rspec/scattered_let.rb +1 -1
  44. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  45. data/lib/rubocop/cop/rspec/shared_context.rb +0 -1
  46. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
  47. data/lib/rubocop/cop/rspec/subject_stub.rb +17 -20
  48. data/lib/rubocop/cop/rspec/unspecified_exception.rb +1 -4
  49. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  50. data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
  51. data/lib/rubocop/cop/rspec_cops.rb +1 -0
  52. data/lib/rubocop/rspec/language.rb +1 -1
  53. data/lib/rubocop/rspec/top_level_describe.rb +0 -4
  54. data/lib/rubocop/rspec/version.rb +1 -1
  55. data/spec/rubocop/cop/rspec/cop_spec.rb +3 -3
  56. data/spec/rubocop/cop/rspec/describe_class_spec.rb +7 -0
  57. data/spec/rubocop/cop/rspec/described_class_spec.rb +113 -80
  58. data/spec/rubocop/cop/rspec/example_wording_spec.rb +33 -0
  59. data/spec/rubocop/cop/rspec/leaky_constant_declaration_spec.rb +91 -0
  60. data/spec/rubocop/cop/rspec/let_setup_spec.rb +2 -2
  61. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +64 -37
  62. data/spec/rubocop/cop/rspec/subject_stub_spec.rb +113 -14
  63. data/spec/rubocop/rspec/language/selector_set_spec.rb +2 -2
  64. metadata +5 -2
@@ -59,7 +59,6 @@ module RuboCop
59
59
 
60
60
  add_offense(
61
61
  node,
62
- location: :expression,
63
62
  message: format(MSG, suffix: glob)
64
63
  )
65
64
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
 
45
45
  def on_send(node)
46
46
  focus_metadata(node) do |focus|
47
- add_offense(focus, location: :expression)
47
+ add_offense(focus)
48
48
  end
49
49
  end
50
50
 
@@ -80,7 +80,6 @@ module RuboCop
80
80
  style_detected(scope_name)
81
81
  add_offense(
82
82
  method_send,
83
- location: :expression,
84
83
  message: explicit_message(scope_name)
85
84
  )
86
85
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
 
44
44
  null_double(node) do |var, receiver|
45
45
  have_received_usage(node) do |expected|
46
- add_offense(receiver, location: :expression) if expected == var
46
+ add_offense(receiver) if expected == var
47
47
  end
48
48
  end
49
49
  end
@@ -68,7 +68,7 @@ module RuboCop
68
68
  return if inside_dynamic_class?(ivar)
69
69
  return if assignment_only? && !ivar_assigned?(node, name)
70
70
 
71
- add_offense(ivar, location: :expression)
71
+ add_offense(ivar)
72
72
  end
73
73
  end
74
74
 
@@ -24,7 +24,7 @@ module RuboCop
24
24
 
25
25
  def on_send(node)
26
26
  invalid_predicate_matcher?(node) do |predicate|
27
- add_offense(predicate, location: :expression)
27
+ add_offense(predicate)
28
28
  end
29
29
  end
30
30
 
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  def on_send(node)
30
30
  example_inclusion_offense(node, alternative_style) do
31
- add_offense(node, location: :expression)
31
+ add_offense(node)
32
32
  end
33
33
  end
34
34
 
@@ -34,7 +34,7 @@ module RuboCop
34
34
  def on_block(node)
35
35
  each?(node) do |arg, body|
36
36
  if single_expectation?(body, arg) || only_expectations?(body, arg)
37
- add_offense(node.send_node, location: :expression)
37
+ add_offense(node.send_node)
38
38
  end
39
39
  end
40
40
  end
@@ -47,7 +47,6 @@ module RuboCop
47
47
  if offending?(sibling)
48
48
  add_offense(
49
49
  node,
50
- location: :expression,
51
50
  message: format(MSG, offending: sibling.method_name)
52
51
  )
53
52
  end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks that no class, module, or constant is declared.
7
+ #
8
+ # Constants, including classes and modules, when declared in a block
9
+ # scope, are defined in global namespace, and leak between examples.
10
+ #
11
+ # If several examples may define a `DummyClass`, instead of being a
12
+ # blank slate class as it will be in the first example, subsequent
13
+ # examples will be reopening it and modifying its behaviour in
14
+ # unpredictable ways.
15
+ # Even worse when a class that exists in the codebase is reopened.
16
+ #
17
+ # Anonymous classes are fine, since they don't result in global
18
+ # namespace name clashes.
19
+ #
20
+ # @see https://relishapp.com/rspec/rspec-mocks/docs/mutating-constants
21
+ #
22
+ # @example Constants leak between examples
23
+ # # bad
24
+ # describe SomeClass do
25
+ # OtherClass = Struct.new
26
+ # CONSTANT_HERE = 'I leak into global namespace'
27
+ # end
28
+ #
29
+ # # good
30
+ # describe SomeClass do
31
+ # before do
32
+ # stub_const('OtherClass', Struct.new)
33
+ # stub_const('CONSTANT_HERE', 'I only exist during this example')
34
+ # end
35
+ # end
36
+ #
37
+ # @example
38
+ # # bad
39
+ # describe SomeClass do
40
+ # class FooClass < described_class
41
+ # def double_that
42
+ # some_base_method * 2
43
+ # end
44
+ # end
45
+ #
46
+ # it { expect(FooClass.new.double_that).to eq(4) }
47
+ # end
48
+ #
49
+ # # good - anonymous class, no constant needs to be defined
50
+ # let(:foo_class) do
51
+ # Class.new(described_class) do
52
+ # def double_that
53
+ # some_base_method * 2
54
+ # end
55
+ # end
56
+ #
57
+ # it { expect(foo_class.new.double_that).to eq(4) }
58
+ # end
59
+ #
60
+ # # good - constant is stubbed
61
+ # describe SomeClass do
62
+ # before do
63
+ # foo_class = Class.new(described_class) do
64
+ # def do_something
65
+ # end
66
+ # end
67
+ # stub_const('FooClass', foo_class)
68
+ # end
69
+ #
70
+ # it { expect(FooClass.new.double_that).to eq(4) }
71
+ # end
72
+ #
73
+ # @example
74
+ # # bad
75
+ # describe SomeClass do
76
+ # module SomeModule
77
+ # class SomeClass
78
+ # def do_something
79
+ # end
80
+ # end
81
+ # end
82
+ # end
83
+ #
84
+ # # good
85
+ # describe SomeClass do
86
+ # before do
87
+ # foo_class = Class.new(described_class) do
88
+ # def do_something
89
+ # end
90
+ # end
91
+ # stub_const('SomeModule::SomeClass', foo_class)
92
+ # end
93
+ # end
94
+ class LeakyConstantDeclaration < Cop
95
+ MSG_CONST = 'Stub constant instead of declaring explicitly.'
96
+ MSG_CLASS = 'Stub class constant instead of declaring explicitly.'
97
+ MSG_MODULE = 'Stub module constant instead of declaring explicitly.'
98
+
99
+ def on_casgn(node)
100
+ return unless inside_describe_block?(node)
101
+
102
+ add_offense(node, message: MSG_CONST)
103
+ end
104
+
105
+ def on_class(node)
106
+ return unless inside_describe_block?(node)
107
+
108
+ add_offense(node, message: MSG_CLASS)
109
+ end
110
+
111
+ def on_module(node)
112
+ return unless inside_describe_block?(node)
113
+
114
+ add_offense(node, message: MSG_MODULE)
115
+ end
116
+
117
+ private
118
+
119
+ def inside_describe_block?(node)
120
+ node.each_ancestor(:block).any?(&method(:in_example_or_shared_group?))
121
+ end
122
+
123
+ def_node_matcher :in_example_or_shared_group?,
124
+ (ExampleGroups::ALL + SharedGroups::ALL).block_pattern
125
+ end
126
+ end
127
+ end
128
+ end
@@ -73,7 +73,7 @@ module RuboCop
73
73
  node.each_child_node do |child|
74
74
  next if child.sibling_index < first_example.sibling_index
75
75
 
76
- add_offense(child, location: :expression) if let?(child)
76
+ add_offense(child) if let?(child)
77
77
  end
78
78
  end
79
79
 
@@ -26,9 +26,7 @@ module RuboCop
26
26
  # expect(Widget.count).to eq(1)
27
27
  # end
28
28
  class LetSetup < Cop
29
- include RuboCop::RSpec::TopLevelDescribe
30
-
31
- MSG = 'Do not use `let!` for test setup.'
29
+ MSG = 'Do not use `let!` to setup objects not referenced in tests.'
32
30
 
33
31
  def_node_search :let_bang, <<-PATTERN
34
32
  (block $(send nil? :let! (sym $_)) args ...)
@@ -40,7 +38,7 @@ module RuboCop
40
38
  return unless example_group?(node)
41
39
 
42
40
  unused_let_bang(node) do |let|
43
- add_offense(let, location: :expression)
41
+ add_offense(let)
44
42
  end
45
43
  end
46
44
 
@@ -26,8 +26,7 @@ module RuboCop
26
26
  return unless example_group?(node)
27
27
  return if node.send_node.arguments?
28
28
 
29
- add_offense(node, location: :expression,
30
- message: format(MSG, method: node.method_name))
29
+ add_offense(node, message: format(MSG, method: node.method_name))
31
30
  end
32
31
  end
33
32
  end
@@ -32,7 +32,7 @@ module RuboCop
32
32
  return if single_top_level_describe?
33
33
  return unless top_level_nodes.first.equal?(node)
34
34
 
35
- add_offense(node, location: :expression)
35
+ add_offense(node)
36
36
  end
37
37
  end
38
38
  end
@@ -50,20 +50,20 @@ module RuboCop
50
50
 
51
51
  MSG = 'Example has too many expectations [%<total>d/%<max>d].'
52
52
 
53
- def_node_search :with_aggregated_failures?, '(sym :aggregate_failures)'
54
- def_node_search :disabled_aggregated_failures?, <<-PATTERN
53
+ def_node_search :with_aggregate_failures?, '(sym :aggregate_failures)'
54
+ def_node_search :disabled_aggregate_failures?, <<-PATTERN
55
55
  (pair (sym :aggregate_failures) (false))
56
56
  PATTERN
57
57
 
58
58
  def_node_matcher :expect?, Expectations::ALL.send_pattern
59
- def_node_matcher :aggregate_failures?, <<-PATTERN
59
+ def_node_matcher :aggregate_failures_block?, <<-PATTERN
60
60
  (block (send _ :aggregate_failures ...) ...)
61
61
  PATTERN
62
62
 
63
63
  def on_block(node)
64
64
  return unless example?(node)
65
65
 
66
- return if example_with_aggregated_failures?(node)
66
+ return if example_with_aggregate_failures?(node)
67
67
 
68
68
  expectations_count = to_enum(:find_expectation, node).count
69
69
 
@@ -76,19 +76,40 @@ module RuboCop
76
76
 
77
77
  private
78
78
 
79
- def example_with_aggregated_failures?(node)
80
- example = node.send_node
79
+ def example_with_aggregate_failures?(example_node)
80
+ node_with_aggregate_failures = find_aggregate_failures(example_node)
81
+ return false unless node_with_aggregate_failures
81
82
 
82
- (aggregated_failures_by_default? ||
83
- with_aggregated_failures?(example)) &&
84
- !disabled_aggregated_failures?(example)
83
+ aggregate_failures?(node_with_aggregate_failures)
84
+ end
85
+
86
+ def find_aggregate_failures(example_node)
87
+ example_node.send_node.each_ancestor(:block)
88
+ .find { |block_node| aggregate_failures_present?(block_node) }
89
+ end
90
+
91
+ def aggregate_failures_present?(node)
92
+ metadata(node)&.any?(&method(:with_aggregate_failures?))
93
+ end
94
+
95
+ def aggregate_failures?(example_or_group_node)
96
+ metadata(example_or_group_node)&.any? do |metadata|
97
+ with_aggregate_failures?(metadata) &&
98
+ !disabled_aggregate_failures?(metadata)
99
+ end
100
+ end
101
+
102
+ def metadata(example_or_group_node)
103
+ RuboCop::RSpec::Example
104
+ .new(example_or_group_node)
105
+ .metadata
85
106
  end
86
107
 
87
108
  def find_expectation(node, &block)
88
- yield if expect?(node) || aggregate_failures?(node)
109
+ yield if expect?(node) || aggregate_failures_block?(node)
89
110
 
90
111
  # do not search inside of aggregate_failures block
91
- return if aggregate_failures?(node)
112
+ return if aggregate_failures_block?(node)
92
113
 
93
114
  node.each_child_node do |child|
94
115
  find_expectation(child, &block)
@@ -98,7 +119,6 @@ module RuboCop
98
119
  def flag_example(node, expectation_count:)
99
120
  add_offense(
100
121
  node.send_node,
101
- location: :expression,
102
122
  message: format(
103
123
  MSG,
104
124
  total: expectation_count,
@@ -110,10 +130,6 @@ module RuboCop
110
130
  def max_expectations
111
131
  Integer(cop_config.fetch('Max', 1))
112
132
  end
113
-
114
- def aggregated_failures_by_default?
115
- cop_config.fetch('AggregateFailuresByDefault', false)
116
- end
117
133
  end
118
134
  end
119
135
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
  subjects = RuboCop::RSpec::ExampleGroup.new(node).subjects
45
45
 
46
46
  subjects[0...-1].each do |subject|
47
- add_offense(subject, location: :expression)
47
+ add_offense(subject)
48
48
  end
49
49
  end
50
50
 
@@ -104,7 +104,6 @@ module RuboCop
104
104
  self.max = nesting
105
105
  add_offense(
106
106
  context.send_node,
107
- location: :expression,
108
107
  message: message(nesting)
109
108
  )
110
109
  end
@@ -33,7 +33,6 @@ module RuboCop
33
33
  find_duplicates(node.body) do |duplicate, name|
34
34
  add_offense(
35
35
  duplicate,
36
- location: :expression,
37
36
  message: format(MSG, name: name)
38
37
  )
39
38
  end
@@ -46,7 +46,7 @@ module RuboCop
46
46
  def on_send(node)
47
47
  return unless pending_block?(node) || skipped_from_metadata?(node)
48
48
 
49
- add_offense(node, location: :expression)
49
+ add_offense(node)
50
50
  end
51
51
 
52
52
  private
@@ -17,7 +17,6 @@ module RuboCop
17
17
  predicate_in_actual?(node) do |predicate|
18
18
  add_offense(
19
19
  node,
20
- location: :expression,
21
20
  message: message_inflected(predicate)
22
21
  )
23
22
  end
@@ -143,7 +142,6 @@ module RuboCop
143
142
  predicate_matcher_block?(node) do |_actual, matcher|
144
143
  add_offense(
145
144
  node,
146
- location: :expression,
147
145
  message: message_explicit(matcher)
148
146
  )
149
147
  ignore_node(node.children.first)
@@ -155,7 +153,6 @@ module RuboCop
155
153
  predicate_matcher?(node) do |_actual, matcher|
156
154
  add_offense(
157
155
  node,
158
- location: :expression,
159
156
  message: message_explicit(matcher)
160
157
  )
161
158
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  return unless example_group?(node)
37
37
 
38
38
  repeated_descriptions(node).each do |repeated_description|
39
- add_offense(repeated_description, location: :expression)
39
+ add_offense(repeated_description)
40
40
  end
41
41
  end
42
42
 
@@ -22,7 +22,7 @@ module RuboCop
22
22
  return unless example_group?(node)
23
23
 
24
24
  repeated_examples(node).each do |repeated_example|
25
- add_offense(repeated_example, location: :expression)
25
+ add_offense(repeated_example)
26
26
  end
27
27
  end
28
28
 
@@ -44,7 +44,7 @@ module RuboCop
44
44
  lets.each_with_index do |node, idx|
45
45
  next if node.sibling_index == first_let.sibling_index + idx
46
46
 
47
- add_offense(node, location: :expression)
47
+ add_offense(node)
48
48
  end
49
49
  end
50
50
  end
@@ -29,7 +29,7 @@ module RuboCop
29
29
  return unless example_group?(node)
30
30
 
31
31
  analyzable_hooks(node).each do |repeated_hook|
32
- add_offense(repeated_hook, location: :expression)
32
+ add_offense(repeated_hook)
33
33
  end
34
34
  end
35
35