rubocop-rspec 3.9.0 → 3.10.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 +15 -0
- data/config/default.yml +14 -0
- data/lib/rubocop/cop/rspec/around_block.rb +22 -0
- data/lib/rubocop/cop/rspec/contain_exactly.rb +8 -22
- data/lib/rubocop/cop/rspec/context_method.rb +1 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/described_class.rb +1 -1
- data/lib/rubocop/cop/rspec/discarded_matcher.rb +113 -0
- data/lib/rubocop/cop/rspec/empty_example_group.rb +3 -2
- data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +7 -2
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -0
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/example_length.rb +1 -1
- data/lib/rubocop/cop/rspec/example_without_description.rb +1 -1
- data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_actual.rb +33 -13
- data/lib/rubocop/cop/rspec/expect_change.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -0
- data/lib/rubocop/cop/rspec/expect_in_let.rb +1 -1
- data/lib/rubocop/cop/rspec/hook_argument.rb +1 -0
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -0
- data/lib/rubocop/cop/rspec/indexed_let.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +13 -0
- data/lib/rubocop/cop/rspec/leading_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
- data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/match_with_simple_regex.rb +89 -0
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -1
- data/lib/rubocop/cop/rspec/mixin/inside_example.rb +16 -0
- data/lib/rubocop/cop/rspec/mixin/metadata.rb +1 -0
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +1 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/no_expectation_example.rb +1 -0
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/redundant_around.rb +1 -0
- data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
- data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
- data/lib/rubocop/cop/rspec/scattered_let.rb +11 -5
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
- data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +3 -6
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +17 -10
- data/lib/rubocop/cop/rspec/subject_declaration.rb +17 -1
- data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +3 -5
- data/lib/rubocop/cop/rspec/yield.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +2 -0
- data/lib/rubocop/rspec/description_extractor.rb +1 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +1 -0
- metadata +27 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b5b6ac2158196f3722c9b53e233a5f96becfced4ae47abe76ad577f3e258ff1c
|
|
4
|
+
data.tar.gz: be3134943d1be32c95f193ef984e27ba7ce3091b862e0e338afb8167c3e3b106
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51df438548b237d380ada23cfc3e38d399e86951588acda134bf23abe6ad7b36d206e4eddd52457695ac74aba24ec84535074828827b953ad46ac6932315f0c4
|
|
7
|
+
data.tar.gz: 93eb7b7f6c8cfad2a5e816488ef9a60df934814947086b4b702d634ac9f6643dbafdd8acec8ab1eb8449c8e76e710ff794990b22213d7335e916b65054b87981
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Master (Unreleased)
|
|
4
4
|
|
|
5
|
+
## 3.10.0 (2026-06-05)
|
|
6
|
+
|
|
7
|
+
- Add new cop `RSpec/MatchWithSimpleRegex` to suggest `include` matcher when `match` is used with simple string literals without regex-specific features. ([@bquorning])
|
|
8
|
+
- Add new cop `RSpec/DiscardedMatcher` to detect matchers in void context (e.g. missing `.and` between compound matchers). ([@ydakuka])
|
|
9
|
+
- Add support for `itblock` nodes. ([@Darhazer])
|
|
10
|
+
- `RSpec/ScatteredLet` now preserves the order of `let`s during auto-correction. ([@Darhazer])
|
|
11
|
+
- Fix a false negative for `RSpec/EmptyLineAfterFinalLet` inside `shared_examples` / `include_examples` / `it_behaves_like` blocks. ([@Darhazer])
|
|
12
|
+
- Fix a false positive for `RSpec/ContainExactly` when `contain_exactly` has multiple splat arguments. ([@ydah])
|
|
13
|
+
- Add autocorrect support for `RSpec/SubjectDeclaration`. ([@eugeneius])
|
|
14
|
+
- Fix false negatives for `RSpec/SpecFilePathFormat` when the expected class path only partially matches a path segment. ([@ydah])
|
|
15
|
+
- Fix a false negative for `RSpec/ExpectActual` when the matcher takes no arguments (e.g. `expect("foo").to be_present`, `expect(1).to be`). ([@cvx])
|
|
16
|
+
|
|
5
17
|
## 3.9.0 (2026-01-07)
|
|
6
18
|
|
|
7
19
|
- Fix a false positive for `RSpec/LeakyLocalVariable` when variables are used only in example metadata (e.g., skip messages). ([@ydah])
|
|
@@ -988,6 +1000,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
988
1000
|
[@composerinteralia]: https://github.com/composerinteralia
|
|
989
1001
|
[@corsonknowles]: https://github.com/corsonknowles
|
|
990
1002
|
[@corydiamand]: https://github.com/corydiamand
|
|
1003
|
+
[@cvx]: https://github.com/cvx
|
|
991
1004
|
[@d4rky-pl]: https://github.com/d4rky-pl
|
|
992
1005
|
[@darhazer]: https://github.com/Darhazer
|
|
993
1006
|
[@daveworth]: https://github.com/daveworth
|
|
@@ -1006,6 +1019,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1006
1019
|
[@elebow]: https://github.com/elebow
|
|
1007
1020
|
[@elisefitz15]: https://github.com/EliseFitz15
|
|
1008
1021
|
[@elliterate]: https://github.com/elliterate
|
|
1022
|
+
[@eugeneius]: https://github.com/eugeneius
|
|
1009
1023
|
[@faucct]: https://github.com/faucct
|
|
1010
1024
|
[@foton]: https://github.com/foton
|
|
1011
1025
|
[@francois-ferrandis]: https://github.com/francois-ferrandis
|
|
@@ -1103,6 +1117,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1103
1117
|
[@yasu551]: https://github.com/yasu551
|
|
1104
1118
|
[@ybiquitous]: https://github.com/ybiquitous
|
|
1105
1119
|
[@ydah]: https://github.com/ydah
|
|
1120
|
+
[@ydakuka]: https://github.com/ydakuka
|
|
1106
1121
|
[@yevhene]: https://github.com/yevhene
|
|
1107
1122
|
[@ypresto]: https://github.com/ypresto
|
|
1108
1123
|
[@yujideveloper]: https://github.com/yujideveloper
|
data/config/default.yml
CHANGED
|
@@ -311,6 +311,13 @@ RSpec/Dialect:
|
|
|
311
311
|
VersionAdded: '1.33'
|
|
312
312
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Dialect
|
|
313
313
|
|
|
314
|
+
RSpec/DiscardedMatcher:
|
|
315
|
+
Description: Checks for matchers that are used in void context.
|
|
316
|
+
Enabled: pending
|
|
317
|
+
VersionAdded: '3.10'
|
|
318
|
+
CustomMatcherMethods: []
|
|
319
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DiscardedMatcher
|
|
320
|
+
|
|
314
321
|
RSpec/DuplicatedMetadata:
|
|
315
322
|
Description: Avoid duplicated metadata.
|
|
316
323
|
Enabled: true
|
|
@@ -636,6 +643,13 @@ RSpec/MatchArray:
|
|
|
636
643
|
VersionAdded: '2.19'
|
|
637
644
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MatchArray
|
|
638
645
|
|
|
646
|
+
RSpec/MatchWithSimpleRegex:
|
|
647
|
+
Description: Enforces the use of `include` matcher instead of `match` when the matcher
|
|
648
|
+
is a simple string literal without regex-specific features.
|
|
649
|
+
Enabled: pending
|
|
650
|
+
VersionAdded: '3.10'
|
|
651
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MatchWithSimpleRegex
|
|
652
|
+
|
|
639
653
|
RSpec/MessageChain:
|
|
640
654
|
Description: Check that chains of messages are not being stubbed.
|
|
641
655
|
Enabled: true
|
|
@@ -41,6 +41,11 @@ module RuboCop
|
|
|
41
41
|
(numblock (send nil? :around sym ?) ...)
|
|
42
42
|
PATTERN
|
|
43
43
|
|
|
44
|
+
# @!method hook_itblock(node)
|
|
45
|
+
def_node_matcher :hook_itblock, <<~PATTERN
|
|
46
|
+
(itblock (send nil? :around sym ?) ...)
|
|
47
|
+
PATTERN
|
|
48
|
+
|
|
44
49
|
# @!method find_arg_usage(node)
|
|
45
50
|
def_node_search :find_arg_usage, <<~PATTERN
|
|
46
51
|
{(send $... {:call :run}) (send _ _ $...) (yield $...) (block-pass $...)}
|
|
@@ -62,6 +67,12 @@ module RuboCop
|
|
|
62
67
|
end
|
|
63
68
|
end
|
|
64
69
|
|
|
70
|
+
def on_itblock(node)
|
|
71
|
+
hook_itblock(node) do
|
|
72
|
+
check_for_itblock(node)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
65
76
|
private
|
|
66
77
|
|
|
67
78
|
def add_no_arg_offense(node)
|
|
@@ -89,6 +100,17 @@ module RuboCop
|
|
|
89
100
|
message: format(MSG_UNUSED_ARG, arg: :_1)
|
|
90
101
|
)
|
|
91
102
|
end
|
|
103
|
+
|
|
104
|
+
def check_for_itblock(block)
|
|
105
|
+
find_arg_usage(block) do |usage|
|
|
106
|
+
return if usage.include?(s(:lvar, :it))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
add_offense(
|
|
110
|
+
block.children.last,
|
|
111
|
+
message: format(MSG_UNUSED_ARG, arg: :it)
|
|
112
|
+
)
|
|
113
|
+
end
|
|
92
114
|
end
|
|
93
115
|
end
|
|
94
116
|
end
|
|
@@ -12,10 +12,13 @@ module RuboCop
|
|
|
12
12
|
#
|
|
13
13
|
# @example
|
|
14
14
|
# # bad
|
|
15
|
-
# it { is_expected.to contain_exactly(*
|
|
15
|
+
# it { is_expected.to contain_exactly(*array) }
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# it { is_expected.to match_array(array) }
|
|
16
19
|
#
|
|
17
20
|
# # good
|
|
18
|
-
# it { is_expected.to
|
|
21
|
+
# it { is_expected.to contain_exactly(*array1, *array2) }
|
|
19
22
|
#
|
|
20
23
|
# # good
|
|
21
24
|
# it { is_expected.to contain_exactly(content, *array) }
|
|
@@ -27,29 +30,12 @@ module RuboCop
|
|
|
27
30
|
RESTRICT_ON_SEND = %i[contain_exactly].freeze
|
|
28
31
|
|
|
29
32
|
def on_send(node)
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
check_populated_collection(node)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def check_populated_collection(node)
|
|
38
|
-
return unless node.each_child_node.all?(&:splat_type?)
|
|
33
|
+
return unless node.arguments.one? && node.first_argument.splat_type?
|
|
39
34
|
|
|
40
35
|
add_offense(node) do |corrector|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def autocorrect_for_populated_array(node, corrector)
|
|
46
|
-
arrays = node.arguments.map do |splat_node|
|
|
47
|
-
splat_node.children.first
|
|
36
|
+
array = node.first_argument.children.first
|
|
37
|
+
corrector.replace(node, "match_array(#{array.source})")
|
|
48
38
|
end
|
|
49
|
-
corrector.replace(
|
|
50
|
-
node,
|
|
51
|
-
"match_array(#{arrays.map(&:source).join(' + ')})"
|
|
52
|
-
)
|
|
53
39
|
end
|
|
54
40
|
end
|
|
55
41
|
end
|
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
|
38
38
|
...)
|
|
39
39
|
PATTERN
|
|
40
40
|
|
|
41
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
41
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
42
42
|
context_method(node) do |context|
|
|
43
43
|
add_offense(context) do |corrector|
|
|
44
44
|
corrector.replace(node.send_node.loc.selector, 'describe')
|
|
@@ -70,7 +70,7 @@ module RuboCop
|
|
|
70
70
|
(block (send #rspec? { :context :shared_context } $(any_str ...) ...) ...)
|
|
71
71
|
PATTERN
|
|
72
72
|
|
|
73
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
73
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
74
74
|
context_wording(node) do |context|
|
|
75
75
|
unless matches_allowed_pattern?(description(context))
|
|
76
76
|
add_offense(context, message: message)
|
|
@@ -110,7 +110,7 @@ module RuboCop
|
|
|
110
110
|
def_node_search :contains_described_class?,
|
|
111
111
|
'(send nil? :described_class)'
|
|
112
112
|
|
|
113
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
113
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
114
114
|
# In case the explicit style is used, we need to remember what's
|
|
115
115
|
# being described.
|
|
116
116
|
@described_class, body = described_constant(node)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpec
|
|
6
|
+
# Checks for matchers that are used in void context.
|
|
7
|
+
#
|
|
8
|
+
# Matcher calls like `change`, `receive`, etc. that appear as
|
|
9
|
+
# standalone expressions have their result silently discarded.
|
|
10
|
+
# This usually means a missing `.and` to chain compound matchers.
|
|
11
|
+
#
|
|
12
|
+
# The list of matcher methods can be configured
|
|
13
|
+
# with `CustomMatcherMethods`.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# # bad
|
|
17
|
+
# specify do
|
|
18
|
+
# expect { result }
|
|
19
|
+
# .to change { obj.foo }.from(1).to(2)
|
|
20
|
+
# change { obj.bar }.from(3).to(4)
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# # good
|
|
24
|
+
# specify do
|
|
25
|
+
# expect { result }
|
|
26
|
+
# .to change { obj.foo }.from(1).to(2)
|
|
27
|
+
# .and change { obj.bar }.from(3).to(4)
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# specify do
|
|
32
|
+
# expect { result }.to change { obj.foo }.from(1).to(2)
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
class DiscardedMatcher < Base
|
|
36
|
+
include InsideExample
|
|
37
|
+
|
|
38
|
+
MSG = 'The result of `%<method>s` is not used. ' \
|
|
39
|
+
'Did you mean to chain it with `.and`?'
|
|
40
|
+
|
|
41
|
+
MATCHER_METHODS = %i[
|
|
42
|
+
change have_received output
|
|
43
|
+
receive receive_messages receive_message_chain
|
|
44
|
+
].to_set.freeze
|
|
45
|
+
|
|
46
|
+
def on_send(node)
|
|
47
|
+
check_discarded_matcher(node, node)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
51
|
+
check_discarded_matcher(node.send_node, node)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def check_discarded_matcher(send_node, node)
|
|
57
|
+
return unless matcher_call?(send_node)
|
|
58
|
+
return unless inside_example?(node)
|
|
59
|
+
return unless example_with_matcher_expectation?(node)
|
|
60
|
+
|
|
61
|
+
target = find_outermost_chain(node)
|
|
62
|
+
return unless void_value?(target)
|
|
63
|
+
|
|
64
|
+
add_offense(target, message: format(MSG, method: node.method_name))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def example_with_matcher_expectation?(node)
|
|
68
|
+
example_node =
|
|
69
|
+
node.each_ancestor(:block).find { |ancestor| example?(ancestor) }
|
|
70
|
+
|
|
71
|
+
example_node.each_descendant(:send).any? do |send_node|
|
|
72
|
+
expectation_with_matcher?(send_node)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def expectation_with_matcher?(node)
|
|
77
|
+
%i[to to_not not_to].include?(node.method_name) &&
|
|
78
|
+
node.arguments.any? do |arg|
|
|
79
|
+
arg.each_node(:send).any? { |s| matcher_call?(s) }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def void_value?(node)
|
|
84
|
+
case node.parent.type
|
|
85
|
+
when :block
|
|
86
|
+
example?(node.parent)
|
|
87
|
+
when :begin, :case, :when
|
|
88
|
+
void_value?(node.parent)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def matcher_call?(node)
|
|
93
|
+
node.receiver.nil? && all_matcher_methods.include?(node.method_name)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def all_matcher_methods
|
|
97
|
+
@all_matcher_methods ||=
|
|
98
|
+
(MATCHER_METHODS + custom_matcher_methods).freeze
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def custom_matcher_methods
|
|
102
|
+
cop_config.fetch('CustomMatcherMethods', []).map(&:to_sym)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def find_outermost_chain(node)
|
|
106
|
+
current = node
|
|
107
|
+
current = current.parent while current.parent.receiver == current
|
|
108
|
+
current
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -38,6 +38,7 @@ module RuboCop
|
|
|
38
38
|
class EmptyExampleGroup < Base
|
|
39
39
|
extend AutoCorrector
|
|
40
40
|
|
|
41
|
+
include InsideExample
|
|
41
42
|
include RangeHelp
|
|
42
43
|
|
|
43
44
|
MSG = 'Empty example group detected.'
|
|
@@ -136,9 +137,9 @@ module RuboCop
|
|
|
136
137
|
}
|
|
137
138
|
PATTERN
|
|
138
139
|
|
|
139
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
140
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
140
141
|
return if node.each_ancestor(:any_def).any?
|
|
141
|
-
return if
|
|
142
|
+
return if inside_example?(node)
|
|
142
143
|
|
|
143
144
|
example_group_body(node) do |body|
|
|
144
145
|
next unless offensive?(body)
|
|
@@ -34,7 +34,7 @@ module RuboCop
|
|
|
34
34
|
(block $(send nil? #Hooks.all ...) _ nil?)
|
|
35
35
|
PATTERN
|
|
36
36
|
|
|
37
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
37
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
38
38
|
empty_hook?(node) do |hook|
|
|
39
39
|
add_offense(hook) do |corrector|
|
|
40
40
|
corrector.remove(
|
|
@@ -46,7 +46,7 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
MSG = 'Add an empty line after `%<example>s`.'
|
|
48
48
|
|
|
49
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
49
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
50
50
|
return unless example?(node)
|
|
51
51
|
return if allowed_one_liner?(node)
|
|
52
52
|
|
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
|
29
29
|
|
|
30
30
|
MSG = 'Add an empty line after `%<example_group>s`.'
|
|
31
31
|
|
|
32
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
32
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
33
33
|
return unless spec_group?(node)
|
|
34
34
|
|
|
35
35
|
missing_separating_line_offense(node) do |method|
|
|
@@ -23,8 +23,13 @@ module RuboCop
|
|
|
23
23
|
|
|
24
24
|
MSG = 'Add an empty line after the last `%<let>s`.'
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
# @!method example_group_or_include?(node)
|
|
27
|
+
def_node_matcher :example_group_or_include?, <<~PATTERN
|
|
28
|
+
(block (send #rspec? {#SharedGroups.all #ExampleGroups.all #Includes.all} ...) args $_)
|
|
29
|
+
PATTERN
|
|
30
|
+
|
|
31
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
32
|
+
return unless example_group_or_include?(node)
|
|
28
33
|
|
|
29
34
|
final_let = node.body.child_nodes.reverse.find { |child| let?(child) }
|
|
30
35
|
|
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
|
22
22
|
|
|
23
23
|
MSG = 'Add an empty line after `%<subject>s`.'
|
|
24
24
|
|
|
25
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
25
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
26
26
|
return unless subject?(node)
|
|
27
27
|
return unless inside_example_group?(node)
|
|
28
28
|
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
|
|
60
60
|
LABEL = 'Example'
|
|
61
61
|
|
|
62
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
63
63
|
return unless example?(node)
|
|
64
64
|
|
|
65
65
|
check_code_length(node)
|
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
|
66
66
|
# @!method example_description(node)
|
|
67
67
|
def_node_matcher :example_description, '(send nil? _ $(str $_))'
|
|
68
68
|
|
|
69
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
69
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
70
70
|
return unless example?(node)
|
|
71
71
|
|
|
72
72
|
check_example_without_description(node.send_node)
|
|
@@ -78,7 +78,7 @@ module RuboCop
|
|
|
78
78
|
} ...) ...)
|
|
79
79
|
PATTERN
|
|
80
80
|
|
|
81
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
81
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
82
82
|
it_description(node) do |description_node, message|
|
|
83
83
|
if message.match?(SHOULD_PREFIX)
|
|
84
84
|
add_wording_offense(description_node, MSG_SHOULD)
|
|
@@ -19,12 +19,15 @@ module RuboCop
|
|
|
19
19
|
# expect(name).to eq("John")
|
|
20
20
|
#
|
|
21
21
|
# # bad (not supported autocorrection)
|
|
22
|
+
# expect(42).to be_even
|
|
22
23
|
# expect(false).to eq(true)
|
|
24
|
+
# expect("user").to be_present
|
|
23
25
|
#
|
|
24
26
|
class ExpectActual < Base
|
|
25
27
|
extend AutoCorrector
|
|
26
28
|
|
|
27
29
|
MSG = 'Provide the actual value you are testing to `expect(...)`.'
|
|
30
|
+
MSG_NO_ARG = 'Test a non-literal value with `expect(...)`.'
|
|
28
31
|
|
|
29
32
|
RESTRICT_ON_SEND = Runners.all
|
|
30
33
|
|
|
@@ -65,26 +68,43 @@ module RuboCop
|
|
|
65
68
|
)
|
|
66
69
|
PATTERN
|
|
67
70
|
|
|
71
|
+
# @!method expect_literal_no_arg(node)
|
|
72
|
+
def_node_matcher :expect_literal_no_arg, <<~PATTERN
|
|
73
|
+
(send
|
|
74
|
+
(send nil? :expect $#literal?)
|
|
75
|
+
#Runners.all
|
|
76
|
+
$(send nil? $_)
|
|
77
|
+
)
|
|
78
|
+
PATTERN
|
|
79
|
+
|
|
68
80
|
def on_send(node)
|
|
69
81
|
expect_literal(node) do |actual, send_node, matcher, expected|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
next if literal?(expected)
|
|
75
|
-
|
|
76
|
-
corrector.replace(actual, expected.source)
|
|
77
|
-
if matcher == :be
|
|
78
|
-
corrector.replace(expected, actual.source)
|
|
79
|
-
else
|
|
80
|
-
corrector.replace(send_node, "#{matcher}(#{actual.source})")
|
|
81
|
-
end
|
|
82
|
-
end
|
|
82
|
+
register_offense(actual, send_node, matcher, expected)
|
|
83
|
+
end
|
|
84
|
+
expect_literal_no_arg(node) do |actual, send_node, matcher|
|
|
85
|
+
register_offense(actual, send_node, matcher, nil)
|
|
83
86
|
end
|
|
84
87
|
end
|
|
85
88
|
|
|
86
89
|
private
|
|
87
90
|
|
|
91
|
+
def register_offense(actual, send_node, matcher, expected)
|
|
92
|
+
return if SKIPPED_MATCHERS.include?(matcher)
|
|
93
|
+
|
|
94
|
+
message = expected.nil? ? MSG_NO_ARG : MSG
|
|
95
|
+
add_offense(actual, message: message) do |corrector|
|
|
96
|
+
next unless CORRECTABLE_MATCHERS.include?(matcher)
|
|
97
|
+
next if expected.nil? || literal?(expected)
|
|
98
|
+
|
|
99
|
+
corrector.replace(actual, expected.source)
|
|
100
|
+
if matcher == :be
|
|
101
|
+
corrector.replace(expected, actual.source)
|
|
102
|
+
else
|
|
103
|
+
corrector.replace(send_node, "#{matcher}(#{actual.source})")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
88
108
|
# This is not implemented using a NodePattern because it seems
|
|
89
109
|
# to not be able to match against an explicit (nil) sexp
|
|
90
110
|
def literal?(node)
|
|
@@ -88,7 +88,7 @@ module RuboCop
|
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
91
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
92
92
|
return unless style == :method_call
|
|
93
93
|
|
|
94
94
|
expect_change_with_block(node) do |receiver, message|
|
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
|
22
22
|
# @!method expectation(node)
|
|
23
23
|
def_node_search :expectation, '(send nil? #Expectations.all ...)'
|
|
24
24
|
|
|
25
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
25
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
26
26
|
return unless let?(node)
|
|
27
27
|
return if node.body.nil?
|
|
28
28
|
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
}
|
|
60
60
|
PATTERN
|
|
61
61
|
|
|
62
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
63
63
|
return unless spec_group?(node)
|
|
64
64
|
|
|
65
65
|
children = node.body&.child_nodes
|
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
|
42
42
|
...)
|
|
43
43
|
PATTERN
|
|
44
44
|
|
|
45
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
45
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
46
46
|
return unless example?(node)
|
|
47
47
|
|
|
48
48
|
null_double(node) do |var, receiver|
|
|
@@ -38,6 +38,13 @@ module RuboCop
|
|
|
38
38
|
)
|
|
39
39
|
PATTERN
|
|
40
40
|
|
|
41
|
+
# @!method each_itblock?(node)
|
|
42
|
+
def_node_matcher :each_itblock?, <<~PATTERN
|
|
43
|
+
(itblock
|
|
44
|
+
(send ... :each) _ (...)
|
|
45
|
+
)
|
|
46
|
+
PATTERN
|
|
47
|
+
|
|
41
48
|
# @!method expectation?(node)
|
|
42
49
|
def_node_matcher :expectation?, <<~PATTERN
|
|
43
50
|
(send (send nil? :expect (lvar %)) :to ...)
|
|
@@ -55,6 +62,12 @@ module RuboCop
|
|
|
55
62
|
end
|
|
56
63
|
end
|
|
57
64
|
|
|
65
|
+
def on_itblock(node)
|
|
66
|
+
each_itblock?(node) do
|
|
67
|
+
check_offense(node, :it)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
58
71
|
private
|
|
59
72
|
|
|
60
73
|
def check_offense(node, argument)
|
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
|
37
37
|
|
|
38
38
|
MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
|
|
39
39
|
|
|
40
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
40
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
41
41
|
return unless subject?(node)
|
|
42
42
|
return unless inside_example_group?(node)
|
|
43
43
|
|
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
|
55
55
|
[RSpec::ScatteredLet]
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
58
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
59
59
|
return unless example_group_with_body?(node)
|
|
60
60
|
|
|
61
61
|
check_let_declarations(node.body) if multiline_block?(node.body)
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
# @!method method_called?(node)
|
|
60
60
|
def_node_search :method_called?, '(send nil? %)'
|
|
61
61
|
|
|
62
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
62
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
63
63
|
return unless example_or_shared_group_or_including?(node)
|
|
64
64
|
|
|
65
65
|
unused_let_bang(node) do |let|
|