rubocop-rspec 3.4.0 → 3.6.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -0
  3. data/README.md +7 -4
  4. data/config/default.yml +6 -0
  5. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  6. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  7. data/lib/rubocop/cop/rspec/be_nil.rb +4 -0
  8. data/lib/rubocop/cop/rspec/change_by_zero.rb +3 -4
  9. data/lib/rubocop/cop/rspec/context_wording.rb +6 -1
  10. data/lib/rubocop/cop/rspec/described_class.rb +4 -2
  11. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -1
  12. data/lib/rubocop/cop/rspec/empty_example_group.rb +4 -4
  13. data/lib/rubocop/cop/rspec/example_wording.rb +1 -3
  14. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
  15. data/lib/rubocop/cop/rspec/expect_actual.rb +1 -1
  16. data/lib/rubocop/cop/rspec/expect_change.rb +21 -2
  17. data/lib/rubocop/cop/rspec/focus.rb +15 -11
  18. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
  19. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
  20. data/lib/rubocop/cop/rspec/implicit_expect.rb +3 -3
  21. data/lib/rubocop/cop/rspec/implicit_subject.rb +8 -0
  22. data/lib/rubocop/cop/rspec/include_examples.rb +39 -0
  23. data/lib/rubocop/cop/rspec/indexed_let.rb +2 -0
  24. data/lib/rubocop/cop/rspec/message_spies.rb +4 -0
  25. data/lib/rubocop/cop/rspec/nested_groups.rb +2 -2
  26. data/lib/rubocop/cop/rspec/no_expectation_example.rb +1 -1
  27. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  28. data/lib/rubocop/cop/rspec/pending_without_reason.rb +2 -3
  29. data/lib/rubocop/cop/rspec/predicate_matcher.rb +18 -8
  30. data/lib/rubocop/cop/rspec/receive_messages.rb +1 -2
  31. data/lib/rubocop/cop/rspec/redundant_around.rb +1 -1
  32. data/lib/rubocop/cop/rspec/return_from_stub.rb +5 -4
  33. data/lib/rubocop/cop/rspec/scattered_setup.rb +10 -2
  34. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
  35. data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
  36. data/lib/rubocop/cop/rspec_cops.rb +1 -0
  37. data/lib/rubocop/rspec/config_formatter.rb +4 -9
  38. data/lib/rubocop/rspec/hook.rb +3 -1
  39. data/lib/rubocop/rspec/language.rb +3 -6
  40. data/lib/rubocop/rspec/plugin.rb +37 -0
  41. data/lib/rubocop/rspec/version.rb +1 -1
  42. data/lib/rubocop/rspec/wording.rb +0 -2
  43. data/lib/rubocop/rspec.rb +0 -7
  44. data/lib/rubocop-rspec.rb +1 -3
  45. metadata +28 -6
  46. data/lib/rubocop/rspec/inject.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a2433cc3212de48725bca4fa2f8216ef0742950d1b0ad0a52e0d717050ede4e
4
- data.tar.gz: 3e43165a772275ece7344813b6dfc5196669763013c0bedc3b75107cf6fa0f30
3
+ metadata.gz: 4e4727f136d1a399d3108fd010c76ac61f7fce57e3c89cb2d97ad121d558af44
4
+ data.tar.gz: dcc8c7f61c420d5938807dae3a610f53c6be8c657687e38542f701db2aec4ce2
5
5
  SHA512:
6
- metadata.gz: 41a9171c098150e3349977cd08df5f369fe2d33c0bb4f1b29edcfb2633eae2c880ab1ff71a9c4a1ee5dc28645a6359ecd3e4ab99a822ac576523386e78364ee2
7
- data.tar.gz: af1c164ac3d8c9e9ed61611fa9877c459656f695fd45c057c776af99304fcd8471fa00d958d7bf650ba46f82056ae96accd12cfeec17eb1a00129baffdb51768
6
+ metadata.gz: 651f8b53a0b70738f9dc084df5bdf471ce65ae4d7f0c2db8cc2eb557178fd541f317bee36ae062b8109aed72b452f69eb1c965db923d5d9875c86de9ab84f23e
7
+ data.tar.gz: f189ec83b4b663df4e3aca303f2f273fded6d5529e689d7f9421f379dd46203da95236a4f649414466b0f6050b0d9e7d952df003b4b63ed45722c1764fd1a094
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 3.6.0 (2025-04-18)
6
+
7
+ - Fix false positive in `RSpec/Pending`, where it would mark the default block `it` as an offense. ([@bquorning])
8
+ - Fix issue when `Style/ContextWording` is configured with a Prefix being interpreted as a boolean, like `on`. ([@sakuro])
9
+ - Add new `RSpec/IncludeExamples` cop to enforce using `it_behaves_like` over `include_examples`. ([@dvandersluis])
10
+ - Change `RSpec/ScatteredSetup` to allow `around` hooks to be scattered. ([@ydah])
11
+ - Fix an error `RSpec/ChangeByZero` cop when without expect block. ([@lee266])
12
+ - Fix a false positive for `RSpec/DescribedClass` when `SkipBlocks` is true and numblocks are used. ([@earlopain])
13
+
14
+ ## 3.5.0 (2025-02-16)
15
+
16
+ - Don't let `RSpec/PredicateMatcher` replace `respond_to?` with two arguments with the RSpec `respond_to` matcher. ([@bquorning])
17
+ - Fix `RSpec/PredicateMatcher` support for `eql` and `equal` matchers. ([@bquorning])
18
+ - Pluginfy RuboCop RSpec. ([@koic])
19
+
5
20
  ## 3.4.0 (2025-01-20)
