rubocop-rspec 1.38.0 → 1.42.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 +44 -0
- data/CODE_OF_CONDUCT.md +17 -0
- data/README.md +1 -61
- data/config/default.yml +147 -17
- data/lib/rubocop-rspec.rb +3 -1
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +11 -18
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +11 -18
- data/lib/rubocop/cop/rspec/be.rb +1 -1
- data/lib/rubocop/cop/rspec/be_eql.rb +5 -5
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +18 -16
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +8 -9
- data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +69 -0
- data/lib/rubocop/cop/rspec/context_method.rb +5 -7
- data/lib/rubocop/cop/rspec/cop.rb +9 -29
- data/lib/rubocop/cop/rspec/describe_class.rb +20 -5
- data/lib/rubocop/cop/rspec/describe_method.rb +0 -1
- data/lib/rubocop/cop/rspec/described_class.rb +10 -7
- data/lib/rubocop/cop/rspec/dialect.rb +4 -11
- data/lib/rubocop/cop/rspec/empty_hook.rb +46 -0
- data/lib/rubocop/cop/rspec/empty_line_after_example.rb +5 -3
- data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +5 -5
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +4 -1
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +5 -5
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +4 -1
- data/lib/rubocop/cop/rspec/example_wording.rb +6 -7
- data/lib/rubocop/cop/rspec/expect_actual.rb +7 -10
- data/lib/rubocop/cop/rspec/expect_change.rb +9 -34
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +2 -2
- data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
- data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +23 -20
- data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +10 -16
- data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +6 -7
- data/lib/rubocop/cop/rspec/file_path.rb +32 -4
- data/lib/rubocop/cop/rspec/hook_argument.rb +11 -17
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +9 -28
- data/lib/rubocop/cop/rspec/implicit_expect.rb +6 -14
- data/lib/rubocop/cop/rspec/implicit_subject.rb +8 -5
- data/lib/rubocop/cop/rspec/instance_spy.rb +17 -11
- data/lib/rubocop/cop/rspec/instance_variable.rb +3 -7
- data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +2 -5
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +4 -5
- data/lib/rubocop/cop/rspec/leading_subject.rb +12 -19
- data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -4
- data/lib/rubocop/cop/rspec/let_before_examples.rb +9 -25
- data/lib/rubocop/cop/rspec/let_setup.rb +15 -3
- data/lib/rubocop/cop/rspec/message_chain.rb +6 -5
- data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/message_spies.rb +1 -2
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -18
- data/lib/rubocop/cop/rspec/named_subject.rb +7 -7
- data/lib/rubocop/cop/rspec/nested_groups.rb +9 -10
- data/lib/rubocop/cop/rspec/not_to_not.rb +4 -5
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +25 -55
- data/lib/rubocop/cop/rspec/rails/http_status.rb +6 -8
- data/lib/rubocop/cop/rspec/receive_counts.rb +14 -16
- data/lib/rubocop/cop/rspec/receive_never.rb +10 -10
- data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +11 -1
- data/lib/rubocop/cop/rspec/return_from_stub.rb +11 -21
- data/lib/rubocop/cop/rspec/scattered_let.rb +11 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +7 -20
- data/lib/rubocop/cop/rspec/shared_examples.rb +6 -8
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +14 -17
- data/lib/rubocop/cop/rspec/subject_stub.rb +23 -51
- data/lib/rubocop/cop/rspec/variable_definition.rb +56 -0
- data/lib/rubocop/cop/rspec/variable_name.rb +47 -0
- data/lib/rubocop/cop/rspec/yield.rb +13 -10
- data/lib/rubocop/cop/rspec_cops.rb +5 -1
- data/lib/rubocop/rspec/blank_line_separation.rb +0 -8
- data/lib/rubocop/rspec/corrector/move_node.rb +52 -0
- data/lib/rubocop/rspec/description_extractor.rb +2 -6
- data/lib/rubocop/rspec/example.rb +1 -1
- data/lib/rubocop/rspec/example_group.rb +21 -49
- data/lib/rubocop/rspec/factory_bot.rb +7 -1
- data/lib/rubocop/rspec/language.rb +8 -0
- data/lib/rubocop/rspec/language/node_pattern.rb +5 -1
- data/lib/rubocop/rspec/top_level_group.rb +44 -0
- data/lib/rubocop/rspec/variable.rb +16 -0
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +17 -10
- data/lib/rubocop/rspec/util.rb +0 -19
@@ -31,6 +31,7 @@ module RuboCop
|
|
31
31
|
# it { is_expected.to have_http_status :error }
|
32
32
|
#
|
33
33
|
class HttpStatus < Cop
|
34
|
+
extend AutoCorrector
|
34
35
|
include ConfigurableEnforcedStyle
|
35
36
|
|
36
37
|
def_node_matcher :http_status, <<-PATTERN
|
@@ -42,14 +43,9 @@ module RuboCop
|
|
42
43
|
checker = checker_class.new(ast_node)
|
43
44
|
return unless checker.offensive?
|
44
45
|
|
45
|
-
add_offense(checker.node, message: checker.message)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def autocorrect(node)
|
50
|
-
lambda do |corrector|
|
51
|
-
checker = checker_class.new(node)
|
52
|
-
corrector.replace(node.loc.expression, checker.preferred_style)
|
46
|
+
add_offense(checker.node, message: checker.message) do |corrector|
|
47
|
+
corrector.replace(checker.node, checker.preferred_style)
|
48
|
+
end
|
53
49
|
end
|
54
50
|
end
|
55
51
|
|
@@ -70,6 +66,7 @@ module RuboCop
|
|
70
66
|
'to describe HTTP status code.'
|
71
67
|
|
72
68
|
attr_reader :node
|
69
|
+
|
73
70
|
def initialize(node)
|
74
71
|
@node = node
|
75
72
|
end
|
@@ -110,6 +107,7 @@ module RuboCop
|
|
110
107
|
ALLOWED_STATUSES = %i[error success missing redirect].freeze
|
111
108
|
|
112
109
|
attr_reader :node
|
110
|
+
|
113
111
|
def initialize(node)
|
114
112
|
@node = node
|
115
113
|
end
|
@@ -24,6 +24,8 @@ module RuboCop
|
|
24
24
|
# expect(foo).to receive(:bar).at_most(:twice).times
|
25
25
|
#
|
26
26
|
class ReceiveCounts < Cop
|
27
|
+
extend AutoCorrector
|
28
|
+
|
27
29
|
MSG = 'Use `%<alternative>s` instead of `%<original>s`.'
|
28
30
|
|
29
31
|
def_node_matcher :receive_counts, <<-PATTERN
|
@@ -38,27 +40,23 @@ module RuboCop
|
|
38
40
|
|
39
41
|
offending_range = range(node, offending_node)
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
)
|
43
|
+
msg = message_for(offending_node, offending_range.source)
|
44
|
+
add_offense(offending_range, message: msg) do |corrector|
|
45
|
+
autocorrect(corrector, offending_node, offending_range)
|
46
|
+
end
|
46
47
|
end
|
47
48
|
end
|
48
49
|
|
49
|
-
|
50
|
-
lambda do |corrector|
|
51
|
-
replacement = matcher_for(
|
52
|
-
node.method_name,
|
53
|
-
node.first_argument.source.to_i
|
54
|
-
)
|
50
|
+
private
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
52
|
+
def autocorrect(corrector, node, range)
|
53
|
+
replacement = matcher_for(
|
54
|
+
node.method_name,
|
55
|
+
node.first_argument.source.to_i
|
56
|
+
)
|
60
57
|
|
61
|
-
|
58
|
+
corrector.replace(range, replacement)
|
59
|
+
end
|
62
60
|
|
63
61
|
def message_for(node, source)
|
64
62
|
alternative = matcher_for(
|
@@ -14,6 +14,7 @@ module RuboCop
|
|
14
14
|
# expect(foo).not_to receive(:bar)
|
15
15
|
#
|
16
16
|
class ReceiveNever < Cop
|
17
|
+
extend AutoCorrector
|
17
18
|
MSG = 'Use `not_to receive` instead of `never`.'
|
18
19
|
|
19
20
|
def_node_search :method_on_stub?, '(send nil? :receive ...)'
|
@@ -21,18 +22,17 @@ module RuboCop
|
|
21
22
|
def on_send(node)
|
22
23
|
return unless node.method_name == :never && method_on_stub?(node)
|
23
24
|
|
24
|
-
add_offense(
|
25
|
-
node
|
26
|
-
|
27
|
-
)
|
25
|
+
add_offense(node.loc.selector) do |corrector|
|
26
|
+
autocorrect(corrector, node)
|
27
|
+
end
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
private
|
31
|
+
|
32
|
+
def autocorrect(corrector, node)
|
33
|
+
corrector.replace(node.parent.loc.selector, 'not_to')
|
34
|
+
range = node.loc.dot.with(end_pos: node.loc.selector.end_pos)
|
35
|
+
corrector.remove(range)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -34,6 +34,15 @@ module RuboCop
|
|
34
34
|
# it { cool_predicate }
|
35
35
|
# end
|
36
36
|
#
|
37
|
+
# # good
|
38
|
+
# context Array do
|
39
|
+
# it { is_expected.to respond_to :each }
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# context Hash do
|
43
|
+
# it { is_expected.to respond_to :each }
|
44
|
+
# end
|
45
|
+
#
|
37
46
|
class RepeatedExampleGroupBody < Cop
|
38
47
|
MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
|
39
48
|
|
@@ -43,6 +52,7 @@ module RuboCop
|
|
43
52
|
|
44
53
|
def_node_matcher :metadata, '(block (send _ _ _ $...) ...)'
|
45
54
|
def_node_matcher :body, '(block _ args $...)'
|
55
|
+
def_node_matcher :const_arg, '(block (send _ _ $const ...) ...)'
|
46
56
|
|
47
57
|
def_node_matcher :skip_or_pending?, <<-PATTERN
|
48
58
|
(block <(send nil? {:skip :pending}) ...>)
|
@@ -75,7 +85,7 @@ module RuboCop
|
|
75
85
|
end
|
76
86
|
|
77
87
|
def signature_keys(group)
|
78
|
-
[metadata(group), body(group)]
|
88
|
+
[metadata(group), body(group), const_arg(group)]
|
79
89
|
end
|
80
90
|
|
81
91
|
def message(group, repeats)
|
@@ -34,48 +34,40 @@ module RuboCop
|
|
34
34
|
# allow(Foo).to receive(:bar) { bar.baz }
|
35
35
|
#
|
36
36
|
class ReturnFromStub < Cop
|
37
|
+
extend AutoCorrector
|
37
38
|
include ConfigurableEnforcedStyle
|
38
39
|
|
39
40
|
MSG_AND_RETURN = 'Use `and_return` for static values.'
|
40
41
|
MSG_BLOCK = 'Use block for static values.'
|
41
42
|
|
42
43
|
def_node_search :contains_stub?, '(send nil? :receive (...))'
|
44
|
+
def_node_matcher :stub_with_block?, '(block #contains_stub? ...)'
|
43
45
|
def_node_search :and_return_value, <<-PATTERN
|
44
46
|
$(send _ :and_return $(...))
|
45
47
|
PATTERN
|
46
48
|
|
47
49
|
def on_send(node)
|
48
|
-
return unless contains_stub?(node)
|
49
50
|
return unless style == :block
|
51
|
+
return unless contains_stub?(node)
|
50
52
|
|
51
53
|
check_and_return_call(node)
|
52
54
|
end
|
53
55
|
|
54
56
|
def on_block(node)
|
55
|
-
return unless contains_stub?(node)
|
56
57
|
return unless style == :and_return
|
58
|
+
return unless stub_with_block?(node)
|
57
59
|
|
58
60
|
check_block_body(node)
|
59
61
|
end
|
60
62
|
|
61
|
-
def autocorrect(node)
|
62
|
-
if style == :block
|
63
|
-
AndReturnCallCorrector.new(node)
|
64
|
-
else
|
65
|
-
BlockBodyCorrector.new(node)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
63
|
private
|
70
64
|
|
71
65
|
def check_and_return_call(node)
|
72
66
|
and_return_value(node) do |and_return, args|
|
73
67
|
unless dynamic?(args)
|
74
|
-
add_offense(
|
75
|
-
and_return
|
76
|
-
|
77
|
-
message: MSG_BLOCK
|
78
|
-
)
|
68
|
+
add_offense(and_return.loc.selector, message: MSG_BLOCK) do |corr|
|
69
|
+
AndReturnCallCorrector.new(and_return).call(corr)
|
70
|
+
end
|
79
71
|
end
|
80
72
|
end
|
81
73
|
end
|
@@ -83,11 +75,9 @@ module RuboCop
|
|
83
75
|
def check_block_body(block)
|
84
76
|
body = block.body
|
85
77
|
unless dynamic?(body) # rubocop:disable Style/GuardClause
|
86
|
-
add_offense(
|
87
|
-
block
|
88
|
-
|
89
|
-
message: MSG_AND_RETURN
|
90
|
-
)
|
78
|
+
add_offense(block.loc.begin, message: MSG_AND_RETURN) do |corrector|
|
79
|
+
BlockBodyCorrector.new(block).call(corrector)
|
80
|
+
end
|
91
81
|
end
|
92
82
|
end
|
93
83
|
|
@@ -152,7 +142,7 @@ module RuboCop
|
|
152
142
|
return if heredoc?
|
153
143
|
|
154
144
|
corrector.replace(
|
155
|
-
block
|
145
|
+
block,
|
156
146
|
"#{block.send_node.source}.and_return(#{body.source})"
|
157
147
|
)
|
158
148
|
end
|
@@ -27,6 +27,8 @@ module RuboCop
|
|
27
27
|
# end
|
28
28
|
#
|
29
29
|
class ScatteredLet < Cop
|
30
|
+
extend AutoCorrector
|
31
|
+
|
30
32
|
MSG = 'Group all let/let! blocks in the example group together.'
|
31
33
|
|
32
34
|
def on_block(node)
|
@@ -44,9 +46,17 @@ module RuboCop
|
|
44
46
|
lets.each_with_index do |node, idx|
|
45
47
|
next if node.sibling_index == first_let.sibling_index + idx
|
46
48
|
|
47
|
-
add_offense(node)
|
49
|
+
add_offense(node) do |corrector|
|
50
|
+
RuboCop::RSpec::Corrector::MoveNode.new(
|
51
|
+
node, corrector, processed_source
|
52
|
+
).move_after(first_let)
|
53
|
+
end
|
48
54
|
end
|
49
55
|
end
|
56
|
+
|
57
|
+
def find_first_let(node)
|
58
|
+
node.children.find { |child| let?(child) }
|
59
|
+
end
|
50
60
|
end
|
51
61
|
end
|
52
62
|
end
|
@@ -51,6 +51,8 @@ module RuboCop
|
|
51
51
|
# end
|
52
52
|
#
|
53
53
|
class SharedContext < Cop
|
54
|
+
extend AutoCorrector
|
55
|
+
|
54
56
|
MSG_EXAMPLES = "Use `shared_examples` when you don't "\
|
55
57
|
'define context.'
|
56
58
|
|
@@ -68,22 +70,14 @@ module RuboCop
|
|
68
70
|
|
69
71
|
def on_block(node)
|
70
72
|
context_with_only_examples(node) do
|
71
|
-
|
73
|
+
add_offense(node.send_node, message: MSG_EXAMPLES) do |corrector|
|
74
|
+
corrector.replace(node.send_node.loc.selector, 'shared_examples')
|
75
|
+
end
|
72
76
|
end
|
73
77
|
|
74
78
|
examples_with_only_context(node) do
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
def autocorrect(node)
|
80
|
-
lambda do |corrector|
|
81
|
-
context_with_only_examples(node.parent) do
|
82
|
-
corrector.replace(node.loc.selector, 'shared_examples')
|
83
|
-
end
|
84
|
-
|
85
|
-
examples_with_only_context(node.parent) do
|
86
|
-
corrector.replace(node.loc.selector, 'shared_context')
|
79
|
+
add_offense(node.send_node, message: MSG_CONTEXT) do |corrector|
|
80
|
+
corrector.replace(node.send_node.loc.selector, 'shared_context')
|
87
81
|
end
|
88
82
|
end
|
89
83
|
end
|
@@ -97,13 +91,6 @@ module RuboCop
|
|
97
91
|
def examples_with_only_context(node)
|
98
92
|
shared_example(node) { yield if context?(node) && !examples?(node) }
|
99
93
|
end
|
100
|
-
|
101
|
-
def add_shared_item_offense(node, message)
|
102
|
-
add_offense(
|
103
|
-
node,
|
104
|
-
message: message
|
105
|
-
)
|
106
|
-
end
|
107
94
|
end
|
108
95
|
end
|
109
96
|
end
|
@@ -21,6 +21,8 @@ module RuboCop
|
|
21
21
|
# include_examples 'foo bar baz'
|
22
22
|
#
|
23
23
|
class SharedExamples < Cop
|
24
|
+
extend AutoCorrector
|
25
|
+
|
24
26
|
def_node_matcher :shared_examples,
|
25
27
|
(SharedGroups::ALL + Includes::ALL).send_pattern
|
26
28
|
|
@@ -30,14 +32,9 @@ module RuboCop
|
|
30
32
|
next unless ast_node&.sym_type?
|
31
33
|
|
32
34
|
checker = Checker.new(ast_node)
|
33
|
-
add_offense(checker.node, message: checker.message)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def autocorrect(node)
|
38
|
-
lambda do |corrector|
|
39
|
-
checker = Checker.new(node)
|
40
|
-
corrector.replace(node.loc.expression, checker.preferred_style)
|
35
|
+
add_offense(checker.node, message: checker.message) do |corrector|
|
36
|
+
corrector.replace(checker.node, checker.preferred_style)
|
37
|
+
end
|
41
38
|
end
|
42
39
|
end
|
43
40
|
|
@@ -47,6 +44,7 @@ module RuboCop
|
|
47
44
|
'to titleize shared examples.'
|
48
45
|
|
49
46
|
attr_reader :node
|
47
|
+
|
50
48
|
def initialize(node)
|
51
49
|
@node = node
|
52
50
|
end
|
@@ -17,6 +17,8 @@ module RuboCop
|
|
17
17
|
# allow(foo).to receive("bar.baz")
|
18
18
|
#
|
19
19
|
class SingleArgumentMessageChain < Cop
|
20
|
+
extend AutoCorrector
|
21
|
+
|
20
22
|
MSG = 'Use `%<recommended>s` instead of calling '\
|
21
23
|
'`%<called>s` with a single argument.'
|
22
24
|
|
@@ -30,22 +32,23 @@ module RuboCop
|
|
30
32
|
message_chain(node) do |arg|
|
31
33
|
return if valid_usage?(arg)
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
35
|
+
method = node.method_name
|
36
|
+
msg = format(MSG, recommended: replacement(method), called: method)
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
corrector.replace(node.loc.selector, replacement(node.method_name))
|
40
|
-
message_chain(node) do |arg|
|
41
|
-
autocorrect_hash_arg(corrector, arg) if single_key_hash?(arg)
|
42
|
-
autocorrect_array_arg(corrector, arg) if arg.array_type?
|
38
|
+
add_offense(node.loc.selector, message: msg) do |corrector|
|
39
|
+
autocorrect(corrector, node, method, arg)
|
43
40
|
end
|
44
41
|
end
|
45
42
|
end
|
46
43
|
|
47
44
|
private
|
48
45
|
|
46
|
+
def autocorrect(corrector, node, method, arg)
|
47
|
+
corrector.replace(node.loc.selector, replacement(method))
|
48
|
+
autocorrect_hash_arg(corrector, arg) if single_key_hash?(arg)
|
49
|
+
autocorrect_array_arg(corrector, arg) if arg.array_type?
|
50
|
+
end
|
51
|
+
|
49
52
|
def valid_usage?(node)
|
50
53
|
return true unless node.literal? || node.array_type?
|
51
54
|
|
@@ -63,7 +66,7 @@ module RuboCop
|
|
63
66
|
def autocorrect_hash_arg(corrector, arg)
|
64
67
|
key, value = *arg.children.first
|
65
68
|
|
66
|
-
corrector.replace(arg
|
69
|
+
corrector.replace(arg, key_to_arg(key))
|
67
70
|
corrector.insert_after(arg.parent.loc.end,
|
68
71
|
".and_return(#{value.source})")
|
69
72
|
end
|
@@ -71,7 +74,7 @@ module RuboCop
|
|
71
74
|
def autocorrect_array_arg(corrector, arg)
|
72
75
|
value = arg.children.first
|
73
76
|
|
74
|
-
corrector.replace(arg
|
77
|
+
corrector.replace(arg, value.source)
|
75
78
|
end
|
76
79
|
|
77
80
|
def key_to_arg(node)
|
@@ -82,12 +85,6 @@ module RuboCop
|
|
82
85
|
def replacement(method)
|
83
86
|
method.equal?(:receive_message_chain) ? 'receive' : 'stub'
|
84
87
|
end
|
85
|
-
|
86
|
-
def message(node)
|
87
|
-
method = node.method_name
|
88
|
-
|
89
|
-
format(MSG, recommended: replacement(method), called: method)
|
90
|
-
end
|
91
88
|
end
|
92
89
|
end
|
93
90
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'set'
|
4
|
+
|
3
5
|
module RuboCop
|
4
6
|
module Cop
|
5
7
|
module RSpec
|
@@ -20,6 +22,8 @@ module RuboCop
|
|
20
22
|
# end
|
21
23
|
#
|
22
24
|
class SubjectStub < Cop
|
25
|
+
include RuboCop::RSpec::TopLevelGroup
|
26
|
+
|
23
27
|
MSG = 'Do not stub methods of the object under test.'
|
24
28
|
|
25
29
|
# @!method subject(node)
|
@@ -73,72 +77,40 @@ module RuboCop
|
|
73
77
|
} ...)
|
74
78
|
PATTERN
|
75
79
|
|
76
|
-
def
|
77
|
-
|
80
|
+
def on_top_level_group(node)
|
81
|
+
@explicit_subjects = find_all_explicit_subjects(node)
|
78
82
|
|
79
|
-
|
83
|
+
find_subject_expectations(node) do |stub|
|
80
84
|
add_offense(stub)
|
81
85
|
end
|
82
86
|
end
|
83
87
|
|
84
88
|
private
|
85
89
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
# @yield [RuboCop::Node] message expectations for subject
|
91
|
-
def find_subject_stub(node, &block)
|
92
|
-
find_subject(node) do |subject_name, context|
|
93
|
-
find_subject_expectation(context, subject_name, &block)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# Find a subject message expectation
|
98
|
-
#
|
99
|
-
# @param node [RuboCop::Node]
|
100
|
-
# @param subject_name [Symbol] name of subject
|
101
|
-
#
|
102
|
-
# @yield [RuboCop::Node] message expectation
|
103
|
-
def find_subject_expectation(node, subject_name, &block)
|
104
|
-
# Do not search node if it is an example group with its own subject.
|
105
|
-
return if example_group?(node) && redefines_subject?(node)
|
106
|
-
|
107
|
-
# Yield the current node if it is a message expectation.
|
108
|
-
yield(node) if message_expectation?(node, subject_name)
|
90
|
+
def find_all_explicit_subjects(node)
|
91
|
+
node.each_descendant(:block).with_object({}) do |child, h|
|
92
|
+
name = subject(child)
|
93
|
+
next unless name
|
109
94
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
114
|
-
end
|
95
|
+
outer_example_group = child.each_ancestor.find do |a|
|
96
|
+
example_group?(a)
|
97
|
+
end
|
115
98
|
|
116
|
-
|
117
|
-
|
118
|
-
# @param node [RuboCop::Node]
|
119
|
-
#
|
120
|
-
# @return [Boolean]
|
121
|
-
def redefines_subject?(node)
|
122
|
-
node.each_child_node.any? do |child|
|
123
|
-
subject(child) || redefines_subject?(child)
|
99
|
+
h[outer_example_group] ||= []
|
100
|
+
h[outer_example_group] << name
|
124
101
|
end
|
125
102
|
end
|
126
103
|
|
127
|
-
|
128
|
-
|
129
|
-
# @param node [RuboCop::Node]
|
130
|
-
# @param parent [RuboCop::Node,nil]
|
131
|
-
#
|
132
|
-
# @yieldparam subject_name [Symbol] name of subject being defined
|
133
|
-
# @yieldparam parent [RuboCop::Node] parent of subject definition
|
134
|
-
def find_subject(node, parent: nil, &block)
|
135
|
-
# An implicit subject is defined by RSpec when no subject is declared
|
136
|
-
subject_name = subject(node) || :subject
|
104
|
+
def find_subject_expectations(node, subject_names = [], &block)
|
105
|
+
subject_names = @explicit_subjects[node] if @explicit_subjects[node]
|
137
106
|
|
138
|
-
|
107
|
+
expectation_detected = (subject_names + [:subject]).any? do |name|
|
108
|
+
message_expectation?(node, name)
|
109
|
+
end
|
110
|
+
return yield(node) if expectation_detected
|
139
111
|
|
140
112
|
node.each_child_node do |child|
|
141
|
-
|
113
|
+
find_subject_expectations(child, subject_names, &block)
|
142
114
|
end
|
143
115
|
end
|
144
116
|
end
|