Skip to content

Commit a5d367b

Browse files
authored
Merge pull request #1801 from rubocop/1760
Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`
2 parents 6c2b73c + 9a43467 commit a5d367b

File tree

5 files changed

+101
-11
lines changed

5 files changed

+101
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- Support correcting `assert_not_equal` and `assert_not_nil` in `RSpec/Rails/MinitestAssertions`. ([@G-Rath])
1414
- Fix a false positive for `RSpec/ExpectActual` when used with rspec-rails routing matchers. ([@naveg])
1515
- Add new `RSpec/RepeatedSubjectCall` cop. ([@drcapulet])
16+
- Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah])
1617

1718
## 2.26.1 (2024-01-05)
1819

config/default.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,8 +1132,12 @@ RSpec/Rails/AvoidSetupHook:
11321132
RSpec/Rails/HaveHttpStatus:
11331133
Description: Checks that tests use `have_http_status` instead of equality matchers.
11341134
Enabled: pending
1135+
ResponseMethods:
1136+
- response
1137+
- last_response
11351138
SafeAutoCorrect: false
11361139
VersionAdded: '2.12'
1140+
VersionChanged: "<<next>>"
11371141
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
11381142

11391143
RSpec/Rails/HttpStatus:

docs/modules/ROOT/pages/cops_rspecrails.adoc

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,50 @@ end
4848
| Yes
4949
| Yes (Unsafe)
5050
| 2.12
51-
| -
51+
| <<next>>
5252
|===
5353
5454
Checks that tests use `have_http_status` instead of equality matchers.
5555
5656
=== Examples
5757
58+
==== ResponseMethods: ['response', 'last_response'] (default)
59+
5860
[source,ruby]
5961
----
6062
# bad
6163
expect(response.status).to be(200)
62-
expect(response.code).to eq("200")
64+
expect(last_response.code).to eq("200")
6365
6466
# good
6567
expect(response).to have_http_status(200)
68+
expect(last_response).to have_http_status(200)
69+
----
70+
71+
==== ResponseMethods: ['foo_response']
72+
73+
[source,ruby]
6674
----
75+
# bad
76+
expect(foo_response.status).to be(200)
77+
78+
# good
79+
expect(foo_response).to have_http_status(200)
80+
81+
# also good
82+
expect(response).to have_http_status(200)
83+
expect(last_response).to have_http_status(200)
84+
----
85+
86+
=== Configurable attributes
87+
88+
|===
89+
| Name | Default value | Configurable values
90+
91+
| ResponseMethods
92+
| `response`, `last_response`
93+
| Array
94+
|===
6795
6896
=== References
6997

lib/rubocop/cop/rspec_rails/have_http_status.rb

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,32 @@ module RSpec
66
module Rails
77
# Checks that tests use `have_http_status` instead of equality matchers.
88
#
9-
# @example
9+
# @example ResponseMethods: ['response', 'last_response'] (default)
1010
# # bad
1111
# expect(response.status).to be(200)
12-
# expect(response.code).to eq("200")
12+
# expect(last_response.code).to eq("200")
1313
#
1414
# # good
1515
# 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)
1628
#
1729
class HaveHttpStatus < ::RuboCop::Cop::Base
1830
extend AutoCorrector
1931

2032
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`.'
2335

2436
RUNNERS = %i[to to_not not_to].to_set
2537
RESTRICT_ON_SEND = RUNNERS
@@ -28,26 +40,38 @@ class HaveHttpStatus < ::RuboCop::Cop::Base
2840
def_node_matcher :match_status, <<~PATTERN
2941
(send
3042
(send nil? :expect
31-
$(send (send nil? :response) {:status :code})
43+
$(send $(send nil? #response_methods?) {:status :code})
3244
)
3345
$RUNNERS
3446
$(send nil? {:be :eq :eql :equal} ({int str} $_))
3547
)
3648
PATTERN
3749

38-
def on_send(node)
39-
match_status(node) do |response_status, to, match, status|
50+
def on_send(node) # rubocop:todo Metrics/MethodLength
51+
match_status(node) do
52+
|response_status, response_method, to, match, status|
4053
return unless status.to_s.match?(/\A\d+\z/)
4154

42-
message = format(MSG, to: to, status: status,
55+
message = format(MSG, response: response_method.method_name,
56+
to: to, status: status,
4357
bad_code: node.source)
4458
add_offense(node, message: message) do |corrector|
45-
corrector.replace(response_status, 'response')
59+
corrector.replace(response_status, response_method.method_name)
4660
corrector.replace(match.loc.selector, 'have_http_status')
4761
corrector.replace(match.first_argument, status.to_s)
4862
end
4963
end
5064
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
5175
end
5276
end
5377
end

spec/rubocop/cop/rspec_rails/have_http_status_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@
3434
RUBY
3535
end
3636

37+
it 'registers an offense for `expect(last_response.status).to eql("200")`' do
38+
expect_offense(<<~RUBY)
39+
it { expect(last_response.status).to eql("200") }
40+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `expect(last_response).to have_http_status(200)` over `expect(last_response.status).to eql("200")`.
41+
RUBY
42+
43+
expect_correction(<<~RUBY)
44+
it { expect(last_response).to have_http_status(200) }
45+
RUBY
46+
end
47+
3748
it 'does not register an offense for `is_expected.to be(200)`' do
3849
expect_no_offenses(<<~RUBY)
3950
it { is_expected.to be(200) }
@@ -51,4 +62,26 @@
5162
it { expect(response.status).to eq("404 Not Found") }
5263
RUBY
5364
end
65+
66+
context 'when configured with ResponseMethods: [foo_response]' do
67+
let(:cop_config) { { 'ResponseMethods' => %w[foo_response] } }
68+
69+
it 'registers an offense for `expect(foo_response.status).to be(200)`' do
70+
expect_offense(<<~RUBY)
71+
it { expect(foo_response.status).to be(200) }
72+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `expect(foo_response).to have_http_status(200)` over `expect(foo_response.status).to be(200)`.
73+
RUBY
74+
75+
expect_correction(<<~RUBY)
76+
it { expect(foo_response).to have_http_status(200) }
77+
RUBY
78+
end
79+
80+
it 'does not register an offense for ' \
81+
'`expect(response.status).to be(200)`' do
82+
expect_no_offenses(<<~RUBY)
83+
it { expect(response.status).to be(200) }
84+
RUBY
85+
end
86+
end
5487
end

0 commit comments

Comments
 (0)