rubocop-rspec 3.6.0 → 3.8.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 +22 -1
- data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
- data/lib/rubocop/cop/rspec/described_class.rb +21 -10
- data/lib/rubocop/cop/rspec/dialect.rb +1 -0
- data/lib/rubocop/cop/rspec/example_wording.rb +13 -2
- data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
- data/lib/rubocop/cop/rspec/include_examples.rb +51 -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/leaky_local_variable.rb +138 -0
- data/lib/rubocop/cop/rspec/let_setup.rb +30 -0
- data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +1 -1
- data/lib/rubocop/cop/rspec/mixin/variable.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +1 -3
- data/lib/rubocop/cop/rspec/receive_messages.rb +1 -1
- data/lib/rubocop/cop/rspec/receive_never.rb +26 -0
- data/lib/rubocop/cop/rspec/repeated_example.rb +34 -15
- data/lib/rubocop/cop/rspec/sort_metadata.rb +1 -1
- data/lib/rubocop/cop/rspec/spec_file_path_format.rb +54 -4
- data/lib/rubocop/cop/rspec/variable_definition.rb +2 -10
- data/lib/rubocop/cop/rspec/verified_doubles.rb +51 -3
- data/lib/rubocop/cop/rspec_cops.rb +1 -0
- data/lib/rubocop/rspec/language.rb +10 -4
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +5 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: abe85fc893e4ec59cb9b5f1e999d35bb41692feacc4dba579d135b3eefd6e4c7
|
|
4
|
+
data.tar.gz: 953ba29bf3fce28b79943e69039b638574d74432dac8b896f3dc8e589a20581c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: acdf813781e2081ec34540e83a9a48fc289dab5cc8bff69d9610beb4a3f21c30955794eb4226d40b9f0460a04d38665c2aa4ce0857f35d8bd3a7325d91a861be
|
|
7
|
+
data.tar.gz: 752074b0fdce204596efc71e2358982c73c2fccff07da5da364c88b8ad5e99d5a8bfdbc855a9dd422169adeddf278266352f732092b5aa86d14502016ca77837
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## Master (Unreleased)
|
|
4
4
|
|
|
5
|
+
## 3.8.0 (2025-11-12)
|
|
6
|
+
|
|
7
|
+
- Add new cop `RSpec/LeakyLocalVariable`. ([@lovro-bikic])
|
|
8
|
+
- Bump RuboCop requirement to +1.81. ([@ydah])
|
|
9
|
+
- Fix a false positive for `RSpec/LetSetup` when `let!` used in outer scope. ([@ydah])
|
|
10
|
+
- Fix a false positive for `RSpec/ReceiveNever` cop when `allow(...).to receive(...).never`. ([@ydah])
|
|
11
|
+
- Fix detection of nameless doubles with methods in `RSpec/VerifiedDoubles`. ([@ushi-as])
|
|
12
|
+
- Improve an offense message for `RSpec/RepeatedExample` cop. ([@ydah])
|
|
13
|
+
- Let `RSpec/SpecFilePathFormat` leverage ActiveSupport inflections when configured. ([@corsonknowles], [@bquorning])
|
|
14
|
+
|
|
15
|
+
## 3.7.0 (2025-09-01)
|
|
16
|
+
|
|
17
|
+
- Mark `RSpec/IncludeExamples` as `SafeAutoCorrect: false`. ([@yujideveloper])
|
|
18
|
+
- Fix a false positive for `RSpec/LeakyConstantDeclaration` when defining constants in explicit namespaces. ([@naveg])
|
|
19
|
+
- Add support for error matchers (`raise_exception` and `raise_error`) to `RSpec/Dialect`. ([@lovro-bikic])
|
|
20
|
+
- Don't register offenses for `RSpec/DescribedClass` within `Data.define` blocks. ([@lovro-bikic])
|
|
21
|
+
- Add autocorrection support for `RSpec/IteratedExpectation` for single expectations. ([@lovro-bikic])
|
|
22
|
+
- Exclude all cops from inspecting factorybot files, except if explicitly included. ([@Mth0158])
|
|
23
|
+
- Fix a false positive for `RSpec/ExcessiveDocstringSpacing` when receivers are not RSpec methods. ([@ydah])
|
|
24
|
+
|
|
5
25
|
## 3.6.0 (2025-04-18)
|
|
6
26
|
|
|
7
27
|
- Fix false positive in `RSpec/Pending`, where it would mark the default block `it` as an offense. ([@bquorning])
|
|
@@ -1021,6 +1041,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1021
1041
|
[@mlarraz]: https://github.com/mlarraz
|
|
1022
1042
|
[@mockdeep]: https://github.com/mockdeep
|
|
1023
1043
|
[@mothonmars]: https://github.com/MothOnMars
|
|
1044
|
+
[@mth0158]: https://github.com/Mth0158
|
|
1024
1045
|
[@mvz]: https://github.com/mvz
|
|
1025
1046
|
[@naveg]: https://github.com/naveg
|
|
1026
1047
|
[@nc-holodakg]: https://github.com/nc-holodakg
|
|
@@ -1066,6 +1087,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1066
1087
|
[@tmaier]: https://github.com/tmaier
|
|
1067
1088
|
[@topalovic]: https://github.com/topalovic
|
|
1068
1089
|
[@twalpole]: https://github.com/twalpole
|
|
1090
|
+
[@ushi-as]: https://github.com/ushi-as
|
|
1069
1091
|
[@vzvu3k6k]: https://github.com/vzvu3k6k
|
|
1070
1092
|
[@walf443]: https://github.com/walf443
|
|
1071
1093
|
[@yasu551]: https://github.com/yasu551
|
|
@@ -1073,5 +1095,6 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
|
|
|
1073
1095
|
[@ydah]: https://github.com/ydah
|
|
1074
1096
|
[@yevhene]: https://github.com/yevhene
|
|
1075
1097
|
[@ypresto]: https://github.com/ypresto
|
|
1098
|
+
[@yujideveloper]: https://github.com/yujideveloper
|
|
1076
1099
|
[@zdennis]: https://github.com/zdennis
|
|
1077
1100
|
[@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:
|
|
@@ -535,7 +542,9 @@ RSpec/ImplicitSubject:
|
|
|
535
542
|
RSpec/IncludeExamples:
|
|
536
543
|
Description: Checks for usage of `include_examples`.
|
|
537
544
|
Enabled: pending
|
|
545
|
+
SafeAutoCorrect: false
|
|
538
546
|
VersionAdded: '3.6'
|
|
547
|
+
VersionChanged: '3.7'
|
|
539
548
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IncludeExamples
|
|
540
549
|
|
|
541
550
|
RSpec/IndexedLet:
|
|
@@ -601,6 +610,12 @@ RSpec/LeakyConstantDeclaration:
|
|
|
601
610
|
StyleGuide: https://rspec.rubystyle.guide/#declare-constants
|
|
602
611
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LeakyConstantDeclaration
|
|
603
612
|
|
|
613
|
+
RSpec/LeakyLocalVariable:
|
|
614
|
+
Description: Checks for local variables from outer scopes used inside examples.
|
|
615
|
+
Enabled: pending
|
|
616
|
+
VersionAdded: '3.8'
|
|
617
|
+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LeakyLocalVariable
|
|
618
|
+
|
|
604
619
|
RSpec/LetBeforeExamples:
|
|
605
620
|
Description: Checks for `let` definitions that come after an example.
|
|
606
621
|
Enabled: true
|
|
@@ -782,7 +797,7 @@ RSpec/ReceiveCounts:
|
|
|
782
797
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveCounts
|
|
783
798
|
|
|
784
799
|
RSpec/ReceiveMessages:
|
|
785
|
-
Description:
|
|
800
|
+
Description: Prefer `receive_messages` over multiple `receive`s on the same object.
|
|
786
801
|
Enabled: true
|
|
787
802
|
SafeAutoCorrect: false
|
|
788
803
|
VersionAdded: '2.23'
|
|
@@ -924,7 +939,13 @@ RSpec/SpecFilePathFormat:
|
|
|
924
939
|
IgnoreMethods: false
|
|
925
940
|
IgnoreMetadata:
|
|
926
941
|
type: routing
|
|
942
|
+
InflectorPath: "./config/initializers/inflections.rb"
|
|
943
|
+
SupportedInflectors:
|
|
944
|
+
- default
|
|
945
|
+
- active_support
|
|
946
|
+
EnforcedInflector: default
|
|
927
947
|
VersionAdded: '2.24'
|
|
948
|
+
VersionChanged: '3.8'
|
|
928
949
|
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathFormat
|
|
929
950
|
|
|
930
951
|
RSpec/SpecFilePathSuffix:
|
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
|
67
67
|
|
|
68
68
|
# @!method context_wording(node)
|
|
69
69
|
def_node_matcher :context_wording, <<~PATTERN
|
|
70
|
-
(block (send #rspec? { :context :shared_context } $(
|
|
70
|
+
(block (send #rspec? { :context :shared_context } $(any_str ...) ...) ...)
|
|
71
71
|
PATTERN
|
|
72
72
|
|
|
73
73
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
@@ -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,7 +83,13 @@ 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)
|
|
@@ -10,8 +10,19 @@ module RuboCop
|
|
|
10
10
|
#
|
|
11
11
|
# @see http://betterspecs.org/#should
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
13
|
+
# @safety
|
|
14
|
+
# The autocorrect is experimental - use with care! It can be configured
|
|
15
|
+
# with CustomTransform (e.g. have => has) and IgnoredWords (e.g. only).
|
|
16
|
+
#
|
|
17
|
+
# While the autocorrect will not break your code (it only modifies test
|
|
18
|
+
# description strings, not the actual test logic), it may produce
|
|
19
|
+
# grammatically incorrect English in some cases. Always review the diff
|
|
20
|
+
# when using autocorrect to ensure the descriptions remain natural and
|
|
21
|
+
# accurate.
|
|
22
|
+
#
|
|
23
|
+
# This is not classified as an unsafe autocorrect because it does not
|
|
24
|
+
# affect code behavior, but manual review of changes is strongly
|
|
25
|
+
# recommended.
|
|
15
26
|
#
|
|
16
27
|
# Use the DisallowedExamples setting to prevent unclear or insufficient
|
|
17
28
|
# descriptions. Please note that this config will not be treated as
|
|
@@ -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
|
} ...)
|
|
@@ -12,6 +12,57 @@ module RuboCop
|
|
|
12
12
|
#
|
|
13
13
|
# Prefer using `it_behaves_like` instead.
|
|
14
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
|
+
#
|
|
15
66
|
# @example
|
|
16
67
|
# # bad
|
|
17
68
|
# include_examples 'examples'
|
|
@@ -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
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module RSpec
|
|
6
|
+
# Checks for local variables from outer scopes used inside examples.
|
|
7
|
+
#
|
|
8
|
+
# Local variables assigned outside an example but used within it act
|
|
9
|
+
# as shared state, which can make tests non-deterministic.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad - outside variable used in a hook
|
|
13
|
+
# user = create(:user)
|
|
14
|
+
#
|
|
15
|
+
# before { user.update(admin: true) }
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# let(:user) { create(:user) }
|
|
19
|
+
#
|
|
20
|
+
# before { user.update(admin: true) }
|
|
21
|
+
#
|
|
22
|
+
# # bad - outside variable used in an example
|
|
23
|
+
# user = create(:user)
|
|
24
|
+
#
|
|
25
|
+
# it 'is persisted' do
|
|
26
|
+
# expect(user).to be_persisted
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# # good
|
|
30
|
+
# let(:user) { create(:user) }
|
|
31
|
+
#
|
|
32
|
+
# it 'is persisted' do
|
|
33
|
+
# expect(user).to be_persisted
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# # also good - assigning the variable within the example
|
|
37
|
+
# it 'is persisted' do
|
|
38
|
+
# user = create(:user)
|
|
39
|
+
#
|
|
40
|
+
# expect(user).to be_persisted
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# # bad - outside variable passed to included examples
|
|
44
|
+
# attrs = ['foo', 'bar']
|
|
45
|
+
#
|
|
46
|
+
# it_behaves_like 'some examples', attrs
|
|
47
|
+
#
|
|
48
|
+
# # good
|
|
49
|
+
# it_behaves_like 'some examples' do
|
|
50
|
+
# let(:attrs) { ['foo', 'bar'] }
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# # good - when variable is used only as example description
|
|
54
|
+
# attribute = 'foo'
|
|
55
|
+
#
|
|
56
|
+
# it "#{attribute} is persisted" do
|
|
57
|
+
# expectations
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# # good - when variable is used only to include other examples
|
|
61
|
+
# examples = foo ? 'some examples' : 'other examples'
|
|
62
|
+
#
|
|
63
|
+
# it_behaves_like examples, another_argument
|
|
64
|
+
#
|
|
65
|
+
class LeakyLocalVariable < Base
|
|
66
|
+
MSG = 'Do not use local variables defined outside of ' \
|
|
67
|
+
'examples inside of them.'
|
|
68
|
+
|
|
69
|
+
# @!method example_method?(node)
|
|
70
|
+
def_node_matcher :example_method?, <<~PATTERN
|
|
71
|
+
(send nil? #Examples.all _)
|
|
72
|
+
PATTERN
|
|
73
|
+
|
|
74
|
+
# @!method includes_method?(node)
|
|
75
|
+
def_node_matcher :includes_method?, <<~PATTERN
|
|
76
|
+
(send nil? #Includes.all ...)
|
|
77
|
+
PATTERN
|
|
78
|
+
|
|
79
|
+
def self.joining_forces
|
|
80
|
+
VariableForce
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def after_leaving_scope(scope, _variable_table)
|
|
84
|
+
scope.variables.each_value { |variable| check_references(variable) }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def check_references(variable)
|
|
90
|
+
variable.assignments.each do |assignment|
|
|
91
|
+
next if part_of_example_scope?(assignment.node)
|
|
92
|
+
|
|
93
|
+
assignment.references.each do |reference|
|
|
94
|
+
next unless inside_describe_block?(reference)
|
|
95
|
+
next unless part_of_example_scope?(reference)
|
|
96
|
+
next if allowed_reference?(reference)
|
|
97
|
+
|
|
98
|
+
add_offense(assignment.node)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def allowed_reference?(node)
|
|
104
|
+
node.each_ancestor.any? do |ancestor|
|
|
105
|
+
next true if example_method?(ancestor)
|
|
106
|
+
if includes_method?(ancestor)
|
|
107
|
+
next allowed_includes_arguments?(ancestor, node)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
false
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def allowed_includes_arguments?(node, argument)
|
|
115
|
+
node.arguments[1..].all? do |argument_node|
|
|
116
|
+
next true if argument_node.type?(:dstr, :dsym)
|
|
117
|
+
|
|
118
|
+
argument_node != argument &&
|
|
119
|
+
argument_node.each_descendant.none?(argument)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def part_of_example_scope?(node)
|
|
124
|
+
node.each_ancestor.any? { |ancestor| example_scope?(ancestor) }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def example_scope?(node)
|
|
128
|
+
subject?(node) || let?(node) || hook?(node) || example?(node) ||
|
|
129
|
+
include?(node)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def inside_describe_block?(node)
|
|
133
|
+
node.each_ancestor(:block).any? { |ancestor| spec_group?(ancestor) }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -25,6 +25,18 @@ module RuboCop
|
|
|
25
25
|
# it 'counts widgets' do
|
|
26
26
|
# expect(Widget.count).to eq(1)
|
|
27
27
|
# end
|
|
28
|
+
#
|
|
29
|
+
# # good
|
|
30
|
+
# describe 'a widget' do
|
|
31
|
+
# let!(:my_widget) { create(:widget) }
|
|
32
|
+
# context 'when visiting its page' do
|
|
33
|
+
# let!(:my_widget) { create(:widget, name: 'Special') }
|
|
34
|
+
# it 'counts widgets' do
|
|
35
|
+
# expect(Widget.count).to eq(1)
|
|
36
|
+
# end
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
28
40
|
class LetSetup < Base
|
|
29
41
|
MSG = 'Do not use `let!` to setup objects not referenced in tests.'
|
|
30
42
|
|
|
@@ -59,6 +71,8 @@ module RuboCop
|
|
|
59
71
|
|
|
60
72
|
def unused_let_bang(node)
|
|
61
73
|
child_let_bang(node) do |method_send, method_name|
|
|
74
|
+
next if overrides_outer_let_bang?(node, method_name)
|
|
75
|
+
|
|
62
76
|
yield(method_send) unless method_called?(node, method_name.to_sym)
|
|
63
77
|
end
|
|
64
78
|
end
|
|
@@ -68,6 +82,22 @@ module RuboCop
|
|
|
68
82
|
let_bang(let, &block)
|
|
69
83
|
end
|
|
70
84
|
end
|
|
85
|
+
|
|
86
|
+
def overrides_outer_let_bang?(node, method_name)
|
|
87
|
+
node.each_ancestor(:block).any? do |ancestor|
|
|
88
|
+
next unless example_or_shared_group_or_including?(ancestor)
|
|
89
|
+
|
|
90
|
+
outer_let_bang?(ancestor, method_name)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def outer_let_bang?(ancestor_node, method_name)
|
|
95
|
+
RuboCop::RSpec::ExampleGroup.new(ancestor_node).lets.any? do |let|
|
|
96
|
+
let_bang(let) do |_send, name|
|
|
97
|
+
name == method_name
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
71
101
|
end
|
|
72
102
|
end
|
|
73
103
|
end
|
|
@@ -181,9 +181,7 @@ module RuboCop
|
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
def heredoc_argument?(matcher)
|
|
184
|
-
matcher.arguments.select
|
|
185
|
-
arg.type?(:str, :dstr, :xstr)
|
|
186
|
-
end.any?(&:heredoc?)
|
|
184
|
+
matcher.arguments.select(&:any_str_type?).any?(&:heredoc?)
|
|
187
185
|
end
|
|
188
186
|
|
|
189
187
|
# @!method predicate_matcher?(node)
|
|
@@ -5,6 +5,11 @@ module RuboCop
|
|
|
5
5
|
module RSpec
|
|
6
6
|
# Prefer `not_to receive(...)` over `receive(...).never`.
|
|
7
7
|
#
|
|
8
|
+
# This cop only flags usage with `expect`. It ignores `allow` because
|
|
9
|
+
# `allow(...).to receive(...).never` is a valid way to ensure a method
|
|
10
|
+
# is not called, while `allow(...).not_to receive(...)` would have
|
|
11
|
+
# different semantics.
|
|
12
|
+
#
|
|
8
13
|
# @example
|
|
9
14
|
# # bad
|
|
10
15
|
# expect(foo).to receive(:bar).never
|
|
@@ -12,6 +17,9 @@ module RuboCop
|
|
|
12
17
|
# # good
|
|
13
18
|
# expect(foo).not_to receive(:bar)
|
|
14
19
|
#
|
|
20
|
+
# # not flagged by this cop
|
|
21
|
+
# allow(foo).to receive(:bar).never
|
|
22
|
+
#
|
|
15
23
|
class ReceiveNever < Base
|
|
16
24
|
extend AutoCorrector
|
|
17
25
|
MSG = 'Use `not_to receive` instead of `never`.'
|
|
@@ -20,8 +28,20 @@ module RuboCop
|
|
|
20
28
|
# @!method method_on_stub?(node)
|
|
21
29
|
def_node_search :method_on_stub?, '(send nil? :receive ...)'
|
|
22
30
|
|
|
31
|
+
# @!method expect_to_receive?(node)
|
|
32
|
+
def_node_matcher :expect_to_receive?, <<~PATTERN
|
|
33
|
+
(send
|
|
34
|
+
{
|
|
35
|
+
(send #rspec? {:expect :expect_any_instance_of} ...)
|
|
36
|
+
(block (send #rspec? :expect) ...)
|
|
37
|
+
(send nil? :is_expected)
|
|
38
|
+
}
|
|
39
|
+
:to ...)
|
|
40
|
+
PATTERN
|
|
41
|
+
|
|
23
42
|
def on_send(node)
|
|
24
43
|
return unless node.method?(:never) && method_on_stub?(node)
|
|
44
|
+
return unless used_with_expect?(node)
|
|
25
45
|
|
|
26
46
|
add_offense(node.loc.selector) do |corrector|
|
|
27
47
|
autocorrect(corrector, node)
|
|
@@ -30,6 +50,12 @@ module RuboCop
|
|
|
30
50
|
|
|
31
51
|
private
|
|
32
52
|
|
|
53
|
+
def used_with_expect?(node)
|
|
54
|
+
node.each_ancestor(:send).any? do |ancestor|
|
|
55
|
+
expect_to_receive?(ancestor)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
33
59
|
def autocorrect(corrector, node)
|
|
34
60
|
corrector.replace(node.parent.loc.selector, 'not_to')
|
|
35
61
|
range = node.loc.dot.with(end_pos: node.loc.selector.end_pos)
|
|
@@ -16,36 +16,55 @@ module RuboCop
|
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
18
|
class RepeatedExample < Base
|
|
19
|
-
MSG = "Don't repeat examples within an example group."
|
|
19
|
+
MSG = "Don't repeat examples within an example group. " \
|
|
20
|
+
'Repeated on line(s) %<lines>s.'
|
|
20
21
|
|
|
21
22
|
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
22
23
|
return unless example_group?(node)
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
find_repeated_examples(node).each do |repeated_examples|
|
|
26
|
+
add_offenses_for_repeated_group(repeated_examples)
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
private
|
|
30
31
|
|
|
31
|
-
def
|
|
32
|
-
RuboCop::RSpec::ExampleGroup.new(node)
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
def find_repeated_examples(node)
|
|
33
|
+
examples = RuboCop::RSpec::ExampleGroup.new(node).examples
|
|
34
|
+
|
|
35
|
+
examples
|
|
36
|
+
.group_by { |example| build_example_signature(example) }
|
|
35
37
|
.values
|
|
36
|
-
.
|
|
37
|
-
.flatten
|
|
38
|
-
.map(&:to_node)
|
|
38
|
+
.select { |group| group.size > 1 }
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
def build_example_signature(example)
|
|
42
|
+
signature = [example.metadata, example.implementation]
|
|
44
43
|
if example.definition.method?(:its)
|
|
45
|
-
|
|
44
|
+
signature << example.definition.arguments
|
|
45
|
+
end
|
|
46
|
+
signature
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def add_offenses_for_repeated_group(repeated_examples)
|
|
50
|
+
repeated_examples.each do |example|
|
|
51
|
+
other_lines = extract_other_lines(repeated_examples, example)
|
|
52
|
+
add_offense(example.to_node, message: message(other_lines))
|
|
46
53
|
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def extract_other_lines(examples_group, current_example)
|
|
57
|
+
current_node = current_example.to_node
|
|
58
|
+
|
|
59
|
+
examples_group
|
|
60
|
+
.reject { |ex| ex.to_node.equal?(current_node) }
|
|
61
|
+
.map { |ex| ex.to_node.first_line }
|
|
62
|
+
.uniq
|
|
63
|
+
.sort
|
|
64
|
+
end
|
|
47
65
|
|
|
48
|
-
|
|
66
|
+
def message(other_lines)
|
|
67
|
+
format(MSG, lines: other_lines.join(', '))
|
|
49
68
|
end
|
|
50
69
|
end
|
|
51
70
|
end
|
|
@@ -30,7 +30,7 @@ module RuboCop
|
|
|
30
30
|
|
|
31
31
|
# @!method match_ambiguous_trailing_metadata?(node)
|
|
32
32
|
def_node_matcher :match_ambiguous_trailing_metadata?, <<~PATTERN
|
|
33
|
-
(send _ _ _ ... !{hash sym
|
|
33
|
+
(send _ _ _ ... !{hash sym any_str})
|
|
34
34
|
PATTERN
|
|
35
35
|
|
|
36
36
|
def on_metadata(args, hash)
|
|
@@ -32,6 +32,12 @@ module RuboCop
|
|
|
32
32
|
# # good
|
|
33
33
|
# whatever_spec.rb # describe MyClass, type: :routing do; end
|
|
34
34
|
#
|
|
35
|
+
# @example `EnforcedInflector: active_support`
|
|
36
|
+
# # Enable to use ActiveSupport's inflector for custom acronyms
|
|
37
|
+
# # like HTTP, etc. Set to "default" by default.
|
|
38
|
+
# # Configure `InflectorPath` with the path to the inflector file.
|
|
39
|
+
# # The default is ./config/initializers/inflections.rb.
|
|
40
|
+
#
|
|
35
41
|
class SpecFilePathFormat < Base
|
|
36
42
|
include TopLevelGroup
|
|
37
43
|
include Namespace
|
|
@@ -59,6 +65,53 @@ module RuboCop
|
|
|
59
65
|
|
|
60
66
|
private
|
|
61
67
|
|
|
68
|
+
# Inflector module that uses ActiveSupport for advanced inflection rules
|
|
69
|
+
module ActiveSupportInflector
|
|
70
|
+
def self.call(string)
|
|
71
|
+
ActiveSupport::Inflector.underscore(string)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.prepare_availability(config)
|
|
75
|
+
return if @prepared
|
|
76
|
+
|
|
77
|
+
@prepared = true
|
|
78
|
+
|
|
79
|
+
inflector_path = config.fetch('InflectorPath')
|
|
80
|
+
|
|
81
|
+
unless File.exist?(inflector_path)
|
|
82
|
+
raise "The configured `InflectorPath` #{inflector_path} does " \
|
|
83
|
+
'not exist.'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
require 'active_support/inflector'
|
|
87
|
+
require inflector_path
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Inflector module that uses basic regex-based conversion
|
|
92
|
+
module DefaultInflector
|
|
93
|
+
def self.call(string)
|
|
94
|
+
string
|
|
95
|
+
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
|
|
96
|
+
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2')
|
|
97
|
+
.downcase
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def inflector
|
|
102
|
+
case cop_config.fetch('EnforcedInflector')
|
|
103
|
+
when 'active_support'
|
|
104
|
+
ActiveSupportInflector.prepare_availability(cop_config)
|
|
105
|
+
ActiveSupportInflector
|
|
106
|
+
when 'default'
|
|
107
|
+
DefaultInflector
|
|
108
|
+
else
|
|
109
|
+
# :nocov:
|
|
110
|
+
:noop
|
|
111
|
+
# :nocov:
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
62
115
|
def ensure_correct_file_path(send_node, class_name, arguments)
|
|
63
116
|
pattern = correct_path_pattern(class_name, arguments)
|
|
64
117
|
return if filename_ends_with?(pattern)
|
|
@@ -106,10 +159,7 @@ module RuboCop
|
|
|
106
159
|
end
|
|
107
160
|
|
|
108
161
|
def camel_to_snake_case(string)
|
|
109
|
-
string
|
|
110
|
-
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
|
|
111
|
-
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2')
|
|
112
|
-
.downcase
|
|
162
|
+
inflector.call(string)
|
|
113
163
|
end
|
|
114
164
|
|
|
115
165
|
def custom_transform
|
|
@@ -60,16 +60,8 @@ module RuboCop
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def style_offense?(variable)
|
|
63
|
-
(style == :symbols &&
|
|
64
|
-
(style == :strings &&
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def string?(node)
|
|
68
|
-
node.str_type?
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def symbol?(node)
|
|
72
|
-
node.type?(:sym, :dsym)
|
|
63
|
+
(style == :symbols && variable.str_type?) ||
|
|
64
|
+
(style == :strings && variable.any_sym_type?)
|
|
73
65
|
end
|
|
74
66
|
end
|
|
75
67
|
end
|
|
@@ -10,12 +10,12 @@ module RuboCop
|
|
|
10
10
|
# @example
|
|
11
11
|
# # bad
|
|
12
12
|
# let(:foo) do
|
|
13
|
-
# double(method_name: 'returned value')
|
|
13
|
+
# double("ClassName", method_name: 'returned value')
|
|
14
14
|
# end
|
|
15
15
|
#
|
|
16
16
|
# # bad
|
|
17
17
|
# let(:foo) do
|
|
18
|
-
#
|
|
18
|
+
# spy("ClassName", method_name: 'returned value')
|
|
19
19
|
# end
|
|
20
20
|
#
|
|
21
21
|
# # good
|
|
@@ -23,6 +23,50 @@ module RuboCop
|
|
|
23
23
|
# instance_double("ClassName", method_name: 'returned value')
|
|
24
24
|
# end
|
|
25
25
|
#
|
|
26
|
+
# # good
|
|
27
|
+
# let(:foo) do
|
|
28
|
+
# class_double("ClassName", method_name: 'returned value')
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# # good
|
|
32
|
+
# let(:foo) do
|
|
33
|
+
# object_double("some object", method_name: 'returned value')
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# @example `IgnoreNameless: true (default)`
|
|
37
|
+
# # good
|
|
38
|
+
# let(:foo) do
|
|
39
|
+
# double(method_name: 'returned value')
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# # good
|
|
43
|
+
# let(:foo) do
|
|
44
|
+
# double
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# @example `IgnoreNameless: false`
|
|
48
|
+
# # bad
|
|
49
|
+
# let(:foo) do
|
|
50
|
+
# double(method_name: 'returned value')
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# # bad
|
|
54
|
+
# let(:foo) do
|
|
55
|
+
# double
|
|
56
|
+
# end
|
|
57
|
+
#
|
|
58
|
+
# @example `IgnoreSymbolicNames: false (default)`
|
|
59
|
+
# # bad
|
|
60
|
+
# let(:foo) do
|
|
61
|
+
# double(:foo)
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# @example `IgnoreSymbolicNames: true`
|
|
65
|
+
# # good
|
|
66
|
+
# let(:foo) do
|
|
67
|
+
# double(:foo)
|
|
68
|
+
# end
|
|
69
|
+
#
|
|
26
70
|
class VerifiedDoubles < Base
|
|
27
71
|
MSG = 'Prefer using verifying doubles over normal doubles.'
|
|
28
72
|
RESTRICT_ON_SEND = %i[double spy].freeze
|
|
@@ -34,7 +78,7 @@ module RuboCop
|
|
|
34
78
|
|
|
35
79
|
def on_send(node)
|
|
36
80
|
unverified_double(node) do |name, *_args|
|
|
37
|
-
return if name.nil? && cop_config['IgnoreNameless']
|
|
81
|
+
return if (name.nil? || hash?(name)) && cop_config['IgnoreNameless']
|
|
38
82
|
return if symbol?(name) && cop_config['IgnoreSymbolicNames']
|
|
39
83
|
|
|
40
84
|
add_offense(node)
|
|
@@ -46,6 +90,10 @@ module RuboCop
|
|
|
46
90
|
def symbol?(name)
|
|
47
91
|
name&.sym_type?
|
|
48
92
|
end
|
|
93
|
+
|
|
94
|
+
def hash?(arg)
|
|
95
|
+
arg.hash_type?
|
|
96
|
+
end
|
|
49
97
|
end
|
|
50
98
|
end
|
|
51
99
|
end
|
|
@@ -57,6 +57,7 @@ require_relative 'rspec/it_behaves_like'
|
|
|
57
57
|
require_relative 'rspec/iterated_expectation'
|
|
58
58
|
require_relative 'rspec/leading_subject'
|
|
59
59
|
require_relative 'rspec/leaky_constant_declaration'
|
|
60
|
+
require_relative 'rspec/leaky_local_variable'
|
|
60
61
|
require_relative 'rspec/let_before_examples'
|
|
61
62
|
require_relative 'rspec/let_setup'
|
|
62
63
|
require_relative 'rspec/match_array'
|
|
@@ -72,6 +72,12 @@ module RuboCop
|
|
|
72
72
|
# @!method subject?(node)
|
|
73
73
|
def_node_matcher :subject?, '(block (send nil? #Subjects.all ...) ...)'
|
|
74
74
|
|
|
75
|
+
module ErrorMatchers # :nodoc:
|
|
76
|
+
def self.all(element)
|
|
77
|
+
Language.config['ErrorMatchers'].include?(element.to_s)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
75
81
|
module ExampleGroups # :nodoc:
|
|
76
82
|
class << self
|
|
77
83
|
def all(element)
|
|
@@ -200,14 +206,14 @@ module RuboCop
|
|
|
200
206
|
# This is used in Dialect and DescribeClass cops to detect RSpec blocks.
|
|
201
207
|
module ALL # :nodoc:
|
|
202
208
|
def self.all(element)
|
|
203
|
-
[ExampleGroups, Examples, Expectations, Helpers, Hooks,
|
|
204
|
-
Runners, SharedGroups, Subjects]
|
|
209
|
+
[ErrorMatchers, ExampleGroups, Examples, Expectations, Helpers, Hooks,
|
|
210
|
+
Includes, Runners, SharedGroups, Subjects]
|
|
205
211
|
.find { |concept| concept.all(element) }
|
|
206
212
|
end
|
|
207
213
|
end
|
|
208
214
|
|
|
209
|
-
private_constant :ExampleGroups, :Examples, :Expectations,
|
|
210
|
-
:Includes, :Runners, :SharedGroups, :ALL
|
|
215
|
+
private_constant :ErrorMatchers, :ExampleGroups, :Examples, :Expectations,
|
|
216
|
+
:Hooks, :Includes, :Runners, :SharedGroups, :ALL
|
|
211
217
|
end
|
|
212
218
|
end
|
|
213
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.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Backus
|
|
@@ -31,20 +31,14 @@ dependencies:
|
|
|
31
31
|
requirements:
|
|
32
32
|
- - "~>"
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '1.
|
|
35
|
-
- - ">="
|
|
36
|
-
- !ruby/object:Gem::Version
|
|
37
|
-
version: 1.72.1
|
|
34
|
+
version: '1.81'
|
|
38
35
|
type: :runtime
|
|
39
36
|
prerelease: false
|
|
40
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
41
38
|
requirements:
|
|
42
39
|
- - "~>"
|
|
43
40
|
- !ruby/object:Gem::Version
|
|
44
|
-
version: '1.
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: 1.72.1
|
|
41
|
+
version: '1.81'
|
|
48
42
|
description: |
|
|
49
43
|
Code style checking for RSpec files.
|
|
50
44
|
A plugin for the RuboCop code style enforcing & linting tool.
|
|
@@ -123,6 +117,7 @@ files:
|
|
|
123
117
|
- lib/rubocop/cop/rspec/iterated_expectation.rb
|
|
124
118
|
- lib/rubocop/cop/rspec/leading_subject.rb
|
|
125
119
|
- lib/rubocop/cop/rspec/leaky_constant_declaration.rb
|
|
120
|
+
- lib/rubocop/cop/rspec/leaky_local_variable.rb
|
|
126
121
|
- lib/rubocop/cop/rspec/let_before_examples.rb
|
|
127
122
|
- lib/rubocop/cop/rspec/let_setup.rb
|
|
128
123
|
- lib/rubocop/cop/rspec/match_array.rb
|
|
@@ -227,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
227
222
|
- !ruby/object:Gem::Version
|
|
228
223
|
version: '0'
|
|
229
224
|
requirements: []
|
|
230
|
-
rubygems_version: 3.6.
|
|
225
|
+
rubygems_version: 3.6.9
|
|
231
226
|
specification_version: 4
|
|
232
227
|
summary: Code style checking for RSpec files
|
|
233
228
|
test_files: []
|