rubocop-rspec 3.8.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 +25 -0
- data/config/default.yml +22 -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 +3 -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/leaky_local_variable.rb +18 -0
- 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/output.rb +78 -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 +12 -2
- 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 +3 -0
- data/lib/rubocop/rspec/description_extractor.rb +1 -1
- data/lib/rubocop/rspec/hook.rb +13 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- data/lib/rubocop-rspec.rb +1 -0
- metadata +28 -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,25 @@
|
|
|
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
|
+
|
|
17
|
+
## 3.9.0 (2026-01-07)
|
|
18
|
+
|
|
19
|
+
- Fix a false positive for `RSpec/LeakyLocalVariable` when variables are used only in example metadata (e.g., skip messages). ([@ydah])
|
|
20
|
+
- Fix a false positive for `RSpec/ScatteredSetup` when the hook is defined inside a class method. ([@d4rky-pl])
|
|
21
|
+
- Fix a false positive for `RSpec/DescribedClass` inside dynamically evaluated blocks (`class_eval`, `module_eval`, `instance_eval`, `class_exec`, `module_exec`, `instance_exec`). ([@sucicfilip])
|
|
22
|
+
- Add new cop `RSpec/Output`. ([@kevinrobell-st])
|
|
23
|
+
|
|
5
24
|
## 3.8.0 (2025-11-12)
|
|
6
25
|
|
|
7
26
|
- Add new cop `RSpec/LeakyLocalVariable`. ([@lovro-bikic])
|
|
@@ -981,6 +1000,8 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
981
1000
|
[@composerinteralia]: https://github.com/composerinteralia
|
|
982
1001
|
[@corsonknowles]: https://github.com/corsonknowles
|
|
983
1002
|
[@corydiamand]: https://github.com/corydiamand
|
|
1003
|
+
[@cvx]: https://github.com/cvx
|
|
1004
|
+
[@d4rky-pl]: https://github.com/d4rky-pl
|
|
984
1005
|
[@darhazer]: https://github.com/Darhazer
|
|
985
1006
|
[@daveworth]: https://github.com/daveworth
|
|
986
1007
|
[@dduugg]: https://github.com/dduugg
|
|
@@ -998,6 +1019,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
998
1019
|
[@elebow]: https://github.com/elebow
|
|
999
1020
|
[@elisefitz15]: https://github.com/EliseFitz15
|
|
1000
1021
|
[@elliterate]: https://github.com/elliterate
|
|
1022
|
+
[@eugeneius]: https://github.com/eugeneius
|
|
1001
1023
|
[@faucct]: https://github.com/faucct
|
|
1002
1024
|
[@foton]: https://github.com/foton
|
|
1003
1025
|
[@francois-ferrandis]: https://github.com/francois-ferrandis
|
|
@@ -1023,6 +1045,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1023
1045
|
[@jtannas]: https://github.com/jtannas
|
|
1024
1046
|
[@k-s-a]: https://github.com/K-S-A
|
|
1025
1047
|
[@kellysutton]: https://github.com/kellysutton
|
|
1048
|
+
[@kevinrobell-st]: https://github.com/kevinrobell-st
|
|
1026
1049
|
[@koic]: https://github.com/koic
|
|
1027
1050
|
[@krororo]: https://github.com/krororo
|
|
1028
1051
|
[@kuahyeow]: https://github.com/kuahyeow
|
|
@@ -1077,6 +1100,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1077
1100
|
[@smcgivern]: https://github.com/smcgivern
|
|
1078
1101
|
[@splattael]: https://github.com/splattael
|
|
1079
1102
|
[@stephannv]: https://github.com/stephannv
|
|
1103
|
+
[@sucicfilip]: https://github.com/sucicfilip
|
|
1080
1104
|
[@swelther]: https://github.com/swelther
|
|
1081
1105
|
[@t3h2mas]: https://github.com/t3h2mas
|
|
1082
1106
|
[@tdeo]: https://github.com/tdeo
|
|
@@ -1093,6 +1117,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1093
1117
|
[@yasu551]: https://github.com/yasu551
|
|
1094
1118
|
[@ybiquitous]: https://github.com/ybiquitous
|
|
1095
1119
|
[@ydah]: https://github.com/ydah
|
|
1120
|
+
[@ydakuka]: https://github.com/ydakuka
|
|
1096
1121
|
[@yevhene]: https://github.com/yevhene
|
|
1097
1122
|
[@ypresto]: https://github.com/ypresto
|
|
1098
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
|
|
@@ -758,6 +772,14 @@ RSpec/NotToNot:
|
|
|
758
772
|
VersionAdded: '1.4'
|
|
759
773
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/NotToNot
|
|
760
774
|
|
|
775
|
+
RSpec/Output:
|
|
776
|
+
Description: Checks for the use of output calls like puts and print in specs.
|
|
777
|
+
Enabled: pending
|
|
778
|
+
AutoCorrect: contextual
|
|
779
|
+
SafeAutoCorrect: false
|
|
780
|
+
VersionAdded: '3.9'
|
|
781
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Output
|
|
782
|
+
|
|
761
783
|
RSpec/OverwritingSetup:
|
|
762
784
|
Description: Checks if there is a let/subject that overwrites an existing one.
|
|
763
785
|
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)
|
|
@@ -87,6 +87,8 @@ module RuboCop
|
|
|
87
87
|
{
|
|
88
88
|
(send (const nil? {:Class :Module :Struct}) :new ...)
|
|
89
89
|
(send (const nil? :Data) :define ...)
|
|
90
|
+
(send _ {:class_eval :module_eval :instance_eval} ...)
|
|
91
|
+
(send _ {:class_exec :module_exec :instance_exec} ...)
|
|
90
92
|
}
|
|
91
93
|
...
|
|
92
94
|
)
|
|
@@ -108,7 +110,7 @@ module RuboCop
|
|
|
108
110
|
def_node_search :contains_described_class?,
|
|
109
111
|
'(send nil? :described_class)'
|
|
110
112
|
|
|
111
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
113
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
112
114
|
# In case the explicit style is used, we need to remember what's
|
|
113
115
|
# being described.
|
|
114
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
|
|