rubocop-rspec 1.41.0 → 1.44.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 +43 -2
- data/config/default.yml +41 -3
- data/lib/rubocop-rspec.rb +2 -1
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +12 -19
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +12 -19
- data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
- data/lib/rubocop/cop/rspec/around_block.rb +2 -2
- data/lib/rubocop/cop/rspec/base.rb +76 -0
- data/lib/rubocop/cop/rspec/be.rb +2 -2
- data/lib/rubocop/cop/rspec/be_eql.rb +6 -6
- data/lib/rubocop/cop/rspec/before_after_all.rb +1 -1
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +19 -17
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +14 -12
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +1 -1
- data/lib/rubocop/cop/rspec/context_method.rb +7 -9
- data/lib/rubocop/cop/rspec/context_wording.rb +3 -3
- data/lib/rubocop/cop/rspec/cop.rb +2 -66
- data/lib/rubocop/cop/rspec/describe_class.rb +40 -30
- data/lib/rubocop/cop/rspec/describe_method.rb +14 -6
- data/lib/rubocop/cop/rspec/describe_symbol.rb +2 -2
- data/lib/rubocop/cop/rspec/described_class.rb +12 -9
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -1
- data/lib/rubocop/cop/rspec/dialect.rb +5 -12
- data/lib/rubocop/cop/rspec/empty_example_group.rb +124 -6
- data/lib/rubocop/cop/rspec/empty_hook.rb +6 -10
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +5 -7
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +5 -9
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +8 -8
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +5 -9
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +6 -6
- 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 +10 -11
- data/lib/rubocop/cop/rspec/expect_actual.rb +8 -11
- data/lib/rubocop/cop/rspec/expect_change.rb +10 -35
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +3 -3
- data/lib/rubocop/cop/rspec/expect_output.rb +2 -2
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +20 -20
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +20 -22
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +7 -8
- data/lib/rubocop/cop/rspec/file_path.rb +25 -17
- data/lib/rubocop/cop/rspec/focus.rb +7 -11
- data/lib/rubocop/cop/rspec/hook_argument.rb +16 -23
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +13 -14
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -3
- data/lib/rubocop/cop/rspec/implicit_expect.rb +7 -15
- data/lib/rubocop/cop/rspec/implicit_subject.rb +16 -11
- data/lib/rubocop/cop/rspec/instance_spy.rb +18 -12
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +3 -6
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +5 -6
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/leading_subject.rb +27 -20
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +13 -11
- data/lib/rubocop/cop/rspec/let_setup.rb +6 -3
- data/lib/rubocop/cop/rspec/message_chain.rb +7 -6
- data/lib/rubocop/cop/rspec/message_expectation.rb +2 -2
- data/lib/rubocop/cop/rspec/message_spies.rb +2 -3
- data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_describes.rb +11 -8
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +7 -11
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +148 -0
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +18 -19
- data/lib/rubocop/cop/rspec/named_subject.rb +2 -2
- data/lib/rubocop/cop/rspec/nested_groups.rb +4 -4
- data/lib/rubocop/cop/rspec/not_to_not.rb +5 -6
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/pending.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +30 -67
- data/lib/rubocop/cop/rspec/rails/http_status.rb +5 -9
- data/lib/rubocop/cop/rspec/receive_counts.rb +15 -17
- data/lib/rubocop/cop/rspec/receive_never.rb +12 -12
- data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example.rb +2 -2
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +1 -1
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +103 -0
- data/lib/rubocop/cop/rspec/return_from_stub.rb +9 -20
- data/lib/rubocop/cop/rspec/scattered_let.rb +8 -11
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +8 -21
- data/lib/rubocop/cop/rspec/shared_examples.rb +6 -9
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +15 -18
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +172 -0
- data/lib/rubocop/cop/rspec/subject_stub.rb +6 -6
- data/lib/rubocop/cop/rspec/unspecified_exception.rb +1 -1
- data/lib/rubocop/cop/rspec/variable_definition.rb +6 -6
- data/lib/rubocop/cop/rspec/variable_name.rb +28 -9
- data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
- data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
- data/lib/rubocop/cop/rspec/yield.rb +14 -11
- data/lib/rubocop/cop/rspec_cops.rb +3 -0
- data/lib/rubocop/rspec/corrector/move_node.rb +7 -5
- data/lib/rubocop/rspec/description_extractor.rb +1 -1
- data/lib/rubocop/rspec/{blank_line_separation.rb → empty_line_separation.rb} +13 -10
- data/lib/rubocop/rspec/example_group.rb +2 -2
- data/lib/rubocop/rspec/hook.rb +1 -5
- data/lib/rubocop/rspec/language.rb +12 -5
- data/lib/rubocop/rspec/language/node_pattern.rb +6 -1
- data/lib/rubocop/rspec/top_level_describe.rb +2 -2
- data/lib/rubocop/rspec/top_level_group.rb +26 -13
- data/lib/rubocop/rspec/variable.rb +1 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +40 -8
@@ -18,7 +18,9 @@ module RuboCop
|
|
18
18
|
# expect(foo).to have_received(:bar)
|
19
19
|
# end
|
20
20
|
#
|
21
|
-
class InstanceSpy <
|
21
|
+
class InstanceSpy < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
22
24
|
MSG = 'Use `instance_spy` when you check your double '\
|
23
25
|
'with `have_received`.'
|
24
26
|
|
@@ -43,22 +45,26 @@ module RuboCop
|
|
43
45
|
|
44
46
|
null_double(node) do |var, receiver|
|
45
47
|
have_received_usage(node) do |expected|
|
46
|
-
|
48
|
+
next if expected != var
|
49
|
+
|
50
|
+
add_offense(receiver) do |corrector|
|
51
|
+
autocorrect(corrector, receiver)
|
52
|
+
end
|
47
53
|
end
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
51
|
-
|
52
|
-
lambda do |corrector|
|
53
|
-
replacement = 'instance_spy'
|
54
|
-
corrector.replace(node.loc.selector, replacement)
|
57
|
+
private
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
def autocorrect(corrector, node)
|
60
|
+
replacement = 'instance_spy'
|
61
|
+
corrector.replace(node.loc.selector, replacement)
|
62
|
+
|
63
|
+
double_source_map = node.parent.loc
|
64
|
+
as_null_object_range = double_source_map
|
65
|
+
.dot
|
66
|
+
.join(double_source_map.selector)
|
67
|
+
corrector.remove(as_null_object_range)
|
62
68
|
end
|
63
69
|
end
|
64
70
|
end
|
@@ -15,7 +15,7 @@ module RuboCop
|
|
15
15
|
#
|
16
16
|
# # good
|
17
17
|
# expect(foo).to be_something
|
18
|
-
class InvalidPredicateMatcher <
|
18
|
+
class InvalidPredicateMatcher < Base
|
19
19
|
MSG = 'Omit `?` from `%<matcher>s`.'
|
20
20
|
|
21
21
|
def_node_matcher :invalid_predicate_matcher?, <<-PATTERN
|
@@ -24,7 +24,8 @@ module RuboCop
|
|
24
24
|
|
25
25
|
def on_send(node)
|
26
26
|
invalid_predicate_matcher?(node) do |predicate|
|
27
|
-
add_offense(predicate
|
27
|
+
add_offense(predicate,
|
28
|
+
message: format(MSG, matcher: predicate.method_name))
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
@@ -34,10 +35,6 @@ module RuboCop
|
|
34
35
|
name = name.to_s
|
35
36
|
name.start_with?('be_', 'have_') && name.end_with?('?')
|
36
37
|
end
|
37
|
-
|
38
|
-
def message(predicate)
|
39
|
-
format(MSG, matcher: predicate.method_name)
|
40
|
-
end
|
41
38
|
end
|
42
39
|
end
|
43
40
|
end
|
@@ -18,7 +18,8 @@ module RuboCop
|
|
18
18
|
#
|
19
19
|
# # good
|
20
20
|
# it_should_behave_like 'a foo'
|
21
|
-
class ItBehavesLike <
|
21
|
+
class ItBehavesLike < Base
|
22
|
+
extend AutoCorrector
|
22
23
|
include ConfigurableEnforcedStyle
|
23
24
|
|
24
25
|
MSG = 'Prefer `%<replacement>s` over `%<original>s` when including '\
|
@@ -28,14 +29,12 @@ module RuboCop
|
|
28
29
|
|
29
30
|
def on_send(node)
|
30
31
|
example_inclusion_offense(node, alternative_style) do
|
31
|
-
add_offense(node)
|
32
|
+
add_offense(node) do |corrector|
|
33
|
+
corrector.replace(node.loc.selector, style.to_s)
|
34
|
+
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
35
|
-
def autocorrect(node)
|
36
|
-
->(corrector) { corrector.replace(node.loc.selector, style.to_s) }
|
37
|
-
end
|
38
|
-
|
39
38
|
private
|
40
39
|
|
41
40
|
def message(_node)
|
@@ -15,7 +15,7 @@ module RuboCop
|
|
15
15
|
# it 'validates users' do
|
16
16
|
# expect([user1, user2, user3]).to all(be_valid)
|
17
17
|
# end
|
18
|
-
class IteratedExpectation <
|
18
|
+
class IteratedExpectation < Base
|
19
19
|
MSG = 'Prefer using the `all` matcher instead ' \
|
20
20
|
'of iterating over an array.'
|
21
21
|
|
@@ -31,7 +31,9 @@ module RuboCop
|
|
31
31
|
# it { expect_something }
|
32
32
|
# it { expect_something_else }
|
33
33
|
#
|
34
|
-
class LeadingSubject <
|
34
|
+
class LeadingSubject < Base
|
35
|
+
extend AutoCorrector
|
36
|
+
|
35
37
|
MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
|
36
38
|
|
37
39
|
def on_block(node)
|
@@ -41,35 +43,40 @@ module RuboCop
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def check_previous_nodes(node)
|
44
|
-
node
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
message: format(MSG, offending: sibling.method_name)
|
49
|
-
)
|
46
|
+
offending_node(node) do |offender|
|
47
|
+
msg = format(MSG, offending: offender.method_name)
|
48
|
+
add_offense(node, message: msg) do |corrector|
|
49
|
+
autocorrect(corrector, node, offender)
|
50
50
|
end
|
51
|
-
|
52
|
-
break if offending?(sibling) || sibling.equal?(node)
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
54
|
+
private
|
55
|
+
|
56
|
+
def offending_node(node)
|
57
|
+
parent(node).each_child_node.find do |sibling|
|
58
|
+
break if sibling.equal?(node)
|
59
|
+
|
60
|
+
yield sibling if offending?(sibling)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
65
|
-
|
64
|
+
def parent(node)
|
65
|
+
node.each_ancestor(:block).first.body
|
66
|
+
end
|
66
67
|
|
67
|
-
def
|
68
|
-
|
68
|
+
def autocorrect(corrector, node, sibling)
|
69
|
+
RuboCop::RSpec::Corrector::MoveNode.new(
|
70
|
+
node, corrector, processed_source
|
71
|
+
).move_before(sibling)
|
69
72
|
end
|
70
73
|
|
71
|
-
def
|
72
|
-
|
74
|
+
def offending?(node)
|
75
|
+
let?(node) ||
|
76
|
+
hook?(node) ||
|
77
|
+
example?(node) ||
|
78
|
+
spec_group?(node) ||
|
79
|
+
include?(node)
|
73
80
|
end
|
74
81
|
|
75
82
|
def in_spec_block?(node)
|
@@ -93,7 +93,7 @@ module RuboCop
|
|
93
93
|
# stub_const('SomeModule::SomeClass', foo_class)
|
94
94
|
# end
|
95
95
|
# end
|
96
|
-
class LeakyConstantDeclaration <
|
96
|
+
class LeakyConstantDeclaration < Base
|
97
97
|
MSG_CONST = 'Stub constant instead of declaring explicitly.'
|
98
98
|
MSG_CLASS = 'Stub class constant instead of declaring explicitly.'
|
99
99
|
MSG_MODULE = 'Stub module constant instead of declaring explicitly.'
|
@@ -30,7 +30,9 @@ module RuboCop
|
|
30
30
|
# it 'checks what some does' do
|
31
31
|
# expect(some).to be
|
32
32
|
# end
|
33
|
-
class LetBeforeExamples <
|
33
|
+
class LetBeforeExamples < Base
|
34
|
+
extend AutoCorrector
|
35
|
+
|
34
36
|
MSG = 'Move `let` before the examples in the group.'
|
35
37
|
|
36
38
|
def_node_matcher :example_or_group?, <<-PATTERN
|
@@ -46,15 +48,6 @@ module RuboCop
|
|
46
48
|
check_let_declarations(node.body) if multiline_block?(node.body)
|
47
49
|
end
|
48
50
|
|
49
|
-
def autocorrect(node)
|
50
|
-
lambda do |corrector|
|
51
|
-
first_example = find_first_example(node.parent)
|
52
|
-
RuboCop::RSpec::Corrector::MoveNode.new(
|
53
|
-
node, corrector, processed_source
|
54
|
-
).move_before(first_example)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
51
|
private
|
59
52
|
|
60
53
|
def multiline_block?(block)
|
@@ -67,14 +60,23 @@ module RuboCop
|
|
67
60
|
|
68
61
|
node.each_child_node do |child|
|
69
62
|
next if child.sibling_index < first_example.sibling_index
|
63
|
+
next unless let?(child)
|
70
64
|
|
71
|
-
add_offense(child)
|
65
|
+
add_offense(child) do |corrector|
|
66
|
+
autocorrect(corrector, child, first_example)
|
67
|
+
end
|
72
68
|
end
|
73
69
|
end
|
74
70
|
|
75
71
|
def find_first_example(node)
|
76
72
|
node.children.find { |sibling| example_or_group?(sibling) }
|
77
73
|
end
|
74
|
+
|
75
|
+
def autocorrect(corrector, node, first_example)
|
76
|
+
RuboCop::RSpec::Corrector::MoveNode.new(
|
77
|
+
node, corrector, processed_source
|
78
|
+
).move_before(first_example)
|
79
|
+
end
|
78
80
|
end
|
79
81
|
end
|
80
82
|
end
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
# it 'counts widgets' do
|
26
26
|
# expect(Widget.count).to eq(1)
|
27
27
|
# end
|
28
|
-
class LetSetup <
|
28
|
+
class LetSetup < Base
|
29
29
|
MSG = 'Do not use `let!` to setup objects not referenced in tests.'
|
30
30
|
|
31
31
|
def_node_matcher :example_or_shared_group_or_including?,
|
@@ -35,7 +35,10 @@ module RuboCop
|
|
35
35
|
).block_pattern
|
36
36
|
|
37
37
|
def_node_matcher :let_bang, <<-PATTERN
|
38
|
-
|
38
|
+
{
|
39
|
+
(block $(send nil? :let! {(sym $_) (str $_)}) ...)
|
40
|
+
$(send nil? :let! {(sym $_) (str $_)} block_pass)
|
41
|
+
}
|
39
42
|
PATTERN
|
40
43
|
|
41
44
|
def_node_search :method_called?, '(send nil? %)'
|
@@ -52,7 +55,7 @@ module RuboCop
|
|
52
55
|
|
53
56
|
def unused_let_bang(node)
|
54
57
|
child_let_bang(node) do |method_send, method_name|
|
55
|
-
yield(method_send) unless method_called?(node, method_name)
|
58
|
+
yield(method_send) unless method_called?(node, method_name.to_sym)
|
56
59
|
end
|
57
60
|
end
|
58
61
|
|
@@ -13,7 +13,7 @@ module RuboCop
|
|
13
13
|
# thing = Thing.new(baz: 42)
|
14
14
|
# allow(foo).to receive(:bar).and_return(thing)
|
15
15
|
#
|
16
|
-
class MessageChain <
|
16
|
+
class MessageChain < Base
|
17
17
|
MSG = 'Avoid stubbing using `%<method>s`.'
|
18
18
|
|
19
19
|
def_node_matcher :message_chain, <<-PATTERN
|
@@ -21,11 +21,12 @@ module RuboCop
|
|
21
21
|
PATTERN
|
22
22
|
|
23
23
|
def on_send(node)
|
24
|
-
message_chain(node)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
message_chain(node) do
|
25
|
+
add_offense(
|
26
|
+
node.loc.selector,
|
27
|
+
message: format(MSG, method: node.method_name)
|
28
|
+
)
|
29
|
+
end
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
# # good
|
25
25
|
# expect(foo).to receive(:bar)
|
26
26
|
#
|
27
|
-
class MessageExpectation <
|
27
|
+
class MessageExpectation < Base
|
28
28
|
include ConfigurableEnforcedStyle
|
29
29
|
|
30
30
|
MSG = 'Prefer `%<style>s` for setting message expectations.'
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
42
42
|
return correct_style_detected if preferred_style?(match)
|
43
43
|
|
44
44
|
message = format(MSG, style: style)
|
45
|
-
add_offense(match
|
45
|
+
add_offense(match.loc.selector, message: message) do
|
46
46
|
opposite_style_detected
|
47
47
|
end
|
48
48
|
end
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
# # good
|
25
25
|
# expect(foo).to receive(:bar)
|
26
26
|
#
|
27
|
-
class MessageSpies <
|
27
|
+
class MessageSpies < Base
|
28
28
|
include ConfigurableEnforcedStyle
|
29
29
|
|
30
30
|
MSG_RECEIVE = 'Prefer `receive` for setting message expectations.'
|
@@ -48,8 +48,7 @@ module RuboCop
|
|
48
48
|
return correct_style_detected if preferred_style?(message_matcher)
|
49
49
|
|
50
50
|
add_offense(
|
51
|
-
message_matcher,
|
52
|
-
location: :selector,
|
51
|
+
message_matcher.loc.selector,
|
53
52
|
message: error_message(receiver)
|
54
53
|
) { opposite_style_detected }
|
55
54
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module RSpec
|
6
|
-
# Checks for multiple top
|
6
|
+
# Checks for multiple top-level example groups.
|
7
7
|
#
|
8
8
|
# Multiple descriptions for the same class or module should either
|
9
9
|
# be nested or separated into different test files.
|
@@ -22,17 +22,20 @@ module RuboCop
|
|
22
22
|
# describe '.do_something_else' do
|
23
23
|
# end
|
24
24
|
# end
|
25
|
-
class MultipleDescribes <
|
26
|
-
include RuboCop::RSpec::
|
25
|
+
class MultipleDescribes < Base
|
26
|
+
include RuboCop::RSpec::TopLevelGroup
|
27
27
|
|
28
|
-
MSG = 'Do not use multiple top
|
28
|
+
MSG = 'Do not use multiple top-level example groups - '\
|
29
29
|
'try to nest them.'
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
31
|
+
def on_top_level_group(node)
|
32
|
+
top_level_example_groups =
|
33
|
+
top_level_groups.select(&method(:example_group?))
|
34
34
|
|
35
|
-
|
35
|
+
return if top_level_example_groups.one?
|
36
|
+
return unless top_level_example_groups.first.equal?(node)
|
37
|
+
|
38
|
+
add_offense(node.send_node)
|
36
39
|
end
|
37
40
|
end
|
38
41
|
end
|
@@ -45,22 +45,18 @@ module RuboCop
|
|
45
45
|
# end
|
46
46
|
# end
|
47
47
|
#
|
48
|
-
class MultipleExpectations <
|
48
|
+
class MultipleExpectations < Base
|
49
49
|
include ConfigurableMax
|
50
50
|
|
51
51
|
MSG = 'Example has too many expectations [%<total>d/%<max>d].'
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
(send _ _ <(sym :aggregate_failures) ...>)
|
56
|
-
(send _ _ ... (hash <(pair (sym :aggregate_failures) true) ...>))
|
57
|
-
} ...)
|
58
|
-
PATTERN
|
53
|
+
ANYTHING = ->(_node) { true }
|
54
|
+
TRUE = ->(node) { node.true_type? }
|
59
55
|
|
60
|
-
def_node_matcher :
|
56
|
+
def_node_matcher :aggregate_failures?, <<-PATTERN
|
61
57
|
(block {
|
62
58
|
(send _ _ <(sym :aggregate_failures) ...>)
|
63
|
-
(send _ _ ... (hash <(pair (sym :aggregate_failures)
|
59
|
+
(send _ _ ... (hash <(pair (sym :aggregate_failures) %1) ...>))
|
64
60
|
} ...)
|
65
61
|
PATTERN
|
66
62
|
|
@@ -89,12 +85,12 @@ module RuboCop
|
|
89
85
|
node_with_aggregate_failures = find_aggregate_failures(example_node)
|
90
86
|
return false unless node_with_aggregate_failures
|
91
87
|
|
92
|
-
aggregate_failures?(node_with_aggregate_failures)
|
88
|
+
aggregate_failures?(node_with_aggregate_failures, TRUE)
|
93
89
|
end
|
94
90
|
|
95
91
|
def find_aggregate_failures(example_node)
|
96
92
|
example_node.send_node.each_ancestor(:block)
|
97
|
-
.find { |block_node|
|
93
|
+
.find { |block_node| aggregate_failures?(block_node, ANYTHING) }
|
98
94
|
end
|
99
95
|
|
100
96
|
def find_expectation(node, &block)
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks if example groups contain too many `let` and `subject` calls.
|
7
|
+
#
|
8
|
+
# This cop is configurable using the `Max` option and the `AllowSubject`
|
9
|
+
# which will configure the cop to only register offenses on calls to
|
10
|
+
# `let` and not calls to `subject`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # bad
|
14
|
+
# describe MyClass do
|
15
|
+
# let(:foo) { [] }
|
16
|
+
# let(:bar) { [] }
|
17
|
+
# let!(:baz) { [] }
|
18
|
+
# let(:qux) { [] }
|
19
|
+
# let(:quux) { [] }
|
20
|
+
# let(:quuz) { {} }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# describe MyClass do
|
24
|
+
# let(:foo) { [] }
|
25
|
+
# let(:bar) { [] }
|
26
|
+
# let!(:baz) { [] }
|
27
|
+
#
|
28
|
+
# context 'when stuff' do
|
29
|
+
# let(:qux) { [] }
|
30
|
+
# let(:quux) { [] }
|
31
|
+
# let(:quuz) { {} }
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# describe MyClass do
|
37
|
+
# let(:bar) { [] }
|
38
|
+
# let!(:baz) { [] }
|
39
|
+
# let(:qux) { [] }
|
40
|
+
# let(:quux) { [] }
|
41
|
+
# let(:quuz) { {} }
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# describe MyClass do
|
45
|
+
# context 'when stuff' do
|
46
|
+
# let(:foo) { [] }
|
47
|
+
# let(:bar) { [] }
|
48
|
+
# let!(:booger) { [] }
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# context 'when other stuff' do
|
52
|
+
# let(:qux) { [] }
|
53
|
+
# let(:quux) { [] }
|
54
|
+
# let(:quuz) { {} }
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# @example when disabling AllowSubject configuration
|
59
|
+
#
|
60
|
+
# # rubocop.yml
|
61
|
+
# # RSpec/MultipleMemoizedHelpers:
|
62
|
+
# # AllowSubject: false
|
63
|
+
#
|
64
|
+
# # bad - `subject` counts towards memoized helpers
|
65
|
+
# describe MyClass do
|
66
|
+
# subject { {} }
|
67
|
+
# let(:foo) { [] }
|
68
|
+
# let(:bar) { [] }
|
69
|
+
# let!(:baz) { [] }
|
70
|
+
# let(:qux) { [] }
|
71
|
+
# let(:quux) { [] }
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# @example with Max configuration
|
75
|
+
#
|
76
|
+
# # rubocop.yml
|
77
|
+
# # RSpec/MultipleMemoizedHelpers:
|
78
|
+
# # Max: 1
|
79
|
+
#
|
80
|
+
# # bad
|
81
|
+
# describe MyClass do
|
82
|
+
# let(:foo) { [] }
|
83
|
+
# let(:bar) { [] }
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
class MultipleMemoizedHelpers < Base
|
87
|
+
include ConfigurableMax
|
88
|
+
include RuboCop::RSpec::Variable
|
89
|
+
|
90
|
+
MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
|
91
|
+
|
92
|
+
def on_block(node)
|
93
|
+
return unless spec_group?(node)
|
94
|
+
|
95
|
+
count = all_helpers(node).uniq.count
|
96
|
+
|
97
|
+
return if count <= max
|
98
|
+
|
99
|
+
self.max = count
|
100
|
+
add_offense(node, message: format(MSG, count: count, max: max))
|
101
|
+
end
|
102
|
+
|
103
|
+
def on_new_investigation
|
104
|
+
@example_group_memoized_helpers = {}
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
attr_reader :example_group_memoized_helpers
|
110
|
+
|
111
|
+
def all_helpers(node)
|
112
|
+
[
|
113
|
+
*helpers(node),
|
114
|
+
*node.each_ancestor(:block).flat_map(&method(:helpers))
|
115
|
+
]
|
116
|
+
end
|
117
|
+
|
118
|
+
def helpers(node)
|
119
|
+
@example_group_memoized_helpers[node] ||=
|
120
|
+
variable_nodes(node).map do |variable_node|
|
121
|
+
if variable_node.block_type?
|
122
|
+
variable_definition?(variable_node.send_node)
|
123
|
+
else # block-pass (`let(:foo, &bar)`)
|
124
|
+
variable_definition?(variable_node)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def variable_nodes(node)
|
130
|
+
example_group = RuboCop::RSpec::ExampleGroup.new(node)
|
131
|
+
if allow_subject?
|
132
|
+
example_group.lets
|
133
|
+
else
|
134
|
+
example_group.lets + example_group.subjects
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def max
|
139
|
+
cop_config['Max']
|
140
|
+
end
|
141
|
+
|
142
|
+
def allow_subject?
|
143
|
+
cop_config['AllowSubject']
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|