rubocop-rspec 1.38.0 → 1.42.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/README.md +1 -61
  5. data/config/default.yml +147 -17
  6. data/lib/rubocop-rspec.rb +3 -1
  7. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +11 -18
  8. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +11 -18
  9. data/lib/rubocop/cop/rspec/be.rb +1 -1
  10. data/lib/rubocop/cop/rspec/be_eql.rb +5 -5
  11. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +18 -16
  12. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +8 -9
  13. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +69 -0
  14. data/lib/rubocop/cop/rspec/context_method.rb +5 -7
  15. data/lib/rubocop/cop/rspec/cop.rb +9 -29
  16. data/lib/rubocop/cop/rspec/describe_class.rb +20 -5
  17. data/lib/rubocop/cop/rspec/describe_method.rb +0 -1
  18. data/lib/rubocop/cop/rspec/described_class.rb +10 -7
  19. data/lib/rubocop/cop/rspec/dialect.rb +4 -11
  20. data/lib/rubocop/cop/rspec/empty_hook.rb +46 -0
  21. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +5 -3
  22. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +5 -5
  23. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +4 -1
  24. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +5 -5
  25. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +4 -1
  26. data/lib/rubocop/cop/rspec/example_wording.rb +6 -7
  27. data/lib/rubocop/cop/rspec/expect_actual.rb +7 -10
  28. data/lib/rubocop/cop/rspec/expect_change.rb +9 -34
  29. data/lib/rubocop/cop/rspec/expect_in_hook.rb +2 -2
  30. data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
  31. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +23 -20
  32. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +10 -16
  33. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +6 -7
  34. data/lib/rubocop/cop/rspec/file_path.rb +32 -4
  35. data/lib/rubocop/cop/rspec/hook_argument.rb +11 -17
  36. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +9 -28
  37. data/lib/rubocop/cop/rspec/implicit_expect.rb +6 -14
  38. data/lib/rubocop/cop/rspec/implicit_subject.rb +8 -5
  39. data/lib/rubocop/cop/rspec/instance_spy.rb +17 -11
  40. data/lib/rubocop/cop/rspec/instance_variable.rb +3 -7
  41. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +2 -5
  42. data/lib/rubocop/cop/rspec/it_behaves_like.rb +4 -5
  43. data/lib/rubocop/cop/rspec/leading_subject.rb +12 -19
  44. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -4
  45. data/lib/rubocop/cop/rspec/let_before_examples.rb +9 -25
  46. data/lib/rubocop/cop/rspec/let_setup.rb +15 -3
  47. data/lib/rubocop/cop/rspec/message_chain.rb +6 -5
  48. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
  49. data/lib/rubocop/cop/rspec/message_spies.rb +1 -2
  50. data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -18
  51. data/lib/rubocop/cop/rspec/named_subject.rb +7 -7
  52. data/lib/rubocop/cop/rspec/nested_groups.rb +9 -10
  53. data/lib/rubocop/cop/rspec/not_to_not.rb +4 -5
  54. data/lib/rubocop/cop/rspec/predicate_matcher.rb +25 -55
  55. data/lib/rubocop/cop/rspec/rails/http_status.rb +6 -8
  56. data/lib/rubocop/cop/rspec/receive_counts.rb +14 -16
  57. data/lib/rubocop/cop/rspec/receive_never.rb +10 -10
  58. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +11 -1
  59. data/lib/rubocop/cop/rspec/return_from_stub.rb +11 -21
  60. data/lib/rubocop/cop/rspec/scattered_let.rb +11 -1
  61. data/lib/rubocop/cop/rspec/shared_context.rb +7 -20
  62. data/lib/rubocop/cop/rspec/shared_examples.rb +6 -8
  63. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +14 -17
  64. data/lib/rubocop/cop/rspec/subject_stub.rb +23 -51
  65. data/lib/rubocop/cop/rspec/variable_definition.rb +56 -0
  66. data/lib/rubocop/cop/rspec/variable_name.rb +47 -0
  67. data/lib/rubocop/cop/rspec/yield.rb +13 -10
  68. data/lib/rubocop/cop/rspec_cops.rb +5 -1
  69. data/lib/rubocop/rspec/blank_line_separation.rb +0 -8
  70. data/lib/rubocop/rspec/corrector/move_node.rb +52 -0
  71. data/lib/rubocop/rspec/description_extractor.rb +2 -6
  72. data/lib/rubocop/rspec/example.rb +1 -1
  73. data/lib/rubocop/rspec/example_group.rb +21 -49
  74. data/lib/rubocop/rspec/factory_bot.rb +7 -1
  75. data/lib/rubocop/rspec/language.rb +8 -0
  76. data/lib/rubocop/rspec/language/node_pattern.rb +5 -1
  77. data/lib/rubocop/rspec/top_level_group.rb +44 -0
  78. data/lib/rubocop/rspec/variable.rb +16 -0
  79. data/lib/rubocop/rspec/version.rb +1 -1
  80. metadata +17 -10
  81. data/lib/rubocop/rspec/util.rb +0 -19
