Skip to content

Commit

Permalink
Merge pull request #1365 from r7kamura/feature/infer
Browse files Browse the repository at this point in the history
Add `RSpec/Rails/InferredSpecType` cop
  • Loading branch information
pirj authored Oct 20, 2022
2 parents a0c8bb7 + 7f86436 commit 9806c0f
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,5 @@ RSpec/Rails/AvoidSetupHook:
Enabled: true
RSpec/Rails/HaveHttpStatus:
Enabled: true
RSpec/Rails/InferredSpecType:
Enabled: true
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Add `AllowedPatterns` configuration option to `RSpec/NoExpectationExample`. ([@ydah][])
* Improve `RSpec/NoExpectationExample` cop to ignore examples skipped or pending via metatada. ([@pirj][])
* Add `RSpec/FactoryBot/ConsistentParenthesesStyle` cop. ([@Liberatys][])
* Add `RSpec/Rails/InferredSpecType` cop. ([@r7kamura][])

## 2.13.2 (2022-09-23)

Expand Down
23 changes: 23 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,29 @@ RSpec/Rails/HaveHttpStatus:
VersionAdded: '2.12'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus

RSpec/Rails/InferredSpecType:
Description: Identifies redundant spec type.
Enabled: pending
Safe: false
VersionAdded: '2.14'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/InferredSpecType
Inferences:
channels: channel
controllers: controller
features: feature
generator: generator
helpers: helper
jobs: job
mailboxes: mailbox
mailers: mailer
models: model
requests: request
integration: request
api: request
routing: routing
system: system
views: view

RSpec/Rails/HttpStatus:
Description: Enforces use of symbolic or numeric value to describe HTTP status.
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,6 @@
* xref:cops_rspec_rails.adoc#rspecrails/avoidsetuphook[RSpec/Rails/AvoidSetupHook]
* xref:cops_rspec_rails.adoc#rspecrails/havehttpstatus[RSpec/Rails/HaveHttpStatus]
* xref:cops_rspec_rails.adoc#rspecrails/httpstatus[RSpec/Rails/HttpStatus]
* xref:cops_rspec_rails.adoc#rspecrails/inferredspectype[RSpec/Rails/InferredSpecType]

// END_COP_LIST
83 changes: 83 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec_rails.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,86 @@ it { is_expected.to have_http_status :error }
=== References

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

== RSpec/Rails/InferredSpecType

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| No
| Yes (Unsafe)
| 2.14
| -
|===

Identifies redundant spec type.

After setting up rspec-rails, you will have enabled
`config.infer_spec_type_from_file_location!` by default in
spec/rails_helper.rb. This cop works in conjunction with this config.
If you disable this config, disable this cop as well.

=== Safety

This cop is marked as unsafe because
`config.infer_spec_type_from_file_location!` may not be enabled.

=== Examples

[source,ruby]
----
# bad
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
end
# good
# spec/models/user_spec.rb
RSpec.describe User do
end
# good
# spec/models/user_spec.rb
RSpec.describe User, type: :common do
end
----

==== `Inferences` configuration

[source,ruby]
----
# .rubocop.yml
# RSpec/InferredSpecType:
# Inferences:
# services: service
# bad
# spec/services/user_spec.rb
RSpec.describe User, type: :service do
end
# good
# spec/services/user_spec.rb
RSpec.describe User do
end
# good
# spec/services/user_spec.rb
RSpec.describe User, type: :common do
end
----

=== Configurable attributes

|===
| Name | Default value | Configurable values

| Inferences
| `{"channels"=>"channel", "controllers"=>"controller", "features"=>"feature", "generator"=>"generator", "helpers"=>"helper", "jobs"=>"job", "mailboxes"=>"mailbox", "mailers"=>"mailer", "models"=>"model", "requests"=>"request", "integration"=>"request", "api"=>"request", "routing"=>"routing", "system"=>"system", "views"=>"view"}`
|
|===

=== References

* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/InferredSpecType
135 changes: 135 additions & 0 deletions lib/rubocop/cop/rspec/rails/inferred_spec_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
module Rails
# Identifies redundant spec type.
#
# After setting up rspec-rails, you will have enabled
# `config.infer_spec_type_from_file_location!` by default in
# spec/rails_helper.rb. This cop works in conjunction with this config.
# If you disable this config, disable this cop as well.
#
# @safety
# This cop is marked as unsafe because
# `config.infer_spec_type_from_file_location!` may not be enabled.
#
# @example
# # bad
# # spec/models/user_spec.rb
# RSpec.describe User, type: :model do
# end
#
# # good
# # spec/models/user_spec.rb
# RSpec.describe User do
# end
#
# # good
# # spec/models/user_spec.rb
# RSpec.describe User, type: :common do
# end
#
# @example `Inferences` configuration
# # .rubocop.yml
# # RSpec/InferredSpecType:
# # Inferences:
# # services: service
#
# # bad
# # spec/services/user_spec.rb
# RSpec.describe User, type: :service do
# end
#
# # good
# # spec/services/user_spec.rb
# RSpec.describe User do
# end
#
# # good
# # spec/services/user_spec.rb
# RSpec.describe User, type: :common do
# end
class InferredSpecType < Base
extend AutoCorrector

MSG = 'Remove redundant spec type.'

# @param [RuboCop::AST::BlockNode] node
def on_block(node)
return unless example_group?(node)

pair_node = describe_with_type(node)
return unless pair_node
return unless inferred_type?(pair_node)

removable_node = detect_removable_node(pair_node)
add_offense(removable_node) do |corrector|
autocorrect(corrector, removable_node)
end
end
alias on_numblock on_block

private

# @!method describe_with_type(node)
# @param [RuboCop::AST::BlockNode] node
# @return [RuboCop::AST::PairNode, nil]
def_node_matcher :describe_with_type, <<~PATTERN
(block
(send #rspec? #ExampleGroups.all
...
(hash <$(pair (sym :type) sym) ...>)
)
...
)
PATTERN

# @param [RuboCop::AST::Corrector] corrector
# @param [RuboCop::AST::Node] node
def autocorrect(corrector, node)
corrector.remove(
node.location.expression.with(
begin_pos: node.left_sibling.location.expression.end_pos
)
)
end

# @param [RuboCop::AST::PairNode] node
# @return [RuboCop::AST::Node]
def detect_removable_node(node)
if node.parent.pairs.size == 1
node.parent
else
node
end
end

# @return [String]
def file_path
processed_source.file_path
end

# @param [RuboCop::AST::PairNode] node
# @return [Boolean]
def inferred_type?(node)
inferred_type_from_file_path.inspect == node.value.source
end

# @return [Symbol, nil]
def inferred_type_from_file_path
inferences.find do |prefix, type|
break type.to_sym if file_path.include?("spec/#{prefix}/")
end
end

# @return [Hash]
def inferences
cop_config['Inferences'] || {}
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
rescue LoadError
# Rails/HttpStatus cannot be loaded if rack/utils is unavailable.
end
require_relative 'rspec/rails/inferred_spec_type'

require_relative 'rspec/align_left_let_brace'
require_relative 'rspec/align_right_let_brace'
Expand Down
Loading

0 comments on commit 9806c0f

Please sign in to comment.