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