Skip to content

Commit

Permalink
Add new RSpec/IsExpectedSpecify cop
Browse files Browse the repository at this point in the history
Fix: #1658
  • Loading branch information
ydah committed Feb 27, 2024
1 parent f9abbde commit 18cbdfb
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Expand Up @@ -165,6 +165,8 @@ RSpec/IdenticalEqualityAssertion:
Enabled: true
RSpec/IndexedLet:
Enabled: true
RSpec/IsExpectedSpecify:
Enabled: true
RSpec/MatchArray:
Enabled: true
RSpec/MetadataStyle:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

## Master (Unreleased)

- Add new `RSpec/IsExpectedSpecify` cop. ([@ydah])
- Add support for `assert_true` and `assert_false` to `RSpec/Rails/MinitestAssertions`. ([@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])
Expand Down
7 changes: 7 additions & 0 deletions config/default.yml
Expand Up @@ -548,6 +548,13 @@ RSpec/InstanceVariable:
StyleGuide: https://rspec.rubystyle.guide/#instance-variables
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable

RSpec/IsExpectedSpecify:
Description: Check for `specify` with `is_expected` and one-liner expectations.
Enabled: pending
VersionAdded: "<<next>>"
StyleGuide: https://rspec.rubystyle.guide/#it-and-specify
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IsExpectedSpecify

RSpec/ItBehavesLike:
Description: Checks that only one `it_behaves_like` style is used.
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Expand Up @@ -52,6 +52,7 @@
* xref:cops_rspec.adoc#rspecindexedlet[RSpec/IndexedLet]
* xref:cops_rspec.adoc#rspecinstancespy[RSpec/InstanceSpy]
* xref:cops_rspec.adoc#rspecinstancevariable[RSpec/InstanceVariable]
* xref:cops_rspec.adoc#rspecisexpectedspecify[RSpec/IsExpectedSpecify]
* xref:cops_rspec.adoc#rspecitbehaveslike[RSpec/ItBehavesLike]
* xref:cops_rspec.adoc#rspeciteratedexpectation[RSpec/IteratedExpectation]
* xref:cops_rspec.adoc#rspecleadingsubject[RSpec/LeadingSubject]
Expand Down
36 changes: 36 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec.adoc
Expand Up @@ -2747,6 +2747,42 @@ end
* https://rspec.rubystyle.guide/#instance-variables
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable
== RSpec/IsExpectedSpecify
|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
| Pending
| Yes
| Yes
| <<next>>
| -
|===
Check for `specify` with `is_expected` and one-liner expectations.
=== Examples
[source,ruby]
----
# bad
specify { is_expected.to be_truthy }
# good
it { is_expected.to be_truthy }
# good
specify do
# ...
end
specify { expect(sqrt(4)).to eq(2) }
----
=== References
* https://rspec.rubystyle.guide/#it-and-specify
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IsExpectedSpecify
== RSpec/ItBehavesLike
|===
Expand Down
45 changes: 45 additions & 0 deletions lib/rubocop/cop/rspec/is_expected_specify.rb
@@ -0,0 +1,45 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Check for `specify` with `is_expected` and one-liner expectations.
#
# @example
# # bad
# specify { is_expected.to be_truthy }
#
# # good
# it { is_expected.to be_truthy }
#
# # good
# specify do
# # ...
# end
# specify { expect(sqrt(4)).to eq(2) }
#
class IsExpectedSpecify < Base
extend AutoCorrector

RESTRICT_ON_SEND = %i[specify].freeze
IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze
MSG = 'Use `it` instead of `specify`.'

# @!method offense?(node)
def_node_matcher :offense?, <<~PATTERN
(block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...))
PATTERN

def on_send(node)
block_node = node.parent
return unless block_node&.single_line? && offense?(block_node)

selector = node.loc.selector
add_offense(selector) do |corrector|
corrector.replace(selector, 'it')
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Expand Up @@ -78,6 +78,7 @@
require_relative 'rspec/indexed_let'
require_relative 'rspec/instance_spy'
require_relative 'rspec/instance_variable'
require_relative 'rspec/is_expected_specify'
require_relative 'rspec/it_behaves_like'
require_relative 'rspec/iterated_expectation'
require_relative 'rspec/leading_subject'
Expand Down
44 changes: 44 additions & 0 deletions spec/rubocop/cop/rspec/is_expected_specify_spec.rb
@@ -0,0 +1,44 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::IsExpectedSpecify, :config do
it 'registers an offense when using `specify` and one-liner style' do
expect_offense(<<~RUBY)
specify { is_expected.to be_truthy }
^^^^^^^ Use `it` instead of `specify`.
specify { are_expected.to be_falsy }
^^^^^^^ Use `it` instead of `specify`.
RUBY

expect_correction(<<~RUBY)
it { is_expected.to be_truthy }
it { are_expected.to be_falsy }
RUBY
end

it 'does not register an offense when using `specify` ' \
'and not one-liner style' do
expect_no_offenses(<<~RUBY)
specify { expect(sqrt(4)).to eq(2) }
RUBY
end

it 'does not register an offense when using `specify` and multi line' do
expect_no_offenses(<<~RUBY)
specify do
is_expected.to be_truthy
end
RUBY
end

it 'does not register an offense when using `it` and one-liner style' do
expect_no_offenses(<<~RUBY)
it { is_expected.to be_truthy }
RUBY
end

it 'does not register an offense when using `specify` with metadata' do
expect_no_offenses(<<~RUBY)
specify "pending", :pending
RUBY
end
end

0 comments on commit 18cbdfb

Please sign in to comment.