6
21
 
7
22
  - Fix `RSpec/SortMetadata` cop to limit sorting to trailing metadata arguments. ([@cbliard])
@@ -992,6 +1007,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
992
1007
  [@krororo]: https://github.com/krororo
993
1008
  [@kuahyeow]: https://github.com/kuahyeow
994
1009
  [@lazycoder9]: https://github.com/lazycoder9
1010
+ [@lee266]: https://github.com/lee266
995
1011
  [@leoarnold]: https://github.com/leoarnold
996
1012
  [@liberatys]: https://github.com/Liberatys
997
1013
  [@lokhi]: https://github.com/lokhi
@@ -1032,6 +1048,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
1032
1048
  [@rrosenblum]: https://github.com/rrosenblum
1033
1049
  [@rspeicher]: https://github.com/rspeicher
1034
1050
  [@rst-j]: https://github.com/RST-J
1051
+ [@sakuro]: https://github.com/sakuro
1035
1052
  [@samrjenkins]: https://github.com/samrjenkins
1036
1053
  [@schmijos]: https://github.com/schmijos
1037
1054
  [@seanpdoyle]: https://github.com/seanpdoyle
data/README.md CHANGED
@@ -48,13 +48,13 @@ ways to do this:
48
48
  Put this into your `.rubocop.yml`.
49
49
 
50
50
  ```yaml
51
- require: rubocop-rspec
51
+ plugins: rubocop-rspec
52
52
  ```
53
53
 
54
54
  Alternatively, use the following array notation when specifying multiple extensions.
55
55
 
56
56
  ```yaml
57
- require:
57
+ plugins:
58
58
  - rubocop-other-extension
59
59
  - rubocop-rspec
60
60
  ```
@@ -62,17 +62,20 @@ require:
62
62
  Now you can run `rubocop` and it will automatically load the RuboCop RSpec
63
63
  cops together with the standard cops.
64
64
 
65
+ > [!NOTE]
66
+ > The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`.
67
+
65
68
  ### Command line
66
69
 
67
70
  ```bash
68
- rubocop --require rubocop-rspec
71
+ rubocop --plugin rubocop-rspec
69
72
  ```
70
73
 
71
74
  ### Rake task
72
75
 
73
76
  ```ruby
74
77
  RuboCop::RakeTask.new do |task|
75
- task.requires << 'rubocop-rspec'
78
+ task.plugins << 'rubocop-rspec'
76
79
  end
