rubocop-rspec 1.44.1 → 2.2.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -1
  3. data/README.md +5 -1
  4. data/config/default.yml +171 -81
  5. data/lib/rubocop-rspec.rb +8 -8
  6. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +7 -3
  7. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +7 -3
  8. data/lib/rubocop/cop/rspec/any_instance.rb +6 -10
  9. data/lib/rubocop/cop/rspec/around_block.rb +1 -1
  10. data/lib/rubocop/cop/rspec/base.rb +6 -55
  11. data/lib/rubocop/cop/rspec/be.rb +2 -2
  12. data/lib/rubocop/cop/rspec/be_eql.rb +1 -0
  13. data/lib/rubocop/cop/rspec/before_after_all.rb +5 -3
  14. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +4 -2
  15. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +3 -2
  16. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +2 -0
  17. data/lib/rubocop/cop/rspec/describe_class.rb +2 -2
  18. data/lib/rubocop/cop/rspec/describe_method.rb +2 -2
  19. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -0
  20. data/lib/rubocop/cop/rspec/described_class.rb +1 -2
  21. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -2
  22. data/lib/rubocop/cop/rspec/dialect.rb +1 -1
  23. data/lib/rubocop/cop/rspec/empty_example_group.rb +6 -45
  24. data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
  25. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
  26. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  27. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +1 -1
  28. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -1
  29. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -2
  30. data/lib/rubocop/cop/rspec/expect_actual.rb +2 -1
  31. data/lib/rubocop/cop/rspec/expect_change.rb +1 -0
  32. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  33. data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
  34. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +1 -0
  35. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -0
  36. data/lib/rubocop/cop/rspec/file_path.rb +24 -17
  37. data/lib/rubocop/cop/rspec/focus.rb +43 -8
  38. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -4
  39. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +2 -2
  40. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -0
  41. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  42. data/lib/rubocop/cop/rspec/implicit_subject.rb +1 -0
  43. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  44. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  45. data/lib/rubocop/cop/rspec/it_behaves_like.rb +2 -1
  46. data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
  47. data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -2
  48. data/lib/rubocop/cop/rspec/let_setup.rb +7 -4
  49. data/lib/rubocop/cop/rspec/message_chain.rb +4 -10
  50. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -0
  51. data/lib/rubocop/cop/rspec/message_spies.rb +3 -3
  52. data/lib/rubocop/cop/rspec/mixin/comments_help.rb +38 -0
  53. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +51 -0
  54. data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
  55. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
  56. data/lib/rubocop/cop/rspec/mixin/variable.rb +20 -0
  57. data/lib/rubocop/cop/rspec/multiple_describes.rb +2 -3
  58. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
  59. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +3 -1
  60. data/lib/rubocop/cop/rspec/named_subject.rb +8 -12
  61. data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
  62. data/lib/rubocop/cop/rspec/not_to_not.rb +1 -0
  63. data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
  64. data/lib/rubocop/cop/rspec/pending.rb +13 -5
  65. data/lib/rubocop/cop/rspec/predicate_matcher.rb +3 -3
  66. data/lib/rubocop/cop/rspec/rails/http_status.rb +1 -0
  67. data/lib/rubocop/cop/rspec/receive_counts.rb +2 -0
  68. data/lib/rubocop/cop/rspec/receive_never.rb +1 -0
  69. data/lib/rubocop/cop/rspec/repeated_include_example.rb +3 -2
  70. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -0
  71. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  72. data/lib/rubocop/cop/rspec/shared_context.rb +18 -11
  73. data/lib/rubocop/cop/rspec/shared_examples.rb +3 -1
  74. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +2 -1
  75. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  76. data/lib/rubocop/cop/rspec/subject_stub.rb +16 -6
  77. data/lib/rubocop/cop/rspec/unspecified_exception.rb +1 -0
  78. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
  79. data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
  80. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -0
  81. data/lib/rubocop/cop/rspec/void_expect.rb +1 -0
  82. data/lib/rubocop/cop/rspec_cops.rb +0 -1
  83. data/lib/rubocop/rspec/align_let_brace.rb +1 -1
  84. data/lib/rubocop/rspec/concept.rb +2 -2
  85. data/lib/rubocop/rspec/config_formatter.rb +5 -3
  86. data/lib/rubocop/rspec/corrector/move_node.rb +7 -10
  87. data/lib/rubocop/rspec/example_group.rb +15 -5
  88. data/lib/rubocop/rspec/hook.rb +1 -1
  89. data/lib/rubocop/rspec/inject.rb +4 -2
  90. data/lib/rubocop/rspec/language.rb +143 -109
  91. data/lib/rubocop/rspec/language/node_pattern.rb +7 -24
  92. data/lib/rubocop/rspec/node.rb +1 -1
  93. data/lib/rubocop/rspec/version.rb +1 -1
  94. metadata +12 -15
  95. data/lib/rubocop/cop/rspec/cop.rb +0 -10
  96. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +0 -41
  97. data/lib/rubocop/rspec.rb +0 -12
  98. data/lib/rubocop/rspec/empty_line_separation.rb +0 -48
  99. data/lib/rubocop/rspec/final_end_location.rb +0 -17
  100. data/lib/rubocop/rspec/top_level_describe.rb +0 -52
  101. data/lib/rubocop/rspec/top_level_group.rb +0 -57
  102. data/lib/rubocop/rspec/variable.rb +0 -16
