rubocop-rspec 1.27.0 → 1.28.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +1 -1
  4. data/config/default.yml +15 -10
  5. data/lib/rubocop/cop/rspec/describe_method.rb +1 -1
  6. data/lib/rubocop/cop/rspec/empty_example_group.rb +1 -1
  7. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +2 -4
  8. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +1 -3
  9. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -3
  10. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -3
  11. data/lib/rubocop/cop/rspec/example_without_description.rb +3 -4
  12. data/lib/rubocop/cop/rspec/expect_in_hook.rb +8 -23
  13. data/lib/rubocop/cop/rspec/expect_output.rb +0 -2
  14. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +146 -0
  15. data/lib/rubocop/cop/rspec/instance_spy.rb +0 -2
  16. data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
  17. data/lib/rubocop/cop/rspec/leading_subject.rb +1 -6
  18. data/lib/rubocop/cop/rspec/let_before_examples.rb +0 -2
  19. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +35 -0
  20. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
  21. data/lib/rubocop/cop/rspec/multiple_subjects.rb +4 -4
  22. data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
  23. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  24. data/lib/rubocop/cop/rspec/receive_never.rb +43 -0
  25. data/lib/rubocop/cop/rspec/scattered_let.rb +0 -2
  26. data/lib/rubocop/cop/rspec/shared_context.rb +3 -3
  27. data/lib/rubocop/cop/rspec/void_expect.rb +1 -1
  28. data/lib/rubocop/cop/rspec_cops.rb +4 -3
  29. data/lib/rubocop/rspec/align_let_brace.rb +1 -3
  30. data/lib/rubocop/rspec/blank_line_separation.rb +6 -0
  31. data/lib/rubocop/rspec/example_group.rb +0 -7
  32. data/lib/rubocop/rspec/language/node_pattern.rb +6 -0
  33. data/lib/rubocop/rspec/version.rb +1 -1
  34. data/rubocop-rspec.gemspec +2 -2
  35. data/spec/project/project_requires_spec.rb +13 -3
  36. data/spec/rubocop/cop/rspec/before_after_all_spec.rb +2 -2
  37. data/spec/rubocop/cop/rspec/empty_line_after_example_group_spec.rb +15 -0
  38. data/spec/rubocop/cop/rspec/factory_bot/attribute_defined_statically_spec.rb +156 -0
  39. data/spec/rubocop/cop/rspec/let_before_examples_spec.rb +0 -5
  40. data/spec/rubocop/cop/rspec/missing_example_group_argument_spec.rb +55 -0
  41. data/spec/rubocop/cop/rspec/overwriting_setup_spec.rb +0 -5
  42. data/spec/rubocop/cop/rspec/receive_never_spec.rb +45 -0
  43. data/spec/rubocop/cop/rspec/scattered_let_spec.rb +0 -5
  44. data/spec/shared/smoke_test_examples.rb +25 -0
  45. data/spec/smoke_tests/empty_spec.rb +0 -0
  46. data/spec/smoke_tests/factory_bot_spec.rb +11 -0
  47. data/spec/smoke_tests/no_tests_spec.rb +4 -0
  48. data/spec/smoke_tests/weird_rspec_spec.rb +233 -0
  49. data/spec/spec_helper.rb +4 -0
  50. metadata +24 -11
  51. data/lib/rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically.rb +0 -93
  52. data/lib/rubocop/cop/rspec/factory_bot/static_attribute_defined_dynamically.rb +0 -81
  53. data/spec/rubocop/cop/rspec/factory_bot/dynamic_attribute_defined_statically_spec.rb +0 -139
  54. data/spec/rubocop/cop/rspec/factory_bot/static_attribute_defined_dynamically_spec.rb +0 -107
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2cb62faf05dbb3d997032a8fbac1d3d1897542d7a98b1540c54afebc10a6a294
4
- data.tar.gz: 6b1fae2d79e473c5e8295fe6ccb5e843e8801b2ec6ea3c54625e8ba16e7c522d
3
+ metadata.gz: 6138f067fde5bf88b21e56895a967a55e8bc9357dfd16d0044fd2777c5cbadc7
4
+ data.tar.gz: 64d630cc6a9f3d1413a8155486f002a1d524f9750ebf2c18d5c5bc485c21e438
5
5
  SHA512:
