rubocop-rspec 2.25.0 → 2.27.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/config/default.yml +39 -4
  4. data/lib/rubocop/cop/rspec/around_block.rb +3 -3
  5. data/lib/rubocop/cop/rspec/be.rb +1 -1
  6. data/lib/rubocop/cop/rspec/be_empty.rb +1 -0
  7. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  8. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  9. data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
  10. data/lib/rubocop/cop/rspec/before_after_all.rb +7 -13
  11. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -2
  12. data/lib/rubocop/cop/rspec/change_by_zero.rb +30 -4
  13. data/lib/rubocop/cop/rspec/context_method.rb +2 -2
  14. data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
  15. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  16. data/lib/rubocop/cop/rspec/described_class.rb +33 -11
  17. data/lib/rubocop/cop/rspec/empty_example_group.rb +2 -2
  18. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -2
  19. data/lib/rubocop/cop/rspec/example_without_description.rb +11 -2
  20. data/lib/rubocop/cop/rspec/example_wording.rb +11 -2
  21. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
  22. data/lib/rubocop/cop/rspec/expect_actual.rb +5 -2
  23. data/lib/rubocop/cop/rspec/expect_change.rb +2 -2
  24. data/lib/rubocop/cop/rspec/expect_output.rb +1 -4
  25. data/lib/rubocop/cop/rspec/focus.rb +2 -2
  26. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
  27. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
  28. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -2
  29. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  30. data/lib/rubocop/cop/rspec/implicit_subject.rb +2 -2
  31. data/lib/rubocop/cop/rspec/instance_spy.rb +2 -2
  32. data/lib/rubocop/cop/rspec/instance_variable.rb +2 -2
  33. data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
  34. data/lib/rubocop/cop/rspec/iterated_expectation.rb +3 -3
  35. data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
  36. data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
  37. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -2
  38. data/lib/rubocop/cop/rspec/message_spies.rb +0 -2
  39. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +2 -2
  40. data/lib/rubocop/cop/rspec/multiple_expectations.rb +12 -7
  41. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  42. data/lib/rubocop/cop/rspec/pending_without_reason.rb +1 -1
  43. data/lib/rubocop/cop/rspec/predicate_matcher.rb +6 -6
  44. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -1
  45. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +34 -10
  46. data/lib/rubocop/cop/rspec/rails/http_status.rb +1 -1
  47. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +314 -22
  48. data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
  49. data/lib/rubocop/cop/rspec/receive_messages.rb +1 -1
  50. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
  51. data/lib/rubocop/cop/rspec/remove_const.rb +40 -0
  52. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
  53. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +2 -2
  54. data/lib/rubocop/cop/rspec/repeated_include_example.rb +1 -1
  55. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +124 -0
  56. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  57. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  58. data/lib/rubocop/cop/rspec/shared_examples.rb +66 -20
  59. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +2 -3
  60. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  61. data/lib/rubocop/cop/rspec/subject_stub.rb +4 -4
  62. data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -2
  63. data/lib/rubocop/cop/rspec/variable_definition.rb +2 -2
  64. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  65. data/lib/rubocop/cop/rspec/void_expect.rb +2 -2
  66. data/lib/rubocop/cop/rspec_cops.rb +4 -0
  67. data/lib/rubocop/rspec/language.rb +8 -8
  68. data/lib/rubocop/rspec/version.rb +1 -1
  69. data/lib/rubocop/rspec/wording.rb +8 -0
  70. metadata +10 -6
@@ -22,6 +22,9 @@ module RuboCop
22
22
  # it 'should find nothing' do
23
23
  # end
24
24
  #
25
+ # it 'will find nothing' do
26
+ # end
27
+ #
25
28
  # # good
26
29
  # it 'finds nothing' do
27
30
  # end
@@ -47,25 +50,30 @@ module RuboCop
47
50
  extend AutoCorrector
48
51
 
49
52
  MSG_SHOULD = 'Do not use should when describing your tests.'
53
+ MSG_WILL = 'Do not use the future tense when describing your tests.'
50
54
  MSG_IT = "Do not repeat 'it' when describing your tests."
