Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cop idea: detect not_to have_selector capybara matchers #378

Closed
Darhazer opened this issue Mar 22, 2017 · 14 comments · Fixed by #1405
Closed

Cop idea: detect not_to have_selector capybara matchers #378

Darhazer opened this issue Mar 22, 2017 · 14 comments · Fixed by #1405
Labels

Comments

@Darhazer
Copy link
Member

Darhazer commented Mar 22, 2017

The have_* collection of capybara matchers has the same set of have_no matches, for checking that there is no content matching. This creates the option to write the expectation in two forms:

expect(page).to have_no_selector
expect(page).not_to have_selector

For consistency, it would be nice to have a cop that enforces only one of the styles.

This goes for all of:

have_button
have_checked_field
have_css
have_field
have_link
have_select
have_selector
have_table
have_text
have_unchecked_field
have_xpath

Additionally the cop could detect usage of the node_matchers with eq false:
expect(has_selector?('#something')).to eq false

@timrogers
Copy link
Contributor

timrogers commented Sep 28, 2017

Are we sure this is a problem? The Capybara readme says the following:

Capybara's RSpec matchers, however, are smart enough to handle either form.
The two following statements are functionally equivalent:

expect(page).not_to have_xpath('a')
expect(page).to have_no_xpath('a')

I could be misunderstanding!

@Darhazer
Copy link
Member Author

I can't prove (although I'm pretty sure they have different behavior), but even if that is the case, it still good to have cop enforcing consistent usage. Maybe it should be configurable if one prefers the not_to style

@timrogers
Copy link
Contributor

I'm not going to have time to jump on this for now I'm afraid!

@Darhazer
Copy link
Member Author

Anyway your input was very valuable, thank you for looking at this

@Darhazer Darhazer added the cop label Jan 3, 2018
@kirhgoff
Copy link

This is a very good suggestion, while functionally they are equivalent, expect(page).to have_no_css('aaa') will actually wait for a timeout until the object disappears, while expect(page).not_to have_css('aaa') will momentarily fail if object is still there.

@ybiquitous
Copy link
Contributor

This is a very nice suggestion and must be useful for so many developers!

I found a similar warning on the "Capybara cheatsheet":

Use should have_no_* versions with RSpec matchers because should_not have_* doesn’t wait for a timeout from the driver.

image

@ybiquitous
Copy link
Contributor

A similar warning on the Codeship blog:

...
That brings us to a common mistake: flipping the assertion instead of the finder, like this: refute page.has_content?("Dashboard").
...
Which means that this is 15 seconds (the default) slower than assert page.has_no_content?("Dashboard").

https://blog.codeship.com/faster-rails-tests/

@badosu
Copy link

badosu commented Feb 13, 2019

Just for reference I tested a big codebase refactor from one form to another and did not see any improvement.

It seems that for rspec the matcher is smart enough to understand not_to have_selector, as the README says (although not emphatic enough for me).

So as far as it goes my opinion is that, apart from stylistic preferences, this cop is not necessary.

On the other hand, a !expect(page).to have_selector seems like a pretty important cop to have as it still suffers from the timeout issue.

@claudiosv
Copy link

claudiosv commented Aug 13, 2020

This cop would be great! For now I used this regular expression:

.not_to\s+(have_button|have_checked_field|have_css|have_field|have_link|have_select|have_selector|have_table|have_text|have_unchecked_field|have_xpath)

It seems negated matchers are defined here. They are not all functionally equivalent, as mentioned above.

Side note: I'd be happy to write a cop for this if there is a tutorial on how to write one.

@pirj
Copy link
Member

pirj commented Aug 13, 2020

@claudiosv that would be awesome!
You can copy-paste lib/rubocop/cop/rspec/capybara/visibility_matcher.rb, extract the common CAPYBARA_MATCHER_METHODS and capybara_matcher?. For the node pattern to match this should work:

          def_node_matcher :negative_expectation_matcher?, <<~PATTERN
            (send nil? (send nil? :expect _) {:not_to :to_not} (send nil? #capybara_matcher? ...))
          PATTERN

For the hook:

def on_send(node)
  negative_expectation_matcher?(node) { |node| add_offense(node) }
end

@pirj
Copy link
Member

pirj commented Aug 14, 2020

There is also a pull request to move hardcoded definitions into configuration #956, you may as well move CAPYBARA_MATCHER_METHODS there @claudiosv

@gonzaloriestra
Copy link

I've checked that expect(page).not_to have_selector does wait for the selector to disappear when it's removed, so I think the Capybara documentation is right: both ways are equivalent.

@dacook
Copy link

dacook commented Jan 27, 2022

Thanks for confirming that Capybara does handle these two forms the same way (at least, it does now).

But as Darhazer pointed out, it's still worth having the cop even just to enforce consistent usage.

@Darhazer
Copy link
Member Author

Darhazer commented Feb 1, 2022

Updated the description to make it clear that it's about the style preference/consistency, and not for an actual issue with the implicit waits.

getschomp added a commit to getschomp/cheatsheets that referenced this issue Mar 7, 2022
Removes the bad negative example and adds a new one
that still has bad performance. It use to be that `not_to` would wait
in a non-performant way:
https://www.cloudbees.com/blog/faster-rails-tests 
but now it it no longer waits: 
rubocop/rubocop-rspec#378 (comment)
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 4, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 4, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 5, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 5, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 12, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 12, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 12, 2022
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 13, 2022
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 13, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 14, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 14, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 14, 2022
ydah added a commit to ydah/rubocop-rspec that referenced this issue Oct 15, 2022
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 15, 2022
pirj pushed a commit to ydah/rubocop-rspec that referenced this issue Oct 15, 2022
pirj pushed a commit to rubocop/rubocop-capybara that referenced this issue Dec 29, 2022
rstacruz pushed a commit to rstacruz/cheatsheets that referenced this issue Mar 14, 2023
Removes the bad negative example and adds a new one
that still has bad performance. It use to be that `not_to` would wait
in a non-performant way:
https://www.cloudbees.com/blog/faster-rails-tests 
but now it it no longer waits: 
rubocop/rubocop-rspec#378 (comment)
ydah added a commit to rubocop/rubocop-factory_bot that referenced this issue Apr 13, 2023
ydah added a commit to rubocop/rubocop-rspec_rails that referenced this issue Mar 27, 2024
ydah added a commit to rubocop/rubocop-rspec_rails that referenced this issue Mar 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants