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
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks that right braces for adjacent single line lets are aligned.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # let(:foobar) { blahblah }
12
+ # let(:baz) { bar }
13
+ # let(:a) { b }
14
+ #
15
+ # # good
16
+ # let(:foobar) { blahblah }
17
+ # let(:baz) { bar }
18
+ # let(:a) { b }
19
+ #
20
+ class AlignRightLetBrace < Cop
21
+ MSG = 'Align right let brace'.freeze
22
+
23
+ def self.autocorrect_incompatible_with
24
+ [Layout::ExtraSpacing]
25
+ end
26
+
27
+ def investigate(_)
28
+ token_aligner.offending_tokens.each do |let|
29
+ add_offense(let, :end)
30
+ end
31
+ end
32
+
33
+ def autocorrect(let)
34
+ lambda do |corrector|
35
+ corrector.insert_before(
36
+ let.loc.end,
37
+ token_aligner.indent_for(let)
38
+ )
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def token_aligner
45
+ @token_aligner ||=
46
+ RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  # end
22
22
  # end
23
23
  class AnyInstance < Cop
24
- MSG = 'Avoid stubbing using `%{method}`.'.freeze
24
+ MSG = 'Avoid stubbing using `%<method>s`.'.freeze
25
25
 
26
26
  def_node_matcher :disallowed_stub, <<-PATTERN
27
27
  (send _ ${:any_instance :allow_any_instance_of :expect_any_instance_of} ...)
@@ -5,9 +5,11 @@ module RuboCop
5
5
  # Clone of the the normal RuboCop::Cop::Cop class so we can rewrite
6
6
  # the inherited method without breaking functionality
7
7
  class WorkaroundCop
8
- # Overwrite the cop inherited method to be a noop. Our RSpec::Cop
8
+ # Remove the cop inherited method to be a noop. Our RSpec::Cop
9
9
  # class will invoke the inherited hook instead
10
- def self.inherited(*); end
10
+ class << self
11
+ remove_method :inherited
12
+ end
11
13
 
12
14
  # Special case `Module#<` so that the rspec support rubocop exports
13
15
  # is compatible with our subclass
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Do not use `expect` in hooks such as `before`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # before do
11
+ # expect(something).to eq 'foo'
12
+ # end
13
+ #
14
+ # # bad
15
+ # after do
16
+ # expect_any_instance_of(Something).to receive(:foo)
17
+ # end
18
+ #
19
+ # # good
20
+ # it do
21
+ # expect(something).to eq 'foo'
22
+ # end
23
+ class ExpectInHook < Cop
24
+ MSG = 'Do not use `%<expect>s` in `%<hook>s` hook'.freeze
25
+ HOOKS = Hooks::ALL.node_pattern_union.freeze
26
+
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
37
+
38
+ 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, :selector,
43
+ message(method, hook_name))
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ 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
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module FactoryGirl
7
+ # Prefer declaring dynamic attribute values in a block.
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
+ # # good
23
+ # kind :static
24
+ #
25
+ # # good
26
+ # comments_count 0
27
+ #
28
+ # # good
29
+ # type User::MAGIC
30
+ class DynamicAttributeDefinedStatically < Cop
31
+ MSG = 'Use a block to set a dynamic value to an attribute.'.freeze
32
+
33
+ def_node_matcher :dynamic_defined_statically?, <<-PATTERN
34
+ (send nil _ (send ... ))
35
+ PATTERN
36
+
37
+ def_node_search :factory_attributes, <<-PATTERN
38
+ (block (send nil {:factory :trait} ...) _ { (begin $...) $(send ...) } )
39
+ PATTERN
40
+
41
+ def on_block(node)
42
+ return if node.method_name == :trait
43
+ factory_attributes(node).to_a.flatten.each do |attribute|
44
+ if dynamic_defined_statically?(attribute)
45
+ add_offense(attribute, :expression)
46
+ end
47
+ end
48
+ end
49
+
50
+ def autocorrect(node)
51
+ if method_uses_parens?(node.location)
52
+ autocorrect_replacing_parens(node)
53
+ else
54
+ autocorrect_without_parens(node)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def method_uses_parens?(location)
61
+ return false unless location.begin && location.end
62
+ location.begin.source == '(' && location.end.source == ')'
63
+ end
64
+
65
+ def autocorrect_replacing_parens(node)
66
+ lambda do |corrector|
67
+ corrector.replace(node.location.begin, ' { ')
68
+ corrector.replace(node.location.end, ' }')
69
+ end
70
+ end
71
+
72
+ def autocorrect_without_parens(node)
73
+ lambda do |corrector|
74
+ arguments = node.descendants.first
75
+ expression = arguments.location.expression
76
+ corrector.insert_before(expression, '{ ')
77
+ corrector.insert_after(expression, ' }')
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -66,10 +66,10 @@ module RuboCop
66
66
  HOOKS = Hooks::ALL.node_pattern_union.freeze