51
55
  MSG_INSUFFICIENT_DESCRIPTION = 'Your example description is ' \
52
56
  'insufficient.'
53
57
 
54
58
  SHOULD_PREFIX = /\Ashould(?:n't)?\b/i.freeze
59
+ WILL_PREFIX = /\A(?:will|won't)\b/i.freeze
55
60
  IT_PREFIX = /\Ait /i.freeze
56
61
 
57
62
  # @!method it_description(node)
58
- def_node_matcher :it_description, <<-PATTERN
63
+ def_node_matcher :it_description, <<~PATTERN
59
64
  (block (send _ :it ${
60
65
  (str $_)
61
66
  (dstr (str $_ ) ...)
62
67
  } ...) ...)
63
68
  PATTERN
64
69
 
70
+ # rubocop:disable Metrics/MethodLength
65
71
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
66
72
  it_description(node) do |description_node, message|
67
73
  if message.match?(SHOULD_PREFIX)
68
74
  add_wording_offense(description_node, MSG_SHOULD)
75
+ elsif message.match?(WILL_PREFIX)
76
+ add_wording_offense(description_node, MSG_WILL)
69
77
  elsif message.match?(IT_PREFIX)
70
78
  add_wording_offense(description_node, MSG_IT)
71
79
  elsif insufficient_docstring?(description_node)
@@ -74,6 +82,7 @@ module RuboCop
74
82
  end
75
83
  end
76
84
  end
85
+ # rubocop:enable Metrics/MethodLength
77
86
 
78
87
  private
79
88
 
@@ -100,7 +109,7 @@ module RuboCop
100
109
  def replacement_text(node)
101
110
  text = text(node)
102
111
 
103
- if text.match?(SHOULD_PREFIX)
112
+ if text.match?(SHOULD_PREFIX) || text.match?(WILL_PREFIX)
104
113
  RuboCop::RSpec::Wording.new(
105
114
  text,
106
115
  ignore: ignored_words,
@@ -29,7 +29,7 @@ module RuboCop
29
29
  MSG = 'Excessive whitespace.'
30
30
 
31
31
  # @!method example_description(node)
32
- def_node_matcher :example_description, <<-PATTERN
32
+ def_node_matcher :example_description, <<~PATTERN
33
33
  (send _ {#Examples.all #ExampleGroups.all} ${
34
34
  $str
35
35
  $(dstr ({str dstr `sym} ...) ...)
@@ -50,7 +50,8 @@ module RuboCop
50
50
  regexp
51
51
  ].freeze
52
52
 
53
- SUPPORTED_MATCHERS = %i[eq eql equal be].freeze
53
+ SKIPPED_MATCHERS = %i[route_to be_routable].freeze
54
+ CORRECTABLE_MATCHERS = %i[eq eql equal be].freeze
54
55
 
55
56
  # @!method expect_literal(node)
56
57
  def_node_matcher :expect_literal, <<~PATTERN
@@ -66,8 +67,10 @@ module RuboCop
66
67
 
67
68
  def on_send(node)
68
69
  expect_literal(node) do |actual, matcher, expected|
70
+ next if SKIPPED_MATCHERS.include?(matcher)
71
+
69
72
  add_offense(actual.source_range) do |corrector|
70
- next unless SUPPORTED_MATCHERS.include?(matcher)
73
+ next unless CORRECTABLE_MATCHERS.include?(matcher)
71
74
  next if literal?(expected)
72
75
 
73
76
  swap(corrector, actual, expected)
@@ -38,12 +38,12 @@ module RuboCop
38
38
  RESTRICT_ON_SEND = %i[change].freeze
39
39
 
40
40
  # @!method expect_change_with_arguments(node)
41
- def_node_matcher :expect_change_with_arguments, <<-PATTERN
41
+ def_node_matcher :expect_change_with_arguments, <<~PATTERN
42
42
  (send nil? :change $_ ({sym str} $_))
43
43
  PATTERN
44
44
 
45
45
  # @!method expect_change_with_block(node)
46
- def_node_matcher :expect_change_with_block, <<-PATTERN
46
+ def_node_matcher :expect_change_with_block, <<~PATTERN
47
47
  (block
48
48
  (send nil? :change)
49
49
  (args)
@@ -22,10 +22,7 @@ module RuboCop
22
22
  def on_gvasgn(node)
23
23
  return unless inside_example_scope?(node)
24
24
 
25
- # rubocop:disable InternalAffairs/NodeDestructuring
26
- variable_name, _rhs = *node
27
- # rubocop:enable InternalAffairs/NodeDestructuring
28
- name = variable_name[1..]
25
+ name = node.name[1..]
29
26
  return unless name.eql?('stdout') || name.eql?('stderr')
30
27
 
31
28
  add_offense(node.loc.name, message: format(MSG, name: name))
@@ -56,7 +56,7 @@ module RuboCop
56
56
  MSG = 'Focused spec found.'
57
57
 
58
58
  # @!method focusable_selector?(node)
59
- def_node_matcher :focusable_selector?, <<-PATTERN
59
+ def_node_matcher :focusable_selector?, <<~PATTERN
60
60
  {
61
61
  #ExampleGroups.regular
62
62
  #ExampleGroups.skipped
@@ -68,7 +68,7 @@ module RuboCop
68
68
  PATTERN
69
69
 
70
70
  # @!method metadata(node)
71
- def_node_matcher :metadata, <<-PATTERN
71
+ def_node_matcher :metadata, <<~PATTERN
72
72
  {(send #rspec? #focusable_selector? <$(sym :focus) ...>)
73
73
  (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))}
74
74
  PATTERN
@@ -66,12 +66,12 @@ module RuboCop
66
66
  EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'
67
67
 
68
68
  # @!method scoped_hook(node)
69
- def_node_matcher :scoped_hook, <<-PATTERN
69
+ def_node_matcher :scoped_hook, <<~PATTERN
70
70
  ({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
71
71
  PATTERN
72
72
 
73
73
  # @!method unscoped_hook(node)
74
- def_node_matcher :unscoped_hook, <<-PATTERN
74
+ def_node_matcher :unscoped_hook, <<~PATTERN
75
75
  ({block numblock} $(send _ #Hooks.all) ...)
76
76
  PATTERN
77
77
 
@@ -28,7 +28,7 @@ module RuboCop
28
28
  MSG = 'Move `%<hook>s` above the examples in the group.'
29
29
 
30
30
  # @!method example_or_group?(node)
31
- def_node_matcher :example_or_group?, <<-PATTERN
31
+ def_node_matcher :example_or_group?, <<~PATTERN
32
32
  {
33
33
  ({block numblock} {
34
34
  (send #rspec? #ExampleGroups.all ...)
@@ -22,7 +22,7 @@ module RuboCop
22
22
  RESTRICT_ON_SEND = %i[is_expected should should_not].freeze
23
23
 
24
24
  # @!method lambda?(node)
25
- def_node_matcher :lambda?, <<-PATTERN
25
+ def_node_matcher :lambda?, <<~PATTERN
26
26
  {
27
27
  (send (const nil? :Proc) :new)
28
28
  (send nil? {:proc :lambda})
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def_node_matcher :lambda_subject?, '(block #lambda? ...)'
34
34
 
35
35
  # @!method implicit_expect(node)
36
- def_node_matcher :implicit_expect, <<-PATTERN
36
+ def_node_matcher :implicit_expect, <<~PATTERN
37
37
  $(send nil? {:is_expected :should :should_not} ...)
38
38
  PATTERN
39
39
 
@@ -31,7 +31,7 @@ module RuboCop
31
31
  RESTRICT_ON_SEND = Runners.all + %i[should should_not]
32
32
 
33
33
  # @!method implicit_expect(node)
34
- def_node_matcher :implicit_expect, <<-PATTERN
34
+ def_node_matcher :implicit_expect, <<~PATTERN
35
35
  {
36
36
  (send nil? ${:should :should_not} ...)
37
37
  (send (send nil? $:is_expected) #Runners.all ...)
@@ -78,12 +78,12 @@ module RuboCop
78
78
  ].freeze
79
79
 
80
80
  # @!method explicit_unnamed_subject?(node)
81
- def_node_matcher :explicit_unnamed_subject?, <<-PATTERN
81
+ def_node_matcher :explicit_unnamed_subject?, <<~PATTERN
82
82
  (send nil? :expect (send nil? :subject))
83
83
  PATTERN
84
84
 
85
85
  # @!method implicit_subject?(node)
86
- def_node_matcher :implicit_subject?, <<-PATTERN
86
+ def_node_matcher :implicit_subject?, <<~PATTERN
87
87
  (send nil? {:should :should_not :is_expected} ...)
88
88
  PATTERN
89
89
 
@@ -25,7 +25,7 @@ module RuboCop
25
25
  'with `have_received`.'
26
26
 
27
27
  # @!method null_double(node)
28
- def_node_search :null_double, <<-PATTERN
28
+ def_node_search :null_double, <<~PATTERN
29
29
  (lvasgn $_
30
30
  (send
31
31
  $(send nil? :instance_double
@@ -33,7 +33,7 @@ module RuboCop
33
33
  PATTERN
34
34
 
35
35
  # @!method have_received_usage(node)
36
- def_node_search :have_received_usage, <<-PATTERN
36
+ def_node_search :have_received_usage, <<~PATTERN
37
37
  (send
38
38
  (send nil? :expect
39
39
  (lvar $_)) :to
@@ -52,12 +52,12 @@ module RuboCop
52
52
  'a method call, or a local variable (if possible).'
53
53
 
54
54
  # @!method dynamic_class?(node)
55
- def_node_matcher :dynamic_class?, <<-PATTERN
55
+ def_node_matcher :dynamic_class?, <<~PATTERN
56
56
  (block (send (const nil? :Class) :new ...) ...)
57
57
  PATTERN
58
58
 
59
59
  # @!method custom_matcher?(node)
60
- def_node_matcher :custom_matcher?, <<-PATTERN
60
+ def_node_matcher :custom_matcher?, <<~PATTERN
61
61
  (block {
62
62
  (send nil? :matcher sym)
63
63
  (send (const (const nil? :RSpec) :Matchers) :define sym)
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Check for `specify` with `is_expected` and one-liner expectations.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # specify { is_expected.to be_truthy }
11
+ #
12
+ # # good
13
+ # it { is_expected.to be_truthy }
14
+ #
15
+ # # good
16
+ # specify do
17
+ # # ...
18
+ # end
19
+ # specify { expect(sqrt(4)).to eq(2) }
20
+ #
21
+ class IsExpectedSpecify < Base
22
+ extend AutoCorrector
23
+
24
+ RESTRICT_ON_SEND = %i[specify].freeze
25
+ IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze
26
+ MSG = 'Use `it` instead of `specify`.'
27
+
28
+ # @!method offense?(node)
29
+ def_node_matcher :offense?, <<~PATTERN
30
+ (block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...))
31
+ PATTERN
32
+
33
+ def on_send(node)
34
+ block_node = node.parent
35
+ return unless block_node&.single_line? && offense?(block_node)
36
+
37
+ selector = node.loc.selector
38
+ add_offense(selector) do |corrector|
39
+ corrector.replace(selector, 'it')
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  'of iterating over an array.'
22
22
 
23
23
  # @!method each?(node)
24
- def_node_matcher :each?, <<-PATTERN
24
+ def_node_matcher :each?, <<~PATTERN
25
25
  (block
26
26
  (send ... :each)
27
27
  (args (arg $_))
@@ -30,14 +30,14 @@ module RuboCop
30
30
  PATTERN
31
31
 
32
32
  # @!method each_numblock?(node)
33
- def_node_matcher :each_numblock?, <<-PATTERN
33
+ def_node_matcher :each_numblock?, <<~PATTERN
34
34
  (numblock
35
35
  (send ... :each) _ $(...)
36
36
  )
37
37
  PATTERN
38
38
 
39
39
  # @!method expectation?(node)
40
- def_node_matcher :expectation?, <<-PATTERN
40
+ def_node_matcher :expectation?, <<~PATTERN
41
41
  (send (send nil? :expect (lvar %)) :to ...)
42
42
  PATTERN
43
43
 
@@ -36,7 +36,7 @@ module RuboCop
36
36
  MSG = 'Move `let` before the examples in the group.'
37
37
 
38
38
  # @!method example_or_group?(node)
39
- def_node_matcher :example_or_group?, <<-PATTERN
39
+ def_node_matcher :example_or_group?, <<~PATTERN
40
40
  {
41
41
  (block (send nil? {#ExampleGroups.all #Examples.all} ...) ...)
42
42
  (send nil? #Includes.examples ...)
@@ -37,7 +37,7 @@ module RuboCop
37
37
  PATTERN
38
38
 
39
39
  # @!method let_bang(node)
40
- def_node_matcher :let_bang, <<-PATTERN
40
+ def_node_matcher :let_bang, <<~PATTERN
41
41
  {
42
42
  (block $(send nil? :let! {(sym $_) (str $_)}) ...)
43
43
  $(send nil? :let! {(sym $_) (str $_)} block_pass)
@@ -29,11 +29,10 @@ module RuboCop
29
29
 
30
30
  MSG = 'Prefer `%<style>s` for setting message expectations.'
31
31
 
32
- SUPPORTED_STYLES = %w[allow expect].freeze
33
32
  RESTRICT_ON_SEND = %i[to].freeze
34
33
 
35
34
  # @!method message_expectation(node)
36
- def_node_matcher :message_expectation, <<-PATTERN
35
+ def_node_matcher :message_expectation, <<~PATTERN
37
36
  (send $(send nil? {:expect :allow} ...) :to #receive_message?)
38
37
  PATTERN
39
38
 
@@ -39,8 +39,6 @@ module RuboCop
39
39
  'expectations. Setup `%<source>s` as a spy using ' \
40
40
  '`allow` or `instance_spy`.'
41
41
 
42
- SUPPORTED_STYLES = %w[have_received receive].freeze
43
-
44
42
  RESTRICT_ON_SEND = Runners.all
45
43
 
46
44
  # @!method message_expectation(node)
@@ -8,7 +8,7 @@ module RuboCop
8
8
  extend RuboCop::NodePattern::Macros
9
9
 
10
10
  # @!method skipped_in_metadata?(node)
11
- def_node_matcher :skipped_in_metadata?, <<-PATTERN
11
+ def_node_matcher :skipped_in_metadata?, <<~PATTERN
12
12
  {
13
13
  (send _ _ <(sym {:skip :pending}) ...>)
14
14
  (send _ _ ... (hash <(pair (sym {:skip :pending}) { true str dstr }) ...>))
@@ -30,7 +30,7 @@ module RuboCop
30
30
  #
31
31
  # @param node [RuboCop::AST::Node]
32
32
  # @return [Array<RuboCop::AST::Node>] matching nodes
33
- def_node_matcher :skip_or_pending_inside_block?, <<-PATTERN
33
+ def_node_matcher :skip_or_pending_inside_block?, <<~PATTERN
34
34
  (block <(send nil? {:skip :pending} ...) ...>)
35
35
  PATTERN
36
36
  end
@@ -48,12 +48,17 @@ module RuboCop
48
48
  # end
49
49
  # end
50
50
  #
51
- # @example configuration
52
- # # .rubocop.yml
53
- # # RSpec/MultipleExpectations:
54
- # # Max: 2
51
+ # @example `Max: 1` (default)
52
+ # # bad
53
+ # describe UserCreator do
54
+ # it 'builds a user' do
55
+ # expect(user.name).to eq("John")
56
+ # expect(user.age).to eq(22)
57
+ # end
58
+ # end
55
59
  #
56
- # # not flagged by rubocop
60
+ # @example `Max: 2`
61
+ # # good
57
62
  # describe UserCreator do
58
63
  # it 'builds a user' do
59
64
  # expect(user.name).to eq("John")
@@ -70,7 +75,7 @@ module RuboCop
70
75
  TRUE = ->(node) { node.true_type? }
71
76
 
72
77
  # @!method aggregate_failures?(node)
73
- def_node_matcher :aggregate_failures?, <<-PATTERN
78
+ def_node_matcher :aggregate_failures?, <<~PATTERN
74
79
  (block {
75
80
  (send _ _ <(sym :aggregate_failures) ...>)
76
81
  (send _ _ ... (hash <(pair (sym :aggregate_failures) %1) ...>))
@@ -81,7 +86,7 @@ module RuboCop
81
86
  def_node_matcher :expect?, '(send nil? #Expectations.all ...)'
82
87
 
83
88
  # @!method aggregate_failures_block?(node)
84
- def_node_matcher :aggregate_failures_block?, <<-PATTERN
89
+ def_node_matcher :aggregate_failures_block?, <<~PATTERN
85
90
  (block (send nil? :aggregate_failures ...) ...)
86
91
  PATTERN
87
92
 
@@ -67,7 +67,7 @@ module RuboCop
67
67
  private
68
68
 
69
69
  def skipped?(node)
70
- skippable?(node) && skipped_in_metadata?(node) ||
70
+ (skippable?(node) && skipped_in_metadata?(node)) ||
71
71
  skipped_regular_example_without_body?(node)
72
72
  end
73
73
 
@@ -103,7 +103,7 @@ module RuboCop
103
103
  on_pending_by_metadata(node)
104
104
  return unless (parent = parent_node(node))
105
105
 
106
- if example_group?(parent) || block_node_example_group?(node)
106
+ if spec_group?(parent) || block_node_example_group?(node)
107
107
  on_skipped_by_example_method(node)
108
108
  on_skipped_by_example_group_method(node)
109
109
  elsif example?(parent)
@@ -26,7 +26,7 @@ module RuboCop
26
26
  end
27
27
 
28
28
  # @!method predicate_in_actual?(node)
29
- def_node_matcher :predicate_in_actual?, <<-PATTERN
29
+ def_node_matcher :predicate_in_actual?, <<~PATTERN
30
30
  (send
31
31
  (send nil? :expect {
32
32
  (block $(send !nil? #predicate? ...) ...)
@@ -36,12 +36,12 @@ module RuboCop
36
36
  PATTERN
37
37
 
38
38
  # @!method be_bool?(node)
39
- def_node_matcher :be_bool?, <<-PATTERN
39
+ def_node_matcher :be_bool?, <<~PATTERN
40
40
  (send nil? {:be :eq :eql :equal} {true false})
41
41
  PATTERN
42
42
 
43
43
  # @!method be_boolthy?(node)
44
- def_node_matcher :be_boolthy?, <<-PATTERN
44
+ def_node_matcher :be_boolthy?, <<~PATTERN
45
45
  (send nil? {:be_truthy :be_falsey :be_falsy :a_truthy_value :a_falsey_value :a_falsy_value})
46
46
  PATTERN
47
47
 
@@ -179,7 +179,7 @@ module RuboCop
179
179
  end
180
180
 
181
181
  # @!method predicate_matcher?(node)
182
- def_node_matcher :predicate_matcher?, <<-PATTERN
182
+ def_node_matcher :predicate_matcher?, <<~PATTERN
183
183
  (send
184
184
  (send nil? :expect $!nil?)
185
185
  #Runners.all
@@ -188,7 +188,7 @@ module RuboCop
188
188
  PATTERN
189
189
 
190
190
  # @!method predicate_matcher_block?(node)
191
- def_node_matcher :predicate_matcher_block?, <<-PATTERN
191
+ def_node_matcher :predicate_matcher_block?, <<~PATTERN
192
192
  (block
193
193
  (send
194
194
  (send nil? :expect $!nil?)
@@ -202,7 +202,7 @@ module RuboCop
202
202
 
203
203
  return false if allowed_explicit_matchers.include?(name)
204
204
 
205
- name.start_with?('be_', 'have_') && !name.end_with?('?') ||
205
+ (name.start_with?('be_', 'have_') && !name.end_with?('?')) ||
206
206
  %w[include respond_to].include?(name)
207
207
  end
208
208
 
@@ -23,7 +23,7 @@ module RuboCop
23
23
  MSG = 'Use `before` instead of `setup`.'
24
24
 
25
25
  # @!method setup_call(node)
26
- def_node_matcher :setup_call, <<-PATTERN
26
+ def_node_matcher :setup_call, <<~PATTERN
27
27
  (block
28
28
  $(send nil? :setup)
29
29
  (args) _)
@@ -6,48 +6,72 @@ module RuboCop
6
6
  module Rails
7
7
  # Checks that tests use `have_http_status` instead of equality matchers.
8
8
  #
9
- # @example
9
+ # @example ResponseMethods: ['response', 'last_response'] (default)
10
10
  # # bad
11
11
  # expect(response.status).to be(200)
12
- # expect(response.code).to eq("200")
12
+ # expect(last_response.code).to eq("200")
13
13
  #
14
14
  # # good
15
15
  # expect(response).to have_http_status(200)
16
+ # expect(last_response).to have_http_status(200)
17
+ #
18
+ # @example ResponseMethods: ['foo_response']
19
+ # # bad
20
+ # expect(foo_response.status).to be(200)
21
+ #
22
+ # # good
23
+ # expect(foo_response).to have_http_status(200)
24
+ #
25
+ # # also good
26
+ # expect(response).to have_http_status(200)
27
+ # expect(last_response).to have_http_status(200)
16
28
  #
17
29
  class HaveHttpStatus < ::RuboCop::Cop::Base
18
30
  extend AutoCorrector
19
31
 
20
32
  MSG =
21
- 'Prefer `expect(response).%<to>s have_http_status(%<status>s)` ' \
22
- 'over `%<bad_code>s`.'
33
+ 'Prefer `expect(%<response>s).%<to>s ' \
34
+ 'have_http_status(%<status>s)` over `%<bad_code>s`.'
23
35
 
24
36
  RUNNERS = %i[to to_not not_to].to_set
25
37
  RESTRICT_ON_SEND = RUNNERS
26
38
 
27
39
  # @!method match_status(node)
28
- def_node_matcher :match_status, <<-PATTERN
40
+ def_node_matcher :match_status, <<~PATTERN
29
41
  (send
30
42
  (send nil? :expect
31
- $(send (send nil? :response) {:status :code})
43
+ $(send $(send nil? #response_methods?) {:status :code})
32
44
  )
33
45
  $RUNNERS
34
46
  $(send nil? {:be :eq :eql :equal} ({int str} $_))
35
47
  )
36
48
  PATTERN
37
49
 
38
- def on_send(node)
39
- match_status(node) do |response_status, to, match, status|
50
+ def on_send(node) # rubocop:disable Metrics/MethodLength
51
+ match_status(node) do
52
+ |response_status, response_method, to, match, status|
40
53
  return unless status.to_s.match?(/\A\d+\z/)
41
54
 
42
- message = format(MSG, to: to, status: status,
55
+ message = format(MSG, response: response_method.method_name,
56
+ to: to, status: status,
43
57
  bad_code: node.source)
44
58
  add_offense(node, message: message) do |corrector|
45
- corrector.replace(response_status, 'response')
59
+ corrector.replace(response_status, response_method.method_name)
46
60
  corrector.replace(match.loc.selector, 'have_http_status')
47
61
  corrector.replace(match.first_argument, status.to_s)
48
62
  end
49
63
  end
50
64
  end
65
+
66
+ private
67
+
68
+ def response_methods?(name)
69
+ response_methods.include?(name.to_s)
70
+ end
71
+
72
+ def response_methods
73
+ cop_config.fetch('ResponseMethods', [])
74
+ end
51
75
  end
52
76
  end
53
77
  end
@@ -60,7 +60,7 @@ module RuboCop
60
60
  RESTRICT_ON_SEND = %i[have_http_status].freeze
61
61
 
62
62
  # @!method http_status(node)
63
- def_node_matcher :http_status, <<-PATTERN
63
+ def_node_matcher :http_status, <<~PATTERN
64
64
  (send nil? :have_http_status ${int sym str})
65
65
  PATTERN
66
66