rubocop-rspec 2.29.1 → 3.5.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -3
  3. data/README.md +21 -5
  4. data/config/default.yml +64 -269
  5. data/config/obsoletion.yml +20 -21
  6. data/lib/rubocop/cop/rspec/around_block.rb +2 -4
  7. data/lib/rubocop/cop/rspec/base.rb +0 -1
  8. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  9. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  10. data/lib/rubocop/cop/rspec/be_nil.rb +4 -0
  11. data/lib/rubocop/cop/rspec/change_by_zero.rb +4 -5
  12. data/lib/rubocop/cop/rspec/contain_exactly.rb +1 -0
  13. data/lib/rubocop/cop/rspec/context_wording.rb +15 -9
  14. data/lib/rubocop/cop/rspec/dialect.rb +13 -0
  15. data/lib/rubocop/cop/rspec/empty_example_group.rb +2 -0
  16. data/lib/rubocop/cop/rspec/empty_metadata.rb +1 -0
  17. data/lib/rubocop/cop/rspec/example_wording.rb +3 -5
  18. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
  19. data/lib/rubocop/cop/rspec/expect_actual.rb +3 -3
  20. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  21. data/lib/rubocop/cop/rspec/expect_in_let.rb +42 -0
  22. data/lib/rubocop/cop/rspec/focus.rb +14 -16
  23. data/lib/rubocop/cop/rspec/implicit_expect.rb +3 -3
  24. data/lib/rubocop/cop/rspec/implicit_subject.rb +8 -0
  25. data/lib/rubocop/cop/rspec/indexed_let.rb +6 -3
  26. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
  27. data/lib/rubocop/cop/rspec/match_array.rb +1 -0
  28. data/lib/rubocop/cop/rspec/message_spies.rb +4 -0
  29. data/lib/rubocop/cop/rspec/metadata_style.rb +1 -6
  30. data/lib/rubocop/cop/rspec/missing_expectation_target_method.rb +54 -0
  31. data/lib/rubocop/cop/rspec/mixin/metadata.rb +5 -8
  32. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +7 -0
  33. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -1
  34. data/lib/rubocop/cop/rspec/multiple_expectations.rb +4 -4
  35. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +4 -5
  36. data/lib/rubocop/cop/rspec/named_subject.rb +5 -2
  37. data/lib/rubocop/cop/rspec/nested_groups.rb +2 -1
  38. data/lib/rubocop/cop/rspec/pending_without_reason.rb +0 -5
  39. data/lib/rubocop/cop/rspec/predicate_matcher.rb +25 -11
  40. data/lib/rubocop/cop/rspec/remove_const.rb +0 -1
  41. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +1 -0
  42. data/lib/rubocop/cop/rspec/return_from_stub.rb +5 -4
  43. data/lib/rubocop/cop/rspec/scattered_setup.rb +7 -1
  44. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +3 -4
  45. data/lib/rubocop/cop/rspec/sort_metadata.rb +22 -8
  46. data/lib/rubocop/cop/rspec/stubbed_mock.rb +15 -10
  47. data/lib/rubocop/cop/rspec/subject_stub.rb +2 -2
  48. data/lib/rubocop/cop/rspec/unspecified_exception.rb +21 -14
  49. data/lib/rubocop/cop/rspec/verified_double_reference.rb +14 -53
  50. data/lib/rubocop/cop/rspec/void_expect.rb +6 -1
  51. data/lib/rubocop/cop/rspec_cops.rb +2 -25
  52. data/lib/rubocop/rspec/concept.rb +0 -1
  53. data/lib/rubocop/rspec/config_formatter.rb +4 -32
  54. data/lib/rubocop/rspec/cop/generator.rb +25 -0
  55. data/lib/rubocop/rspec/description_extractor.rb +2 -2
  56. data/lib/rubocop/rspec/hook.rb +1 -1
  57. data/lib/rubocop/rspec/language.rb +0 -1
  58. data/lib/rubocop/rspec/node.rb +1 -1
  59. data/lib/rubocop/rspec/plugin.rb +37 -0
  60. data/lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb +1 -1
  61. data/lib/rubocop/rspec/version.rb +1 -1
  62. data/lib/rubocop/rspec/wording.rb +2 -4
  63. data/lib/rubocop/rspec.rb +0 -7
  64. data/lib/rubocop-rspec.rb +2 -20
  65. metadata +18 -62
  66. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +0 -39
  67. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +0 -104
  68. data/lib/rubocop/cop/rspec/capybara/match_style.rb +0 -38
  69. data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +0 -33
  70. data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +0 -29
  71. data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +0 -24
  72. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +0 -35
  73. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +0 -36
  74. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +0 -35
  75. data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +0 -50
  76. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +0 -40
  77. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +0 -29
  78. data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +0 -33
  79. data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +0 -55
  80. data/lib/rubocop/cop/rspec/file_path.rb +0 -179
  81. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +0 -27
  82. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +0 -35
  83. data/lib/rubocop/cop/rspec/rails/http_status.rb +0 -61
  84. data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +0 -62
  85. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +0 -39
  86. data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +0 -39
  87. data/lib/rubocop/cop/rspec/rails/travel_around.rb +0 -34
  88. data/lib/rubocop/rspec/inject.rb +0 -18
  89. data/lib/rubocop/rspec/language/node_pattern.rb +0 -48
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  # @!method eql_type_with_identity(node)
47
47
  def_node_matcher :eql_type_with_identity, <<~PATTERN
