rubocop-rspec 1.15.1 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -1
  3. data/Gemfile +0 -1
  4. data/config/default.yml +105 -0
  5. data/lib/rubocop-rspec.rb +27 -0
  6. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +51 -0
  7. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +51 -0
  8. data/lib/rubocop/cop/rspec/any_instance.rb +1 -1
  9. data/lib/rubocop/cop/rspec/cop.rb +4 -2
  10. data/lib/rubocop/cop/rspec/expect_in_hook.rb +61 -0
  11. data/lib/rubocop/cop/rspec/factory_girl/dynamic_attribute_defined_statically.rb +84 -0
  12. data/lib/rubocop/cop/rspec/hook_argument.rb +11 -5
  13. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +42 -0
  14. data/lib/rubocop/cop/rspec/let_before_examples.rb +66 -0
  15. data/lib/rubocop/cop/rspec/multiple_expectations.rb +3 -5
  16. data/lib/rubocop/cop/rspec/multiple_subjects.rb +80 -0
  17. data/lib/rubocop/cop/rspec/named_subject.rb +5 -6
  18. data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
  19. data/lib/rubocop/cop/rspec/predicate_matcher.rb +337 -0
  20. data/lib/rubocop/cop/rspec/return_from_stub.rb +83 -0
  21. data/lib/rubocop/cop/rspec/void_expect.rb +52 -0
  22. data/lib/rubocop/rspec/align_let_brace.rb +64 -0
  23. data/lib/rubocop/rspec/config_formatter.rb +7 -4
  24. data/lib/rubocop/rspec/description_extractor.rb +2 -2
  25. data/lib/rubocop/rspec/example_group.rb +25 -2
  26. data/lib/rubocop/rspec/factory_girl.rb +7 -0
  27. data/lib/rubocop/rspec/language.rb +6 -1
  28. data/lib/rubocop/rspec/version.rb +1 -1
  29. data/rubocop-rspec.gemspec +1 -4
  30. data/spec/project/default_config_spec.rb +8 -4
  31. data/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb +62 -0
  32. data/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb +62 -0
  33. data/spec/rubocop/cop/rspec/any_instance_spec.rb +3 -3
  34. data/spec/rubocop/cop/rspec/around_block_spec.rb +11 -11
  35. data/spec/rubocop/cop/rspec/be_eql_spec.rb +7 -7
  36. data/spec/rubocop/cop/rspec/before_after_all_spec.rb +4 -4
  37. data/spec/rubocop/cop/rspec/cop_spec.rb +7 -7
  38. data/spec/rubocop/cop/rspec/describe_class_spec.rb +18 -18
  39. data/spec/rubocop/cop/rspec/describe_method_spec.rb +4 -4
  40. data/spec/rubocop/cop/rspec/describe_symbol_spec.rb +6 -6
  41. data/spec/rubocop/cop/rspec/described_class_spec.rb +18 -18
  42. data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +5 -5
  43. data/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb +9 -9
  44. data/spec/rubocop/cop/rspec/empty_line_after_subject_spec.rb +5 -5
  45. data/spec/rubocop/cop/rspec/example_length_spec.rb +6 -6
  46. data/spec/rubocop/cop/rspec/example_wording_spec.rb +10 -10
  47. data/spec/rubocop/cop/rspec/expect_actual_spec.rb +10 -10
  48. data/spec/rubocop/cop/rspec/expect_in_hook_spec.rb +79 -0
  49. data/spec/rubocop/cop/rspec/expect_output_spec.rb +7 -7
  50. data/spec/rubocop/cop/rspec/factory_girl/dynamic_attribute_defined_statically_spec.rb +87 -0
  51. data/spec/rubocop/cop/rspec/file_path_spec.rb +29 -29
  52. data/spec/rubocop/cop/rspec/focus_spec.rb +6 -6
  53. data/spec/rubocop/cop/rspec/hook_argument_spec.rb +35 -23
  54. data/spec/rubocop/cop/rspec/implicit_expect_spec.rb +10 -10
  55. data/spec/rubocop/cop/rspec/instance_spy_spec.rb +4 -4
  56. data/spec/rubocop/cop/rspec/instance_variable_spec.rb +7 -7
  57. data/spec/rubocop/cop/rspec/invalid_predicate_matcher_spec.rb +37 -0
  58. data/spec/rubocop/cop/rspec/it_behaves_like_spec.rb +4 -4
  59. data/spec/rubocop/cop/rspec/iterated_expectation_spec.rb +8 -8
  60. data/spec/rubocop/cop/rspec/leading_subject_spec.rb +5 -5
  61. data/spec/rubocop/cop/rspec/let_before_examples_spec.rb +83 -0
  62. data/spec/rubocop/cop/rspec/let_setup_spec.rb +4 -4
  63. data/spec/rubocop/cop/rspec/message_chain_spec.rb +2 -2
  64. data/spec/rubocop/cop/rspec/message_expectation_spec.rb +4 -4
  65. data/spec/rubocop/cop/rspec/message_spies_spec.rb +18 -18
  66. data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +3 -3
  67. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +45 -9
  68. data/spec/rubocop/cop/rspec/multiple_subjects_spec.rb +96 -0
  69. data/spec/rubocop/cop/rspec/named_subject_spec.rb +4 -4
  70. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +3 -3
  71. data/spec/rubocop/cop/rspec/not_to_not_spec.rb +4 -4
  72. data/spec/rubocop/cop/rspec/overwriting_setup_spec.rb +4 -4
  73. data/spec/rubocop/cop/rspec/predicate_matcher_spec.rb +335 -0
  74. data/spec/rubocop/cop/rspec/repeated_description_spec.rb +5 -5
  75. data/spec/rubocop/cop/rspec/repeated_example_spec.rb +5 -5
  76. data/spec/rubocop/cop/rspec/return_from_stub_spec.rb +85 -0
  77. data/spec/rubocop/cop/rspec/scattered_let_spec.rb +2 -2
  78. data/spec/rubocop/cop/rspec/scattered_setup_spec.rb +8 -8
  79. data/spec/rubocop/cop/rspec/shared_context_spec.rb +10 -10
  80. data/spec/rubocop/cop/rspec/single_argument_message_chain_spec.rb +10 -10
  81. data/spec/rubocop/cop/rspec/subject_stub_spec.rb +9 -9
  82. data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +7 -7
  83. data/spec/rubocop/cop/rspec/void_expect_spec.rb +47 -0
  84. data/spec/rubocop/rspec/config_formatter_spec.rb +2 -0
  85. data/spec/spec_helper.rb +1 -1
  86. data/spec/support/expect_offense.rb +17 -0
  87. metadata +39 -51
  88. data/spec/expect_violation/expectation_spec.rb +0 -85
  89. 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
- (block
46
- (send nil {:it :specify :before :after :around} ...)
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.children.each do |child|
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 = '`%{name}` is already defined.'.freeze
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