@@ -29,14 +29,14 @@ module RuboCop
29
29
 
30
30
  MSG_RECEIVE = 'Prefer `receive` for setting message expectations.'
31
31
 
32
- MSG_HAVE_RECEIVED = 'Prefer `have_received` for setting message '\
33
- 'expectations. Setup `%<source>s` as a spy using '\
32
+ MSG_HAVE_RECEIVED = 'Prefer `have_received` for setting message ' \
33
+ 'expectations. Setup `%<source>s` as a spy using ' \
34
34
  '`allow` or `instance_spy`.'
35
35
 
36
36
  SUPPORTED_STYLES = %w[have_received receive].freeze
37
37
 
38
38
  def_node_matcher :message_expectation, %(
39
- (send (send nil? :expect $_) #{Runners::ALL.node_pattern_union} ...)
39
+ (send (send nil? :expect $_) #Runners.all ...)
40
40
  )
41
41
 
42
42
  def_node_search :receive_message, %(
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Help methods for working with nodes containing comments.
7
+ module CommentsHelp
8
+ include FinalEndLocation
9
+
10
+ def source_range_with_comment(node)
11
+ begin_pos = begin_pos_with_comment(node).begin_pos
12
+ end_pos = end_line_position(node).end_pos
13
+
14
+ Parser::Source::Range.new(buffer, begin_pos, end_pos)
15
+ end
16
+
17
+ def begin_pos_with_comment(node)
18
+ first_comment = processed_source.ast_with_comments[node].first
19
+
20
+ start_line_position(first_comment || node)
21
+ end
22
+
23
+ def start_line_position(node)
24
+ buffer.line_range(node.loc.line)
25
+ end
26
+
27
+ def end_line_position(node)
28
+ end_line = buffer.line_for_position(final_end_location(node).end_pos)
29
+ buffer.line_range(end_line)
30
+ end
31
+
32
+ def buffer
33
+ processed_source.buffer
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps determine the offending location if there is not an empty line
7
+ # following the node. Allows comments to follow directly after.
8
+ module EmptyLineSeparation
9
+ include FinalEndLocation
10
+ include RangeHelp
11
+
12
+ def missing_separating_line_offense(node)
13
+ return if last_child?(node)
14
+
15
+ missing_separating_line(node) do |location|
16
+ msg = yield(node.method_name)
17
+ add_offense(location, message: msg) do |corrector|
18
+ corrector.insert_after(location.end, "\n")
19
+ end
20
+ end
21
+ end
22
+
23
+ def missing_separating_line(node)
24
+ line = final_end_location(node).line
25
+
26
+ line += 1 while comment_line?(processed_source[line])
27
+
28
+ return if processed_source[line].blank?
29
+
30
+ yield offending_loc(line)
31
+ end
32
+
33
+ def offending_loc(last_line)
34
+ offending_line = processed_source[last_line - 1]
35
+
36
+ content_length = offending_line.lstrip.length
37
+ start = offending_line.length - content_length
38
+
39
+ source_range(processed_source.buffer,
40
+ last_line, start, content_length)
41
+ end
42
+
43
+ def last_child?(node)
44
+ return true unless node.parent&.begin_type?
45
+
46
+ node.equal?(node.parent.children.last)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps find the true end location of nodes which might contain heredocs.
7
+ module FinalEndLocation
8
+ def final_end_location(start_node)
9
+ heredoc_endings =
10
+ start_node.each_node(:str, :dstr, :xstr)
11
+ .select(&:heredoc?)
12
+ .map { |node| node.loc.heredoc_end }
13
+
14
+ [start_node.loc.end, *heredoc_endings].max_by(&:line)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helper methods for top level example group cops
7
+ module TopLevelGroup
8
+ extend RuboCop::NodePattern::Macros
9
+
10
+ def on_new_investigation
11
+ super
12
+
13
+ top_level_groups.each do |node|
14
+ on_top_level_example_group(node) if example_group?(node)
15
+ on_top_level_group(node)
16
+ end
17
+ end
18
+
19
+ def top_level_groups
20
+ @top_level_groups ||=
21
+ top_level_nodes(root_node).select { |n| spec_group?(n) }
22
+ end
23
+
24
+ private
25
+
26
+ # Dummy methods to be overridden in the consumer
27
+ def on_top_level_example_group(_node); end
28
+
29
+ def on_top_level_group(_node); end
30
+
31
+ def top_level_group?(node)
32
+ top_level_groups.include?(node)
33
+ end
34
+
35
+ def top_level_nodes(node)
36
+ return [] if node.nil?
37
+
38
+ case node.type
39
+ when :begin
40
+ node.children
41
+ when :module, :class
42
+ top_level_nodes(node.body)
43
+ else
44
+ [node]
45
+ end
46
+ end
47
+
48
+ def root_node
49
+ processed_source.ast
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps check offenses with variable definitions
7
+ module Variable
8
+ extend RuboCop::NodePattern::Macros
9
+
10
+ Subjects = RuboCop::RSpec::Language::Subjects
11
+ Helpers = RuboCop::RSpec::Language::Helpers
12
+
13
+ def_node_matcher :variable_definition?, <<~PATTERN
14
+ (send nil? {#Subjects.all #Helpers.all}
15
+ $({sym str dsym dstr} ...) ...)
16
+ PATTERN
17
+ end
18
+ end
19
+ end
20
+ end
@@ -23,10 +23,9 @@ module RuboCop
23
23
  # end
24
24
  # end
25
25
  class MultipleDescribes < Base
26
- include RuboCop::RSpec::TopLevelGroup
26
+ include TopLevelGroup
27
27
 
28
- MSG = 'Do not use multiple top-level example groups - '\
29
- 'try to nest them.'
28
+ MSG = 'Do not use multiple top-level example groups - try to nest them.'
30
29
 
31
30
  def on_top_level_group(node)
32
31
  top_level_example_groups =
@@ -60,7 +60,7 @@ module RuboCop
60
60
  } ...)
61
61
  PATTERN
62
62
 
63
- def_node_matcher :expect?, Expectations::ALL.send_pattern
63
+ def_node_matcher :expect?, send_pattern('#Expectations.all')
64
64
  def_node_matcher :aggregate_failures_block?, <<-PATTERN
65
65
  (block (send nil? :aggregate_failures ...) ...)
66
66
  PATTERN
@@ -85,7 +85,7 @@ module RuboCop
85
85
  #
86
86
  class MultipleMemoizedHelpers < Base
87
87
  include ConfigurableMax
88
- include RuboCop::RSpec::Variable
88
+ include Variable
89
89
 
90
90
  MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
91
91
 
@@ -101,6 +101,7 @@ module RuboCop
101
101
  end
102
102
 
103
103
  def on_new_investigation
104
+ super
104
105
  @example_group_memoized_helpers = {}
105
106
  end
106
107
 
@@ -128,6 +129,7 @@ module RuboCop
128
129
 
129
130
  def variable_nodes(node)
130
131
  example_group = RuboCop::RSpec::ExampleGroup.new(node)
132
+
131
133
  if allow_subject?
132
134
  example_group.lets
133
135
  else
@@ -42,24 +42,20 @@ module RuboCop
42
42
  # it { is_expected.to be_valid }
43
43
  # end
44
44
  class NamedSubject < Base
45
- MSG = 'Name your test subject if you need '\
46
- 'to reference it explicitly.'
45
+ MSG = 'Name your test subject if you need to reference it explicitly.'
47
46
 
48
- def_node_matcher :rspec_block?, <<-PATTERN
49
- {
50
- #{Examples::ALL.block_pattern}
51
- #{Hooks::ALL.block_pattern}
52
- }
53
- PATTERN
47
+ def_node_matcher :example_or_hook_block?,
48
+ block_pattern('{#Examples.all #Hooks.all}')
54
49
 
55
- def_node_matcher :shared_example?, <<-PATTERN
56
- #{SharedGroups::EXAMPLES.block_pattern}
57
- PATTERN
50
+ def_node_matcher :shared_example?,
51
+ block_pattern('#SharedGroups.examples')
58
52
 
59
53
  def_node_search :subject_usage, '$(send nil? :subject)'
60
54
 
61
55
  def on_block(node)
62
- return if !rspec_block?(node) || ignored_shared_example?(node)
56
+ if !example_or_hook_block?(node) || ignored_shared_example?(node)
57
+ return
58
+ end
63
59
 
64
60
  subject_usage(node) do |subject_node|
65
61
  add_offense(subject_node.loc.selector)
@@ -87,7 +87,7 @@ module RuboCop
87
87
  #
88
88
  class NestedGroups < Base
89
89
  include ConfigurableMax
90
- include RuboCop::RSpec::TopLevelGroup
90
+ include TopLevelGroup
91
91
 
92
92
  MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
93
93
 
@@ -20,6 +20,7 @@ module RuboCop
20
20
  include ConfigurableEnforcedStyle
21
21
 
22
22
  MSG = 'Prefer `%<replacement>s` over `%<original>s`.'
23
+ RESTRICT_ON_SEND = %i[not_to to_not].freeze
23
24
 
24
25
  def_node_matcher :not_to_not_offense, '(send _ % ...)'
25
26
 
@@ -24,7 +24,8 @@ module RuboCop
24
24
  class OverwritingSetup < Base
25
25
  MSG = '`%<name>s` is already defined.'
26
26
 
27
- def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern
27
+ def_node_matcher :setup?, block_pattern('{#Helpers.all #Subjects.all}')
28
+
28
29
  def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
29
30
 
30
31
  def on_block(node)
@@ -34,10 +34,10 @@ module RuboCop
34
34
  class Pending < Base
35
35
  MSG = 'Pending spec found.'
36
36
 
37
- PENDING = Examples::PENDING + Examples::SKIPPED + ExampleGroups::SKIPPED
38
- SKIPPABLE = ExampleGroups::GROUPS + Examples::EXAMPLES
39
-
40
- def_node_matcher :skippable?, SKIPPABLE.send_pattern
37
+ def_node_matcher :skippable?,
38
+ send_pattern(<<~PATTERN)
39
+ {#ExampleGroups.regular #Examples.regular}
40
+ PATTERN
41
41
 
42
42
  def_node_matcher :skipped_in_metadata?, <<-PATTERN
43
43
  {
@@ -47,7 +47,15 @@ module RuboCop
47
47
  PATTERN
48
48
 
49
49
  def_node_matcher :skip_or_pending?, '{(sym :skip) (sym :pending)}'
50
- def_node_matcher :pending_block?, PENDING.send_pattern
50
+
51
+ def_node_matcher :pending_block?,
52
+ send_pattern(<<~PATTERN)
53
+ {
54
+ #ExampleGroups.skipped
55
+ #Examples.skipped
56
+ #Examples.pending
57
+ }
58
+ PATTERN
51
59
 
52
60
  def on_send(node)
53
61
  return unless pending_block?(node) || skipped?(node)
@@ -30,7 +30,7 @@ module RuboCop
30
30
  (send nil? :expect {
31
31
  (block $(send !nil? #predicate? ...) ...)
32
32
  $(send !nil? #predicate? ...)})
33
- $#{Runners::ALL.node_pattern_union}
33
+ $#Runners.all
34
34
  $#boolean_matcher?)
35
35
  PATTERN
36
36
 
@@ -155,7 +155,7 @@ module RuboCop
155
155
  def_node_matcher :predicate_matcher?, <<-PATTERN
156
156
  (send
157
157
  (send nil? :expect $!nil?)
158
- #{Runners::ALL.node_pattern_union}
158
+ #Runners.all
159
159
  {$(send nil? #predicate_matcher_name? ...)
160
160
  (block $(send nil? #predicate_matcher_name? ...) ...)})
161
161
  PATTERN
@@ -164,7 +164,7 @@ module RuboCop
164
164
  (block
165
165
  (send
166
166
  (send nil? :expect $!nil?)
167
- #{Runners::ALL.node_pattern_union}
167
+ #Runners.all
168
168
  $(send nil? #predicate_matcher_name?))
169
169
  ...)
170
170
  PATTERN
@@ -33,6 +33,7 @@ module RuboCop
33
33
  class HttpStatus < Base
34
34
  extend AutoCorrector
35
35
  include ConfigurableEnforcedStyle
36
+ RESTRICT_ON_SEND = %i[have_http_status].freeze
36
37
 
37
38
  def_node_matcher :http_status, <<-PATTERN
38
39
  (send nil? :have_http_status ${int sym})
@@ -28,6 +28,8 @@ module RuboCop
28
28
 
29
29
  MSG = 'Use `%<alternative>s` instead of `%<original>s`.'
30
30
 
31
+ RESTRICT_ON_SEND = %i[times].freeze
32
+
31
33
  def_node_matcher :receive_counts, <<-PATTERN
32
34
  (send $(send _ {:exactly :at_least :at_most} (int {1 2})) :times)
33
35
  PATTERN
@@ -16,6 +16,7 @@ module RuboCop
16
16
  class ReceiveNever < Base
17
17
  extend AutoCorrector
18
18
  MSG = 'Use `not_to receive` instead of `never`.'
19
+ RESTRICT_ON_SEND = %i[never].freeze
19
20
 
20
21
  def_node_search :method_on_stub?, '(send nil? :receive ...)'
21
22
 
@@ -54,10 +54,11 @@ module RuboCop
54
54
  (begin <#include_examples? #include_examples? ...>)
55
55
  PATTERN
56
56
 
57
- def_node_matcher :include_examples?, Includes::EXAMPLES.send_pattern
57
+ def_node_matcher :include_examples?,
58
+ send_pattern('#Includes.examples')
58
59
 
59
60
  def_node_matcher :shared_examples_name, <<-PATTERN
60
- (send _ #{Includes::EXAMPLES.node_pattern_union} $_ ...)
61
+ (send _ #Includes.examples $_ ...)
61
62
  PATTERN
62
63
 
63
64
  def on_begin(node)
@@ -39,6 +39,7 @@ module RuboCop
39
39
 
40
40
  MSG_AND_RETURN = 'Use `and_return` for static values.'
41
41
  MSG_BLOCK = 'Use block for static values.'
42
+ RESTRICT_ON_SEND = %i[and_return].freeze
42
43
 
43
44
  def_node_search :contains_stub?, '(send nil? :receive (...))'
44
45
  def_node_matcher :stub_with_block?, '(block #contains_stub? ...)'
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # end
24
24
  #
25
25
  class ScatteredSetup < Base
26
- MSG = 'Do not define multiple `%<hook_name>s` hooks in the same '\
26
+ MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
27
27
  'example group (also defined on %<lines>s).'
28
28
 
29
29
  def on_block(node)