6
- metadata.gz: 2136057006988a3e05ab8be06f16125e76c520d1e46d656dd8aee9d7ae0c841fbc55d07e247e332120ab32b1f0a41e5b24827012104c8c3b75ac7e15de7e9395
7
- data.tar.gz: 6a55cfcf03e4c2b15157e3292a89e161b3a25dc10ee45325912e9bf0fd4a13705b7d9b1e7c5f309c9a5ea9fc608a333af144a36844e95d6f20658cde5d290fd5
6
+ metadata.gz: 57246fef93f5061741c542a5710c16c905ff086da1d06974473e8b6ad54a6c84e26c4d79f7f538a92bca7d53ceb4c20e9eace2d65536922168e4ca227bb5a152
7
+ data.tar.gz: 6940142c7e889246bab48ccd06dc35784731181b774fc047ba9359f5e5b5975b5b10cee3a2ac970e11142ed1ae9dfc004e88e30cba5470945088a23cd7506b8d
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 1.28.0 (2018-08-14)
6
+
7
+ * Add `RSpec/ReceiveNever` cop enforcing usage of `not_to receive` instead of `never` matcher. ([@Darhazer][])
8
+ * Fix false positive in `RSpec/EmptyLineAfterExampleGroup` cop when example is inside `if`. ([@Darhazer][])
9
+ * Add `RSpec/MissingExampleGroupArgument` to enforce first argument for an example group. ([@geniou][])
10
+ * Drop support for ruby `2.1`. ([@bquorning][])
11
+ * Add `FactoryBot/AttributeDefinedStatically` cop to help FactoryBot users with the deprecation of static attributes. ([@composerinteralia][], [@seanpdoyle][])
12
+ * Remove `FactoryBot/DynamicAttributeDefinedStatically` and `FactoryBot/StaticAttributeDefinedDynamically` cops. ([@composerinteralia][])
13
+
5
14
  ## 1.27.0 (2018-06-14)
6
15
 
7
16
  * `RSpec/LeadingSubject` now enforces subject to be before any examples, hooks or let declarations. ([@Darhazer][])
@@ -351,3 +360,5 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
351
360
  [@abrom]: https://github.com/abrom
352
361
  [@patrickomatic]: https://github.com/patrickomatic
353
362
  [@tdeo]: https://github.com/tdeo
