rubocop-rspec 1.43.2 → 2.0.1

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -4
  3. data/README.md +5 -1
  4. data/config/default.yml +183 -67
  5. data/lib/rubocop-rspec.rb +7 -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/around_block.rb +2 -2
  9. data/lib/rubocop/cop/rspec/base.rb +7 -54
  10. data/lib/rubocop/cop/rspec/be.rb +2 -2
  11. data/lib/rubocop/cop/rspec/before_after_all.rb +3 -3
  12. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +2 -2
  13. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +3 -2
  14. data/lib/rubocop/cop/rspec/describe_class.rb +34 -15
  15. data/lib/rubocop/cop/rspec/describe_method.rb +2 -2
  16. data/lib/rubocop/cop/rspec/described_class.rb +1 -2
  17. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -2
  18. data/lib/rubocop/cop/rspec/dialect.rb +1 -1
  19. data/lib/rubocop/cop/rspec/empty_example_group.rb +33 -38
  20. data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
  21. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
  22. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  23. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +1 -1
  24. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -1
  25. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -2
  26. data/lib/rubocop/cop/rspec/expect_actual.rb +2 -1
  27. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  28. data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
  29. data/lib/rubocop/cop/rspec/file_path.rb +1 -1
  30. data/lib/rubocop/cop/rspec/focus.rb +13 -7
  31. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -4
  32. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +2 -2
  33. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -2
  34. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  35. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  36. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  37. data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -1
  38. data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
  39. data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -2
  40. data/lib/rubocop/cop/rspec/let_setup.rb +7 -4
  41. data/lib/rubocop/cop/rspec/message_spies.rb +3 -3
  42. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +51 -0
  43. data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
  44. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
  45. data/lib/rubocop/cop/rspec/mixin/variable.rb +20 -0
  46. data/lib/rubocop/cop/rspec/multiple_describes.rb +2 -3
  47. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
  48. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +3 -1
  49. data/lib/rubocop/cop/rspec/named_subject.rb +8 -12
  50. data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
  51. data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
  52. data/lib/rubocop/cop/rspec/pending.rb +13 -5
  53. data/lib/rubocop/cop/rspec/predicate_matcher.rb +3 -3
  54. data/lib/rubocop/cop/rspec/repeated_include_example.rb +104 -0
  55. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  56. data/lib/rubocop/cop/rspec/shared_context.rb +18 -11
  57. data/lib/rubocop/cop/rspec/shared_examples.rb +3 -1
  58. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
  59. data/lib/rubocop/cop/rspec/stubbed_mock.rb +172 -0
  60. data/lib/rubocop/cop/rspec/subject_stub.rb +20 -10
  61. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
  62. data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
  63. data/lib/rubocop/cop/rspec_cops.rb +2 -1
  64. data/lib/rubocop/rspec/align_let_brace.rb +1 -1
  65. data/lib/rubocop/rspec/concept.rb +2 -2
  66. data/lib/rubocop/rspec/config_formatter.rb +5 -3
  67. data/lib/rubocop/rspec/corrector/move_node.rb +1 -1
  68. data/lib/rubocop/rspec/example_group.rb +15 -5
  69. data/lib/rubocop/rspec/hook.rb +2 -6
  70. data/lib/rubocop/rspec/inject.rb +4 -2
  71. data/lib/rubocop/rspec/language.rb +144 -105
  72. data/lib/rubocop/rspec/language/node_pattern.rb +7 -24
  73. data/lib/rubocop/rspec/node.rb +1 -1
  74. data/lib/rubocop/rspec/version.rb +1 -1
  75. metadata +25 -13
  76. data/lib/rubocop/cop/rspec/cop.rb +0 -10
  77. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +0 -41
  78. data/lib/rubocop/rspec.rb +0 -12
  79. data/lib/rubocop/rspec/empty_line_separation.rb +0 -48
  80. data/lib/rubocop/rspec/final_end_location.rb +0 -17
  81. data/lib/rubocop/rspec/top_level_describe.rb +0 -52
  82. data/lib/rubocop/rspec/top_level_group.rb +0 -57
  83. data/lib/rubocop/rspec/variable.rb +0 -16
@@ -29,7 +29,7 @@ module RuboCop
29
29
  MSG = 'Empty hook detected.'
30
30
 
31
31
  def_node_matcher :empty_hook?, <<~PATTERN
