Skip to content

Commit

Permalink
Add new RSpec/MultipleTestTargetsPerSpecFile cop
Browse files Browse the repository at this point in the history
  • Loading branch information
ydah committed Aug 16, 2023
1 parent ff30ce1 commit 231e3be
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
45 changes: 45 additions & 0 deletions spec/rubocop/cop/rspec/multiple_test_targets_per_spec_file_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 231e3be

Please sign in to comment.