Skip to content

Commit

Permalink
Merge pull request #1893 from krororo/add-expectation_target_method
Browse files Browse the repository at this point in the history
Add new `RSpec/MissingExpectationTargetMethod` cop
  • Loading branch information
ydah authored Jun 7, 2024
2 parents ef447c1 + 87e0e4a commit ea7a5d8
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ RSpec/MatchArray:
Enabled: true
RSpec/MetadataStyle:
Enabled: true
RSpec/MissingExpectationTargetMethod:
Enabled: true
RSpec/NoExpectationExample:
Enabled: true
RSpec/PendingWithoutReason:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Remove `RSpec/Capybara/FeatureMethods` cop. If you are using this cop, change it to use `RSpec/Dialect`. ([@ydah])
- Support `AutoCorrect: contextual` option for LSP. ([@ydah])
- Enable all pending cops. ([@bquorning])
- Add new `RSpec/MissingExpectationTargetMethod` cop. ([@krororo])

## 2.29.2 (2024-05-02)

Expand Down Expand Up @@ -924,6 +925,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
[@k-s-a]: https://github.com/K-S-A
[@kellysutton]: https://github.com/kellysutton
[@koic]: https://github.com/koic
[@krororo]: https://github.com/krororo
[@kuahyeow]: https://github.com/kuahyeow
[@lazycoder9]: https://github.com/lazycoder9
[@leoarnold]: https://github.com/leoarnold
Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@ RSpec/MissingExampleGroupArgument:
VersionAdded: '1.28'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument

RSpec/MissingExpectationTargetMethod:
Description: Checks if `.to`, `not_to` or `to_not` are used.
Enabled: pending
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExpectationTargetMethod

RSpec/MultipleDescribes:
Description: Checks for multiple top-level example groups.
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 @@ -66,6 +66,7 @@
* xref:cops_rspec.adoc#rspecmessagespies[RSpec/MessageSpies]
* xref:cops_rspec.adoc#rspecmetadatastyle[RSpec/MetadataStyle]
* xref:cops_rspec.adoc#rspecmissingexamplegroupargument[RSpec/MissingExampleGroupArgument]
* xref:cops_rspec.adoc#rspecmissingexpectationtargetmethod[RSpec/MissingExpectationTargetMethod]
* xref:cops_rspec.adoc#rspecmultipledescribes[RSpec/MultipleDescribes]
* xref:cops_rspec.adoc#rspecmultipleexpectations[RSpec/MultipleExpectations]
* xref:cops_rspec.adoc#rspecmultiplememoizedhelpers[RSpec/MultipleMemoizedHelpers]
Expand Down
36 changes: 36 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3406,6 +3406,42 @@ end
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExampleGroupArgument
== RSpec/MissingExpectationTargetMethod
|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
| Pending
| Yes
| No
| <<next>>
| -
|===
Checks if `.to`, `not_to` or `to_not` are used.
The RSpec::Expectations::ExpectationTarget must use `to`, `not_to` or
`to_not` to run. Therefore, this cop checks if other methods are used.
=== Examples
[source,ruby]
----
# bad
expect(something).kind_of? Foo
is_expected == 42
expect{something}.eq? BarError
# good
expect(something).to be_a Foo
is_expected.to eq 42
expect{something}.to raise_error BarError
----
=== References
* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MissingExpectationTargetMethod
== RSpec/MultipleDescribes
|===
Expand Down
54 changes: 54 additions & 0 deletions lib/rubocop/cop/rspec/missing_expectation_target_method.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Checks if `.to`, `not_to` or `to_not` are used.
#
# The RSpec::Expectations::ExpectationTarget must use `to`, `not_to` or
# `to_not` to run. Therefore, this cop checks if other methods are used.
#
# @example
# # bad
# expect(something).kind_of? Foo
# is_expected == 42
# expect{something}.eq? BarError
#
# # good
# expect(something).to be_a Foo
# is_expected.to eq 42
# expect{something}.to raise_error BarError
#
class MissingExpectationTargetMethod < Base
MSG = 'Use `.to`, `.not_to` or `.to_not` to set an expectation.'
RESTRICT_ON_SEND = %i[expect is_expected].freeze

# @!method expect?(node)
def_node_matcher :expect?, <<~PATTERN
{
(send nil? :expect ...)
(send nil? :is_expected)
}
PATTERN

# @!method expect_block?(node)
def_node_matcher :expect_block?, <<~PATTERN
(block #expect? (args) _body)
PATTERN

# @!method expectation_without_runner?(node)
def_node_matcher :expectation_without_runner?, <<~PATTERN
(send {#expect? #expect_block?} !#Runners.all ...)
PATTERN

def on_send(node)
node = node.parent if node.parent&.block_type?

expectation_without_runner?(node.parent) do
add_offense(node.parent.loc.selector)
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 @@ -64,6 +64,7 @@
require_relative 'rspec/message_spies'
require_relative 'rspec/metadata_style'
require_relative 'rspec/missing_example_group_argument'
require_relative 'rspec/missing_expectation_target_method'
require_relative 'rspec/multiple_describes'
require_relative 'rspec/multiple_expectations'
require_relative 'rspec/multiple_memoized_helpers'
Expand Down
81 changes: 81 additions & 0 deletions spec/rubocop/cop/rspec/missing_expectation_target_method_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::MissingExpectationTargetMethod do
it 'registers offenses to other than `to` or `not_to`' do
expect_offense(<<~RUBY)
it 'something' do
something = 1
expect(something).kind_of? Integer
^^^^^^^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
end
RUBY
end

it 'registers offenses to other than `to` or `not_to` ' \
'when block has one expression' do
expect_offense(<<~RUBY)
it 'something' do
expect(something).eq? 42
^^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
end
RUBY
end

it 'registers offenses to other than `to` or `not_to` with `is_expected`' do
expect_offense(<<~RUBY)
it 'something' do
is_expected == 42
^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
end
RUBY
end

it 'registers offenses to other than `to` or `not_to` with block' do
expect_offense(<<~RUBY)
it 'something' do
expect{something}.kind_of? StandardError
^^^^^^^^ Use `.to`, `.not_to` or `.to_not` to set an expectation.
end
RUBY
end

it 'accepts void `expect`' do
expect_no_offenses(<<~RUBY)
it 'something' do
expect(something)
end
RUBY
end

it 'accepts only `expect`' do
expect_no_offenses(<<~RUBY)
expect
RUBY
end

%w[to not_to to_not].each do |method|
it "accepts `.#{method}`" do
expect_no_offenses(<<~RUBY)
it 'something' do
expect(something).#{method} be_a Integer
end
RUBY
end

it "accepts `.#{method}` with `is_expected`" do
expect_no_offenses(<<~RUBY)
it 'something' do
is_expected.#{method} eq 42
end
RUBY
end

it "accepts `.#{method}` with block" do
expect_no_offenses(<<~RUBY)
it 'something' do
expect{something}.#{method} raise_error(StandardError)
end
RUBY
end
end
end

0 comments on commit ea7a5d8

Please sign in to comment.