From a0f539f0fbd85b699da443577e00f362c8b6e0ed Mon Sep 17 00:00:00 2001 From: r7kamura Date: Wed, 29 May 2024 08:43:55 +0900 Subject: [PATCH] Add `RSpecRailsStatusCodeCheckBySubject` cop --- .rubocop.yml | 2 + README.md | 1 + config/default.yml | 8 +++ ...spec_rails_status_code_check_by_subject.rb | 52 +++++++++++++++++++ lib/sevencop.rb | 1 + ...rails_status_code_check_by_subject_spec.rb | 41 +++++++++++++++ 6 files changed, 105 insertions(+) create mode 100644 lib/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject.rb create mode 100644 spec/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 4ec5136..5f522f5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -42,6 +42,7 @@ RSpec/FilePath: RSpecMatcherConsistentParentheses: rspec_matcher_consistent_parentheses RSpecMemoizedHelperBlockDelimiter: rspec_memoized_helper_block_delimiter RSpecRailsHaveHttpStatus: rspec_rails_have_http_status + RSpecRailsStatusCodeCheckBySubject: rspec_rails_status_code_check_by_subject RSpec/MultipleExpectations: Enabled: false @@ -54,6 +55,7 @@ RSpec/SpecFilePathFormat: RSpecMatcherConsistentParentheses: rspec_matcher_consistent_parentheses RSpecMemoizedHelperBlockDelimiter: rspec_memoized_helper_block_delimiter RSpecRailsHaveHttpStatus: rspec_rails_have_http_status + RSpecRailsStatusCodeCheckBySubject: rspec_rails_status_code_check_by_subject Sevencop/AutoloadOrdered: Enabled: true diff --git a/README.md b/README.md index 66ff8b1..769adcf 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Note that all cops are `Enabled: false` by default. - [Sevencop/RSpecMatcherConsistentParentheses](lib/rubocop/cop/sevencop/rspec_matcher_consistent_parentheses.rb) - [Sevencop/RSpecMemoizedHelperBlockDelimiter](lib/rubocop/cop/sevencop/rspec_memoized_helper_block_delimiter.rb) - [Sevencop/RSpecRailsHaveHttpStatus](lib/rubocop/cop/sevencop/rspec_rails_have_http_status.rb) +- [Sevencop/RSpecRailsStatusCodeCheckBySubject](lib/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject.rb) ## Notes diff --git a/config/default.yml b/config/default.yml index 6c24811..a7b9429 100644 --- a/config/default.yml +++ b/config/default.yml @@ -145,3 +145,11 @@ Sevencop/RSpecRailsHaveHttpStatus: Include: - "**/spec/controllers/**/*.rb" - "**/spec/requests/**/*.rb" + +Sevencop/RSpecRailsStatusCodeCheckBySubject: + Description: | + Use `expect(response).to have_http_status(code)` instead of `is_expected.to eq(code)`. + Enabled: false + Include: + - "**/spec/controllers/**/*.rb" + - "**/spec/requests/**/*.rb" diff --git a/lib/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject.rb b/lib/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject.rb new file mode 100644 index 0000000..823fb36 --- /dev/null +++ b/lib/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Sevencop + # Use `expect(response).to have_http_status(code)` instead of `is_expected.to eq(code)`. + # + # @example + # # bad + # is_expected.to eq(200) + # + # # good + # expect(response).to have_http_status(200) + # + # # bad + # is_expected.to eq(:ok) + # + # # good + # expect(response).to have_http_status(:ok) + class RSpecRailsStatusCodeCheckBySubject < Base + extend AutoCorrector + + MSG = 'Use `expect(response).to have_http_status(code)` instead of `is_expected.to eq(code)`.' + + RESTRICT_ON_SEND = %i[ + to + ].freeze + + # @param [RuboCop::AST::SendNode] node + # @return [void] + def on_send(node) + return unless is_expected_to_eq_code?(node) + + add_offense(node) do |corrector| + corrector.insert_before(node, "subject\n") + corrector.replace(node.receiver, 'expect(response)') + corrector.replace(node.first_argument.location.selector, 'have_http_status') + end + end + + private + + # @!method is_expected_to_eq_code?(node) + # @param [RuboCop::AST::SendNode] node + # @return [Boolean] + def_node_matcher :is_expected_to_eq_code?, <<~PATTERN + (send (send nil? :is_expected) :to (send nil? :eq {int sym})) + PATTERN + end + end + end +end diff --git a/lib/sevencop.rb b/lib/sevencop.rb index 4576590..d182957 100644 --- a/lib/sevencop.rb +++ b/lib/sevencop.rb @@ -26,3 +26,4 @@ require_relative 'rubocop/cop/sevencop/rspec_matcher_consistent_parentheses' require_relative 'rubocop/cop/sevencop/rspec_memoized_helper_block_delimiter' require_relative 'rubocop/cop/sevencop/rspec_rails_have_http_status' +require_relative 'rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject' diff --git a/spec/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject_spec.rb b/spec/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject_spec.rb new file mode 100644 index 0000000..72b87d2 --- /dev/null +++ b/spec/rubocop/cop/sevencop/rspec_rails_status_code_check_by_subject_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe RuboCop::Cop::Sevencop::RSpecRailsStatusCodeCheckBySubject, :config do + context 'when `is_expected.to redirect_to(root_path)` is used' do + it 'registers no offense' do + expect_no_offenses(<<~RUBY) + is_expected.to redirect_to(root_path) + RUBY + end + end + + context 'when `is_expected.to eq(200)` is used' do + it 'registers offense' do + expect_offense(<<~RUBY) + is_expected.to eq(200) + ^^^^^^^^^^^^^^^^^^^^^^ Use `expect(response).to have_http_status(code)` instead of `is_expected.to eq(code)`. + RUBY + + expect_correction(<<~RUBY) + subject + expect(response).to have_http_status(200) + RUBY + end + end + + context 'when `is_expected.to eq(:ok)` is used' do + it 'registers offense' do + expect_offense(<<~RUBY) + is_expected.to eq(:ok) + ^^^^^^^^^^^^^^^^^^^^^^ Use `expect(response).to have_http_status(code)` instead of `is_expected.to eq(code)`. + RUBY + + expect_correction(<<~RUBY) + subject + expect(response).to have_http_status(:ok) + RUBY + end + end +end