@@ -18,7 +18,6 @@ module RuboCop
18
18
  # end
19
19
  class DescribeMethod < Cop
20
20
  include RuboCop::RSpec::TopLevelDescribe
21
- include RuboCop::RSpec::Util
22
21
 
23
22
  MSG = 'The second argument to describe should be the method '\
24
23
  "being tested. '#instance' or '.class'."
@@ -55,6 +55,7 @@ module RuboCop
55
55
  # end
56
56
  #
57
57
  class DescribedClass < Cop
58
+ extend AutoCorrector
58
59
  include ConfigurableEnforcedStyle
59
60
 
60
61
  DESCRIBED_CLASS = 'described_class'
@@ -85,22 +86,24 @@ module RuboCop
85
86
  return unless body
86
87
 
87
88
  find_usage(body) do |match|
88
- add_offense(match, message: message(match.const_name))
89
+ msg = message(match.const_name)
90
+ add_offense(match, message: msg) do |corrector|
91
+ autocorrect(corrector, match)
92
+ end
89
93
  end
90
94
  end
91
95
 
92
- def autocorrect(node)
96
+ private
97
+
98
+ def autocorrect(corrector, match)
93
99
  replacement = if style == :described_class
94
100
  DESCRIBED_CLASS
95
101
  else
96
102
  @described_class.const_name
97
103
  end
98
- lambda do |corrector|
99
- corrector.replace(node.loc.expression, replacement)
100
- end
101
- end
102
104
 
103
- private
105
+ corrector.replace(match, replacement)
106
+ end
104
107
 
105
108
  def find_usage(node, &block)
106
109
  yield(node) if offensive?(node)
@@ -42,6 +42,7 @@ module RuboCop
42
42
  # # ...
43
43
  # end
44
44
  class Dialect < Cop
45
+ extend AutoCorrector
45
46
  include MethodPreference
46
47
 
47
48
  MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
@@ -52,24 +53,16 @@ module RuboCop
52
53
  return unless rspec_method?(node)
53
54
  return unless preferred_methods[node.method_name]
54
55
 
55
- add_offense(node)
56
- end
56
+ msg = format(MSG, prefer: preferred_method(node.method_name),
57
+ current: node.method_name)
57
58
 
58
- def autocorrect(node)
59
- lambda do |corrector|
59
+ add_offense(node, message: msg) do |corrector|
60
60
  current = node.loc.selector
61
61
  preferred = preferred_method(current.source)
62
62
 
63
63
  corrector.replace(current, preferred)
64
64
  end
65
65
  end
66
-
67
- private
68
-
69
- def message(node)
70
- format(MSG, prefer: preferred_method(node.method_name),
71
- current: node.method_name)
72
- end
73
66
  end
74
67
  end