32
- (block $#{Hooks::ALL.send_pattern} _ nil?)
32
+ (block $#{send_pattern('#Hooks.all')} _ nil?)
33
33
  PATTERN
34
34
 
35
35
  def on_block(node)
@@ -43,7 +43,7 @@ module RuboCop
43
43
  #
44
44
  class EmptyLineAfterExample < Base
45
45
  extend AutoCorrector
46
- include RuboCop::RSpec::EmptyLineSeparation
46
+ include EmptyLineSeparation
47
47
 
48
48
  MSG = 'Add an empty line after `%<example>s`.'
49
49
 
@@ -25,7 +25,7 @@ module RuboCop
25
25
  #
26
26
  class EmptyLineAfterExampleGroup < Base
27
27
  extend AutoCorrector
28
- include RuboCop::RSpec::EmptyLineSeparation
28
+ include EmptyLineSeparation
29
29
 
30
30
  MSG = 'Add an empty line after `%<example_group>s`.'
31
31
 
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # it { does_something }
19
19
  class EmptyLineAfterFinalLet < Base
20
20
  extend AutoCorrector
21
- include RuboCop::RSpec::EmptyLineSeparation
21
+ include EmptyLineSeparation
22
22
 
23
23
  MSG = 'Add an empty line after the last `%<let>s`.'
24
24
 
@@ -35,7 +35,7 @@ module RuboCop
35
35
  #
36
36
  class EmptyLineAfterHook < Base
37
37
  extend AutoCorrector
38
- include RuboCop::RSpec::EmptyLineSeparation
38
+ include EmptyLineSeparation
39
39
 
40
40
  MSG = 'Add an empty line after `%<hook>s`.'
41
41
 
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # let(:foo) { bar }
17
17
  class EmptyLineAfterSubject < Base
18
18
  extend AutoCorrector
19
- include RuboCop::RSpec::EmptyLineSeparation
19
+ include EmptyLineSeparation
20
20
 
21
21
  MSG = 'Add an empty line after `%<subject>s`.'
22
22
 
@@ -32,7 +32,7 @@ module RuboCop
32
32
 
33
33
  def in_spec_block?(node)
34
34
  node.each_ancestor(:block).any? do |ancestor|
35
- Examples::ALL.include?(ancestor.method_name)
35
+ Examples.all(ancestor.method_name)
36
36
  end
37
37
  end
38
38
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  def_node_matcher :expect_literal, <<~PATTERN
49
49
  (send
50
50
  (send nil? :expect $#literal?)
51
- #{Runners::ALL.node_pattern_union}
51
+ #Runners.all
52
52
  {
53
53
  (send (send nil? $:be) :== $_)
54
54
  (send nil? $_ $_ ...)
@@ -60,6 +60,7 @@ module RuboCop
60
60
  expect_literal(node) do |actual, matcher, expected|
61
61
  add_offense(actual.source_range) do |corrector|
62
62
  next unless SUPPORTED_MATCHERS.include?(matcher)
63
+ next if literal?(expected)
63
64
 
64
65
  swap(corrector, actual, expected)
65
66
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
  class ExpectInHook < Base
24
24
  MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'
25
25
 
26
- def_node_search :expectation, Expectations::ALL.send_pattern
26
+ def_node_search :expectation, send_pattern('#Expectations.all')
27
27
 
28
28
  def on_block(node)
29
29
  return unless hook?(node)
@@ -15,7 +15,7 @@ module RuboCop
15
15
  # # good
16
16
  # expect { my_app.print_report }.to output('Hello World').to_stdout
17
17
  class ExpectOutput < Base
18
- MSG = 'Use `expect { ... }.to output(...).to_%<name>s` '\
18
+ MSG = 'Use `expect { ... }.to output(...).to_%<name>s` ' \
19
19
  'instead of mutating $%<name>s.'
20
20
 
21
21
  def on_gvasgn(node)
@@ -57,7 +57,7 @@ module RuboCop
57
57
  # my_class_spec.rb # describe MyClass, '#method'
58
58
  #
59
59
  class FilePath < Base
60
- include RuboCop::RSpec::TopLevelGroup
60
+ include TopLevelGroup
61
61
 
62
62
  MSG = 'Spec path should end with `%<suffix>s`.'
63
63
 
@@ -22,19 +22,25 @@ module RuboCop
22
22
  class Focus < Base
23
23
  MSG = 'Focused spec found.'
24
24
 
25
- focused = ExampleGroups::FOCUSED + Examples::FOCUSED
26
-
27
- def_node_matcher :focusable_selector?,
28
- (ExampleGroups::GROUPS + ExampleGroups::SKIPPED +
29
- Examples::EXAMPLES + Examples::SKIPPED +
30
- Examples::PENDING).node_pattern_union
25
+ def_node_matcher :focusable_selector?, <<-PATTERN
26
+ {
27
+ #ExampleGroups.regular
28
+ #ExampleGroups.skipped
29
+ #Examples.regular
30
+ #Examples.skipped
31
+ #Examples.pending
32
+ }
33
+ PATTERN
31
34
 
32
35
  def_node_matcher :metadata, <<-PATTERN
33
36
  {(send #rspec? #focusable_selector? <$(sym :focus) ...>)
34
37
  (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
35
38
  PATTERN
36
39
 
37
- def_node_matcher :focused_block?, focused.send_pattern
40
+ def_node_matcher :focused_block?,
41
+ send_pattern(<<~PATTERN)
42
+ {#ExampleGroups.focused #Examples.focused}
43
+ PATTERN
38
44
 
39
45
  def on_send(node)
40
46
  focus_metadata(node) do |focus|
@@ -64,13 +64,11 @@ module RuboCop
64
64
  IMPLICIT_MSG = 'Omit the default `%<scope>p` argument for RSpec hooks.'
65
65
  EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
66
66
 
67
- def_node_matcher :hook?, Hooks::ALL.node_pattern_union
68
-
69
67
  def_node_matcher :scoped_hook, <<-PATTERN
70
- (block $(send _ #hook? (sym ${:each :example})) ...)
68
+ (block $(send _ #Hooks.all (sym ${:each :example})) ...)
71
69
  PATTERN
72
70
 
73
- def_node_matcher :unscoped_hook, '(block $(send _ #hook?) ...)'
71
+ def_node_matcher :unscoped_hook, '(block $(send _ #Hooks.all) ...)'
74
72
 
75
73
  def on_block(node)
76
74
  hook(node) do |method_send, scope_name|
@@ -30,8 +30,8 @@ module RuboCop
30
30
 
31
31
  def_node_matcher :example_or_group?, <<-PATTERN
32
32
  {
33
- #{(Examples::ALL + ExampleGroups::ALL).block_pattern}
34
- #{Includes::EXAMPLES.send_pattern}
33
+ #{block_pattern('{#ExampleGroups.all #Examples.all}')}
34
+ #{send_pattern('#Includes.examples')}
35
35
  }
36
36
  PATTERN
37
37
 
@@ -22,8 +22,7 @@ module RuboCop
22
22
  def_node_matcher :lambda?, <<-PATTERN
23
23
  {
24
24
  (send (const nil? :Proc) :new)
25
- (send nil? :proc)
26
- (send nil? :lambda)
25
+ (send nil? {:proc :lambda})
27
26
  }
28
27
  PATTERN
29
28
 
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def_node_matcher :implicit_expect, <<-PATTERN
34
34
  {
35
35
  (send nil? ${:should :should_not} ...)
36
- (send (send nil? $:is_expected) #{Runners::ALL.node_pattern_union} ...)
36
+ (send (send nil? $:is_expected) #Runners.all ...)
37
37
  }
38
38
  PATTERN
39
39
 
@@ -21,7 +21,7 @@ module RuboCop
21
21
  class InstanceSpy < Base
22
22
  extend AutoCorrector
23
23
 
24
- MSG = 'Use `instance_spy` when you check your double '\
24
+ MSG = 'Use `instance_spy` when you check your double ' \
25
25
  'with `have_received`.'
26
26
 
27
27
  def_node_search :null_double, <<-PATTERN
@@ -47,7 +47,7 @@ module RuboCop
47
47
  # end
48
48
  #
49
49
  class InstanceVariable < Base
50
- include RuboCop::RSpec::TopLevelGroup
50
+ include TopLevelGroup
51
51
 
52
52
  MSG = 'Avoid instance variables – use let, ' \
53
53
  'a method call, or a local variable (if possible).'
@@ -22,7 +22,7 @@ module RuboCop
22
22
  extend AutoCorrector
23
23
  include ConfigurableEnforcedStyle
24
24
 
25
- MSG = 'Prefer `%<replacement>s` over `%<original>s` when including '\
25
+ MSG = 'Prefer `%<replacement>s` over `%<original>s` when including ' \
26
26
  'examples in a nested context.'
27
27
 
28
28
  def_node_matcher :example_inclusion_offense, '(send _ % ...)'
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # end
18
18
  class IteratedExpectation < Base
19
19
  MSG = 'Prefer using the `all` matcher instead ' \
20
- 'of iterating over an array.'
20
+ 'of iterating over an array.'
21
21
 
22
22
  def_node_matcher :each?, <<-PATTERN
23
23
  (block
@@ -37,8 +37,8 @@ module RuboCop
37
37
 
38
38
  def_node_matcher :example_or_group?, <<-PATTERN
39
39
  {
40
- #{(Examples::ALL + ExampleGroups::ALL).block_pattern}
41
- #{Includes::EXAMPLES.send_pattern}
40
+ #{block_pattern('{#ExampleGroups.all #Examples.all}')}
41
+ #{send_pattern('#Includes.examples')}
42
42
  }
43
43
  PATTERN
44
44
 
@@ -29,10 +29,13 @@ module RuboCop
29
29
  MSG = 'Do not use `let!` to setup objects not referenced in tests.'
30
30
 
31
31
  def_node_matcher :example_or_shared_group_or_including?,
32
- (
33
- ExampleGroups::ALL + SharedGroups::ALL +
34
- Includes::ALL
35
- ).block_pattern
32
+ block_pattern(<<~PATTERN)
33
+ {
34
+ #SharedGroups.all
35
+ #ExampleGroups.all
36
+ #Includes.all
37
+ }
38
+ PATTERN
36
39
 
37
40
  def_node_matcher :let_bang, <<-PATTERN
38
41
  {
@@ -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,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 =