77
80
  ```
78
81
 
data/config/default.yml CHANGED
@@ -532,6 +532,12 @@ RSpec/ImplicitSubject:
532
532
  VersionChanged: '2.13'
533
533
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitSubject
534
534
 
535
+ RSpec/IncludeExamples:
536
+ Description: Checks for usage of `include_examples`.
537
+ Enabled: pending
538
+ VersionAdded: '3.6'
539
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IncludeExamples
540
+
535
541
  RSpec/IndexedLet:
536
542
  Description: Do not set up test data using indexes (e.g., `item_1`, `item_2`).
537
543
  Enabled: true
@@ -31,7 +31,7 @@ module RuboCop
31
31
 
32
32
  # @!method eq_type_with_identity?(node)
33
33
  def_node_matcher :eq_type_with_identity?, <<~PATTERN
34
- (send nil? :eq {true false nil})
34
+ (send nil? :eq {boolean nil})
35
35
  PATTERN
36
36
 
37
37
  def on_send(node)
@@ -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,8 +101,9 @@ module RuboCop
101
101
 
102
102
  private
103
103
 
104
- # rubocop:disable Metrics/MethodLength
105
104
  def register_offense(node, change_node)
105
+ return unless node.parent.send_type?
106
+
106
107
  if compound_expectations?(node)
107
108
  add_offense(node,
108
109
  message: message_compound(change_node)) do |corrector|
@@ -115,11 +116,9 @@ module RuboCop
115
116
  end
116
117
  end
117
118
  end
118
- # rubocop:enable Metrics/MethodLength
119
119
 
120
120
  def compound_expectations?(node)
121
- node.parent.send_type? &&
122
- %i[and or & |].include?(node.parent.method_name)
121
+ %i[and or & |].include?(node.parent.method_name)
123
122
  end
124
123
 
125
124
  def message(change_node)
@@ -115,7 +115,12 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def prefixes
118
- Array(cop_config.fetch('Prefixes', []))
118
+ Array(cop_config.fetch('Prefixes', [])).tap do |prefixes|
119
+ non_strings = prefixes.reject { |pre| pre.is_a?(String) }
120
+ unless non_strings.empty?
121
+ raise "Non-string prefixes #{non_strings.inspect} detected."
122
+ end
123
+ end
119
124
  end
120
125
  end
121
126
  end
@@ -83,7 +83,7 @@ module RuboCop
83
83
 
84
84
  # @!method rspec_block?(node)
85
85
  def_node_matcher :rspec_block?,
86
- '({block numblock} (send #rspec? #ALL.all ...) ...)'
86
+ '(any_block (send #rspec? #ALL.all ...) ...)'
87
87
 
88
88
  # @!method scope_changing_syntax?(node)
89
89
  def_node_matcher :scope_changing_syntax?, '{def class module}'
@@ -153,7 +153,9 @@ module RuboCop
153
153
  end
154
154
 
155
155
  def skippable_block?(node)
156
- node.block_type? && !rspec_block?(node) && cop_config['SkipBlocks']
156
+ return false unless cop_config['SkipBlocks']
157
+
158
+ node.any_block_type? && !rspec_block?(node)
157
159
  end
158
160
 
159
161
  def only_static_constants?
@@ -24,7 +24,7 @@ module RuboCop
24
24
 
25
25
  # @!method include_rspec_blocks?(node)
26
26
  def_node_search :include_rspec_blocks?, <<~PATTERN
27
- ({block numblock} (send #explicit_rspec? #ExampleGroups.all ...) ...)
27
+ (any_block (send #explicit_rspec? #ExampleGroups.all ...) ...)
28
28
  PATTERN
29
29
 
30
30
  def on_module(node)
@@ -137,7 +137,7 @@ module RuboCop
137
137
  PATTERN
138
138
 
139
139
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
140
- return if node.each_ancestor(:def, :defs).any?
140
+ return if node.each_ancestor(:any_def).any?
141
141
  return if node.each_ancestor(:block).any? { |block| example?(block) }
142
142
 
143
143
  example_group_body(node) do |body|
@@ -155,7 +155,7 @@ module RuboCop
155
155
  return true unless body
156
156
  return false if conditionals_with_examples?(body)
157
157
 
158
- if body.if_type? || body.case_type?
158
+ if body.type?(:if, :case)
159
159
  !examples_in_branches?(body)
160
160
  else
161
161
  !examples?(body)
@@ -163,7 +163,7 @@ module RuboCop
163
163
  end
164
164
 
165
165
  def conditionals_with_examples?(body)
166
- return false unless body.begin_type? || body.case_type?
166
+ return false unless body.type?(:begin, :case)
167
167
 
168
168
  body.each_descendant(:if, :case).any? do |condition_node|
169
169
  examples_in_branches?(condition_node)
@@ -172,7 +172,7 @@ module RuboCop
172
172
 
173
173
  def examples_in_branches?(condition_node)
174
174
  return false unless condition_node
175
- return false if !condition_node.if_type? && !condition_node.case_type?
175
+ return false unless condition_node.type?(:if, :case)
176
176
 
177
177
  condition_node.branches.any? { |branch| examples?(branch) }
178
178
  end
@@ -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,7 +65,7 @@ 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
 
@@ -5,11 +5,30 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks for consistent style of change matcher.
7
7
  #
8
- # Enforces either passing object and attribute as arguments to the matcher
9
- # or passing a block that reads the attribute value.
8
+ # Enforces either passing a receiver and message as method arguments,
9
+ # or a block.
10
10
  #
11
11
  # This cop can be configured using the `EnforcedStyle` option.
12
12
  #
13
+ # @safety
14
+ # Autocorrection is unsafe because `method_call` style calls the
15
+ # receiver *once* and sends the message to it before and after
16
+ # calling the `expect` block, whereas `block` style calls the
17
+ # expression *twice*, including the receiver.
18
+ #
19
+ # If your receiver is dynamic (e.g., the result of a method call) and
20
+ # you expect it to be called before and after the `expect` block,
21
+ # changing from `block` to `method_call` style may break your test.
22
+ #
23
+ # [source,ruby]
24
+ # ----
25
+ # expect { run }.to change { my_method.message }
26
+ # # `my_method` is called before and after `run`
27
+ #
28
+ # expect { run }.to change(my_method, :message)
29
+ # # `my_method` is called once, but `message` is called on it twice
30
+ # ----
31
+ #
13
32
  # @example `EnforcedStyle: method_call` (default)
14
33
  # # bad
15
34
  # expect { run }.to change { Foo.bar }
@@ -73,25 +73,29 @@ module RuboCop
73
73
  PATTERN
74
74
 
75
75
  def on_send(node)
76
- return if node.chained? || node.each_ancestor(:def, :defs).any?
76
+ return if node.chained? || node.each_ancestor(:any_def).any?
77
77
 
78
- focus_metadata(node) do |focus|
79
- add_offense(focus) do |corrector|
80
- if focus.pair_type? || focus.str_type? || focus.sym_type?
81
- corrector.remove(with_surrounding(focus))
82
- elsif focus.send_type?
83
- correct_send(corrector, focus)
84
- end
78
+ if focused_block?(node)
79
+ on_focused_block(node)
80
+ else
81
+ metadata(node) do |focus|
82
+ on_metadata(focus)
85
83
  end
86
84
  end
87
85
  end
88
86
 
89
87
  private
90
88
 
91
- def focus_metadata(node, &block)
92
- 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
93
94
 
94
- metadata(node, &block)
95
+ def on_metadata(node)
96
+ add_offense(node) do |corrector|
97
+ corrector.remove(with_surrounding(node))
98
+ end
95
99
  end
96
100
 
97
101
  def with_surrounding(focus)
@@ -67,12 +67,12 @@ module RuboCop
67
67
 
68
68
  # @!method scoped_hook(node)
69
69
  def_node_matcher :scoped_hook, <<~PATTERN
70
- ({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
70
+ (any_block $(send _ #Hooks.all (sym ${:each :example})) ...)
71
71
  PATTERN
72
72
 
73
73
  # @!method unscoped_hook(node)
74
74
  def_node_matcher :unscoped_hook, <<~PATTERN
75
- ({block numblock} $(send _ #Hooks.all) ...)
75
+ (any_block $(send _ #Hooks.all) ...)
76
76
  PATTERN
77
77
 
78
78
  def on_block(node)
@@ -30,7 +30,7 @@ module RuboCop
30
30
  # @!method example_or_group?(node)
31
31
  def_node_matcher :example_or_group?, <<~PATTERN
32
32
  {
33
- ({block numblock} {
33
+ (any_block {
34
34
  (send #rspec? #ExampleGroups.all ...)
35
35
  (send nil? #Examples.all ...)
36
36
  } ...)
@@ -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
 
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for usage of `include_examples`.
7
+ #
8
+ # `include_examples`, unlike `it_behaves_like`, does not create its
9
+ # own context. As such, using `subject`, `let`, `before`/`after`, etc.
10
+ # within shared examples included with `include_examples` can have
11
+ # unexpected behavior and side effects.
12
+ #
13
+ # Prefer using `it_behaves_like` instead.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # include_examples 'examples'
18
+ #
19
+ # # good
20
+ # it_behaves_like 'examples'
21
+ #
22
+ class IncludeExamples < Base
23
+ extend AutoCorrector
24
+
25
+ MSG = 'Prefer `it_behaves_like` over `include_examples`.'
26
+
27
+ RESTRICT_ON_SEND = %i[include_examples].freeze
28
+
29
+ def on_send(node)
30
+ selector = node.loc.selector
31
+
32
+ add_offense(selector) do |corrector|
33
+ corrector.replace(selector, 'it_behaves_like')
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -74,7 +74,9 @@ module RuboCop
74
74
  private
75
75
 
76
76
  SUFFIX_INDEX_REGEX = /_?\d+$/.freeze
77
+ private_constant :SUFFIX_INDEX_REGEX
77
78
  INDEX_REGEX = /\d+/.freeze
79
+ private_constant :INDEX_REGEX
78
80
 
79
81
  def filter_indexed_lets(candidates)
80
82
  candidates
@@ -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
@@ -133,8 +133,8 @@ module RuboCop
133
133
 
134
134
  def count_up_nesting?(node, example_group)
135
135
  example_group &&
136
- (node.block_type? &&
137
- !allowed_groups.include?(node.method_name.to_s))
136
+ node.block_type? &&
137
+ !allowed_groups.include?(node.method_name.to_s)
138
138
  end
139
139
 
140
140
  def message(nesting)
@@ -65,7 +65,7 @@ module RuboCop
65
65
  # @param [RuboCop::AST::Node] node
66
66
  # @return [Boolean]
67
67
  def_node_matcher :regular_or_focused_example?, <<~PATTERN
68
- ({block numblock} (send nil? {#Examples.regular #Examples.focused} ...) ...)
68
+ (any_block (send nil? {#Examples.regular #Examples.focused} ...) ...)
69
69
  PATTERN
70
70
 
71
71
  # @!method includes_expectation?(node)
@@ -47,7 +47,7 @@ module RuboCop
47
47
 
48
48
  # @!method skippable_example?(node)
49
49
  def_node_matcher :skippable_example?, <<~PATTERN
50
- (send nil? #Examples.regular ...)
50
+ (send nil? #Examples.regular _ ...)
51
51
  PATTERN
52
52
 
53
53
  # @!method pending_block?(node)
@@ -63,8 +63,7 @@ module RuboCop
63
63
  def_node_matcher :skipped_in_example?, <<~PATTERN
64
64
  {
65
65
  (send nil? ${#Examples.skipped #Examples.pending})
66
- (block (send nil? ${#Examples.skipped}) ...)
67
- (numblock (send nil? ${#Examples.skipped}) ...)
66
+ (any_block (send nil? ${#Examples.skipped}) ...)
68
67
  }
69
68
  PATTERN
70
69
 
@@ -75,7 +74,7 @@ module RuboCop
75
74
 
76
75
  # @!method skipped_by_example_method_with_block?(node)
77
76
  def_node_matcher :skipped_by_example_method_with_block?, <<~PATTERN
78
- ({block numblock} (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
77
+ (any_block (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
79
78
  PATTERN
80
79
 
81
80
  # @!method metadata_without_reason?(node)
@@ -15,6 +15,8 @@ module RuboCop
15
15
 
16
16
  def check_inflected(node)
17
17
  predicate_in_actual?(node) do |predicate, to, matcher|
18
+ next if cannot_replace_predicate?(predicate)
19
+
18
20
  msg = message_inflected(predicate)
19
21
  add_offense(node, message: msg) do |corrector|
20
22
  remove_predicate(corrector, predicate)
@@ -35,9 +37,13 @@ module RuboCop
35
37
  $#boolean_matcher? ...)
36
38
  PATTERN
37
39
 
40
+ def cannot_replace_predicate?(send_node)
41
+ send_node.method?(:respond_to?) && send_node.arguments.length > 1
42
+ end
43
+
38
44
  # @!method be_bool?(node)
39
45
  def_node_matcher :be_bool?, <<~PATTERN
40
- (send nil? {:be :eq :eql :equal} {true false})
46
+ (send nil? {:be :eq :eql :equal} boolean)
41
47
  PATTERN
42
48
 
43
49
  # @!method be_boolthy?(node)
@@ -63,7 +69,6 @@ module RuboCop
63
69
  matcher_name: to_predicate_matcher(predicate.method_name))
64
70
  end
65
71
 
66
- # rubocop:disable Metrics/MethodLength
67
72
  def to_predicate_matcher(name)
68
73
  case name = name.to_s
69
74
  when 'is_a?'
@@ -80,7 +85,6 @@ module RuboCop
80
85
  "be_#{name[0..-2]}"
81
86
  end
82
87
  end
83
- # rubocop:enable Metrics/MethodLength
84
88
 
85
89
  def remove_predicate(corrector, predicate)
86
90
  range = predicate.loc.dot.with(
@@ -106,12 +110,16 @@ module RuboCop
106
110
 
107
111
  def true?(to_symbol, matcher)
108
112
  result = case matcher.method_name
109
- when :be, :eq
113
+ when :be, :eq, :eql, :equal
110
114
  matcher.first_argument.true_type?
111
115
  when :be_truthy, :a_truthy_value
112
116
  true
113
117
  when :be_falsey, :be_falsy, :a_falsey_value, :a_falsy_value
114
118
  false
119
+ else
120
+ # :nocov:
121
+ :noop
122
+ # :nocov:
115
123
  end
116
124
  to_symbol == :to ? result : !result
117
125
  end
@@ -174,7 +182,7 @@ module RuboCop
174
182
 
175
183
  def heredoc_argument?(matcher)
176
184
  matcher.arguments.select do |arg|
177
- arg.str_type? || arg.dstr_type? || arg.xstr_type?
185
+ arg.type?(:str, :dstr, :xstr)
178
186
  end.any?(&:heredoc?)
179
187
  end
180
188
 
@@ -233,7 +241,6 @@ module RuboCop
233
241
  corrector.insert_after(actual, ".#{predicate}" + args + block)
234
242
  end
235
243
 
236
- # rubocop:disable Metrics/MethodLength
237
244
  def to_predicate_method(matcher)
238
245
  case matcher = matcher.to_s
239
246
  when 'be_a', 'be_an', 'be_a_kind_of', 'a_kind_of', 'be_kind_of'
@@ -250,7 +257,6 @@ module RuboCop
250
257
  "#{matcher[/\Abe_(.+)/, 1]}?"
251
258
  end
252
259
  end
253
- # rubocop:enable Metrics/MethodLength
254
260
 
255
261
  def replacement_matcher(node)
256
262
  case [cop_config['Strict'], node.method?(:to)]
@@ -260,7 +266,7 @@ module RuboCop
260
266
  'be(false)'
261
267
  when [false, true]
262
268
  'be_truthy'
263
- when [false, false]
269
+ else
264
270
  'be_falsey'
265
271
  end
266
272
  end
@@ -329,6 +335,10 @@ module RuboCop
329
335
  check_inflected(node)
330
336
  when :explicit
331
337
  check_explicit(node)
338
+ else
339
+ # :nocov:
340
+ :noop
341
+ # :nocov:
332
342
  end
333
343
  end
334
344
 
@@ -148,8 +148,7 @@ module RuboCop
148
148
  end
149
149
 
150
150
  def heredoc_or_splat?(node)
151
- ((node.str_type? || node.dstr_type?) && node.heredoc?) ||
152
- node.splat_type?
151
+ (node.type?(:str, :dstr) && node.heredoc?) || node.splat_type?
153
152
  end
154
153
 
155
154
  def requires_quotes?(value)
@@ -41,7 +41,7 @@ module RuboCop
41
41
 
42
42
  # @!method match_redundant_around_hook_block?(node)
43
43
  def_node_matcher :match_redundant_around_hook_block?, <<~PATTERN
44
- ({block numblock} (send _ :around ...) ... (send _ :run))
44
+ (any_block (send _ :around ...) ... (send _ :run))
45
45
  PATTERN
46
46
 
47
47
  # @!method match_redundant_around_hook_send?(node)
@@ -80,10 +80,10 @@ module RuboCop
80
80
 
81
81
  def check_block_body(block)
82
82
  body = block.body
83
- unless dynamic?(body) # rubocop:disable Style/GuardClause
84
- add_offense(block.loc.begin, message: MSG_AND_RETURN) do |corrector|
85
- BlockBodyCorrector.new(block).call(corrector)
86
- end
83
+ return if dynamic?(body)
84
+
85
+ add_offense(block.loc.begin, message: MSG_AND_RETURN) do |corrector|
86
+ BlockBodyCorrector.new(block).call(corrector)
87
87
  end
88
88
  end
89
89
 
@@ -162,6 +162,7 @@ module RuboCop
162
162
  end
163
163
 
164
164
  NULL_BLOCK_BODY = Struct.new(:loc, :source).new(nil, 'nil')
165
+ private_constant :NULL_BLOCK_BODY
165
166
  end
166
167
  end
167
168
  end
@@ -5,7 +5,9 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks for setup scattered across multiple hooks in an example group.
7
7
  #
8
- # Unify `before`, `after`, and `around` hooks when possible.
8
+ # Unify `before` and `after` hooks when possible.
9
+ # However, `around` hooks are allowed to be defined multiple times,
10
+ # as unifying them would typically make the code harder to read.
9
11
  #
10
12
  # @example
11
13
  # # bad
@@ -22,6 +24,12 @@ module RuboCop
22
24
  # end
23
25
  # end
24
26
  #
27
+ # # good
28
+ # describe Foo do
29
+ # around { |example| before1; example.call; after1 }
30
+ # around { |example| before2; example.call; after2 }
31
+ # end
32
+ #
25
33
  class ScatteredSetup < Base
26
34
  include FinalEndLocation
27
35
  include RangeHelp
@@ -48,7 +56,7 @@ module RuboCop
48
56
  def repeated_hooks(node)
49
57
  hooks = RuboCop::RSpec::ExampleGroup.new(node)
50
58
  .hooks
51
- .select(&:knowable_scope?)
59
+ .select { |hook| hook.knowable_scope? && hook.name != :around }
52
60
  .group_by { |hook| [hook.name, hook.scope, hook.metadata] }
53
61
  .values
54
62
  .reject(&:one?)
@@ -69,7 +69,7 @@ module RuboCop
69
69
  end
70
70
 
71
71
  def symbol?(node)
72
- node.sym_type? || node.dsym_type?
72
+ node.type?(:sym, :dsym)
73
73
  end
74
74
  end
75
75
  end
@@ -50,7 +50,7 @@ module RuboCop
50
50
  return unless inside_example_group?(node)
51
51
 
52
52
  variable_definition?(node) do |variable|
53
- return if variable.dstr_type? || variable.dsym_type?
53
+ return if variable.type?(:dstr, :dsym)
54
54
  return if matches_allowed_pattern?(variable.value)
55
55
 
56
56
  check_name(node, variable.value, variable.source_range)
@@ -48,6 +48,7 @@ require_relative 'rspec/identical_equality_assertion'
48
48
  require_relative 'rspec/implicit_block_expectation'
49
49
  require_relative 'rspec/implicit_expect'
50
50
  require_relative 'rspec/implicit_subject'
51
+ require_relative 'rspec/include_examples'
51
52
  require_relative 'rspec/indexed_let'
52
53
  require_relative 'rspec/instance_spy'
53
54
  require_relative 'rspec/instance_variable'
@@ -6,9 +6,7 @@ module RuboCop
6
6
  module RSpec
7
7
  # Builds a YAML config file from two config hashes
8
8
  class ConfigFormatter
9
- EXTENSION_ROOT_DEPARTMENT = %r{^(RSpec/)}.freeze
10
- SUBDEPARTMENTS = [].freeze
11
- AMENDMENTS = %(Metrics/BlockLength)
9
+ EXTENSION_ROOT_DEPARTMENT = %r{^RSpec/}.freeze
12
10
  COP_DOC_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/'
13
11
 
14
12
  def initialize(config, descriptions)
@@ -18,18 +16,15 @@ module RuboCop
18
16
 
19
17
  def dump
20
18
  YAML.dump(unified_config)
21
- .gsub(EXTENSION_ROOT_DEPARTMENT, "\n\\1")
22
- .gsub(*AMENDMENTS, "\n\\0")
23
- .gsub(/^(\s+)- /, '\1 - ')
24
- .gsub('"~"', '~')
19
+ .gsub(%r{^\w+/}, "\n\\0") # Add an extra newline before each cop.
20
+ .gsub(/^(\s+)- /, '\1 - ') # Add 2 spaces before each array element.
21
+ .gsub('"~"', '~') # Don't quote tilde, YAML's null value.
25
22
  end
26
23
 
27
24
  private
28
25
 
29
26
  def unified_config
30
27
  cops.each_with_object(config.dup) do |cop, unified|
31
- next if SUBDEPARTMENTS.include?(cop) || AMENDMENTS.include?(cop)
32
-
33
28
  replace_nil(unified[cop])
34
29
  unified[cop].merge!(descriptions.fetch(cop))
35
30
  unified[cop]['Reference'] = reference(cop)
@@ -64,7 +64,9 @@ module RuboCop
64
64
  end
65
65
 
66
66
  def transform_true(node)
67
- node.true_type? ? true : node
67
+ return true if node.true_type?
68
+
69
+ node
68
70
  end
69
71
 
70
72
  def scope_name
@@ -26,7 +26,7 @@ module RuboCop
26
26
 
27
27
  # @!method example_group?(node)
28
28
  def_node_matcher :example_group?, <<~PATTERN
29
- ({block numblock} (send #rspec? #ExampleGroups.all ...) ...)
29
+ (any_block (send #rspec? #ExampleGroups.all ...) ...)
30
30
  PATTERN
31
31
 
32
32
  # @!method shared_group?(node)
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # @!method spec_group?(node)
37
37
  def_node_matcher :spec_group?, <<~PATTERN
38
- ({block numblock} (send #rspec?
38
+ (any_block (send #rspec?
39
39
  {#SharedGroups.all #ExampleGroups.all}
40
40
  ...) ...)
41
41
  PATTERN
@@ -50,10 +50,7 @@ module RuboCop
50
50
 
51
51
  # @!method hook?(node)
52
52
  def_node_matcher :hook?, <<~PATTERN
53
- {
54
- (numblock (send nil? #Hooks.all ...) ...)
55
- (block (send nil? #Hooks.all ...) ...)
56
- }
53
+ (any_block (send nil? #Hooks.all ...) ...)
57
54
  PATTERN
58
55
 
59
56
  # @!method let?(node)
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lint_roller'
4
+
5
+ module RuboCop
6
+ module RSpec
7
+ # A plugin that integrates RuboCop RSpec with RuboCop's plugin system.
8
+ class Plugin < LintRoller::Plugin
9
+ # :nocov:
10
+ def about
11
+ LintRoller::About.new(
12
+ name: 'rubocop-rspec',
13
+ version: Version::STRING,
14
+ homepage: 'https://github.com/rubocop/rubocop-rspec',
15
+ description: 'Code style checking for RSpec files.'
16
+ )
17
+ end
18
+ # :nocov:
19
+
20
+ def supported?(context)
21
+ context.engine == :rubocop
22
+ end
23
+
24
+ def rules(_context)
25
+ project_root = Pathname.new(__dir__).join('../../..')
26
+
27
+ ConfigObsoletion.files << project_root.join('config', 'obsoletion.yml')
28
+
29
+ LintRoller::Rules.new(
30
+ type: :path,
31
+ config_format: :rubocop,
32
+ value: project_root.join('config/default.yml')
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '3.4.0'
7
+ STRING = '3.6.0'
8
8
  end
9
9
  end
10
10
  end
@@ -17,7 +17,6 @@ module RuboCop
17
17
  @replacements = replace
18
18
  end
19
19
 
20
- # rubocop:disable Metrics/MethodLength
21
20
  def rewrite
22
21
  case text
23
22
  when SHOULDNT_BE_PREFIX
@@ -32,7 +31,6 @@ module RuboCop
32
31
  remove_should_and_pluralize
33
32
  end
34
33
  end
35
- # rubocop:enable Metrics/MethodLength
36
34
 
37
35
  private
38
36
 
data/lib/rubocop/rspec.rb CHANGED
@@ -3,12 +3,5 @@
3
3
  module RuboCop
4
4
  # RuboCop RSpec project namespace
5
5
  module RSpec
6
- PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
7
- CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
8
-
9
- private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
10
-
11
- ::RuboCop::ConfigObsoletion.files << PROJECT_ROOT.join('config',
12
- 'obsoletion.yml')
13
6
  end
14
7
  end
data/lib/rubocop-rspec.rb CHANGED
@@ -6,9 +6,9 @@ require 'yaml'
6
6
  require 'rubocop'
7
7
 
8
8
  require_relative 'rubocop/rspec'
9
- require_relative 'rubocop/rspec/inject'
10
9
  require_relative 'rubocop/rspec/language'
11
10
  require_relative 'rubocop/rspec/node'
11
+ require_relative 'rubocop/rspec/plugin'
12
12
  require_relative 'rubocop/rspec/version'
13
13
  require_relative 'rubocop/rspec/wording'
14
14
 
@@ -34,8 +34,6 @@ require_relative 'rubocop/rspec/example'
34
34
  require_relative 'rubocop/rspec/example_group'
35
35
  require_relative 'rubocop/rspec/hook'
36
36
 
37
- RuboCop::RSpec::Inject.defaults!
38
-
39
37
  require_relative 'rubocop/cop/rspec_cops'
40
38
 
41
39
  # We have to register our autocorrect incompatibilities in RuboCop's cops
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Backus
@@ -9,22 +9,42 @@ authors:
9
9
  - Nils Gemeinhardt
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-01-20 00:00:00.000000000 Z
12
+ date: 1980-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: lint_roller
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.1'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.1'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: rubocop
16
30
  requirement: !ruby/object:Gem::Requirement
17
31
  requirements:
18
32
  - - "~>"
19
33
  - !ruby/object:Gem::Version
20
- version: '1.61'
34
+ version: '1.72'
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: 1.72.1
21
38
  type: :runtime
22
39
  prerelease: false
23
40
  version_requirements: !ruby/object:Gem::Requirement
24
41
  requirements:
25
42
  - - "~>"
26
43
  - !ruby/object:Gem::Version
27
- version: '1.61'
44
+ version: '1.72'
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.72.1
28
48
  description: |
29
49
  Code style checking for RSpec files.
30
50
  A plugin for the RuboCop code style enforcing & linting tool.
@@ -94,6 +114,7 @@ files:
94
114
  - lib/rubocop/cop/rspec/implicit_block_expectation.rb
95
115
  - lib/rubocop/cop/rspec/implicit_expect.rb
96
116
  - lib/rubocop/cop/rspec/implicit_subject.rb
117
+ - lib/rubocop/cop/rspec/include_examples.rb
97
118
  - lib/rubocop/cop/rspec/indexed_let.rb
98
119
  - lib/rubocop/cop/rspec/instance_spy.rb
99
120
  - lib/rubocop/cop/rspec/instance_variable.rb
@@ -178,9 +199,9 @@ files:
178
199
  - lib/rubocop/rspec/example.rb
179
200
  - lib/rubocop/rspec/example_group.rb
180
201
  - lib/rubocop/rspec/hook.rb
181
- - lib/rubocop/rspec/inject.rb
182
202
  - lib/rubocop/rspec/language.rb
183
203
  - lib/rubocop/rspec/node.rb
204
+ - lib/rubocop/rspec/plugin.rb
184
205
  - lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb
185
206
  - lib/rubocop/rspec/version.rb
186
207
  - lib/rubocop/rspec/wording.rb
@@ -191,6 +212,7 @@ metadata:
191
212
  changelog_uri: https://github.com/rubocop/rubocop-rspec/blob/master/CHANGELOG.md
192
213
  documentation_uri: https://docs.rubocop.org/rubocop-rspec/
193
214
  rubygems_mfa_required: 'true'
215
+ default_lint_roller_plugin: RuboCop::RSpec::Plugin
194
216
  rdoc_options: []
195
217
  require_paths:
196
218
  - lib
@@ -205,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
227
  - !ruby/object:Gem::Version
206
228
  version: '0'
207
229
  requirements: []
208
- rubygems_version: 3.6.2
230
+ rubygems_version: 3.6.7
209
231
  specification_version: 4
210
232
  summary: Code style checking for RSpec files
211
233
  test_files: []
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module RSpec
5
- # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
6
- # bit of our configuration.
7
- module Inject
8
- def self.defaults!
9
- path = CONFIG_DEFAULT.to_s
10
- hash = ConfigLoader.send(:load_yaml_configuration, path)
11
- config = RuboCop::Config.new(hash, path)
12
- puts "configuration from #{path}" if ConfigLoader.debug?
13
- config = ConfigLoader.merge_with_default(config, path)
14
- ConfigLoader.instance_variable_set(:@default_configuration, config)
15
- end
16
- end
17
- end
18
- end