Skip to content

Commit

Permalink
Add new RSpec/DescriptiveDescription cop
Browse files Browse the repository at this point in the history
Fix: #1754
  • Loading branch information
ydah committed Feb 7, 2024
1 parent f062f92 commit 069a3c1
Show file tree
Hide file tree
Showing 8 changed files with 242 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/ClassCheck:
Enabled: true
RSpec/ContainExactly:
Enabled: true
RSpec/DescriptiveDescription:
Enabled: true
RSpec/DuplicatedMetadata:
Enabled: true
RSpec/EmptyMetadata:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Master (Unreleased)

- Add new `RSpec/DescriptiveDescription` cop. ([@ydah])
- Support asserts with messages in `Rspec/BeEmpty`. ([@G-Rath])
- Add support for `assert_empty`, `assert_not_empty` and `refute_empty` to `RSpec/Rails/MinitestAssertions`. ([@ydah])
- Support correcting some `*_predicate` assertions in `RSpec/Rails/MinitestAssertions`. ([@G-Rath])
Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ RSpec/DescribedClassModuleWrapping:
VersionAdded: '1.37'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribedClassModuleWrapping

RSpec/DescriptiveDescription:
Description: Description should be descriptive.
Enabled: pending
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescriptiveDescription

RSpec/Dialect:
Description: Enforces custom RSpec dialects.
Enabled: false
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 @@ -22,6 +22,7 @@
* xref:cops_rspec.adoc#rspecdescribesymbol[RSpec/DescribeSymbol]
* xref:cops_rspec.adoc#rspecdescribedclass[RSpec/DescribedClass]
* xref:cops_rspec.adoc#rspecdescribedclassmodulewrapping[RSpec/DescribedClassModuleWrapping]
* xref:cops_rspec.adoc#rspecdescriptivedescription[RSpec/DescriptiveDescription]
* xref:cops_rspec.adoc#rspecdialect[RSpec/Dialect]
* xref:cops_rspec.adoc#rspecduplicatedmetadata[RSpec/DuplicatedMetadata]
* xref:cops_rspec.adoc#rspecemptyexamplegroup[RSpec/EmptyExampleGroup]
Expand Down
61 changes: 61 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,67 @@ end
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribedClassModuleWrapping
* https://github.com/rubocop/rubocop-rspec/issues/735
== RSpec/DescriptiveDescription
|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
| Pending
| Yes
| No
| <<next>>
| -
|===
Description should be descriptive.
If `example group` or `example` contains only `execute string`, numbers,
and regular expressions, the description is not clear.
=== Examples
[source,ruby]
----
# bad
describe `time` do
# ...
end
# bad
context /when foo/ do
# ...
end
# bad
it 10000 do
# ...
end
# good
describe Foo do
# ...
end
# good
describe '#foo' do
# ...
end
# good
context "when #{foo} is bar" do
# ...
end
# good
it 'does something' do
# ...
end
----
=== References
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescriptiveDescription
== RSpec/Dialect
|===
Expand Down
73 changes: 73 additions & 0 deletions lib/rubocop/cop/rspec/descriptive_description.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Description should be descriptive.
#
# If `example group` or `example` contains only `execute string`, numbers,
# and regular expressions, the description is not clear.
#
# @example
# # bad
# describe `time` do
# # ...
# end
#
# # bad
# context /when foo/ do
# # ...
# end
#
# # bad
# it 10000 do
# # ...
# end
#
# # good
# describe Foo do
# # ...
# end
#
# # good
# describe '#foo' do
# # ...
# end
#
# # good
# context "when #{foo} is bar" do
# # ...
# end
#
# # good
# it 'does something' do
# # ...
# end
#
class DescriptiveDescription < Base
MSG = 'Description should be descriptive.'

# @!method example_groups_or_example?(node)
def_node_matcher :example_groups_or_example?, <<~PATTERN
(block (send #rspec? {#ExampleGroups.all #Examples.all} $_) ...)
PATTERN

def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
example_groups_or_example?(node) do |arg|
add_offense(arg) if offense?(arg)
end
end

private

def offense?(node)
if node.send_type?
node.child_nodes.all? { |child| offense?(child) }
else
node.xstr_type? || node.int_type? || node.regexp_type?
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 @@ -48,6 +48,7 @@
require_relative 'rspec/describe_symbol'
require_relative 'rspec/described_class'
require_relative 'rspec/described_class_module_wrapping'
require_relative 'rspec/descriptive_description'
require_relative 'rspec/dialect'
require_relative 'rspec/duplicated_metadata'
require_relative 'rspec/empty_example_group'
Expand Down
97 changes: 97 additions & 0 deletions spec/rubocop/cop/rspec/descriptive_description_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::DescriptiveDescription, :config do
it 'registers an offense when using `describe` with only execute string' do
expect_offense(<<~RUBY)
describe `time` do
^^^^^^ Description should be descriptive.
end
RUBY
end

it 'registers an offense when using `context` with only execute string' do
expect_offense(<<~RUBY)
context `time` do
^^^^^^ Description should be descriptive.
end
RUBY
end

it 'registers an offense when using `it` with only execute string' do
expect_offense(<<~RUBY)
it `time` do
^^^^^^ Description should be descriptive.
end
RUBY
end

it 'registers an offense when using `describe` with only regex' do
expect_offense(<<~RUBY)
describe /time/ do
^^^^^^ Description should be descriptive.
end
RUBY
end

it 'registers an offense when using `describe` with only Integer' do
expect_offense(<<~RUBY)
describe 10000 do
^^^^^ Description should be descriptive.
end
RUBY
end

it 'does not register an offense when using `describe` with a string' do
expect_no_offenses(<<~RUBY)
describe '#foo' do
end
RUBY
end

it 'does not register an offense when using `describe` with a class' do
expect_no_offenses(<<~RUBY)
describe Foo do
end
RUBY
end

it 'does not register an offense when using `context` with a string' do
expect_no_offenses(<<~RUBY)
context 'when foo is bar' do
end
RUBY
end

it 'does not register an offense when using `it` with a string' do
expect_no_offenses(<<~RUBY)
it 'does something' do
end
RUBY
end

it 'does not register an offense when using `describe` with an ' \
'interpolation string' do
expect_no_offenses(<<~RUBY)
describe "foo \#{bar}" do
end
RUBY
end

it 'does not register an offense when using `describe` with a ' \
'heredoc string' do
expect_no_offenses(<<~RUBY)
describe <<~DESC do
foo
DESC
end
RUBY
end

it 'does not register an offense when using `describe` with a string and ' \
'execute string' do
expect_no_offenses(<<~RUBY)
describe 'foo' + `time` do
end
RUBY
end
end

0 comments on commit 069a3c1

Please sign in to comment.