75
68
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for empty before and after hooks.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # before {}
11
+ # after do; end
12
+ # before(:all) do
13
+ # end
14
+ # after(:all) { }
15
+ #
16
+ # # good
17
+ # before { create_users }
18
+ # after do
19
+ # cleanup_users
20
+ # end
21
+ # before(:all) do
22
+ # create_feed
23
+ # end
24
+ # after(:all) { cleanup_feed }
25
+ class EmptyHook < Cop
26
+ extend AutoCorrector
27
+ include RuboCop::Cop::RangeHelp
28
+
29
+ MSG = 'Empty hook detected.'
30
+
31
+ def_node_matcher :empty_hook?, <<~PATTERN
32
+ (block $#{Hooks::ALL.send_pattern} _ nil?)
33
+ PATTERN
34
+
35
+ def on_block(node)
36
+ empty_hook?(node) do |hook|
37
+ add_offense(hook) do |corrector|
38
+ range = range_with_surrounding_space(range: node.loc.expression)
39
+ corrector.remove(range)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -42,6 +42,7 @@ module RuboCop
42
42
  # end
43
43
  #
44
44
  class EmptyLineAfterExample < Cop
45
+ extend AutoCorrector
45
46
  include RuboCop::RSpec::BlankLineSeparation
46
47
 
47
48
  MSG = 'Add an empty line after `%<example>s`.'
@@ -52,9 +53,10 @@ module RuboCop
52
53
  return if allowed_one_liner?(node)
53
54
 
54
55
  missing_separating_line(node) do |location|
55
- add_offense(node,
56
- location: location,
57
- message: format(MSG, example: node.method_name))
56
+ msg = format(MSG, example: node.method_name)
57
+ add_offense(location, message: msg) do |corrector|
58
+ corrector.insert_after(location.end, "\n")
59
+ end
58
60
  end
59
61
  end
60
62
 
@@ -24,6 +24,7 @@ module RuboCop
24
24
  # end
25
25
  #
26
26
  class EmptyLineAfterExampleGroup < Cop
27
+ extend AutoCorrector
27
28
  include RuboCop::RSpec::BlankLineSeparation
28
29
 
29
30
  MSG = 'Add an empty line after `%<example_group>s`.'
@@ -33,11 +34,10 @@ module RuboCop
33
34
  return if last_child?(node)
34
35
 
35
36
  missing_separating_line(node) do |location|
36
- add_offense(
37
- node,
38
- location: location,
39
- message: format(MSG, example_group: node.method_name)
40
- )
37
+ msg = format(MSG, example_group: node.method_name)
38
+ add_offense(location, message: msg) do |corrector|
39
+ corrector.insert_after(location.end, "\n")
40
+ end
41
41
  end
42
42
  end
43
43
  end
@@ -17,6 +17,7 @@ module RuboCop
17
17
  #
18
18
  # it { does_something }
19
19
  class EmptyLineAfterFinalLet < Cop
20
+ extend AutoCorrector
20
21
  include RuboCop::RSpec::BlankLineSeparation
21
22
 
22
23
  MSG = 'Add an empty line after the last `let` block.'
@@ -30,7 +31,9 @@ module RuboCop
30
31
  return if last_child?(latest_let)
31
32
 
32
33
  missing_separating_line(latest_let) do |location|
33
- add_offense(latest_let, location: location)
34
+ add_offense(location) do |corrector|
35
+ corrector.insert_after(location.end, "\n")
36
+ end
34
37
  end
35
38
  end
36
39
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
  # it { does_something }
35
35
  #
36
36
  class EmptyLineAfterHook < Cop
37
+ extend AutoCorrector
37
38
  include RuboCop::RSpec::BlankLineSeparation
38
39
 
39
40
  MSG = 'Add an empty line after `%<hook>s`.'
@@ -43,11 +44,10 @@ module RuboCop
43
44
  return if last_child?(node)
44
45
 
45
46
  missing_separating_line(node) do |location|
46
- add_offense(
47
- node,
48
- location: location,
49
- message: format(MSG, hook: node.method_name)
50
- )
47
+ msg = format(MSG, hook: node.method_name)
48
+ add_offense(location, message: msg) do |corrector|
49
+ corrector.insert_after(location.end, "\n")
50
+ end
51
51
  end
52
52
  end
53
53
  end
@@ -15,6 +15,7 @@ module RuboCop
15
15
  #
16
16
  # let(:foo) { bar }
17
17
  class EmptyLineAfterSubject < Cop
18
+ extend AutoCorrector
18
19
  include RuboCop::RSpec::BlankLineSeparation
19
20
 
20
21
  MSG = 'Add empty line after `subject`.'
@@ -24,7 +25,9 @@ module RuboCop
24
25
  return if last_child?(node)
25
26
 
26
27
  missing_separating_line(node) do |location|
27
- add_offense(node, location: location)
28
+ add_offense(location) do |corrector|
29
+ corrector.insert_after(location.end, "\n")
30
+ end
28
31
  end
29
32
  end
30
33
 
@@ -30,6 +30,8 @@ module RuboCop
30
30
  # it 'does things' do
31
31
  # end
32
32
  class ExampleWording < Cop
33
+ extend AutoCorrector
34
+
33
35
  MSG_SHOULD = 'Do not use should when describing your tests.'
34
36
  MSG_IT = "Do not repeat 'it' when describing your tests."
35
37
 
@@ -53,16 +55,13 @@ module RuboCop
53
55
  end
54
56
  end
55
57
 
56
- def autocorrect(node)
57
- lambda do |corrector|
58
- corrector.replace(docstring(node), replacement_text(node))
59
- end
60
- end
61
-
62
58
  private
63
59
 
64
60
  def add_wording_offense(node, message)
65
- add_offense(node, location: docstring(node), message: message)
61
+ docstring = docstring(node)
62
+ add_offense(docstring, message: message) do |corrector|
63
+ corrector.replace(docstring, replacement_text(node))
64
+ end
66
65
  end
67
66
 
68
67
  def docstring(node)
@@ -17,6 +17,8 @@ module RuboCop
17
17
  # expect(name).to eq("John")
18
18
  #
19
19
  class ExpectActual < Cop
20
+ extend AutoCorrector
21
+
20
22
  MSG = 'Provide the actual you are testing to `expect(...)`.'
21
23
 
22
24
  SIMPLE_LITERALS = %i[
@@ -55,17 +57,12 @@ module RuboCop
55
57
  PATTERN
56
58
 
57
59
  def on_send(node)
58
- expect_literal(node) do |argument|
59
- add_offense(node, location: argument.source_range)
60
- end
61
- end
62
-
63
- def autocorrect(node)
64
- actual, matcher, expected = expect_literal(node)
65
- lambda do |corrector|
66
- return unless SUPPORTED_MATCHERS.include?(matcher)
60
+ expect_literal(node) do |actual, matcher, expected|
61
+ add_offense(actual.source_range) do |corrector|
62
+ next unless SUPPORTED_MATCHERS.include?(matcher)
67
63
 
68
- swap(corrector, actual, expected)
64
+ swap(corrector, actual, expected)
65
+ end
69
66
  end
70
67
  end
71
68
 
@@ -30,6 +30,7 @@ module RuboCop
30
30
  # expect { run }.to change { user.reload.name }
31
31
  #
32
32
  class ExpectChange < Cop
33
+ extend AutoCorrector
33
34
  include ConfigurableEnforcedStyle
34
35
 
35
36
  MSG_BLOCK = 'Prefer `change(%<obj>s, :%<attr>s)`.'
@@ -51,10 +52,11 @@ module RuboCop
51
52
  return unless style == :block
52
53
 
53
54
  expect_change_with_arguments(node) do |receiver, message|
54
- add_offense(
55
- node,
56
- message: format(MSG_CALL, obj: receiver, attr: message)
57
- )
55
+ msg = format(MSG_CALL, obj: receiver, attr: message)
56
+ add_offense(node, message: msg) do |corrector|
57
+ replacement = "change { #{receiver}.#{message} }"
58
+ corrector.replace(node, replacement)
59
+ end
58
60
  end
59
61
  end
60
62
 
@@ -62,37 +64,10 @@ module RuboCop
62
64
  return unless style == :method_call
63
65
 
64
66
  expect_change_with_block(node) do |receiver, message|
65
- add_offense(
66
- node,
67
- message: format(MSG_BLOCK, obj: receiver, attr: message)
68
- )
69
- end
70
- end
71
-
72
- def autocorrect(node)
73
- if style == :block
74
- autocorrect_method_call_to_block(node)
75
- else
76
- autocorrect_block_to_method_call(node)
77
- end
78
- end
79
-
80
- private
81
-
82
- def autocorrect_method_call_to_block(node)
83
- lambda do |corrector|
84
- expect_change_with_arguments(node) do |receiver, message|
85
- replacement = "change { #{receiver}.#{message} }"
86
- corrector.replace(node.loc.expression, replacement)
87
- end
88
- end
89
- end
90
-
91
- def autocorrect_block_to_method_call(node)
92
- lambda do |corrector|
93
- expect_change_with_block(node) do |receiver, message|
67
+ msg = format(MSG_BLOCK, obj: receiver, attr: message)
68
+ add_offense(node, message: msg) do |corrector|
94
69
  replacement = "change(#{receiver}, :#{message})"
95
- corrector.replace(node.loc.expression, replacement)
70
+ corrector.replace(node, replacement)
96
71
  end
97
72
  end
98
73
  end
@@ -30,8 +30,8 @@ module RuboCop
30
30
  return if node.body.nil?
31
31
 
32
32
  expectation(node.body) do |expect|
33
- add_offense(expect, location: :selector,
34
- message: message(expect, node))
33
+ add_offense(expect.loc.selector,
34
+ message: message(expect, node))
35
35
  end
36
36
  end
37
37
 
@@ -27,7 +27,7 @@ module RuboCop
27
27
  name = variable_name[1..-1]
28
28
  return unless name.eql?('stdout') || name.eql?('stderr')
29
29
 
30
- add_offense(node, location: :name, message: format(MSG, name: name))
30
+ add_offense(node.loc.name, message: format(MSG, name: name))
31
31
  end
32
32
 
33
33
  private
@@ -25,35 +25,42 @@ module RuboCop
25
25
  # # good
26
26
  # count { 1 }
27
27
  class AttributeDefinedStatically < Cop
28
+ extend AutoCorrector
29
+
28
30
  MSG = 'Use a block to declare attribute values.'
29
31
 
30
32
  def_node_matcher :value_matcher, <<-PATTERN
31
33
  (send _ !#reserved_method? $...)
32
34
  PATTERN
33
35
 
34
- def_node_search :factory_attributes, <<-PATTERN
36
+ def_node_matcher :factory_attributes, <<-PATTERN
35
37
  (block (send _ #attribute_defining_method? ...) _ { (begin $...) $(send ...) } )
36
38
  PATTERN
37
39
 
38
40
  def on_block(node)
39
- factory_attributes(node).to_a.flatten.each do |attribute|
41
+ attributes = factory_attributes(node) || []
42
+ attributes = [attributes] unless attributes.is_a?(Array)
43
+
44
+ attributes.each do |attribute|
40
45
  next unless offensive_receiver?(attribute.receiver, node)
41
46
  next if proc?(attribute) || association?(attribute.first_argument)
42
47
 
43
- add_offense(attribute)
48
+ add_offense(attribute) do |corrector|
49
+ autocorrect(corrector, attribute)
50
+ end
44
51
  end
45
52
  end
46
53
 
47
- def autocorrect(node)
54
+ private
55
+
56
+ def autocorrect(corrector, node)
48
57
  if node.parenthesized?
49
- autocorrect_replacing_parens(node)
58
+ autocorrect_replacing_parens(corrector, node)
50
59
  else
51
- autocorrect_without_parens(node)
60
+ autocorrect_without_parens(corrector, node)
52
61
  end
53
62
  end
54
63
 
55
- private
56
-
57
64
  def offensive_receiver?(receiver, node)
58
65
  receiver.nil? ||
59
66
  receiver.self_type? ||
@@ -74,24 +81,20 @@ module RuboCop
74
81
 
75
82
  def_node_matcher :association?, '(hash <(pair (sym :factory) _) ...>)'
76
83
 
77
- def autocorrect_replacing_parens(node)
84
+ def autocorrect_replacing_parens(corrector, node)
78
85
  left_braces, right_braces = braces(node)
79
86
 
80
- lambda do |corrector|
81
- corrector.replace(node.location.begin, ' ' + left_braces)
82
- corrector.replace(node.location.end, right_braces)
83
- end
87
+ corrector.replace(node.location.begin, ' ' + left_braces)
88
+ corrector.replace(node.location.end, right_braces)
84
89
  end
85
90
 
86
- def autocorrect_without_parens(node)
91
+ def autocorrect_without_parens(corrector, node)
87
92
  left_braces, right_braces = braces(node)
88
93
 
89
- lambda do |corrector|
90
- argument = node.first_argument
91
- expression = argument.location.expression
92
- corrector.insert_before(expression, left_braces)
93
- corrector.insert_after(expression, right_braces)
94
- end
94
+ argument = node.first_argument
95
+ expression = argument.location.expression
96
+ corrector.insert_before(expression, left_braces)
97
+ corrector.insert_after(expression, right_braces)
95
98
  end
96
99
 
97
100
  def braces(node)