48
- (send _ :to $(send nil? :eql {true false int float sym nil}))
48
+ (send _ :to $(send nil? :eql {boolean int float sym nil}))
49
49
  PATTERN
50
50
 
51
51
  def on_send(node)
@@ -48,6 +48,10 @@ module RuboCop
48
48
  check_be_style(node)
49
49
  when :be_nil
50
50
  check_be_nil_style(node)
51
+ else
52
+ # :nocov:
53
+ :noop
54
+ # :nocov:
51
55
  end
52
56
  end
53
57
 
@@ -101,24 +101,23 @@ module RuboCop
101
101
 
102
102
  private
103
103
 
104
- # rubocop:disable Metrics/MethodLength
105
104
  def register_offense(node, change_node)
106
105
  if compound_expectations?(node)
107
- add_offense(node.source_range,
106
+ add_offense(node,
108
107
  message: message_compound(change_node)) do |corrector|
109
108
  autocorrect_compound(corrector, node)
110
109
  end
111
110
  else
112
- add_offense(node.source_range,
111
+ add_offense(node,
113
112
  message: message(change_node)) do |corrector|
114
113
  autocorrect(corrector, node, change_node)
115
114
  end
116
115
  end
117
116
  end
118
- # rubocop:enable Metrics/MethodLength
119
117
 
120
118
  def compound_expectations?(node)
121
- %i[and or & |].include?(node.parent.method_name)
119
+ node.parent.send_type? &&
120
+ %i[and or & |].include?(node.parent.method_name)
122
121
  end
123
122
 
124
123
  def message(change_node)
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # Checks where `contain_exactly` is used.
7
7
  #
8
8
  # This cop checks for the following:
9
+ #
9
10
  # - Prefer `match_array` when matching array values.
10
11
  # - Prefer `be_empty` when using `contain_exactly` with no arguments.
11
12
  #
@@ -12,6 +12,9 @@ module RuboCop
12
12
  #
13
13
  # @see http://www.betterspecs.org/#contexts
14
14
  #
15
+ # If both `Prefixes` and `AllowedPatterns` are empty, this cop will always
16
+ # report an offense. So you need to set at least one of them.
17
+ #
15
18
  # @example `Prefixes` configuration
16
19
  # # .rubocop.yml
17
20
  # # RSpec/ContextWording:
@@ -58,7 +61,9 @@ module RuboCop
58
61
  class ContextWording < Base
59
62
  include AllowedPattern
60
63
 
61
- MSG = 'Context description should match %<patterns>s.'
64
+ MSG_MATCH = 'Context description should match %<patterns>s.'
65
+ MSG_ALWAYS = 'Current settings will always report an offense. Please ' \
66
+ 'add allowed words to `Prefixes` or `AllowedPatterns`.'
62
67
 
63
68
  # @!method context_wording(node)
64
69
  def_node_matcher :context_wording, <<~PATTERN
@@ -67,8 +72,7 @@ module RuboCop
67
72
 
68
73
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
69
74
  context_wording(node) do |context|
70
- if bad_pattern?(context)
71
- message = format(MSG, patterns: expect_patterns)
75
+ unless matches_allowed_pattern?(description(context))
72
76
  add_offense(context, message: message)
73
77
  end
74
78
  end
@@ -84,12 +88,6 @@ module RuboCop
84
88
  @prefix_regexes ||= prefixes.map { |pre| /^#{Regexp.escape(pre)}\b/ }
85
89
  end
86
90
 
87
- def bad_pattern?(node)
88
- return false if allowed_patterns.empty?
89
-
90
- !matches_allowed_pattern?(description(node))
91
- end
92
-
93
91
  def description(context)
94
92
  if context.xstr_type?
95
93
  context.value.value
@@ -98,6 +96,14 @@ module RuboCop
98
96
  end
99
97
  end
100
98
 
99
+ def message
100
+ if allowed_patterns.empty?
101
+ MSG_ALWAYS
102
+ else
103
+ format(MSG_MATCH, patterns: expect_patterns)
104
+ end
105
+ end
106
+
101
107
  def expect_patterns
102
108
  inspected = allowed_patterns.map do |pattern|
103
109
  pattern.inspect.gsub(/\A"|"\z/, '/')
@@ -29,6 +29,19 @@ module RuboCop
29
29
  # PreferredMethods:
30
30
  # context: describe
31
31
  #
32
+ # If you were previously using the `RSpec/Capybara/FeatureMethods` cop and
33
+ # want to keep disabling all Capybara-specific methods that have the same
34
+ # native RSpec method (e.g. are just aliases), use the following config:
35
+ #
36
+ # RSpec/Dialect:
37
+ # PreferredMethods:
38
+ # background: :before
39
+ # scenario: :it
40
+ # xscenario: :xit
41
+ # given: :let
42
+ # given!: :let!
43
+ # feature: :describe
44
+ #
32
45
  # You can expect the following behavior:
33
46
  #
34
47
  # @example
@@ -130,6 +130,7 @@ module RuboCop
130
130
  def_node_matcher :examples?, <<~PATTERN
131
131
  {
132
132
  #examples_directly_or_in_block?
133
+ #examples_in_branches?
133
134
  (begin <#examples_directly_or_in_block? ...>)
134
135
  (begin <#examples_in_branches? ...>)
135
136
  }
@@ -170,6 +171,7 @@ module RuboCop
170
171
  end
171
172
 
172
173
  def examples_in_branches?(condition_node)
174
+ return false unless condition_node
173
175
  return false if !condition_node.if_type? && !condition_node.case_type?
174
176
 
175
177
  condition_node.branches.any? { |branch| examples?(branch) }
@@ -21,6 +21,7 @@ module RuboCop
21
21
 
22
22
  def on_metadata(_symbols, hash)
23
23
  return unless hash&.pairs&.empty?
24
+ return if hash.children.any?(&:kwsplat_type?)
24
25
 
25
26
  add_offense(hash) do |corrector|
26
27
  remove_empty_metadata(corrector, hash)
@@ -55,8 +55,8 @@ module RuboCop
55
55
  MSG_INSUFFICIENT_DESCRIPTION = 'Your example description is ' \
56
56
  'insufficient.'
57
57
 
58
- SHOULD_PREFIX = /\Ashould(?:n't)?\b/i.freeze
59
- WILL_PREFIX = /\A(?:will|won't)\b/i.freeze
58
+ SHOULD_PREFIX = /\Ashould(?:n't|n’t)?\b/i.freeze
59
+ WILL_PREFIX = /\A(?:will|won't|won’t)\b/i.freeze
60
60
  IT_PREFIX = /\Ait /i.freeze
61
61
 
62
62
  # @!method it_description(node)
@@ -67,7 +67,6 @@ module RuboCop
67
67
  } ...) ...)
68
68
  PATTERN
69
69
 
70
- # rubocop:disable Metrics/MethodLength
71
70
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
72
71
  it_description(node) do |description_node, message|
73
72
  if message.match?(SHOULD_PREFIX)
@@ -82,7 +81,6 @@ module RuboCop
82
81
  end
83
82
  end
84
83
  end
85
- # rubocop:enable Metrics/MethodLength
86
84
 
87
85
  private
88
86
 
@@ -128,7 +126,7 @@ module RuboCop
128
126
  node.node_parts.map { |child_node| text(child_node) }.join
129
127
  when :str
130
128
  node.value
131
- when :begin
129
+ else
132
130
  node.source
133
131
  end
134
132
  end
@@ -100,7 +100,7 @@ module RuboCop
100
100
  node.node_parts.map { |child_node| text(child_node) }.join
101
101
  when :str, :sym
102
102
  node.value
103
- when :begin
103
+ else
104
104
  node.source
105
105
  end
106
106
  end
@@ -65,11 +65,11 @@ module RuboCop
65
65
  )
66
66
  PATTERN
67
67
 
68
- def on_send(node) # rubocop:disable Metrics/MethodLength
68
+ def on_send(node)
69
69
  expect_literal(node) do |actual, send_node, matcher, expected|
70
70
  next if SKIPPED_MATCHERS.include?(matcher)
71
71
 
72
- add_offense(actual.source_range) do |corrector|
72
+ add_offense(actual) do |corrector|
73
73
  next unless CORRECTABLE_MATCHERS.include?(matcher)
74
74
  next if literal?(expected)
75
75
 
@@ -97,7 +97,7 @@ module RuboCop
97
97
 
98
98
  def complex_literal?(node)
99
99
  COMPLEX_LITERALS.include?(node.type) &&
100
- node.each_child_node.all?(&method(:literal?))
100
+ node.each_child_node.all? { |child_node| literal?(child_node) }
101
101
  end
102
102
  end
103
103
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  # @!method expectation(node)
28
28
  def_node_search :expectation, '(send nil? #Expectations.all ...)'
29
29
 
30
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
30
+ def on_block(node)
31
31
  return unless hook?(node)
32
32
  return if node.body.nil?
33
33
 
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Do not use `expect` in let.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # let(:foo) do
11
+ # expect(something).to eq 'foo'
12
+ # end
13
+ #
14
+ # # good
15
+ # it do
16
+ # expect(something).to eq 'foo'
17
+ # end
18
+ #
19
+ class ExpectInLet < Base
20
+ MSG = 'Do not use `%<expect>s` in let'
21
+
22
+ # @!method expectation(node)
23
+ def_node_search :expectation, '(send nil? #Expectations.all ...)'
24
+
25
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
26
+ return unless let?(node)
27
+ return if node.body.nil?
28
+
29
+ expectation(node.body) do |expect|
30
+ add_offense(expect.loc.selector, message: message(expect))
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def message(expect)
37
+ format(MSG, expect: expect.method_name)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -29,12 +29,6 @@ module RuboCop
29
29
  # describe 'test' do; end
30
30
  #
31
31
  # # bad
32
- # fdescribe 'test' do; end
33
- #
34
- # # good
35
- # describe 'test' do; end
36
- #
37
- # # bad
38
32
  # shared_examples 'test', focus: true do; end
39
33
  #
40
34
  # # good
@@ -81,23 +75,27 @@ module RuboCop
81
75
  def on_send(node)
82
76
  return if node.chained? || node.each_ancestor(:def, :defs).any?
83
77
 
84
- focus_metadata(node) do |focus|
85
- add_offense(focus) do |corrector|
86
- if focus.pair_type? || focus.str_type? || focus.sym_type?
87
- corrector.remove(with_surrounding(focus))
88
- elsif focus.send_type?
89
- correct_send(corrector, focus)
90
- end
78
+ if focused_block?(node)
79
+ on_focused_block(node)
80
+ else
81
+ metadata(node) do |focus|
82
+ on_metadata(focus)
91
83
  end
92
84
  end
93
85
  end
94
86
 
95
87
  private
96
88
 
97
- def focus_metadata(node, &block)
98
- yield(node) if focused_block?(node)
89
+ def on_focused_block(node)
90
+ add_offense(node) do |corrector|
91
+ correct_send(corrector, node)
92
+ end
93
+ end
99
94
 
100
- metadata(node, &block)
95
+ def on_metadata(node)
96
+ add_offense(node) do |corrector|
97
+ corrector.remove(with_surrounding(node))
98
+ end
101
99
  end
102
100
 
103
101
  def with_surrounding(focus)
@@ -46,7 +46,7 @@ module RuboCop
46
46
 
47
47
  ENFORCED_REPLACEMENTS = alternatives.merge(alternatives.invert).freeze
48
48
 
49
- def on_send(node) # rubocop:disable Metrics/MethodLength
49
+ def on_send(node)
50
50
  return unless (source_range = offending_expect(node))
51
51
 
52
52
  expectation_source = source_range.source
@@ -69,13 +69,13 @@ module RuboCop
69
69
  def offending_expect(node)
70
70
  case implicit_expect(node)
71
71
  when :is_expected
72
- is_expected_range(node.loc)
72
+ range_for_is_expected(node.loc)
73
73
  when :should, :should_not
74
74
  node.loc.selector
75
75
  end
76
76
  end
77
77
 
78
- def is_expected_range(source_map) # rubocop:disable Naming/PredicateName
78
+ def range_for_is_expected(source_map)
79
79
  Parser::Source::Range.new(
80
80
  source_map.expression.source_buffer,
81
81
  source_map.expression.begin_pos,
@@ -107,6 +107,10 @@ module RuboCop
107
107
  corrector.replace(node.location.selector, 'expect(subject).to')
108
108
  when :should_not
109
109
  corrector.replace(node.location.selector, 'expect(subject).not_to')
110
+ else
111
+ # :nocov:
112
+ :noop
113
+ # :nocov:
110
114
  end
111
115
  end
112
116
 
@@ -129,6 +133,10 @@ module RuboCop
129
133
  implicit_subject_in_non_its_and_non_single_line?(node)
130
134
  when :single_statement_only
131
135
  implicit_subject_in_non_its_and_non_single_statement?(node)
136
+ else
137
+ # :nocov:
138
+ :noop
139
+ # :nocov:
132
140
  end
133
141
  end
134
142
 
@@ -48,8 +48,8 @@ module RuboCop
48
48
  include AllowedIdentifiers
49
49
  include AllowedPattern
50
50
 
51
- MSG = 'This `let` statement uses index in its name. Please give it ' \
52
- 'a meaningful name.'
51
+ MSG = 'This `let` statement uses `%<index>s` in its name. ' \
52
+ 'Please give it a meaningful name.'
53
53
 
54
54
  # @!method let_name(node)
55
55
  def_node_matcher :let_name, <<~PATTERN
@@ -66,14 +66,17 @@ module RuboCop
66
66
  return unless children
67
67
 
68
68
  filter_indexed_lets(children).each do |let_node|
69
- add_offense(let_node)
69
+ index = let_name(let_node)[INDEX_REGEX]
70
+ add_offense(let_node, message: format(MSG, index: index))
70
71
  end
71
72
  end
72
73
 
73
74
  private
74
75
 
75
76
  SUFFIX_INDEX_REGEX = /_?\d+$/.freeze
77
+ private_constant :SUFFIX_INDEX_REGEX
76
78
  INDEX_REGEX = /\d+/.freeze
79
+ private_constant :INDEX_REGEX
77
80
 
78
81
  def filter_indexed_lets(candidates)
79
82
  candidates
@@ -119,7 +119,7 @@ module RuboCop
119
119
  private
120
120
 
121
121
  def inside_describe_block?(node)
122
- node.each_ancestor(:block).any?(&method(:spec_group?))
122
+ node.each_ancestor(:block).any? { |ancestor| spec_group?(ancestor) }
123
123
  end
124
124
  end
125
125
  end
@@ -6,6 +6,7 @@ module RuboCop
6
6
  # Checks where `match_array` is used.
7
7
  #
8
8
  # This cop checks for the following:
9
+ #
9
10
  # - Prefer `contain_exactly` when matching an array with values.
10
11
  # - Prefer `eq` when using `match_array` with an empty array literal.
11
12
  #
@@ -80,6 +80,10 @@ module RuboCop
80
80
  MSG_RECEIVE
81
81
  when :have_received
82
82
  format(MSG_HAVE_RECEIVED, source: receiver.source)
83
+ else
84
+ # :nocov:
85
+ :noop
86
+ # :nocov:
83
87
  end
84
88
  end
85
89
  end
@@ -45,13 +45,8 @@ module RuboCop
45
45
  PATTERN
46
46
 
47
47
  def on_metadata(symbols, hash)
48
- # RSpec example groups accept two string arguments. In such a case,
49
- # the rspec_metadata matcher will interpret the second string
50
- # argument as a metadata symbol.
51
- symbols.shift if symbols.first&.str_type?
52
-
53
48
  symbols.each do |symbol|
54
- on_metadata_symbol(symbol)
49
+ on_metadata_symbol(symbol) if symbol.sym_type?
55
50
  end
56
51
 
57
52
  return unless hash
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks if `.to`, `not_to` or `to_not` are used.
7
+ #
8
+ # The RSpec::Expectations::ExpectationTarget must use `to`, `not_to` or
9
+ # `to_not` to run. Therefore, this cop checks if other methods are used.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # expect(something).kind_of? Foo
14
+ # is_expected == 42
15
+ # expect{something}.eq? BarError
16
+ #
17
+ # # good
18
+ # expect(something).to be_a Foo
19
+ # is_expected.to eq 42
20
+ # expect{something}.to raise_error BarError
21
+ #
22
+ class MissingExpectationTargetMethod < Base
23
+ MSG = 'Use `.to`, `.not_to` or `.to_not` to set an expectation.'
24
+ RESTRICT_ON_SEND = %i[expect is_expected].freeze
25
+
26
+ # @!method expect?(node)
27
+ def_node_matcher :expect?, <<~PATTERN
28
+ {
29
+ (send nil? :expect ...)
30
+ (send nil? :is_expected)
31
+ }
32
+ PATTERN
33
+
34
+ # @!method expect_block?(node)
35
+ def_node_matcher :expect_block?, <<~PATTERN
36
+ (block #expect? (args) _body)
37
+ PATTERN
38
+
39
+ # @!method expectation_without_runner?(node)
40
+ def_node_matcher :expectation_without_runner?, <<~PATTERN
41
+ (send {#expect? #expect_block?} !#Runners.all ...)
42
+ PATTERN
43
+
44
+ def on_send(node)
45
+ node = node.parent if node.parent&.block_type?
46
+
47
+ expectation_without_runner?(node.parent) do
48
+ add_offense(node.parent.loc.selector)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -47,15 +47,12 @@ module RuboCop
47
47
  private
48
48
 
49
49
  def on_metadata_arguments(metadata_arguments)
50
- *symbols, last = metadata_arguments
51
- hash = nil
52
- case last&.type
53
- when :hash
54
- hash = last
55
- when :sym
56
- symbols << last
50
+ if metadata_arguments.last&.hash_type?
51
+ *metadata_arguments, hash = metadata_arguments
52
+ on_metadata(metadata_arguments, hash)
53
+ else
54
+ on_metadata(metadata_arguments, nil)
57
55
  end
58
- on_metadata(symbols, hash)
59
56
  end
60
57
  end
61
58
  end
@@ -7,6 +7,10 @@ module RuboCop
7
7
  module TopLevelGroup
8
8
  extend RuboCop::NodePattern::Macros
9
9
 
10
+ DEPRECATED_MODULE_METHOD_WARNING =
11
+ 'top_level_group? is deprecated and will be ' \
12
+ 'removed in the next major version of rubocop_rspec.'
13
+
10
14
  def on_new_investigation
11
15
  super
12
16
 
@@ -28,7 +32,10 @@ module RuboCop
28
32
 
29
33
  def on_top_level_group(_node); end
30
34
 
35
+ # @private
36
+ # @deprecated All callers of this method have been removed.
31
37
  def top_level_group?(node)
38
+ warn DEPRECATED_MODULE_METHOD_WARNING, uplevel: 1
32
39
  top_level_groups.include?(node)
33
40
  end
34
41
 
@@ -30,7 +30,7 @@ module RuboCop
30
30
 
31
31
  def on_top_level_group(node)
32
32
  top_level_example_groups =
33
- top_level_groups.select(&method(:example_group?))
33
+ top_level_groups.select { |group| example_group?(group) }
34
34
 
35
35
  return if top_level_example_groups.one?
36
36
  return unless top_level_example_groups.first.equal?(node)
@@ -67,12 +67,12 @@ module RuboCop
67
67
  # end
68
68
  #
69
69
  class MultipleExpectations < Base
70
- include ConfigurableMax
71
-
72
70
  MSG = 'Example has too many expectations [%<total>d/%<max>d].'
73
71
 
74
72
  ANYTHING = ->(_node) { true }
75
- TRUE = ->(node) { node.true_type? }
73
+ TRUE_NODE = lambda(&:true_type?)
74
+
75
+ exclude_limit 'Max'
76
76
 
77
77
  # @!method aggregate_failures?(node)
78
78
  def_node_matcher :aggregate_failures?, <<~PATTERN
@@ -110,7 +110,7 @@ module RuboCop
110
110
  node_with_aggregate_failures = find_aggregate_failures(example_node)
111
111
  return false unless node_with_aggregate_failures
112
112
 
113
- aggregate_failures?(node_with_aggregate_failures, TRUE)
113
+ aggregate_failures?(node_with_aggregate_failures, TRUE_NODE)
114
114
  end
115
115
 
116
116
  def find_aggregate_failures(example_node)
@@ -82,11 +82,12 @@ module RuboCop
82
82
  # end
83
83
  #
84
84
  class MultipleMemoizedHelpers < Base
85
- include ConfigurableMax
86
85
  include Variable
87
86
 
88
87
  MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
89
88
 
89
+ exclude_limit 'Max'
90
+
90
91
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
91
92
  return unless spec_group?(node)
92
93
 
@@ -108,10 +109,8 @@ module RuboCop
108
109
  attr_reader :example_group_memoized_helpers
109
110
 
110
111
  def all_helpers(node)
111
- [
112
- *helpers(node),
113
- *node.each_ancestor(:block).flat_map(&method(:helpers))
114
- ]
112
+ helpers(node) +
113
+ node.each_ancestor(:block).flat_map { |ancestor| helpers(ancestor) }
115
114
  end
116
115
 
117
116
  def helpers(node)
@@ -107,8 +107,11 @@ module RuboCop
107
107
  private
108
108
 
109
109
  def ignored_shared_example?(node)
110
- cop_config['IgnoreSharedExamples'] &&
111
- node.each_ancestor(:block).any?(&method(:shared_example?))
110
+ return false unless cop_config['IgnoreSharedExamples']
111
+
112
+ node.each_ancestor(:block).any? do |ancestor|
113
+ shared_example?(ancestor)
114
+ end
112
115
  end
113
116
 
114
117
  def check_explicit_subject(node)
@@ -92,7 +92,6 @@ module RuboCop
92
92
  # end
93
93
  #
94
94
  class NestedGroups < Base
95
- include ConfigurableMax
96
95
  include TopLevelGroup
97
96
 
98
97
  MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
@@ -103,6 +102,8 @@ module RuboCop
103
102
  "Configuration key `#{DEPRECATED_MAX_KEY}` for #{cop_name} is " \
104
103
  'deprecated in favor of `Max`. Please use that instead.'
105
104
 
105
+ exclude_limit 'Max'
106
+
106
107
  def on_top_level_group(node)
107
108
  find_nested_example_groups(node) do |example_group, nesting|
108
109
  self.max = nesting