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.
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