Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new RSpec/MultipleTestTargetsPerSpecFile cop #1699

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rubocop.yml
Expand Up @@ -153,6 +153,8 @@ RSpec/IndexedLet:
Enabled: true
RSpec/MatchArray:
Enabled: true
RSpec/MultipleTestTargetsPerSpecFile:
Enabled: true
RSpec/NoExpectationExample:
Enabled: true
RSpec/PendingWithoutReason:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
## Master (Unreleased)

- Fix an infinite loop error when `RSpec/ExcessiveDocstringSpacing` finds a description with non-ASCII leading/trailing whitespace. ([@bcgraham])
- Add new `RSpec/MultipleTestTargetsPerSpecFile` cop. ([@ydah])

## 2.23.2 (2023-08-09)

Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Expand Up @@ -649,6 +649,12 @@ RSpec/MultipleSubjects:
VersionAdded: '1.16'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleSubjects

RSpec/MultipleTestTargetsPerSpecFile:
Description: Checks that spec files only include one test target object.
Enabled: pending
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleTestTargetsPerSpecFile

RSpec/NamedSubject:
Description: Checks for explicitly referenced test subjects.
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Expand Up @@ -65,6 +65,7 @@
* xref:cops_rspec.adoc#rspecmultipleexpectations[RSpec/MultipleExpectations]
* xref:cops_rspec.adoc#rspecmultiplememoizedhelpers[RSpec/MultipleMemoizedHelpers]
* xref:cops_rspec.adoc#rspecmultiplesubjects[RSpec/MultipleSubjects]
* xref:cops_rspec.adoc#rspecmultipletesttargetsperspecfile[RSpec/MultipleTestTargetsPerSpecFile]
* xref:cops_rspec.adoc#rspecnamedsubject[RSpec/NamedSubject]
* xref:cops_rspec.adoc#rspecnestedgroups[RSpec/NestedGroups]
* xref:cops_rspec.adoc#rspecnoexpectationexample[RSpec/NoExpectationExample]
Expand Down
44 changes: 44 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec.adoc
Expand Up @@ -3561,6 +3561,50 @@ end

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

== RSpec/MultipleTestTargetsPerSpecFile

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

| Pending
| Yes
| No
| <<next>>
| -
|===

Checks that spec files only include one test target object.

=== Examples

[source,ruby]
----
# bad
RSpec.describe User do
# ...
describe User::Admin do
# ...
end
end

# bad
RSpec.describe User do
# ...
end
RSpec.describe Admin do
# ...
end

# good
RSpec.describe User do
# ...
end
----

=== References

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

== RSpec/NamedSubject

|===
Expand Down
51 changes: 51 additions & 0 deletions lib/rubocop/cop/rspec/multiple_test_targets_per_spec_file.rb
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Checks that spec files only include one test target object.
#
# @example
# # bad
# RSpec.describe User do
# # ...
# describe User::Admin do
# # ...
# end
# end
#
# # bad
# RSpec.describe User do
# # ...
# end
# RSpec.describe Admin do
# # ...
# end
#
# # good
# RSpec.describe User do
# # ...
# end
#
class MultipleTestTargetsPerSpecFile < Base
MSG = 'Spec files should only include one test target object.'

# @!method describe_classes(node)
def_node_search :describe_classes, <<~PATTERN
(block $(send #rspec? #ExampleGroups.all (const ...) ...) ...)
PATTERN

def on_investigation_end
return unless processed_source.ast

describes = describe_classes(processed_source.ast)
return if describes.count <= 1

describes.each do |node|
add_offense(node)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Expand Up @@ -91,6 +91,7 @@
require_relative 'rspec/multiple_expectations'
require_relative 'rspec/multiple_memoized_helpers'
require_relative 'rspec/multiple_subjects'
require_relative 'rspec/multiple_test_targets_per_spec_file'
require_relative 'rspec/named_subject'
require_relative 'rspec/nested_groups'
require_relative 'rspec/no_expectation_example'
Expand Down
@@ -0,0 +1,45 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::MultipleTestTargetsPerSpecFile, :config do
it 'registers an offense when include a class and a nested class describe' do
expect_offense(<<~RUBY)
RSpec.describe User do
^^^^^^^^^^^^^^^^^^^ Spec files should only include one test target object.
# ...
describe User::Admin do
^^^^^^^^^^^^^^^^^^^^ Spec files should only include one test target object.
# ...
end

describe User::Moderator do
^^^^^^^^^^^^^^^^^^^^^^^^ Spec files should only include one test target object.
# ...
end
end
RUBY
end

it 'registers an offense when include multiple classes' do
expect_offense(<<~RUBY)
RSpec.describe User do
^^^^^^^^^^^^^^^^^^^ Spec files should only include one test target object.
# ...
end

RSpec.describe Admin do
^^^^^^^^^^^^^^^^^^^^ Spec files should only include one test target object.
# ...
end
RUBY
end

it 'does not register an offense when include only class' do
expect_no_offenses(<<~RUBY)
RSpec.describe User do
describe '#method' do
# ...
end
end
RUBY
end
end