rubocop-rspec 1.15.1 → 1.16.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 +20 -1
- data/Gemfile +0 -1
- data/config/default.yml +105 -0
- data/lib/rubocop-rspec.rb +27 -0
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +51 -0
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +51 -0
- data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
- data/lib/rubocop/cop/rspec/cop.rb +4 -2
- data/lib/rubocop/cop/rspec/expect_in_hook.rb +61 -0
- data/lib/rubocop/cop/rspec/factory_girl/dynamic_attribute_defined_statically.rb +84 -0
- data/lib/rubocop/cop/rspec/hook_argument.rb +11 -5
- data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +42 -0
- data/lib/rubocop/cop/rspec/let_before_examples.rb +66 -0
- data/lib/rubocop/cop/rspec/multiple_expectations.rb +3 -5
- data/lib/rubocop/cop/rspec/multiple_subjects.rb +80 -0
- data/lib/rubocop/cop/rspec/named_subject.rb +5 -6
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
- data/lib/rubocop/cop/rspec/predicate_matcher.rb +337 -0
- data/lib/rubocop/cop/rspec/return_from_stub.rb +83 -0
- data/lib/rubocop/cop/rspec/void_expect.rb +52 -0
- data/lib/rubocop/rspec/align_let_brace.rb +64 -0
- data/lib/rubocop/rspec/config_formatter.rb +7 -4
- data/lib/rubocop/rspec/description_extractor.rb +2 -2
- data/lib/rubocop/rspec/example_group.rb +25 -2
- data/lib/rubocop/rspec/factory_girl.rb +7 -0
- data/lib/rubocop/rspec/language.rb +6 -1
- data/lib/rubocop/rspec/version.rb +1 -1
- data/rubocop-rspec.gemspec +1 -4
- data/spec/project/default_config_spec.rb +8 -4
- data/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb +62 -0
- data/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb +62 -0
- data/spec/rubocop/cop/rspec/any_instance_spec.rb +3 -3
- data/spec/rubocop/cop/rspec/around_block_spec.rb +11 -11
- data/spec/rubocop/cop/rspec/be_eql_spec.rb +7 -7
- data/spec/rubocop/cop/rspec/before_after_all_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/cop_spec.rb +7 -7
- data/spec/rubocop/cop/rspec/describe_class_spec.rb +18 -18
- data/spec/rubocop/cop/rspec/describe_method_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/describe_symbol_spec.rb +6 -6
- data/spec/rubocop/cop/rspec/described_class_spec.rb +18 -18
- data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +5 -5
- data/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb +9 -9
- data/spec/rubocop/cop/rspec/empty_line_after_subject_spec.rb +5 -5
- data/spec/rubocop/cop/rspec/example_length_spec.rb +6 -6
- data/spec/rubocop/cop/rspec/example_wording_spec.rb +10 -10
- data/spec/rubocop/cop/rspec/expect_actual_spec.rb +10 -10
- data/spec/rubocop/cop/rspec/expect_in_hook_spec.rb +79 -0
- data/spec/rubocop/cop/rspec/expect_output_spec.rb +7 -7
- data/spec/rubocop/cop/rspec/factory_girl/dynamic_attribute_defined_statically_spec.rb +87 -0
- data/spec/rubocop/cop/rspec/file_path_spec.rb +29 -29
- data/spec/rubocop/cop/rspec/focus_spec.rb +6 -6
- data/spec/rubocop/cop/rspec/hook_argument_spec.rb +35 -23
- data/spec/rubocop/cop/rspec/implicit_expect_spec.rb +10 -10
- data/spec/rubocop/cop/rspec/instance_spy_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/instance_variable_spec.rb +7 -7
- data/spec/rubocop/cop/rspec/invalid_predicate_matcher_spec.rb +37 -0
- data/spec/rubocop/cop/rspec/it_behaves_like_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/iterated_expectation_spec.rb +8 -8
- data/spec/rubocop/cop/rspec/leading_subject_spec.rb +5 -5
- data/spec/rubocop/cop/rspec/let_before_examples_spec.rb +83 -0
- data/spec/rubocop/cop/rspec/let_setup_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/message_chain_spec.rb +2 -2
- data/spec/rubocop/cop/rspec/message_expectation_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/message_spies_spec.rb +18 -18
- data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +3 -3
- data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +45 -9
- data/spec/rubocop/cop/rspec/multiple_subjects_spec.rb +96 -0
- data/spec/rubocop/cop/rspec/named_subject_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/nested_groups_spec.rb +3 -3
- data/spec/rubocop/cop/rspec/not_to_not_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/overwriting_setup_spec.rb +4 -4
- data/spec/rubocop/cop/rspec/predicate_matcher_spec.rb +335 -0
- data/spec/rubocop/cop/rspec/repeated_description_spec.rb +5 -5
- data/spec/rubocop/cop/rspec/repeated_example_spec.rb +5 -5
- data/spec/rubocop/cop/rspec/return_from_stub_spec.rb +85 -0
- data/spec/rubocop/cop/rspec/scattered_let_spec.rb +2 -2
- data/spec/rubocop/cop/rspec/scattered_setup_spec.rb +8 -8
- data/spec/rubocop/cop/rspec/shared_context_spec.rb +10 -10
- data/spec/rubocop/cop/rspec/single_argument_message_chain_spec.rb +10 -10
- data/spec/rubocop/cop/rspec/subject_stub_spec.rb +9 -9
- data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +7 -7
- data/spec/rubocop/cop/rspec/void_expect_spec.rb +47 -0
- data/spec/rubocop/rspec/config_formatter_spec.rb +2 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/expect_offense.rb +17 -0
- metadata +39 -51
- data/spec/expect_violation/expectation_spec.rb +0 -85
- data/spec/support/expect_violation.rb +0 -170
@@ -42,9 +42,10 @@ module RuboCop
|
|
42
42
|
'to reference it explicitly.'.freeze
|
43
43
|
|
44
44
|
def_node_matcher :rspec_block?, <<-PATTERN
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
{
|
46
|
+
#{Examples::ALL.block_pattern}
|
47
|
+
#{Hooks::ALL.block_pattern}
|
48
|
+
}
|
48
49
|
PATTERN
|
49
50
|
|
50
51
|
def_node_matcher :unnamed_subject, '$(send nil :subject)'
|
@@ -60,11 +61,9 @@ module RuboCop
|
|
60
61
|
private
|
61
62
|
|
62
63
|
def subject_usage(node, &block)
|
63
|
-
return unless node.is_a?(Parser::AST::Node)
|
64
|
-
|
65
64
|
unnamed_subject(node, &block)
|
66
65
|
|
67
|
-
node.
|
66
|
+
node.each_child_node do |child|
|
68
67
|
subject_usage(child, &block)
|
69
68
|
end
|
70
69
|
end
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
22
22
|
# let(:baz) { baz }
|
23
23
|
# let!(:other) { other }
|
24
24
|
class OverwritingSetup < Cop
|
25
|
-
MSG = '
|
25
|
+
MSG = '`%<name>s` is already defined.'.freeze
|
26
26
|
|
27
27
|
def_node_matcher :setup?, <<-PATTERN
|
28
28
|
(block (send nil {:let :let! :subject} (sym $_)) ...)
|
@@ -0,0 +1,337 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module RSpec
|
4
|
+
# A helper for `inflected` style
|
5
|
+
module InflectedHelper
|
6
|
+
extend NodePattern::Macros
|
7
|
+
|
8
|
+
MSG_INFLECTED = 'Prefer using `%<matcher_name>s` matcher over ' \
|
9
|
+
'`%<predicate_name>s`.'.freeze
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def check_inflected(node)
|
14
|
+
predicate_in_actual?(node) do |predicate|
|
15
|
+
add_offense(node, node.loc.expression, message_inflected(predicate))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def_node_matcher :predicate_in_actual?, <<-PATTERN
|
20
|
+
(send
|
21
|
+
(send nil :expect {
|
22
|
+
(block $(send !nil #predicate? ...) ...)
|
23
|
+
$(send !nil #predicate? ...)})
|
24
|
+
${:to :not_to :to_not}
|
25
|
+
$#boolean_matcher?)
|
26
|
+
PATTERN
|
27
|
+
|
28
|
+
def_node_matcher :be_bool?, <<-PATTERN
|
29
|
+
(send nil {:be :eq :eql :equal} {true false})
|
30
|
+
PATTERN
|
31
|
+
|
32
|
+
def_node_matcher :be_boolthy?, <<-PATTERN
|
33
|
+
(send nil {:be_truthy :be_falsey :be_falsy :a_truthy_value :a_falsey_value :a_falsy_value})
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def boolean_matcher?(node)
|
37
|
+
if cop_config['Strict']
|
38
|
+
be_boolthy?(node)
|
39
|
+
else
|
40
|
+
be_bool?(node) || be_boolthy?(node)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def predicate?(sym)
|
45
|
+
sym.to_s.end_with?('?')
|
46
|
+
end
|
47
|
+
|
48
|
+
def message_inflected(predicate)
|
49
|
+
_recv, predicate_name, = *predicate
|
50
|
+
format(MSG_INFLECTED,
|
51
|
+
predicate_name: predicate_name,
|
52
|
+
matcher_name: to_predicate_matcher(predicate_name))
|
53
|
+
end
|
54
|
+
|
55
|
+
# rubocop:disable Metrics/MethodLength
|
56
|
+
def to_predicate_matcher(name)
|
57
|
+
case name = name.to_s
|
58
|
+
when 'is_a?'
|
59
|
+
'be_a'
|
60
|
+
when 'instance_of?'
|
61
|
+
'be_an_instance_of'
|
62
|
+
when 'include?', 'respond_to?'
|
63
|
+
name[0..-2]
|
64
|
+
when /^has_/
|
65
|
+
name.sub('has_', 'have_')[0..-2]
|
66
|
+
else
|
67
|
+
"be_#{name[0..-2]}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
# rubocop:enable Metrics/MethodLength
|
71
|
+
|
72
|
+
def autocorrect_inflected(node)
|
73
|
+
predicate_in_actual?(node) do |predicate, to, matcher|
|
74
|
+
lambda do |corrector|
|
75
|
+
remove_predicate(corrector, predicate)
|
76
|
+
corrector.replace(node.loc.selector,
|
77
|
+
true?(to, matcher) ? 'to' : 'not_to')
|
78
|
+
rewrite_matcher(corrector, predicate, matcher)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def remove_predicate(corrector, predicate)
|
84
|
+
range = range_between(
|
85
|
+
predicate.loc.dot.begin_pos,
|
86
|
+
predicate.loc.expression.end_pos
|
87
|
+
)
|
88
|
+
corrector.remove(range)
|
89
|
+
|
90
|
+
block_range = block_loc(predicate)
|
91
|
+
corrector.remove(block_range) if block_range
|
92
|
+
end
|
93
|
+
|
94
|
+
def rewrite_matcher(corrector, predicate, matcher)
|
95
|
+
args = args_loc(predicate).source
|
96
|
+
block_loc = block_loc(predicate)
|
97
|
+
block = block_loc ? block_loc.source : ''
|
98
|
+
_recv, name, = *predicate
|
99
|
+
|
100
|
+
corrector.replace(matcher.loc.expression,
|
101
|
+
to_predicate_matcher(name) + args + block)
|
102
|
+
end
|
103
|
+
|
104
|
+
def true?(to, matcher)
|
105
|
+
_recv, name, arg = *matcher
|
106
|
+
result = case name
|
107
|
+
when :be, :eq
|
108
|
+
arg.true_type?
|
109
|
+
when :be_truthy, :a_truthy_value
|
110
|
+
true
|
111
|
+
when :be_falsey, :be_falsy, :a_falsey_value, :a_falsy_value
|
112
|
+
false
|
113
|
+
end
|
114
|
+
to == :to ? result : !result
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# A helper for `explicit` style
|
119
|
+
# rubocop:disable Metrics/ModuleLength
|
120
|
+
module ExplicitHelper
|
121
|
+
extend NodePattern::Macros
|
122
|
+
|
123
|
+
MSG_EXPLICIT = 'Prefer using `%<predicate_name>s` over ' \
|
124
|
+
'`%<matcher_name>s` matcher.'.freeze
|
125
|
+
BUILT_IN_MATCHERS = %w[
|
126
|
+
be_truthy be_falsey be_falsy
|
127
|
+
have_attributes have_received
|
128
|
+
be_between be_within
|
129
|
+
].freeze
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def check_explicit(node)
|
134
|
+
predicate_matcher_block?(node) do |_actual, matcher|
|
135
|
+
add_offense(node, :expression, message_explicit(matcher))
|
136
|
+
ignore_node(node.children.first)
|
137
|
+
return
|
138
|
+
end
|
139
|
+
|
140
|
+
return if part_of_ignored_node?(node)
|
141
|
+
predicate_matcher?(node) do |_actual, matcher|
|
142
|
+
add_offense(node, :expression, message_explicit(matcher))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def_node_matcher :predicate_matcher?, <<-PATTERN
|
147
|
+
(send
|
148
|
+
(send nil :expect $!nil)
|
149
|
+
{:to :not_to :to_not}
|
150
|
+
{$(send nil #predicate_matcher_name? ...)
|
151
|
+
(block $(send nil #predicate_matcher_name? ...) ...)})
|
152
|
+
PATTERN
|
153
|
+
|
154
|
+
def_node_matcher :predicate_matcher_block?, <<-PATTERN
|
155
|
+
(block
|
156
|
+
(send
|
157
|
+
(send nil :expect $!nil)
|
158
|
+
{:to :not_to :to_not}
|
159
|
+
$(send nil #predicate_matcher_name?))
|
160
|
+
...)
|
161
|
+
PATTERN
|
162
|
+
|
163
|
+
def predicate_matcher_name?(name)
|
164
|
+
name = name.to_s
|
165
|
+
name.start_with?('be_', 'have_') &&
|
166
|
+
!BUILT_IN_MATCHERS.include?(name) &&
|
167
|
+
!name.end_with?('?')
|
168
|
+
end
|
169
|
+
|
170
|
+
def message_explicit(matcher)
|
171
|
+
_recv, name, = *matcher
|
172
|
+
format(MSG_EXPLICIT,
|
173
|
+
predicate_name: to_predicate_method(name),
|
174
|
+
matcher_name: name)
|
175
|
+
end
|
176
|
+
|
177
|
+
def autocorrect_explicit(node)
|
178
|
+
autocorrect_explicit_send(node) ||
|
179
|
+
autocorrect_explicit_block(node)
|
180
|
+
end
|
181
|
+
|
182
|
+
def autocorrect_explicit_send(node)
|
183
|
+
predicate_matcher?(node) do |actual, matcher|
|
184
|
+
corrector_explicit(node, actual, matcher, matcher)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def autocorrect_explicit_block(node)
|
189
|
+
predicate_matcher_block?(node) do |actual, matcher|
|
190
|
+
to, = *node
|
191
|
+
corrector_explicit(to, actual, matcher, to)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def corrector_explicit(to, actual, matcher, block_child)
|
196
|
+
lambda do |corrector|
|
197
|
+
replacement_matcher = replacement_matcher(to)
|
198
|
+
corrector.replace(matcher.loc.expression, replacement_matcher)
|
199
|
+
move_predicate(corrector, actual, matcher, block_child)
|
200
|
+
corrector.replace(to.loc.selector, 'to')
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def move_predicate(corrector, actual, matcher, block_child)
|
205
|
+
predicate = to_predicate_method(matcher.method_name)
|
206
|
+
args = args_loc(matcher).source
|
207
|
+
block_loc = block_loc(block_child)
|
208
|
+
block = block_loc ? block_loc.source : ''
|
209
|
+
|
210
|
+
corrector.remove(block_loc) if block_loc
|
211
|
+
corrector.insert_after(actual.loc.expression,
|
212
|
+
".#{predicate}" + args + block)
|
213
|
+
end
|
214
|
+
|
215
|
+
# rubocop:disable Metrics/MethodLength
|
216
|
+
def to_predicate_method(matcher)
|
217
|
+
case matcher = matcher.to_s
|
218
|
+
when 'be_a', 'be_an', 'be_a_kind_of', 'a_kind_of', 'be_kind_of'
|
219
|
+
'is_a?'
|
220
|
+
when 'be_an_instance_of', 'be_instance_of', 'an_instance_of'
|
221
|
+
'instance_of?'
|
222
|
+
when 'include', 'respond_to'
|
223
|
+
matcher + '?'
|
224
|
+
when /^have_(.+)/
|
225
|
+
"has_#{Regexp.last_match(1)}?"
|
226
|
+
else
|
227
|
+
matcher[/^be_(.+)/, 1] + '?'
|
228
|
+
end
|
229
|
+
end
|
230
|
+
# rubocop:enable Metrics/MethodLength
|
231
|
+
|
232
|
+
def replacement_matcher(node)
|
233
|
+
case [cop_config['Strict'], node.method_name == :to]
|
234
|
+
when [true, true]
|
235
|
+
'be(true)'
|
236
|
+
when [true, false]
|
237
|
+
'be(false)'
|
238
|
+
when [false, true]
|
239
|
+
'be_truthy'
|
240
|
+
when [false, false]
|
241
|
+
'be_falsey'
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
# rubocop:enable Metrics/ModuleLength
|
246
|
+
|
247
|
+
# Prefer using predicate matcher over using predicate method directly.
|
248
|
+
#
|
249
|
+
# RSpec defines magic matchers for predicate methods.
|
250
|
+
# This cop recommends to use the predicate matcher instead of using
|
251
|
+
# predicate method directly.
|
252
|
+
#
|
253
|
+
# @example Strict: true, EnforcedStyle: inflected (default)
|
254
|
+
# # bad
|
255
|
+
# expect(foo.something?).to be_truthy
|
256
|
+
#
|
257
|
+
# # good
|
258
|
+
# expect(foo).to be_something
|
259
|
+
#
|
260
|
+
# # also good - It checks "true" strictly.
|
261
|
+
# expect(foo).to be(true)
|
262
|
+
#
|
263
|
+
# @example Strict: false, EnforcedStyle: inflected
|
264
|
+
# # bad
|
265
|
+
# expect(foo.something?).to be_truthy
|
266
|
+
# expect(foo).to be(true)
|
267
|
+
#
|
268
|
+
# # good
|
269
|
+
# expect(foo).to be_something
|
270
|
+
#
|
271
|
+
# @example Strict: true, EnforcedStyle: explicit
|
272
|
+
# # bad
|
273
|
+
# expect(foo).to be_something
|
274
|
+
#
|
275
|
+
# # good - the above code is rewritten to it by this cop
|
276
|
+
# expect(foo.something?).to be(true)
|
277
|
+
#
|
278
|
+
# @example Strict: false, EnforcedStyle: explicit
|
279
|
+
# # bad
|
280
|
+
# expect(foo).to be_something
|
281
|
+
#
|
282
|
+
# # good - the above code is rewritten to it by this cop
|
283
|
+
# expect(foo.something?).to be_truthy
|
284
|
+
class PredicateMatcher < Cop
|
285
|
+
include ConfigurableEnforcedStyle
|
286
|
+
include InflectedHelper
|
287
|
+
include ExplicitHelper
|
288
|
+
|
289
|
+
def on_send(node)
|
290
|
+
case style
|
291
|
+
when :inflected
|
292
|
+
check_inflected(node)
|
293
|
+
when :explicit
|
294
|
+
check_explicit(node)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def on_block(node)
|
299
|
+
check_explicit(node) if style == :explicit
|
300
|
+
end
|
301
|
+
|
302
|
+
private
|
303
|
+
|
304
|
+
def autocorrect(node)
|
305
|
+
case style
|
306
|
+
when :inflected
|
307
|
+
autocorrect_inflected(node)
|
308
|
+
when :explicit
|
309
|
+
autocorrect_explicit(node)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# returns args location with whitespace
|
314
|
+
# @example
|
315
|
+
# foo 1, 2
|
316
|
+
# ^^^^^
|
317
|
+
def args_loc(send_node)
|
318
|
+
range_between(send_node.loc.selector.end_pos,
|
319
|
+
send_node.loc.expression.end_pos)
|
320
|
+
end
|
321
|
+
|
322
|
+
# returns block location with whitespace
|
323
|
+
# @example
|
324
|
+
# foo { bar }
|
325
|
+
# ^^^^^^^^
|
326
|
+
def block_loc(send_node)
|
327
|
+
parent = send_node.parent
|
328
|
+
return unless parent.block_type?
|
329
|
+
range_between(
|
330
|
+
send_node.loc.expression.end_pos,
|
331
|
+
parent.loc.expression.end_pos
|
332
|
+
)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module RSpec
|
6
|
+
# Checks for consistent style of stub's return setting.
|
7
|
+
#
|
8
|
+
# Enforces either `and_return` or block-style return in the cases
|
9
|
+
# where the returned value is constant. Ignores dynamic returned values
|
10
|
+
# are the result would be different
|
11
|
+
#
|
12
|
+
# This cop can be configured using the `EnforcedStyle` option
|
13
|
+
#
|
14
|
+
# @example `EncorcedStyle: block`
|
15
|
+
# # bad
|
16
|
+
# allow(Foo).to receive(:bar).and_return("baz")
|
17
|
+
# expect(Foo).to receive(:bar).and_return("baz")
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# allow(Foo).to receive(:bar) { "baz" }
|
21
|
+
# expect(Foo).to receive(:bar) { "baz" }
|
22
|
+
# # also good as the returned value is dynamic
|
23
|
+
# allow(Foo).to receive(:bar).and_return(bar.baz)
|
24
|
+
#
|
25
|
+
# @example `EncorcedStyle: and_return`
|
26
|
+
# # bad
|
27
|
+
# allow(Foo).to receive(:bar) { "baz" }
|
28
|
+
# expect(Foo).to receive(:bar) { "baz" }
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# allow(Foo).to receive(:bar).and_return("baz")
|
32
|
+
# expect(Foo).to receive(:bar).and_return("baz")
|
33
|
+
# # also good as the returned value is dynamic
|
34
|
+
# allow(Foo).to receive(:bar) { bar.baz }
|
35
|
+
#
|
36
|
+
class ReturnFromStub < Cop
|
37
|
+
include ConfigurableEnforcedStyle
|
38
|
+
|
39
|
+
MSG_AND_RETURN = 'Use `and_return` for static values.'.freeze
|
40
|
+
MSG_BLOCK = 'Use block for static values.'.freeze
|
41
|
+
|
42
|
+
def_node_matcher :receive_with_block, <<-PATTERN
|
43
|
+
(block
|
44
|
+
(send nil :receive ...)
|
45
|
+
(args)
|
46
|
+
$(...)
|
47
|
+
)
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def_node_matcher :and_return_value, <<-PATTERN
|
51
|
+
(send
|
52
|
+
(send nil :receive (...)) :and_return $(...)
|
53
|
+
)
|
54
|
+
PATTERN
|
55
|
+
|
56
|
+
def on_block(node)
|
57
|
+
return unless style == :and_return
|
58
|
+
receive_with_block(node) do |args|
|
59
|
+
add_offense(node, :expression, MSG_AND_RETURN) unless dynamic?(args)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_send(node)
|
64
|
+
return unless style == :block
|
65
|
+
|
66
|
+
and_return_value(node) do |args|
|
67
|
+
add_offense(node, :expression, MSG_BLOCK) unless dynamic?(args)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def dynamic?(node)
|
74
|
+
if node.array_type?
|
75
|
+
return node.each_child_node.any? { |child| dynamic?(child) }
|
76
|
+
end
|
77
|
+
|
78
|
+
!node.literal?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|