67
67
 
68
68
  def_node_matcher :scoped_hook, <<-PATTERN
69
- (block $(send nil #{HOOKS} (sym ${:each :example})) ...)
69
+ (block $(send _ #{HOOKS} (sym ${:each :example})) ...)
70
70
  PATTERN
71
71
 
72
- def_node_matcher :unscoped_hook, "(block $(send nil #{HOOKS}) ...)"
72
+ def_node_matcher :unscoped_hook, "(block $(send _ #{HOOKS}) ...)"
73
73
 
74
74
  def on_block(node)
75
75
  hook(node) do |method_send, scope_name|
@@ -82,11 +82,10 @@ module RuboCop
82
82
  end
83
83
 
84
84
  def autocorrect(node)
85
- scope = "(#{style.inspect})" unless implicit_style?
86
- hook = "#{node.method_name}#{scope}"
85
+ scope = implicit_style? ? '' : "(#{style.inspect})"
87
86
 
88
87
  lambda do |corrector|
89
- corrector.replace(node.loc.expression, hook)
88
+ corrector.replace(argument_range(node), scope)
90
89
  end
91
90
  end
92
91
 
@@ -114,6 +113,13 @@ module RuboCop
114
113
  def hook(node, &block)
115
114
  scoped_hook(node, &block) || unscoped_hook(node, &block)
116
115
  end
116
+
117
+ def argument_range(send_node)
118
+ range_between(
119
+ send_node.loc.selector.end_pos,
120
+ send_node.loc.expression.end_pos
121
+ )
122
+ end
117
123
  end
118
124
  end
119
125
  end
@@ -0,0 +1,42 @@
1
+ module RuboCop
2
+ module Cop
3
+ module RSpec
4
+ # Checks invalid usage for predicate matcher.
5
+ #
6
+ # Predicate matcher does not need a question.
7
+ # This cop checks an unnecessary question in predicate matcher.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # expect(foo).to be_something?
13
+ #
14
+ # # good
15
+ # expect(foo).to be_something
16
+ class InvalidPredicateMatcher < Cop
17
+ MSG = 'Omit `?` from `%<matcher>s`.'.freeze
18
+
19
+ def_node_matcher :invalid_predicate_matcher?, <<-PATTERN
20
+ (send (send nil :expect ...) {:to :not_to :to_not} $(send nil #predicate?))
21
+ PATTERN
22
+
23
+ def on_send(node)
24
+ invalid_predicate_matcher?(node) do |predicate|
25
+ add_offense(predicate, :expression)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def predicate?(name)
32
+ name = name.to_s
33
+ name.start_with?('be_', 'have_') && name.end_with?('?')
34
+ end
35
+
36
+ def message(predicate)
37
+ format(MSG, matcher: predicate.method_name)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks for `let` definitions that come after an example.
7
+ #
8
+ # @example
9
+ # # Bad
10
+ # let(:foo) { bar }
11
+ #
12
+ # it 'checks what foo does' do
13
+ # expect(foo).to be
14
+ # end
15
+ #
16
+ # let(:some) { other }
17
+ #
18
+ # it 'checks what some does' do
19
+ # expect(some).to be
20
+ # end
21
+ #
22
+ # # Good
23
+ # let(:foo) { bar }
24
+ # let(:some) { other }
25
+ #
26
+ # it 'checks what foo does' do
27
+ # expect(foo).to be
28
+ # end
29
+ #
30
+ # it 'checks what some does' do
31
+ # expect(some).to be
32
+ # end
33
+ class LetBeforeExamples < Cop
34
+ MSG = 'Move `let` before the examples in the group.'.freeze
35
+
36
+ def_node_matcher :let?, '(block (send nil {:let :let!} ...) ...)'
37
+ def_node_matcher :example_or_group?, <<-PATTERN
38
+ {
39
+ #{(Examples::ALL + ExampleGroups::ALL).block_pattern}
40
+ #{Includes::EXAMPLES.send_pattern}
41
+ }
42
+ PATTERN
43
+
44
+ def on_block(node)
45
+ return unless example_group_with_body?(node)
46
+
47
+ _describe, _args, body = *node
48
+
49
+ check_let_declarations(body)
50
+ end
51
+
52
+ def check_let_declarations(node)
53
+ example_found = false
54
+
55
+ node.each_child_node do |child|
56
+ if let?(child)
57
+ add_offense(child, :expression) if example_found
58
+ elsif example_or_group?(child)
59
+ example_found = true
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -48,14 +48,14 @@ module RuboCop
48
48
  class MultipleExpectations < Cop
49
49
  include ConfigurableMax
50
50
 
51
- MSG = 'Example has too many expectations [%{total}/%{max}].'.freeze
51
+ MSG = 'Example has too many expectations [%<total>d/%<max>d].'.freeze
52
52
 
53
53
  def_node_search :with_aggregated_failures?, '(sym :aggregate_failures)'
54
54
  def_node_search :disabled_aggregated_failures?, <<-PATTERN
55
55
  (pair (sym :aggregate_failures) (false))
56
56
  PATTERN
57
57
 
58
- def_node_matcher :expect?, '(send _ :expect ...)'
58
+ def_node_matcher :expect?, Expectations::ALL.send_pattern
59
59
  def_node_matcher :aggregate_failures?, <<-PATTERN
60
60
  (block (send _ :aggregate_failures ...) ...)
61
61
  PATTERN
@@ -84,14 +84,12 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def find_expectation(node, &block)
87
- return unless node.is_a?(Parser::AST::Node)
88
-
89
87
  yield if expect?(node) || aggregate_failures?(node)
90
88
 
91
89
  # do not search inside of aggregate_failures block
92
90
  return if aggregate_failures?(node)
93
91
 
94
- node.children.each do |child|
92
+ node.each_child_node do |child|
95
93
  find_expectation(child, &block)
96
94
  end
97
95
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks if an example group defines `subject` multiple times.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # describe Foo do
12
+ # subject(:user) { User.new }
13
+ # subject(:post) { Post.new }
14
+ # end
15
+ #
16
+ # # good
17
+ # describe Foo do
18
+ # let(:user) { User.new }
19
+ # subject(:post) { Post.new }
20
+ # end
21
+ #
22
+ # The autocorrect behavior for this cop depends on the type of
23
+ # duplication:
24
+ #
25
+ # - If multiple named subjects are defined then this probably indicates
26
+ # that the overwritten subjects (all subjects except the last
27
+ # definition) are effectively being used to define helpers. In this
28
+ # case they are replaced with `let`.
29
+ #
30
+ # - If multiple unnamed subjects are defined though then this can *only*
31
+ # be dead code and we remove the overwritten subject definitions.
32
+ #
33
+ # - If subjects are defined with `subject!` then we don't autocorrect.
34
+ # This is enough of an edge case that people can just move this to
35
+ # a `before` hook on their own
36
+ class MultipleSubjects < Cop
37
+ MSG = 'Do not set more than one subject per example group'.freeze
38
+
39
+ def_node_matcher :named_subject?, <<-PATTERN
40
+ (block (send nil :subject $sym) args ...)
41
+ PATTERN
42
+
43
+ def on_block(node)
44
+ return unless example_group?(node)
45
+
46
+ subjects = RuboCop::RSpec::ExampleGroup.new(node).subjects
47
+
48
+ subjects[0...-1].each do |subject|
49
+ add_offense(subject, :expression)
50
+ end
51
+ end
52
+
53
+ def autocorrect(node)
54
+ return unless node.method_name.equal?(:subject) # Ignore `subject!`
55
+
56
+ if named_subject?(node)
57
+ rename_autocorrect(node)
58
+ else
59
+ remove_autocorrect(node)
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def rename_autocorrect(node)
66
+ lambda do |corrector|
67
+ send_node, _args, _body = *node
68
+ corrector.replace(send_node.loc.selector, 'let')
69
+ end
70
+ end
71
+
72
+ def remove_autocorrect(node)
73
+ lambda do |corrector|
74
+ corrector.remove(node.loc.expression)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end