Skip to content

Commit

Permalink
Add a new Capybara/NegationMatcherAfterVisit cop
Browse files Browse the repository at this point in the history
Resolve: #7

This PR adds a new `Capybara/NegationMatcherAfterVisit` cop.
  • Loading branch information
ydah committed Oct 2, 2023
1 parent c449767 commit 1bd059f
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Edge (Unreleased)

- Add a new `Capybara/NegationMatcherAfterVisit` cop. ([@ydah])

## 2.19.0 (2023-09-20)

- Add new `Capybara/RSpec/PredicateMatcher` cop. ([@ydah])
Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Capybara/NegationMatcher:
- not_to
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcher

Capybara/NegationMatcherAfterVisit:
Description: Do not allow negative matchers to be used immediately after `visit`.
Enabled: pending
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcherAfterVisit

Capybara/SpecificActions:
Description: Checks for there is a more specific actions offered by Capybara.
Enabled: pending
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 @@ -6,6 +6,7 @@
* xref:cops_capybara.adoc#capybaracurrentpathexpectation[Capybara/CurrentPathExpectation]
* xref:cops_capybara.adoc#capybaramatchstyle[Capybara/MatchStyle]
* xref:cops_capybara.adoc#capybaranegationmatcher[Capybara/NegationMatcher]
* xref:cops_capybara.adoc#capybaranegationmatcheraftervisit[Capybara/NegationMatcherAfterVisit]
* xref:cops_capybara.adoc#capybaraspecificactions[Capybara/SpecificActions]
* xref:cops_capybara.adoc#capybaraspecificfinders[Capybara/SpecificFinders]
* xref:cops_capybara.adoc#capybaraspecificmatcher[Capybara/SpecificMatcher]
Expand Down
43 changes: 43 additions & 0 deletions docs/modules/ROOT/pages/cops_capybara.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,49 @@ expect(page).to have_no_css('a')

* https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcher

== Capybara/NegationMatcherAfterVisit

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

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

Do not allow negative matchers to be used immediately after `visit`.

=== Examples

[source,ruby]
----
# bad
visit foo_path
expect(page).to have_no_link('bar')
expect(page).to have_css('a')
# good
visit foo_path
expect(page).to have_css('a')
expect(page).to have_no_link('bar')
# bad
visit foo_path
expect(page).not_to have_link('bar')
expect(page).to have_css('a')
# good
visit foo_path
expect(page).to have_css('a')
expect(page).not_to have_link('bar')
----

=== References

* https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcherAfterVisit

== Capybara/SpecificActions

|===
Expand Down
10 changes: 10 additions & 0 deletions lib/rubocop/cop/capybara/mixin/capybara_help.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ module Capybara
# Help methods for capybara.
# @api private
module CapybaraHelp
CAPYBARA_MATCHERS = %w[
selector css xpath text title current_path link button
field checked_field unchecked_field select table
sibling ancestor content
].freeze
POSITIVE_MATCHERS =
Set.new(CAPYBARA_MATCHERS) { |element| :"have_#{element}" }.freeze
NEGATIVE_MATCHERS =
Set.new(CAPYBARA_MATCHERS) { |element| :"have_no_#{element}" }
.freeze
COMMON_OPTIONS = %w[
id class style
].freeze
Expand Down
11 changes: 1 addition & 10 deletions lib/rubocop/cop/capybara/negation_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,9 @@ module Capybara
class NegationMatcher < ::RuboCop::Cop::Base
extend AutoCorrector
include ConfigurableEnforcedStyle
include CapybaraHelp

MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
CAPYBARA_MATCHERS = %w[
selector css xpath text title current_path link button
field checked_field unchecked_field select table
sibling ancestor content
].freeze
POSITIVE_MATCHERS =
Set.new(CAPYBARA_MATCHERS) { |element| :"have_#{element}" }.freeze
NEGATIVE_MATCHERS =
Set.new(CAPYBARA_MATCHERS) { |element| :"have_no_#{element}" }
.freeze
RESTRICT_ON_SEND = (POSITIVE_MATCHERS + NEGATIVE_MATCHERS).freeze

# @!method not_to?(node)
Expand Down
51 changes: 51 additions & 0 deletions lib/rubocop/cop/capybara/negation_matcher_after_visit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Capybara
# Do not allow negative matchers to be used immediately after `visit`.
#
# @example
# # bad
# visit foo_path
# expect(page).to have_no_link('bar')
# expect(page).to have_css('a')
#
# # good
# visit foo_path
# expect(page).to have_css('a')
# expect(page).to have_no_link('bar')
#
# # bad
# visit foo_path
# expect(page).not_to have_link('bar')
# expect(page).to have_css('a')
#
# # good
# visit foo_path
# expect(page).to have_css('a')
# expect(page).not_to have_link('bar')
#
class NegationMatcherAfterVisit < ::RuboCop::Cop::Base
include CapybaraHelp

MSG = 'Do not use negation matcher immediately after visit.'
RESTRICT_ON_SEND = %i[visit].freeze

# @!method negation_matcher?(node)
def_node_matcher :negation_matcher?, <<~PATTERN
{
(send (send nil? :expect _) :to (send nil? %NEGATIVE_MATCHERS ...))
(send (send nil? :expect _) :not_to (send nil? %POSITIVE_MATCHERS ...))
}
PATTERN

def on_send(node)
negation_matcher?(node.right_sibling) do
add_offense(node.right_sibling)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/capybara_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative 'capybara/current_path_expectation'
require_relative 'capybara/match_style'
require_relative 'capybara/negation_matcher'
require_relative 'capybara/negation_matcher_after_visit'
require_relative 'capybara/specific_actions'
require_relative 'capybara/specific_finders'
require_relative 'capybara/specific_matcher'
Expand Down
30 changes: 30 additions & 0 deletions spec/rubocop/cop/capybara/negation_matcher_after_visit_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Capybara::NegationMatcherAfterVisit, :config do
it 'registers an offense when using `have_no_*` after ' \
'immediately `visit` method call' do
expect_offense(<<~RUBY)
visit foo_path
expect(page).to have_no_link('bar')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use negation matcher immediately after visit.
RUBY
end

it 'registers an offense when using `not_to` with `have_*` after ' \
'immediately `visit` method call' do
expect_offense(<<~RUBY)
visit foo_path
expect(page).not_to have_link('bar')
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use negation matcher immediately after visit.
RUBY
end

it 'does not register an offense when using positive matchers after ' \
'immediately `visit` method call' do
expect_no_offenses(<<~RUBY)
visit foo_path
expect(page).to have_css('a')
expect(page).to have_no_link('bar')
RUBY
end
end

0 comments on commit 1bd059f

Please sign in to comment.