Skip to content

Commit f05f6f8

Browse files
akiomikpirj
authored andcommitted
Add new RSpec/Rails/HaveHttpStatus cop
1 parent d028400 commit f05f6f8

File tree

8 files changed

+126
-0
lines changed

8 files changed

+126
-0
lines changed

.rubocop.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,5 @@ RSpec/FactoryBot/SyntaxMethods:
107107
Enabled: true
108108
RSpec/Rails/AvoidSetupHook:
109109
Enabled: true
110+
RSpec/Rails/HaveHttpStatus:
111+
Enabled: true

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Fix error in `RSpec/RSpec/FactoryBot/CreateList` cop for empty block. ([@tejasbubane][])
1010
* Update `RSpec/MultipleExpectations` cop documentation with examples of aggregate_failures use. ([@edgibbs][])
1111
* Declare autocorrect as unsafe for `RSpec/VerifiedDoubleReference`. ([@Drowze][])
12+
* Add new `RSpec/Rails/HaveHttpStatus` cop. ([@akiomik][])
1213

1314
## 2.11.1 (2022-05-18)
1415

@@ -706,3 +707,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
706707
[@ngouy]: https://github.com/ngouy
707708
[@edgibbs]: https://github.com/edgibbs
708709
[@Drowze]: https://github.com/Drowze
710+
[@akiomik]: https://github.com/akiomik

config/default.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,13 @@ RSpec/Rails/AvoidSetupHook:
909909
VersionAdded: '2.4'
910910
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/AvoidSetupHook
911911

912+
RSpec/Rails/HaveHttpStatus:
913+
Description: Checks that tests use `have_http_status` instead of equality matchers.
914+
Enabled: pending
915+
SafeAutoCorrect: false
916+
VersionAdded: "<<next>>"
917+
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
918+
912919
RSpec/Rails/HttpStatus:
913920
Description: Enforces use of symbolic or numeric value to describe HTTP status.
914921
Enabled: true

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
=== Department xref:cops_rspec_rails.adoc[RSpec/Rails]
107107

108108
* xref:cops_rspec_rails.adoc#rspecrails/avoidsetuphook[RSpec/Rails/AvoidSetupHook]
109+
* xref:cops_rspec_rails.adoc#rspecrails/havehttpstatus[RSpec/Rails/HaveHttpStatus]
109110
* xref:cops_rspec_rails.adoc#rspecrails/httpstatus[RSpec/Rails/HttpStatus]
110111

111112
// END_COP_LIST

docs/modules/ROOT/pages/cops_rspec_rails.adoc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,35 @@ end
3333

3434
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/AvoidSetupHook
3535

36+
== RSpec/Rails/HaveHttpStatus
37+
38+
|===
39+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
40+
41+
| Pending
42+
| Yes
43+
| Yes (Unsafe)
44+
| <<next>>
45+
| -
46+
|===
47+
48+
Checks that tests use `have_http_status` instead of equality matchers.
49+
50+
=== Examples
51+
52+
[source,ruby]
53+
----
54+
# bad
55+
expect(response.status).to be(200)
56+
57+
# good
58+
expect(response).to have_http_status(200)
59+
----
60+
61+
=== References
62+
63+
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
64+
3665
== RSpec/Rails/HttpStatus
3766

3867
|===
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
module Rails
7+
# Checks that tests use `have_http_status` instead of equality matchers.
8+
#
9+
# @example
10+
# # bad
11+
# expect(response.status).to be(200)
12+
#
13+
# # good
14+
# expect(response).to have_http_status(200)
15+
#
16+
class HaveHttpStatus < Base
17+
extend AutoCorrector
18+
19+
MSG =
20+
'Prefer `expect(response).%<to>s have_http_status(%<status>i)` ' \
21+
'over `expect(response.status).%<to>s %<match>s`.'
22+
23+
# @!method match_status(node)
24+
def_node_matcher :match_status, <<-PATTERN
25+
(send
26+
(send nil? :expect
27+
$(send (send nil? :response) :status)
28+
)
29+
$#Runners.all
30+
$(send nil? {:be :eq :eql :equal} (int $_))
31+
)
32+
PATTERN
33+
34+
def on_send(node)
35+
match_status(node) do |response_status, to, match, status|
36+
message = format(MSG, to: to, match: match.source, status: status)
37+
add_offense(node, message: message) do |corrector|
38+
corrector.replace(response_status.source_range, 'response')
39+
corrector.replace(match.loc.selector, 'have_http_status')
40+
end
41+
end
42+
end
43+
end
44+
end
45+
end
46+
end
47+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require_relative 'rspec/factory_bot/syntax_methods'
1212

1313
require_relative 'rspec/rails/avoid_setup_hook'
14+
require_relative 'rspec/rails/have_http_status'
1415
begin
1516
require_relative 'rspec/rails/http_status'
1617
rescue LoadError
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::Rails::HaveHttpStatus do
4+
it 'registers an offense for `expect(response.status).to be(200)`' do
5+
expect_offense(<<~RUBY)
6+
it { expect(response.status).to be(200) }
7+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `expect(response).to have_http_status(200)` over `expect(response.status).to be(200)`.
8+
RUBY
9+
10+
expect_correction(<<~RUBY)
11+
it { expect(response).to have_http_status(200) }
12+
RUBY
13+
end
14+
15+
it 'registers an offense for `expect(response.status).not_to eq(404)`' do
16+
expect_offense(<<~RUBY)
17+
it { expect(response.status).not_to eq(404) }
18+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer `expect(response).not_to have_http_status(404)` over `expect(response.status).not_to eq(404)`.
19+
RUBY
20+
21+
expect_correction(<<~RUBY)
22+
it { expect(response).not_to have_http_status(404) }
23+
RUBY
24+
end
25+
26+
it 'does not register an offense for `is_expected.to be(200)`' do
27+
expect_no_offenses(<<~RUBY)
28+
it { is_expected.to be(200) }
29+
RUBY
30+
end
31+
32+
it 'does not register an offense for `expect(res.status).to be(200)`' do
33+
expect_no_offenses(<<~RUBY)
34+
it { expect(res.status).to be(200) }
35+
RUBY
36+
end
37+
end

0 commit comments

Comments
 (0)