rubocop-rspec 1.44.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
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)