rubocop-rspec 1.43.2 → 2.0.1
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 +39 -4
- data/README.md +5 -1
- data/config/default.yml +183 -67
- data/lib/rubocop-rspec.rb +7 -8
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +7 -3
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +7 -3
- data/lib/rubocop/cop/rspec/around_block.rb +2 -2
- data/lib/rubocop/cop/rspec/base.rb +7 -54
- data/lib/rubocop/cop/rspec/be.rb +2 -2
- data/lib/rubocop/cop/rspec/before_after_all.rb +3 -3
- data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +2 -2
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +3 -2
- data/lib/rubocop/cop/rspec/describe_class.rb +34 -15
- data/lib/rubocop/cop/rspec/describe_method.rb +2 -2
- data/lib/rubocop/cop/rspec/described_class.rb +1 -2
- data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -2
- data/lib/rubocop/cop/rspec/dialect.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_example_group.rb +33 -38
- 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 +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -2
- data/lib/rubocop/cop/rspec/expect_actual.rb +2 -1
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
- data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
- data/lib/rubocop/cop/rspec/file_path.rb +1 -1
- data/lib/rubocop/cop/rspec/focus.rb +13 -7
- data/lib/rubocop/cop/rspec/hook_argument.rb +2 -4
- data/lib/rubocop/cop/rspec/hooks_before_examples.rb +2 -2
- data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -2
- data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
- data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -1
- data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -2
- data/lib/rubocop/cop/rspec/let_setup.rb +7 -4
- data/lib/rubocop/cop/rspec/message_spies.rb +3 -3
- data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +51 -0
- data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
- data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
- data/lib/rubocop/cop/rspec/mixin/variable.rb +20 -0
- data/lib/rubocop/cop/rspec/multiple_describes.rb +2 -3
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
- data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +3 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +8 -12
- data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
- data/lib/rubocop/cop/rspec/pending.rb +13 -5
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +3 -3
- data/lib/rubocop/cop/rspec/repeated_include_example.rb +104 -0
- data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/shared_context.rb +18 -11
- data/lib/rubocop/cop/rspec/shared_examples.rb +3 -1
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec/stubbed_mock.rb +172 -0
- data/lib/rubocop/cop/rspec/subject_stub.rb +20 -10
- data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
- data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +2 -1
- data/lib/rubocop/rspec/align_let_brace.rb +1 -1
- data/lib/rubocop/rspec/concept.rb +2 -2
- data/lib/rubocop/rspec/config_formatter.rb +5 -3
- data/lib/rubocop/rspec/corrector/move_node.rb +1 -1
- data/lib/rubocop/rspec/example_group.rb +15 -5
- data/lib/rubocop/rspec/hook.rb +2 -6
- data/lib/rubocop/rspec/inject.rb +4 -2
- data/lib/rubocop/rspec/language.rb +144 -105
- data/lib/rubocop/rspec/language/node_pattern.rb +7 -24
- data/lib/rubocop/rspec/node.rb +1 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- metadata +25 -13
- data/lib/rubocop/cop/rspec/cop.rb +0 -10
- data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +0 -41
- data/lib/rubocop/rspec.rb +0 -12
- data/lib/rubocop/rspec/empty_line_separation.rb +0 -48
- data/lib/rubocop/rspec/final_end_location.rb +0 -17
- data/lib/rubocop/rspec/top_level_describe.rb +0 -52
- data/lib/rubocop/rspec/top_level_group.rb +0 -57
- data/lib/rubocop/rspec/variable.rb +0 -16
@@ -60,7 +60,7 @@ module RuboCop
|
|
60
60
|
} ...)
|
61
61
|
PATTERN
|
62
62
|
|
63
|
-
def_node_matcher :expect?, Expectations
|
63
|
+
def_node_matcher :expect?, send_pattern('#Expectations.all')
|
64
64
|
def_node_matcher :aggregate_failures_block?, <<-PATTERN
|
65
65
|
(block (send nil? :aggregate_failures ...) ...)
|
66
66
|
PATTERN
|
@@ -85,7 +85,7 @@ module RuboCop
|
|
85
85
|
#
|
86
86
|
class MultipleMemoizedHelpers < Base
|
87
87
|
include ConfigurableMax
|
88
|
-
include
|
88
|
+
include Variable
|
89
89
|
|
90
90
|
MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
|
91
91
|
|
@@ -101,6 +101,7 @@ module RuboCop
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def on_new_investigation
|
104
|
+
super
|
104
105
|
@example_group_memoized_helpers = {}
|
105
106
|
end
|
106
107
|
|
@@ -128,6 +129,7 @@ module RuboCop
|
|
128
129
|
|
129
130
|
def variable_nodes(node)
|
130
131
|
example_group = RuboCop::RSpec::ExampleGroup.new(node)
|
132
|
+
|
131
133
|
if allow_subject?
|
132
134
|
example_group.lets
|
133
135
|
else
|
@@ -42,24 +42,20 @@ module RuboCop
|
|
42
42
|
# it { is_expected.to be_valid }
|
43
43
|
# end
|
44
44
|
class NamedSubject < Base
|
45
|
-
MSG = 'Name your test subject if you need '
|
46
|
-
'to reference it explicitly.'
|
45
|
+
MSG = 'Name your test subject if you need to reference it explicitly.'
|
47
46
|
|
48
|
-
def_node_matcher :
|
49
|
-
|
50
|
-
#{Examples::ALL.block_pattern}
|
51
|
-
#{Hooks::ALL.block_pattern}
|
52
|
-
}
|
53
|
-
PATTERN
|
47
|
+
def_node_matcher :example_or_hook_block?,
|
48
|
+
block_pattern('{#Examples.all #Hooks.all}')
|
54
49
|
|
55
|
-
def_node_matcher :shared_example?,
|
56
|
-
|
57
|
-
PATTERN
|
50
|
+
def_node_matcher :shared_example?,
|
51
|
+
block_pattern('#SharedGroups.examples')
|
58
52
|
|
59
53
|
def_node_search :subject_usage, '$(send nil? :subject)'
|
60
54
|
|
61
55
|
def on_block(node)
|
62
|
-
|
56
|
+
if !example_or_hook_block?(node) || ignored_shared_example?(node)
|
57
|
+
return
|
58
|
+
end
|
63
59
|
|
64
60
|
subject_usage(node) do |subject_node|
|
65
61
|
add_offense(subject_node.loc.selector)
|
@@ -24,7 +24,8 @@ module RuboCop
|
|
24
24
|
class OverwritingSetup < Base
|
25
25
|
MSG = '`%<name>s` is already defined.'
|
26
26
|
|
27
|
-
def_node_matcher :setup?, (Helpers
|
27
|
+
def_node_matcher :setup?, block_pattern('{#Helpers.all #Subjects.all}')
|
28
|
+
|
28
29
|
def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
|
29
30
|
|
30
31
|
def on_block(node)
|
@@ -34,10 +34,10 @@ module RuboCop
|
|
34
34
|
class Pending < Base
|
35
35
|
MSG = 'Pending spec found.'
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
def_node_matcher :skippable?,
|
38
|
+
send_pattern(<<~PATTERN)
|
39
|
+
{#ExampleGroups.regular #Examples.regular}
|
40
|
+
PATTERN
|
41
41
|
|
42
42
|
def_node_matcher :skipped_in_metadata?, <<-PATTERN
|
43
43
|
{
|
@@ -47,7 +47,15 @@ module RuboCop
|
|
47
47
|
PATTERN
|
48
48
|
|
49
49
|
def_node_matcher :skip_or_pending?, '{(sym :skip) (sym :pending)}'
|
50
|
-
|
50
|
+
|
51
|
+
def_node_matcher :pending_block?,
|
52
|
+
send_pattern(<<~PATTERN)
|
53
|
+
{
|
54
|
+
#ExampleGroups.skipped
|
55
|
+
#Examples.skipped
|
56
|
+
#Examples.pending
|
57
|
+
}
|
58
|
+
PATTERN
|
51
59
|
|
52
60
|
def on_send(node)
|
53
61
|
return unless pending_block?(node) || skipped?(node)
|
@@ -30,7 +30,7 @@ module RuboCop
|
|
30
30
|
(send nil? :expect {
|
31
31
|
(block $(send !nil? #predicate? ...) ...)
|
32
32
|
$(send !nil? #predicate? ...)})
|
33
|
-
$#
|
33
|
+
$#Runners.all
|
34
34
|
$#boolean_matcher?)
|
35
35
|
PATTERN
|
36
36
|
|
@@ -155,7 +155,7 @@ module RuboCop
|
|
155
155
|
def_node_matcher :predicate_matcher?, <<-PATTERN
|
156
156
|
(send
|
157
157
|
(send nil? :expect $!nil?)
|
158
|
-
#
|
158
|
+
#Runners.all
|
159
159
|
{$(send nil? #predicate_matcher_name? ...)
|
160
160
|
(block $(send nil? #predicate_matcher_name? ...) ...)})
|
161
161
|
PATTERN
|
@@ -164,7 +164,7 @@ module RuboCop
|
|
164
164
|
(block
|
165
165
|
(send
|
166
166
|
(send nil? :expect $!nil?)
|
167
|
-
#
|
167
|
+
#Runners.all
|
168
168
|
$(send nil? #predicate_matcher_name?))
|
169
169
|
...)
|
170
170
|
PATTERN
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Check for repeated include of shared examples.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# describe 'foo' do
|
12
|
+
# include_examples 'cool stuff'
|
13
|
+
# include_examples 'cool stuff'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# describe 'foo' do
|
18
|
+
# it_behaves_like 'a cool', 'thing'
|
19
|
+
# it_behaves_like 'a cool', 'thing'
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # bad
|
23
|
+
# context 'foo' do
|
24
|
+
# it_should_behave_like 'a duck'
|
25
|
+
# it_should_behave_like 'a duck'
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # good
|
29
|
+
# describe 'foo' do
|
30
|
+
# include_examples 'cool stuff'
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# describe 'bar' do
|
34
|
+
# include_examples 'cool stuff'
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# # good
|
38
|
+
# describe 'foo' do
|
39
|
+
# it_behaves_like 'a cool', 'thing'
|
40
|
+
# it_behaves_like 'a cool', 'person'
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # good
|
44
|
+
# context 'foo' do
|
45
|
+
# it_should_behave_like 'a duck'
|
46
|
+
# it_should_behave_like 'a goose'
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
class RepeatedIncludeExample < Base
|
50
|
+
MSG = 'Repeated include of shared_examples %<name>s ' \
|
51
|
+
'on line(s) %<repeat>s'
|
52
|
+
|
53
|
+
def_node_matcher :several_include_examples?, <<-PATTERN
|
54
|
+
(begin <#include_examples? #include_examples? ...>)
|
55
|
+
PATTERN
|
56
|
+
|
57
|
+
def_node_matcher :include_examples?,
|
58
|
+
send_pattern('#Includes.examples')
|
59
|
+
|
60
|
+
def_node_matcher :shared_examples_name, <<-PATTERN
|
61
|
+
(send _ #Includes.examples $_ ...)
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
def on_begin(node)
|
65
|
+
return unless several_include_examples?(node)
|
66
|
+
|
67
|
+
repeated_include_examples(node).each do |item, repeats|
|
68
|
+
add_offense(item, message: message(item, repeats))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def repeated_include_examples(node)
|
75
|
+
node
|
76
|
+
.children
|
77
|
+
.select { |child| literal_include_examples?(child) }
|
78
|
+
.group_by { |child| signature_keys(child) }
|
79
|
+
.values
|
80
|
+
.reject(&:one?)
|
81
|
+
.flat_map { |items| add_repeated_lines(items) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def literal_include_examples?(node)
|
85
|
+
include_examples?(node) &&
|
86
|
+
node.arguments.all?(&:recursive_literal_or_const?)
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_repeated_lines(items)
|
90
|
+
repeated_lines = items.map(&:first_line)
|
91
|
+
items.map { |item| [item, repeated_lines - [item.first_line]] }
|
92
|
+
end
|
93
|
+
|
94
|
+
def signature_keys(item)
|
95
|
+
item.arguments
|
96
|
+
end
|
97
|
+
|
98
|
+
def message(item, repeats)
|
99
|
+
format(MSG, name: shared_examples_name(item).source, repeat: repeats)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
class ScatteredSetup < Base
|
26
|
-
MSG = 'Do not define multiple `%<hook_name>s` hooks in the same '\
|
26
|
+
MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
|
27
27
|
'example group (also defined on %<lines>s).'
|
28
28
|
|
29
29
|
def on_block(node)
|
@@ -53,20 +53,27 @@ module RuboCop
|
|
53
53
|
class SharedContext < Base
|
54
54
|
extend AutoCorrector
|
55
55
|
|
56
|
-
MSG_EXAMPLES = "Use `shared_examples` when you don't "
|
57
|
-
|
56
|
+
MSG_EXAMPLES = "Use `shared_examples` when you don't define context."
|
57
|
+
MSG_CONTEXT = "Use `shared_context` when you don't define examples."
|
58
58
|
|
59
|
-
|
60
|
-
|
59
|
+
def_node_search :examples?,
|
60
|
+
send_pattern('{#Includes.examples #Examples.all}')
|
61
61
|
|
62
|
-
|
63
|
-
|
62
|
+
def_node_search :context?, <<-PATTERN
|
63
|
+
(
|
64
|
+
send #rspec? {
|
65
|
+
#Subjects.all
|
66
|
+
#Helpers.all
|
67
|
+
#Includes.context
|
68
|
+
#Hooks.all
|
69
|
+
} ...
|
70
|
+
)
|
71
|
+
PATTERN
|
64
72
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def_node_matcher :shared_example, SharedGroups::EXAMPLES.block_pattern
|
73
|
+
def_node_matcher :shared_context,
|
74
|
+
block_pattern('#SharedGroups.context')
|
75
|
+
def_node_matcher :shared_example,
|
76
|
+
block_pattern('#SharedGroups.examples')
|
70
77
|
|
71
78
|
def on_block(node)
|
72
79
|
context_with_only_examples(node) do
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
19
19
|
class SingleArgumentMessageChain < Base
|
20
20
|
extend AutoCorrector
|
21
21
|
|
22
|
-
MSG = 'Use `%<recommended>s` instead of calling '\
|
22
|
+
MSG = 'Use `%<recommended>s` instead of calling ' \
|
23
23
|
'`%<called>s` with a single argument.'
|
24
24
|
|
25
25
|
def_node_matcher :message_chain, <<-PATTERN
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks that message expectations do not have a configured response.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# expect(foo).to receive(:bar).with(42).and_return("hello world")
|
12
|
+
#
|
13
|
+
# # good (without spies)
|
14
|
+
# allow(foo).to receive(:bar).with(42).and_return("hello world")
|
15
|
+
# expect(foo).to receive(:bar).with(42)
|
16
|
+
#
|
17
|
+
class StubbedMock < Base
|
18
|
+
MSG = 'Prefer `%<replacement>s` over `%<method_name>s` when ' \
|
19
|
+
'configuring a response.'
|
20
|
+
|
21
|
+
# @!method message_expectation?(node)
|
22
|
+
# Match message expectation matcher
|
23
|
+
#
|
24
|
+
# @example source that matches
|
25
|
+
# receive(:foo)
|
26
|
+
#
|
27
|
+
# @example source that matches
|
28
|
+
# receive_message_chain(:foo, :bar)
|
29
|
+
#
|
30
|
+
# @example source that matches
|
31
|
+
# receive(:foo).with('bar')
|
32
|
+
#
|
33
|
+
# @param node [RuboCop::AST::Node]
|
34
|
+
# @return [Array<RuboCop::AST::Node>] matching nodes
|
35
|
+
def_node_matcher :message_expectation?, <<-PATTERN
|
36
|
+
{
|
37
|
+
(send nil? { :receive :receive_message_chain } ...) # receive(:foo)
|
38
|
+
(send (send nil? :receive ...) :with ...) # receive(:foo).with('bar')
|
39
|
+
}
|
40
|
+
PATTERN
|
41
|
+
|
42
|
+
def_node_matcher :configured_response?, <<~PATTERN
|
43
|
+
{ :and_return :and_raise :and_throw :and_yield
|
44
|
+
:and_call_original :and_wrap_original }
|
45
|
+
PATTERN
|
46
|
+
|
47
|
+
# @!method expectation(node)
|
48
|
+
# Match expectation
|
49
|
+
#
|
50
|
+
# @example source that matches
|
51
|
+
# is_expected.to be_in_the_bar
|
52
|
+
#
|
53
|
+
# @example source that matches
|
54
|
+
# expect(cocktail).to contain_exactly(:fresh_orange_juice, :campari)
|
55
|
+
#
|
56
|
+
# @example source that matches
|
57
|
+
# expect_any_instance_of(Officer).to be_alert
|
58
|
+
#
|
59
|
+
# @param node [RuboCop::AST::Node]
|
60
|
+
# @yield [RuboCop::AST::Node] expectation, method name, matcher
|
61
|
+
def_node_matcher :expectation, <<~PATTERN
|
62
|
+
(send
|
63
|
+
$(send nil? $#Expectations.all ...)
|
64
|
+
:to $_)
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
# @!method matcher_with_configured_response(node)
|
68
|
+
# Match matcher with a configured response
|
69
|
+
#
|
70
|
+
# @example source that matches
|
71
|
+
# receive(:foo).and_return('bar')
|
72
|
+
#
|
73
|
+
# @example source that matches
|
74
|
+
# receive(:lower).and_raise(SomeError)
|
75
|
+
#
|
76
|
+
# @example source that matches
|
77
|
+
# receive(:redirect).and_call_original
|
78
|
+
#
|
79
|
+
# @param node [RuboCop::AST::Node]
|
80
|
+
# @yield [RuboCop::AST::Node] matcher
|
81
|
+
def_node_matcher :matcher_with_configured_response, <<~PATTERN
|
82
|
+
(send #message_expectation? #configured_response? _)
|
83
|
+
PATTERN
|
84
|
+
|
85
|
+
# @!method matcher_with_return_block(node)
|
86
|
+
# Match matcher with a return block
|
87
|
+
#
|
88
|
+
# @example source that matches
|
89
|
+
# receive(:foo) { 'bar' }
|
90
|
+
#
|
91
|
+
# @param node [RuboCop::AST::Node]
|
92
|
+
# @yield [RuboCop::AST::Node] matcher
|
93
|
+
def_node_matcher :matcher_with_return_block, <<~PATTERN
|
94
|
+
(block #message_expectation? args _) # receive(:foo) { 'bar' }
|
95
|
+
PATTERN
|
96
|
+
|
97
|
+
# @!method matcher_with_hash(node)
|
98
|
+
# Match matcher with a configured response defined as a hash
|
99
|
+
#
|
100
|
+
# @example source that matches
|
101
|
+
# receive_messages(foo: 'bar', baz: 'qux')
|
102
|
+
#
|
103
|
+
# @example source that matches
|
104
|
+
# receive_message_chain(:foo, bar: 'baz')
|
105
|
+
#
|
106
|
+
# @param node [RuboCop::AST::Node]
|
107
|
+
# @yield [RuboCop::AST::Node] matcher
|
108
|
+
def_node_matcher :matcher_with_hash, <<~PATTERN
|
109
|
+
{
|
110
|
+
(send nil? :receive_messages hash) # receive_messages(foo: 'bar', baz: 'qux')
|
111
|
+
(send nil? :receive_message_chain ... hash) # receive_message_chain(:foo, bar: 'baz')
|
112
|
+
}
|
113
|
+
PATTERN
|
114
|
+
|
115
|
+
# @!method matcher_with_blockpass(node)
|
116
|
+
# Match matcher with a configured response in block-pass
|
117
|
+
#
|
118
|
+
# @example source that matches
|
119
|
+
# receive(:foo, &canned)
|
120
|
+
#
|
121
|
+
# @example source that matches
|
122
|
+
# receive_message_chain(:foo, :bar, &canned)
|
123
|
+
#
|
124
|
+
# @example source that matches
|
125
|
+
# receive(:foo).with('bar', &canned)
|
126
|
+
#
|
127
|
+
# @param node [RuboCop::AST::Node]
|
128
|
+
# @yield [RuboCop::AST::Node] matcher
|
129
|
+
def_node_matcher :matcher_with_blockpass, <<~PATTERN
|
130
|
+
{
|
131
|
+
(send nil? { :receive :receive_message_chain } ... block_pass) # receive(:foo, &canned)
|
132
|
+
(send (send nil? :receive ...) :with ... block_pass) # receive(:foo).with('foo', &canned)
|
133
|
+
}
|
134
|
+
PATTERN
|
135
|
+
|
136
|
+
def on_send(node)
|
137
|
+
expectation(node, &method(:on_expectation))
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def on_expectation(expectation, method_name, matcher)
|
143
|
+
flag_expectation = lambda do
|
144
|
+
add_offense(expectation, message: msg(method_name))
|
145
|
+
end
|
146
|
+
|
147
|
+
matcher_with_configured_response(matcher, &flag_expectation)
|
148
|
+
matcher_with_return_block(matcher, &flag_expectation)
|
149
|
+
matcher_with_hash(matcher, &flag_expectation)
|
150
|
+
matcher_with_blockpass(matcher, &flag_expectation)
|
151
|
+
end
|
152
|
+
|
153
|
+
def msg(method_name)
|
154
|
+
format(MSG,
|
155
|
+
method_name: method_name,
|
156
|
+
replacement: replacement(method_name))
|
157
|
+
end
|
158
|
+
|
159
|
+
def replacement(method_name)
|
160
|
+
case method_name
|
161
|
+
when :expect
|
162
|
+
:allow
|
163
|
+
when :is_expected
|
164
|
+
'allow(subject)'
|
165
|
+
when :expect_any_instance_of
|
166
|
+
:allow_any_instance_of
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|