rubocop-rspec 3.5.0 → 3.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/config/default.yml +16 -1
- data/lib/rubocop/cop/rspec/change_by_zero.rb +3 -2
- data/lib/rubocop/cop/rspec/context_wording.rb +6 -1
- data/lib/rubocop/cop/rspec/described_class.rb +25 -12
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -1
- data/lib/rubocop/cop/rspec/dialect.rb +1 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +4 -4
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_change.rb +21 -2
- data/lib/rubocop/cop/rspec/focus.rb +1 -1
- data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/include_examples.rb +90 -0
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +32 -10
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +7 -0
- data/lib/rubocop/cop/rspec/nested_groups.rb +2 -2
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +1 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -1
- data/lib/rubocop/cop/rspec/pending_without_reason.rb +2 -3
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_messages.rb +2 -3
- data/lib/rubocop/cop/rspec/redundant_around.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_setup.rb +10 -2
- data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
- data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +1 -0
- data/lib/rubocop/rspec/hook.rb +3 -1
- data/lib/rubocop/rspec/language.rb +13 -10
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 645fbfddd891af3477e31993bc8da92fc57300cbcfcd8274ee0f68cfbbe97983
|
4
|
+
data.tar.gz: 6a97e91acb933ecbe09691c090efbff715ca12adcaef7027e8d324411ec0d78d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca10e4b0b7e285136c60aa0035a018407263968fcced15155999ef5e2b6ac4edac45bcbd6c82dd355616a40ef110f9191cc986d5a8922aaab42c8118bff55c89
|
7
|
+
data.tar.gz: 7a22acf8143f309128504884a1634e23504b40821aabf14d9b20392c6c11ce7a34ad3ab48945e1b023594a794677bdf6526e7236f705fd331ab244a746375453
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,25 @@
|
|
2
2
|
|
3
3
|
## Master (Unreleased)
|
4
4
|
|
5
|
+
## 3.7.0 (2025-09-01)
|
6
|
+
|
7
|
+
- Mark `RSpec/IncludeExamples` as `SafeAutoCorrect: false`. ([@yujideveloper])
|
8
|
+
- Fix a false positive for `RSpec/LeakyConstantDeclaration` when defining constants in explicit namespaces. ([@naveg])
|
9
|
+
- Add support for error matchers (`raise_exception` and `raise_error`) to `RSpec/Dialect`. ([@lovro-bikic])
|
10
|
+
- Don't register offenses for `RSpec/DescribedClass` within `Data.define` blocks. ([@lovro-bikic])
|
11
|
+
- Add autocorrection support for `RSpec/IteratedExpectation` for single expectations. ([@lovro-bikic])
|
12
|
+
- Exclude all cops from inspecting factorybot files, except if explicitly included. ([@Mth0158])
|
13
|
+
- Fix a false positive for `RSpec/ExcessiveDocstringSpacing` when receivers are not RSpec methods. ([@ydah])
|
14
|
+
|
15
|
+
## 3.6.0 (2025-04-18)
|
16
|
+
|
17
|
+
- Fix false positive in `RSpec/Pending`, where it would mark the default block `it` as an offense. ([@bquorning])
|
18
|
+
- Fix issue when `Style/ContextWording` is configured with a Prefix being interpreted as a boolean, like `on`. ([@sakuro])
|
19
|
+
- Add new `RSpec/IncludeExamples` cop to enforce using `it_behaves_like` over `include_examples`. ([@dvandersluis])
|
20
|
+
- Change `RSpec/ScatteredSetup` to allow `around` hooks to be scattered. ([@ydah])
|
21
|
+
- Fix an error `RSpec/ChangeByZero` cop when without expect block. ([@lee266])
|
22
|
+
- Fix a false positive for `RSpec/DescribedClass` when `SkipBlocks` is true and numblocks are used. ([@earlopain])
|
23
|
+
|
5
24
|
## 3.5.0 (2025-02-16)
|
6
25
|
|
7
26
|
- Don't let `RSpec/PredicateMatcher` replace `respond_to?` with two arguments with the RSpec `respond_to` matcher. ([@bquorning])
|
@@ -998,6 +1017,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
998
1017
|
[@krororo]: https://github.com/krororo
|
999
1018
|
[@kuahyeow]: https://github.com/kuahyeow
|
1000
1019
|
[@lazycoder9]: https://github.com/lazycoder9
|
1020
|
+
[@lee266]: https://github.com/lee266
|
1001
1021
|
[@leoarnold]: https://github.com/leoarnold
|
1002
1022
|
[@liberatys]: https://github.com/Liberatys
|
1003
1023
|
[@lokhi]: https://github.com/lokhi
|
@@ -1011,6 +1031,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
1011
1031
|
[@mlarraz]: https://github.com/mlarraz
|
1012
1032
|
[@mockdeep]: https://github.com/mockdeep
|
1013
1033
|
[@mothonmars]: https://github.com/MothOnMars
|
1034
|
+
[@mth0158]: https://github.com/Mth0158
|
1014
1035
|
[@mvz]: https://github.com/mvz
|
1015
1036
|
[@naveg]: https://github.com/naveg
|
1016
1037
|
[@nc-holodakg]: https://github.com/nc-holodakg
|
@@ -1038,6 +1059,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
1038
1059
|
[@rrosenblum]: https://github.com/rrosenblum
|
1039
1060
|
[@rspeicher]: https://github.com/rspeicher
|
1040
1061
|
[@rst-j]: https://github.com/RST-J
|
1062
|
+
[@sakuro]: https://github.com/sakuro
|
1041
1063
|
[@samrjenkins]: https://github.com/samrjenkins
|
1042
1064
|
[@schmijos]: https://github.com/schmijos
|
1043
1065
|
[@seanpdoyle]: https://github.com/seanpdoyle
|
@@ -1062,5 +1084,6 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
1062
1084
|
[@ydah]: https://github.com/ydah
|
1063
1085
|
[@yevhene]: https://github.com/yevhene
|
1064
1086
|
[@ypresto]: https://github.com/ypresto
|
1087
|
+
[@yujideveloper]: https://github.com/yujideveloper
|
1065
1088
|
[@zdennis]: https://github.com/zdennis
|
1066
1089
|
[@zverok]: https://github.com/zverok
|
data/config/default.yml
CHANGED
@@ -6,6 +6,10 @@ RSpec:
|
|
6
6
|
Include:
|
7
7
|
- "**/*_spec.rb"
|
8
8
|
- "**/spec/**/*"
|
9
|
+
Exclude:
|
10
|
+
- "**/spec/factories.rb"
|
11
|
+
- "**/spec/factories/**/*.rb"
|
12
|
+
- "**/features/support/factories/**/*.rb"
|
9
13
|
Language:
|
10
14
|
inherit_mode:
|
11
15
|
merge:
|
@@ -79,6 +83,9 @@ RSpec:
|
|
79
83
|
- prepend_after
|
80
84
|
- after
|
81
85
|
- append_after
|
86
|
+
ErrorMatchers:
|
87
|
+
- raise_error
|
88
|
+
- raise_exception
|
82
89
|
Includes:
|
83
90
|
inherit_mode:
|
84
91
|
merge:
|
@@ -532,6 +539,14 @@ RSpec/ImplicitSubject:
|
|
532
539
|
VersionChanged: '2.13'
|
533
540
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ImplicitSubject
|
534
541
|
|
542
|
+
RSpec/IncludeExamples:
|
543
|
+
Description: Checks for usage of `include_examples`.
|
544
|
+
Enabled: pending
|
545
|
+
SafeAutoCorrect: false
|
546
|
+
VersionAdded: '3.6'
|
547
|
+
VersionChanged: '3.7'
|
548
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IncludeExamples
|
549
|
+
|
535
550
|
RSpec/IndexedLet:
|
536
551
|
Description: Do not set up test data using indexes (e.g., `item_1`, `item_2`).
|
537
552
|
Enabled: true
|
@@ -776,7 +791,7 @@ RSpec/ReceiveCounts:
|
|
776
791
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveCounts
|
777
792
|
|
778
793
|
RSpec/ReceiveMessages:
|
779
|
-
Description:
|
794
|
+
Description: Prefer `receive_messages` over multiple `receive`s on the same object.
|
780
795
|
Enabled: true
|
781
796
|
SafeAutoCorrect: false
|
782
797
|
VersionAdded: '2.23'
|
@@ -102,6 +102,8 @@ module RuboCop
|
|
102
102
|
private
|
103
103
|
|
104
104
|
def register_offense(node, change_node)
|
105
|
+
return unless node.parent.send_type?
|
106
|
+
|
105
107
|
if compound_expectations?(node)
|
106
108
|
add_offense(node,
|
107
109
|
message: message_compound(change_node)) do |corrector|
|
@@ -116,8 +118,7 @@ module RuboCop
|
|
116
118
|
end
|
117
119
|
|
118
120
|
def compound_expectations?(node)
|
119
|
-
node.parent.
|
120
|
-
%i[and or & |].include?(node.parent.method_name)
|
121
|
+
%i[and or & |].include?(node.parent.method_name)
|
121
122
|
end
|
122
123
|
|
123
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
|
@@ -13,6 +13,20 @@ module RuboCop
|
|
13
13
|
# `OnlyStaticConstants` is only relevant when `EnforcedStyle` is
|
14
14
|
# `described_class`.
|
15
15
|
#
|
16
|
+
# There's a known caveat with rspec-rails's `controller` helper that
|
17
|
+
# runs its block in a different context, and `described_class` is not
|
18
|
+
# available to it. `SkipBlocks` option excludes detection in all
|
19
|
+
# non-RSpec related blocks.
|
20
|
+
#
|
21
|
+
# To narrow down this setting to only a specific directory, it is
|
22
|
+
# possible to use an overriding configuration file local to that
|
23
|
+
# directory.
|
24
|
+
#
|
25
|
+
# @safety
|
26
|
+
# Autocorrection is unsafe when `SkipBlocks: false` because
|
27
|
+
# `described_class` might not be available within the block (for
|
28
|
+
# example, in rspec-rails's `controller` helper).
|
29
|
+
#
|
16
30
|
# @example `EnforcedStyle: described_class` (default)
|
17
31
|
# # bad
|
18
32
|
# describe MyClass do
|
@@ -47,15 +61,6 @@ module RuboCop
|
|
47
61
|
# subject { MyClass.do_something }
|
48
62
|
# end
|
49
63
|
#
|
50
|
-
# There's a known caveat with rspec-rails's `controller` helper that
|
51
|
-
# runs its block in a different context, and `described_class` is not
|
52
|
-
# available to it. `SkipBlocks` option excludes detection in all
|
53
|
-
# non-RSpec related blocks.
|
54
|
-
#
|
55
|
-
# To narrow down this setting to only a specific directory, it is
|
56
|
-
# possible to use an overriding configuration file local to that
|
57
|
-
# directory.
|
58
|
-
#
|
59
64
|
# @example `SkipBlocks: true`
|
60
65
|
# # spec/controllers/.rubocop.yml
|
61
66
|
# # RSpec/DescribedClass:
|
@@ -78,12 +83,18 @@ module RuboCop
|
|
78
83
|
|
79
84
|
# @!method common_instance_exec_closure?(node)
|
80
85
|
def_node_matcher :common_instance_exec_closure?, <<~PATTERN
|
81
|
-
(block
|
86
|
+
(block
|
87
|
+
{
|
88
|
+
(send (const nil? {:Class :Module :Struct}) :new ...)
|
89
|
+
(send (const nil? :Data) :define ...)
|
90
|
+
}
|
91
|
+
...
|
92
|
+
)
|
82
93
|
PATTERN
|
83
94
|
|
84
95
|
# @!method rspec_block?(node)
|
85
96
|
def_node_matcher :rspec_block?,
|
86
|
-
'(
|
97
|
+
'(any_block (send #rspec? #ALL.all ...) ...)'
|
87
98
|
|
88
99
|
# @!method scope_changing_syntax?(node)
|
89
100
|
def_node_matcher :scope_changing_syntax?, '{def class module}'
|
@@ -153,7 +164,9 @@ module RuboCop
|
|
153
164
|
end
|
154
165
|
|
155
166
|
def skippable_block?(node)
|
156
|
-
|
167
|
+
return false unless cop_config['SkipBlocks']
|
168
|
+
|
169
|
+
node.any_block_type? && !rspec_block?(node)
|
157
170
|
end
|
158
171
|
|
159
172
|
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
|
-
(
|
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(:
|
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.
|
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.
|
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
|
175
|
+
return false unless condition_node.type?(:if, :case)
|
176
176
|
|
177
177
|
condition_node.branches.any? { |branch| examples?(branch) }
|
178
178
|
end
|
@@ -30,7 +30,7 @@ module RuboCop
|
|
30
30
|
|
31
31
|
# @!method example_description(node)
|
32
32
|
def_node_matcher :example_description, <<~PATTERN
|
33
|
-
(send
|
33
|
+
(send #rspec? {#Examples.all #ExampleGroups.all} ${
|
34
34
|
$str
|
35
35
|
$(dstr ({str dstr `sym} ...) ...)
|
36
36
|
} ...)
|
@@ -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
|
9
|
-
# or
|
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 }
|
@@ -67,12 +67,12 @@ module RuboCop
|
|
67
67
|
|
68
68
|
# @!method scoped_hook(node)
|
69
69
|
def_node_matcher :scoped_hook, <<~PATTERN
|
70
|
-
(
|
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
|
-
(
|
75
|
+
(any_block $(send _ #Hooks.all) ...)
|
76
76
|
PATTERN
|
77
77
|
|
78
78
|
def on_block(node)
|
@@ -0,0 +1,90 @@
|
|
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
|
+
# @safety
|
16
|
+
# `include_examples` and `it_behaves_like` have different scoping
|
17
|
+
# behaviors.
|
18
|
+
# Changing `include_examples` to `it_behaves_like` creates a new
|
19
|
+
# context, altering setup dependencies, which can lead to unexpected
|
20
|
+
# test failures.
|
21
|
+
# Specifically, the scope of hooks (`before`, `after`, `around`)
|
22
|
+
# changes, which may prevent expected setup from being inherited
|
23
|
+
# correctly.
|
24
|
+
#
|
25
|
+
# Additionally, `let` and `subject` are affected by scoping rules.
|
26
|
+
# When `include_examples` is used, `let` and `subject` defined within
|
27
|
+
# `shared_examples` are evaluated in the caller's context, allowing
|
28
|
+
# access to their values.
|
29
|
+
# In contrast, `it_behaves_like` creates a new context, preventing
|
30
|
+
# access to `let` or `subject` values from the caller's context.
|
31
|
+
#
|
32
|
+
# [source,ruby]
|
33
|
+
# ----
|
34
|
+
# shared_examples "mock behavior" do
|
35
|
+
# before do
|
36
|
+
# allow(service).to receive(:call).and_return("mocked response")
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# it "returns mocked response" do
|
40
|
+
# expect(service.call).to eq "mocked response"
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# context "working example with include_examples" do
|
45
|
+
# let(:service) { double(:service) }
|
46
|
+
#
|
47
|
+
# include_examples "mock behavior"
|
48
|
+
#
|
49
|
+
# it "uses the mocked service" do
|
50
|
+
# expect(service.call).to eq "mocked response" # Passes
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# context "broken example with it_behaves_like" do
|
55
|
+
# let(:service) { double(:service) }
|
56
|
+
#
|
57
|
+
# it_behaves_like "mock behavior"
|
58
|
+
#
|
59
|
+
# it "unexpectedly does not use the mocked service" do
|
60
|
+
# # Fails because `it_behaves_like` does not apply the mock setup
|
61
|
+
# expect(service.call).to eq "mocked response"
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
# ----
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
# # bad
|
68
|
+
# include_examples 'examples'
|
69
|
+
#
|
70
|
+
# # good
|
71
|
+
# it_behaves_like 'examples'
|
72
|
+
#
|
73
|
+
class IncludeExamples < Base
|
74
|
+
extend AutoCorrector
|
75
|
+
|
76
|
+
MSG = 'Prefer `it_behaves_like` over `include_examples`.'
|
77
|
+
|
78
|
+
RESTRICT_ON_SEND = %i[include_examples].freeze
|
79
|
+
|
80
|
+
def on_send(node)
|
81
|
+
selector = node.loc.selector
|
82
|
+
|
83
|
+
add_offense(selector) do |corrector|
|
84
|
+
corrector.replace(selector, 'it_behaves_like')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -17,6 +17,8 @@ module RuboCop
|
|
17
17
|
# end
|
18
18
|
#
|
19
19
|
class IteratedExpectation < Base
|
20
|
+
extend AutoCorrector
|
21
|
+
|
20
22
|
MSG = 'Prefer using the `all` matcher instead ' \
|
21
23
|
'of iterating over an array.'
|
22
24
|
|
@@ -25,14 +27,14 @@ module RuboCop
|
|
25
27
|
(block
|
26
28
|
(send ... :each)
|
27
29
|
(args (arg $_))
|
28
|
-
|
30
|
+
(...)
|
29
31
|
)
|
30
32
|
PATTERN
|
31
33
|
|
32
34
|
# @!method each_numblock?(node)
|
33
35
|
def_node_matcher :each_numblock?, <<~PATTERN
|
34
36
|
(numblock
|
35
|
-
(send ... :each) _
|
37
|
+
(send ... :each) _ (...)
|
36
38
|
)
|
37
39
|
PATTERN
|
38
40
|
|
@@ -42,23 +44,43 @@ module RuboCop
|
|
42
44
|
PATTERN
|
43
45
|
|
44
46
|
def on_block(node)
|
45
|
-
each?(node) do |arg
|
46
|
-
|
47
|
-
add_offense(node.send_node)
|
48
|
-
end
|
47
|
+
each?(node) do |arg|
|
48
|
+
check_offense(node, arg)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
def on_numblock(node)
|
53
|
-
each_numblock?(node) do
|
54
|
-
|
55
|
-
add_offense(node.send_node)
|
56
|
-
end
|
53
|
+
each_numblock?(node) do
|
54
|
+
check_offense(node, :_1)
|
57
55
|
end
|
58
56
|
end
|
59
57
|
|
60
58
|
private
|
61
59
|
|
60
|
+
def check_offense(node, argument)
|
61
|
+
if single_expectation?(node.body, argument)
|
62
|
+
add_offense(node.send_node) do |corrector|
|
63
|
+
next unless node.body.arguments.one?
|
64
|
+
next if uses_argument_in_matcher?(node, argument)
|
65
|
+
|
66
|
+
corrector.replace(node, single_expectation_replacement(node))
|
67
|
+
end
|
68
|
+
elsif only_expectations?(node.body, argument)
|
69
|
+
add_offense(node.send_node)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def single_expectation_replacement(node)
|
74
|
+
collection = node.receiver.source
|
75
|
+
matcher = node.body.first_argument.source
|
76
|
+
|
77
|
+
"expect(#{collection}).to all(#{matcher})"
|
78
|
+
end
|
79
|
+
|
80
|
+
def uses_argument_in_matcher?(node, argument)
|
81
|
+
node.body.first_argument.each_descendant.any?(s(:lvar, argument))
|
82
|
+
end
|
83
|
+
|
62
84
|
def single_expectation?(body, arg)
|
63
85
|
expectation?(body, arg)
|
64
86
|
end
|
@@ -100,18 +100,21 @@ module RuboCop
|
|
100
100
|
|
101
101
|
def on_casgn(node)
|
102
102
|
return unless inside_describe_block?(node)
|
103
|
+
return if explicit_namespace?(node.namespace)
|
103
104
|
|
104
105
|
add_offense(node, message: MSG_CONST)
|
105
106
|
end
|
106
107
|
|
107
108
|
def on_class(node)
|
108
109
|
return unless inside_describe_block?(node)
|
110
|
+
return if explicit_namespace?(node.identifier.namespace)
|
109
111
|
|
110
112
|
add_offense(node, message: MSG_CLASS)
|
111
113
|
end
|
112
114
|
|
113
115
|
def on_module(node)
|
114
116
|
return unless inside_describe_block?(node)
|
117
|
+
return if explicit_namespace?(node.identifier.namespace)
|
115
118
|
|
116
119
|
add_offense(node, message: MSG_MODULE)
|
117
120
|
end
|
@@ -121,6 +124,10 @@ module RuboCop
|
|
121
124
|
def inside_describe_block?(node)
|
122
125
|
node.each_ancestor(:block).any? { |ancestor| spec_group?(ancestor) }
|
123
126
|
end
|
127
|
+
|
128
|
+
def explicit_namespace?(namespace)
|
129
|
+
!namespace.nil?
|
130
|
+
end
|
124
131
|
end
|
125
132
|
end
|
126
133
|
end
|
@@ -133,8 +133,8 @@ module RuboCop
|
|
133
133
|
|
134
134
|
def count_up_nesting?(node, example_group)
|
135
135
|
example_group &&
|
136
|
-
|
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
|
-
(
|
68
|
+
(any_block (send nil? {#Examples.regular #Examples.focused} ...) ...)
|
69
69
|
PATTERN
|
70
70
|
|
71
71
|
# @!method includes_expectation?(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
|
-
(
|
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
|
-
(
|
77
|
+
(any_block (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
|
79
78
|
PATTERN
|
80
79
|
|
81
80
|
# @!method metadata_without_reason?(node)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
|
-
#
|
6
|
+
# Prefer `receive_messages` over multiple `receive`s on the same object.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# The autocorrection is marked as unsafe, because it may change the
|
@@ -148,8 +148,7 @@ module RuboCop
|
|
148
148
|
end
|
149
149
|
|
150
150
|
def heredoc_or_splat?(node)
|
151
|
-
(
|
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
|
-
(
|
44
|
+
(any_block (send _ :around ...) ... (send _ :run))
|
45
45
|
PATTERN
|
46
46
|
|
47
47
|
# @!method match_redundant_around_hook_send?(node)
|
@@ -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
|
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
|
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?)
|
@@ -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.
|
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'
|
data/lib/rubocop/rspec/hook.rb
CHANGED
@@ -26,7 +26,7 @@ module RuboCop
|
|
26
26
|
|
27
27
|
# @!method example_group?(node)
|
28
28
|
def_node_matcher :example_group?, <<~PATTERN
|
29
|
-
(
|
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
|
-
(
|
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)
|
@@ -75,6 +72,12 @@ module RuboCop
|
|
75
72
|
# @!method subject?(node)
|
76
73
|
def_node_matcher :subject?, '(block (send nil? #Subjects.all ...) ...)'
|
77
74
|
|
75
|
+
module ErrorMatchers # :nodoc:
|
76
|
+
def self.all(element)
|
77
|
+
Language.config['ErrorMatchers'].include?(element.to_s)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
78
81
|
module ExampleGroups # :nodoc:
|
79
82
|
class << self
|
80
83
|
def all(element)
|
@@ -203,14 +206,14 @@ module RuboCop
|
|
203
206
|
# This is used in Dialect and DescribeClass cops to detect RSpec blocks.
|
204
207
|
module ALL # :nodoc:
|
205
208
|
def self.all(element)
|
206
|
-
[ExampleGroups, Examples, Expectations, Helpers, Hooks,
|
207
|
-
Runners, SharedGroups, Subjects]
|
209
|
+
[ErrorMatchers, ExampleGroups, Examples, Expectations, Helpers, Hooks,
|
210
|
+
Includes, Runners, SharedGroups, Subjects]
|
208
211
|
.find { |concept| concept.all(element) }
|
209
212
|
end
|
210
213
|
end
|
211
214
|
|
212
|
-
private_constant :ExampleGroups, :Examples, :Expectations,
|
213
|
-
:Includes, :Runners, :SharedGroups, :ALL
|
215
|
+
private_constant :ErrorMatchers, :ExampleGroups, :Examples, :Expectations,
|
216
|
+
:Hooks, :Includes, :Runners, :SharedGroups, :ALL
|
214
217
|
end
|
215
218
|
end
|
216
219
|
end
|
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
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Backus
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
- Nils Gemeinhardt
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: lint_roller
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/rubocop/cop/rspec/implicit_block_expectation.rb
|
115
115
|
- lib/rubocop/cop/rspec/implicit_expect.rb
|
116
116
|
- lib/rubocop/cop/rspec/implicit_subject.rb
|
117
|
+
- lib/rubocop/cop/rspec/include_examples.rb
|
117
118
|
- lib/rubocop/cop/rspec/indexed_let.rb
|
118
119
|
- lib/rubocop/cop/rspec/instance_spy.rb
|
119
120
|
- lib/rubocop/cop/rspec/instance_variable.rb
|
@@ -226,7 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
226
227
|
- !ruby/object:Gem::Version
|
227
228
|
version: '0'
|
228
229
|
requirements: []
|
229
|
-
rubygems_version: 3.6.
|
230
|
+
rubygems_version: 3.6.9
|
230
231
|
specification_version: 4
|
231
232
|
summary: Code style checking for RSpec files
|
232
233
|
test_files: []
|