Skip to content

Commit

Permalink
Extract RSpec Rails cops
Browse files Browse the repository at this point in the history
  • Loading branch information
ydah committed Mar 4, 2024
1 parent 447fa4e commit 2ea437e
Show file tree
Hide file tree
Showing 18 changed files with 241 additions and 2,666 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Master (Unreleased)

- Extract RSpec Rails cops to a separate repository, [`rubocop-rspec-rails`](https://github.com/rubocop/rubocop-rspec-rails). ([@ydah])

## 2.27.1 (2024-03-03)

- Fix a false positive for `RSpec/RepeatedSubjectCall` when `subject.method_call`. ([@ydah])
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ gem 'rubocop-rake', '~> 0.6'
gem 'simplecov', '>= 0.19'
gem 'yard'

gem 'rubocop-rspec-rails', github: 'ydah/rubocop-rspec-rails', branch: 'extract'

local_gemfile = 'Gemfile.local'
eval_gemfile(local_gemfile) if File.exist?(local_gemfile)
2 changes: 2 additions & 0 deletions lib/rubocop-rspec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
require_relative 'rubocop/rspec/example_group'
require_relative 'rubocop/rspec/hook'

require 'rubocop-rspec-rails'

RuboCop::RSpec::Inject.defaults!

require_relative 'rubocop/cop/rspec_cops'
Expand Down
50 changes: 17 additions & 33 deletions lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,23 @@ module RuboCop
module Cop
module RSpec
module Rails
# Checks that tests use RSpec `before` hook over Rails `setup` method.
#
# @example
# # bad
# setup do
# allow(foo).to receive(:bar)
# end
#
# # good
# before do
# allow(foo).to receive(:bar)
# end
#
class AvoidSetupHook < Base
extend AutoCorrector

MSG = 'Use `before` instead of `setup`.'

# @!method setup_call(node)
def_node_matcher :setup_call, <<~PATTERN
(block
$(send nil? :setup)
(args) _)
PATTERN

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
setup_call(node) do |setup|
add_offense(node) do |corrector|
corrector.replace setup, 'before'
end
end
end
end
# @!parse
# # Checks that tests use RSpec `before` hook over Rails `setup`
# # method.
# #
# # @example
# # # bad
# # setup do
# # allow(foo).to receive(:bar)
# # end
# #
# # # good
# # before do
# # allow(foo).to receive(:bar)
# # end
# #
# class AvoidSetupHook < RuboCop::Cop::RSpec::Base; end
AvoidSetupHook = ::RuboCop::Cop::RSpec::Rails::AvoidSetupHook
end
end
end
Expand Down
95 changes: 26 additions & 69 deletions lib/rubocop/cop/rspec/rails/have_http_status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,75 +4,32 @@ module RuboCop
module Cop
module RSpec
module Rails
# Checks that tests use `have_http_status` instead of equality matchers.
#
# @example ResponseMethods: ['response', 'last_response'] (default)
# # bad
# expect(response.status).to be(200)
# expect(last_response.code).to eq("200")
#
# # good
# expect(response).to have_http_status(200)
# expect(last_response).to have_http_status(200)
#
# @example ResponseMethods: ['foo_response']
# # bad
# expect(foo_response.status).to be(200)
#
# # good
# expect(foo_response).to have_http_status(200)
#
# # also good
# expect(response).to have_http_status(200)
# expect(last_response).to have_http_status(200)
#
class HaveHttpStatus < ::RuboCop::Cop::Base
extend AutoCorrector

MSG =
'Prefer `expect(%<response>s).%<to>s ' \
'have_http_status(%<status>s)` over `%<bad_code>s`.'

RUNNERS = %i[to to_not not_to].to_set
RESTRICT_ON_SEND = RUNNERS

# @!method match_status(node)
def_node_matcher :match_status, <<~PATTERN
(send
(send nil? :expect
$(send $(send nil? #response_methods?) {:status :code})
)
$RUNNERS
$(send nil? {:be :eq :eql :equal} ({int str} $_))
)
PATTERN

def on_send(node) # rubocop:disable Metrics/MethodLength
match_status(node) do
|response_status, response_method, to, match, status|
return unless status.to_s.match?(/\A\d+\z/)

message = format(MSG, response: response_method.method_name,
to: to, status: status,
bad_code: node.source)
add_offense(node, message: message) do |corrector|
corrector.replace(response_status, response_method.method_name)
corrector.replace(match.loc.selector, 'have_http_status')
corrector.replace(match.first_argument, status.to_s)
end
end
end

private

def response_methods?(name)
response_methods.include?(name.to_s)
end

def response_methods
cop_config.fetch('ResponseMethods', [])
end
end
# @!parse
# # Checks that tests use `have_http_status` instead of equality
# # matchers.
# #
# # @example ResponseMethods: ['response', 'last_response'] (default)
# # # bad
# # expect(response.status).to be(200)
# # expect(last_response.code).to eq("200")
# #
# # # good
# # expect(response).to have_http_status(200)
# # expect(last_response).to have_http_status(200)
# #
# # @example ResponseMethods: ['foo_response']
# # # bad
# # expect(foo_response.status).to be(200)
# #
# # # good
# # expect(foo_response).to have_http_status(200)
# #
# # # also good
# # expect(response).to have_http_status(200)
# # expect(last_response).to have_http_status(200)
# #
# class HaveHttpStatus < ::RuboCop::Cop::RSpec::Base; end
HaveHttpStatus = ::RuboCop::Cop::RSpec::Rails::HaveHttpStatus
end
end
end
Expand Down

0 comments on commit 2ea437e

Please sign in to comment.