363
+ [@composerinteralia]: https://github.com/composerinteralia
364
+ [@seanpdoyle]: https://github.com/seanpdoyle
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Maintainability](https://api.codeclimate.com/v1/badges/8ffaabf633c968c22bdd/maintainability)](https://codeclimate.com/github/rubocop-hq/rubocop-rspec/maintainability)
8
8
 
9
9
  RSpec-specific analysis for your projects, as an extension to
10
- [RuboCop](https://github.com/bbatsov/rubocop).
10
+ [RuboCop](https://github.com/rubocop-hq/rubocop).
11
11
 
12
12
  ## Installation
13
13
 
@@ -262,6 +262,11 @@ RSpec/MessageSpies:
262
262
  - receive
263
263
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MessageSpies
264
264
 
265
+ RSpec/MissingExampleGroupArgument:
266
+ Description: Checks that the first argument to an example group is not empty.
267
+ Enabled: true
268
+ StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
269
+
265
270
  RSpec/MultipleDescribes:
266
271
  Description: Checks for multiple top level describes.
267
272
  Enabled: true
@@ -314,6 +319,11 @@ RSpec/ReceiveCounts:
314
319
  Description: Check for `once` and `twice` receive counts matchers usage.
315
320
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveCounts
316
321
 
322
+ RSpec/ReceiveNever:
323
+ Enabled: true
324
+ Description: Prefer `not_to receive(...)` over `receive(...).never`.
325
+ StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveNever
326
+
317
327
  RSpec/RepeatedDescription:
318
328
  Enabled: true
319
329
  Description: Check for repeated description strings in example groups.
@@ -395,6 +405,11 @@ Capybara/FeatureMethods:
395
405
  EnabledMethods: []
396
406
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods
397
407
 
408
+ FactoryBot/AttributeDefinedStatically:
409
+ Description: Always declare attribute values as blocks.
410
+ Enabled: true
411
+ StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically
412
+
398
413
  FactoryBot/CreateList:
399
414
  Description: Checks for create_list usage.
400
415
  Enabled: true
@@ -404,16 +419,6 @@ FactoryBot/CreateList:
404
419
  - n_times
405
420
  StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList
406
421
 
407
- FactoryBot/DynamicAttributeDefinedStatically:
408
- Description: Prefer declaring dynamic attribute values in a block.
409
- Enabled: true
410
- StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/DynamicAttributeDefinedStatically
411
-
412
- FactoryBot/StaticAttributeDefinedDynamically:
413
- Description: Prefer declaring static attribute values without a block.
414
- Enabled: true
415
- StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/StaticAttributeDefinedDynamically
416
-
417
422
  Rails/HttpStatus:
418
423
  Description: Enforces use of symbolic or numeric value to describe HTTP status.
419
424
  Enabled: true
@@ -27,7 +27,7 @@ module RuboCop
27
27
 
28
28
  def on_top_level_describe(_node, (_, second_arg))
29
29
  return unless second_arg && second_arg.str_type?
30
- return if METHOD_STRING_MATCHER =~ one(second_arg.children)
30
+ return if METHOD_STRING_MATCHER =~ second_arg.str_content
31
31
 
32
32
  add_offense(second_arg, location: :expression)
33
33
  end
@@ -70,7 +70,7 @@ module RuboCop
70
70
  def on_block(node)
71
71
  return unless example_group?(node) && !contains_example?(node)
72
72
 
73
- add_offense(node.children.first, location: :expression)
73
+ add_offense(node.send_node, location: :expression)
74
74
  end
75
75
 
76
76
  private
@@ -28,11 +28,9 @@ module RuboCop
28
28
 
29
29
  MSG = 'Add an empty line after `%<example_group>s`.'.freeze
30
30
 
31
- def_node_matcher :example_group, ExampleGroups::ALL.block_pattern
32
-
33
31
  def on_block(node)
34
- return unless example_group(node)
35
- return if node.parent && node.equal?(node.parent.children.last)
32
+ return unless example_group?(node)
33
+ return if last_child?(node)
36
34
 
37
35
  missing_separating_line(node) do |location|
38
36
  add_offense(
@@ -21,15 +21,13 @@ module RuboCop
21
21
 
22
22
  MSG = 'Add an empty line after the last `let` block.'.freeze
23
23
 
24
- def_node_matcher :let?, Helpers::ALL.block_pattern
25
-
26
24
  def on_block(node)
27
25
  return unless example_group_with_body?(node)
28
26
 
29
27
  latest_let = node.body.child_nodes.select { |child| let?(child) }.last
30
28
 
31
29
  return if latest_let.nil?
32
- return if latest_let.equal?(node.body.children.last)
30
+ return if last_child?(latest_let)
33
31
 
34
32
  missing_separating_line(latest_let) do |location|
35
33
  add_offense(latest_let, location: location)
@@ -38,11 +38,9 @@ module RuboCop
38
38
 
39
39
  MSG = 'Add an empty line after `%<hook>s`.'.freeze
40
40
 
41
- def_node_matcher :hook?, Hooks::ALL.block_pattern
42
-
43
41
  def on_block(node)
44
42
  return unless hook?(node)
45
- return if node.equal?(node.parent.children.last)
43
+ return if last_child?(node)
46
44
 
47
45
  missing_separating_line(node) do |location|
48
46
  add_offense(
@@ -19,11 +19,9 @@ module RuboCop
19
19
 
20
20
  MSG = 'Add empty line after `subject`.'.freeze
21
21
 
22
- def_node_matcher :subject?, Subject::ALL.block_pattern
23
-
24
22
  def on_block(node)
25
23
  return unless subject?(node) && !in_spec_block?(node)
26
- return if node.equal?(node.parent.children.last)
24
+ return if last_child?(node)
27
25
 
28
26
  missing_separating_line(node) do |location|
29
27
  add_offense(node, location: location, message: MSG)
@@ -54,15 +54,14 @@ module RuboCop
54
54
  'have auto-generated description.'.freeze
55
55
  MSG_ADD_DESCRIPTION = 'Add a description.'.freeze
56
56
 
57
- def_node_matcher :example?, Examples::ALL.send_pattern
58
57
  def_node_matcher :example_description, '(send nil? _ $(str $_))'
59
58
 
60
- def on_send(node)
59
+ def on_block(node)
61
60
  return unless example?(node)
62
61
 
63
- check_example_without_description(node)
62
+ check_example_without_description(node.send_node)
64
63
 
65
- example_description(node) do |message_node, message|
64
+ example_description(node.send_node) do |message_node, message|
66
65
  return unless message.to_s.empty?
67
66
 
68
67
  add_offense(message_node, message: MSG_DEFAULT_ARGUMENT)
@@ -22,38 +22,23 @@ module RuboCop
22
22
  # end
23
23
  class ExpectInHook < Cop
24
24
  MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'.freeze
25
- HOOKS = Hooks::ALL.node_pattern_union.freeze
26
25
 
27
- def_node_matcher :hook, <<-PATTERN
28
- (block (send _ $#{HOOKS} ...) _ $!nil?)
29
- PATTERN
30
-
31
- def_node_search :expect, <<-PATTERN
32
- {
33
- #{Expectations::ALL.send_pattern}
34
- #{Expectations::ALL.block_pattern}
35
- }
36
- PATTERN
26
+ def_node_search :expectation, Expectations::ALL.send_pattern
37
27
 
38
28
  def on_block(node)
39
- hook(node) do |hook_name, body|
40
- expect(body) do |expect|
41
- method = send_node(expect)
42
- add_offense(method, location: :selector,
43
- message: message(method, hook_name))
44
- end
29
+ return unless hook?(node)
30
+ return if node.body.nil?
31
+
32
+ expectation(node.body) do |expect|
33
+ add_offense(expect, location: :selector,
34
+ message: message(expect, node))
45
35
  end
46
36
  end
47
37
 
48
38
  private
49
39
 
50
40
  def message(expect, hook)
51
- format(MSG, expect: expect.method_name, hook: hook)
52
- end
53
-
54
- def send_node(node)
55
- return node if node.send_type?
56
- node.children.first
41
+ format(MSG, expect: expect.method_name, hook: hook.method_name)
57
42
  end
58
43
  end
59
44
  end
@@ -18,8 +18,6 @@ module RuboCop
18
18
  MSG = 'Use `expect { ... }.to output(...).to_%<name>s` '\
19
19
  'instead of mutating $%<name>s.'.freeze
20
20
 
21
- def_node_matcher :hook?, Hooks::ALL.block_pattern
22
-
23
21
  def on_gvasgn(node)
24
22
  return unless inside_example_scope?(node)
25
23
 
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module FactoryBot
7
+ # Always declare attribute values as blocks.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # kind [:active, :rejected].sample
12
+ #
13
+ # # good
14
+ # kind { [:active, :rejected].sample }
15
+ #
16
+ # # bad
17
+ # closed_at 1.day.from_now
18
+ #
19
+ # # good
20
+ # closed_at { 1.day.from_now }
21
+ #
22
+ # # bad
23
+ # count 1
24
+ #
25
+ # # good
26
+ # count { 1 }
27
+ class AttributeDefinedStatically < Cop
28
+ MSG = 'Use a block to declare attribute values.'.freeze
29
+
30
+ ATTRIBUTE_DEFINING_METHODS = %i[factory trait transient ignore].freeze
31
+
32
+ UNPROXIED_METHODS = %i[
33
+ __send__
34
+ __id__
35
+ nil?
36
+ send
37
+ object_id
38
+ extend
39
+ instance_eval
40
+ initialize
41
+ block_given?
42
+ raise
43
+ caller
44
+ method
45
+ ].freeze
46
+
47
+ DEFINITION_PROXY_METHODS = %i[
48
+ add_attribute
49
+ after
50
+ association
51
+ before
52
+ callback
53
+ ignore
54
+ initialize_with
55
+ sequence
56
+ skip_create
57
+ to_create
58
+ ].freeze
59
+
60
+ RESERVED_METHODS =
61
+ DEFINITION_PROXY_METHODS +
62
+ UNPROXIED_METHODS +
63
+ ATTRIBUTE_DEFINING_METHODS
64
+
65
+ def_node_matcher :value_matcher, <<-PATTERN
66
+ (send nil? !#reserved_method? $...)
67
+ PATTERN
68
+
69
+ def_node_search :factory_attributes, <<-PATTERN
70
+ (block (send nil? #attribute_defining_method? ...) _ { (begin $...) $(send ...) } )
71
+ PATTERN
72
+
73
+ def on_block(node)
74
+ factory_attributes(node).to_a.flatten.each do |attribute|
75
+ next if proc?(attribute) || association?(attribute)
76
+ add_offense(attribute, location: :expression)
77
+ end
78
+ end
79
+
80
+ def autocorrect(node)
81
+ if node.parenthesized?
82
+ autocorrect_replacing_parens(node)
83
+ else
84
+ autocorrect_without_parens(node)
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def proc?(attribute)
91
+ value_matcher(attribute).to_a.all?(&:block_pass_type?)
92
+ end
93
+
94
+ def association?(attribute)
95
+ argument = attribute.first_argument
96
+ argument.hash_type? && factory_key?(argument)
97
+ end
98
+
99
+ def factory_key?(hash_node)
100
+ hash_node.keys.any? { |key| key.value == :factory }
101
+ end
102
+
103
+ def autocorrect_replacing_parens(node)
104
+ left_braces, right_braces = braces(node)
105
+
106
+ lambda do |corrector|
107
+ corrector.replace(node.location.begin, ' ' + left_braces)
108
+ corrector.replace(node.location.end, right_braces)
109
+ end
110
+ end
111
+
112
+ def autocorrect_without_parens(node)
113
+ left_braces, right_braces = braces(node)
114
+
115
+ lambda do |corrector|
116
+ argument = node.first_argument
117
+ expression = argument.location.expression
118
+ corrector.insert_before(expression, left_braces)
119
+ corrector.insert_after(expression, right_braces)
120
+ end
121
+ end
122
+
123
+ def braces(node)
124
+ if value_hash_without_braces?(node.first_argument)
125
+ ['{ { ', ' } }']
126
+ else
127
+ ['{ ', ' }']
128
+ end
129
+ end
130
+
131
+ def value_hash_without_braces?(node)
132
+ node.hash_type? && !node.braces?
133
+ end
134
+
135
+ def reserved_method?(method_name)
136
+ RESERVED_METHODS.include?(method_name)
137
+ end
138
+
139
+ def attribute_defining_method?(method_name)
140
+ ATTRIBUTE_DEFINING_METHODS.include?(method_name)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -22,8 +22,6 @@ module RuboCop
22
22
  MSG = 'Use `instance_spy` when you check your double '\
23
23
  'with `have_received`.'.freeze
24
24
 
25
- def_node_matcher :example?, Examples::ALL.block_pattern
26
-
27
25
  def_node_search :null_double, <<-PATTERN
28
26
  (lvasgn $_
29
27
  (send
@@ -32,7 +32,7 @@ module RuboCop
32
32
  def on_block(node)
33
33
  each?(node) do |arg, body|
34
34
  if single_expectation?(body, arg) || only_expectations?(body, arg)
35
- add_offense(node.children.first, location: :expression)
35
+ add_offense(node.send_node, location: :expression)
36
36
  end
37
37
  end
38
38
  end
@@ -37,11 +37,6 @@ module RuboCop
37
37
  MSG = 'Declare `subject` above any other `%<offending>s` ' \
38
38
  'declarations.'.freeze
39
39
 
40
- def_node_matcher :subject?, Subject::ALL.block_pattern
41
- def_node_matcher :let?, Helpers::ALL.block_pattern
42
- def_node_matcher :hook?, Hooks::ALL.block_pattern
43
- def_node_matcher :example?, Examples::ALL.block_pattern
44
-
45
40
  def on_block(node)
46
41
  return unless subject?(node) && !in_spec_block?(node)
47
42
 
@@ -88,7 +83,7 @@ module RuboCop
88
83
 
89
84
  def in_spec_block?(node)
90
85
  node.each_ancestor(:block).any? do |ancestor|
91
- Examples::ALL.include?(ancestor.method_name)
86
+ example?(ancestor)
92
87
  end
93
88
  end
94
89
  end
@@ -36,8 +36,6 @@ module RuboCop
36
36
 
37
37
  MSG = 'Move `let` before the examples in the group.'.freeze
38
38
 
39
- def_node_matcher :let?, Helpers::ALL.block_pattern
40
-
41
39
  def_node_matcher :example_or_group?, <<-PATTERN
42
40
  {
43
41
  #{(Examples::ALL + ExampleGroups::ALL).block_pattern}