rubocop-rspec 2.25.0 → 2.27.1

Sign up to get free protection for your applications and to get access to all the features.
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