Skip to content

Commit 7aa3340

Browse files
committed
Add Capybara/VisibilityMatcher cop
1 parent 84cca39 commit 7aa3340

File tree

7 files changed

+178
-0
lines changed

7 files changed

+178
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Master (Unreleased)
44

55
* Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][])
6+
* Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][])
67

78
## 1.38.1 (2020-02-15)
89

@@ -492,3 +493,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
492493
[@lazycoder9]: https://github.com/lazycoder9
493494
[@elebow]: https://github.com/elebow
494495
[@eitoball]: https://github.com/eitoball
496+
[@aried3r]: https://github.com/aried3r

config/default.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,12 @@ Capybara/FeatureMethods:
475475
EnabledMethods: []
476476
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods
477477

478+
Capybara/VisibilityMatcher:
479+
Description: Checks for boolean visibility in capybara finders.
480+
Enabled: true
481+
VersionAdded: '1.39'
482+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher
483+
478484
FactoryBot/AttributeDefinedStatically:
479485
Description: Always declare attribute values as blocks.
480486
Enabled: true
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
module Capybara
7+
# Checks for boolean visibility in capybara finders.
8+
#
9+
# Capybara lets you find elements that match a certain visibility using
10+
# the `:visible` option. `:visible` accepts both boolean and symbols as
11+
# values, however using booleans can have unwanted effects. `visible:
12+
# false` does not find just invisible elements, but both visible and
13+
# invisible elements. For expressiveness and clarity, use one of the
14+
# symbol values, `:all`, `:hidden` or `:visible`.
15+
# (https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all)
16+
#
17+
# @example
18+
#
19+
# # bad
20+
# expect(page).to have_selector('.foo', visible: false)
21+
#
22+
# # bad
23+
# expect(page).to have_selector('.foo', visible: true)
24+
#
25+
# # good
26+
# expect(page).to have_selector('.foo', visible: :all)
27+
#
28+
# # good
29+
# expect(page).to have_selector('.foo', visible: :hidden)
30+
#
31+
# # good
32+
# expect(page).to have_selector('.foo', visible: :visible)
33+
#
34+
class VisibilityMatcher < Cop
35+
MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
36+
MSG_TRUE = 'Use `:visible` instead of `true`.'
37+
38+
def_node_matcher :visible_true?, <<~PATTERN
39+
(send nil? :have_selector ... (hash <$(pair (sym :visible) true) ...>))
40+
PATTERN
41+
42+
def_node_matcher :visible_false?, <<~PATTERN
43+
(send nil? :have_selector ... (hash <$(pair (sym :visible) false) ...>))
44+
PATTERN
45+
46+
def on_send(node)
47+
visible_false?(node) { |arg| add_offense(arg, message: MSG_FALSE) }
48+
visible_true?(node) { |arg| add_offense(arg, message: MSG_TRUE) }
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require_relative 'rspec/capybara/current_path_expectation'
44
require_relative 'rspec/capybara/feature_methods'
5+
require_relative 'rspec/capybara/visibility_matcher'
56

67
require_relative 'rspec/factory_bot/attribute_defined_statically'
78
require_relative 'rspec/factory_bot/create_list'

manual/cops.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
* [Capybara/CurrentPathExpectation](cops_capybara.md#capybaracurrentpathexpectation)
55
* [Capybara/FeatureMethods](cops_capybara.md#capybarafeaturemethods)
6+
* [Capybara/VisibilityMatcher](cops_capybara.md#capybaravisibilitymatcher)
67

78
#### Department [FactoryBot](cops_factorybot.md)
89

manual/cops_capybara.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,48 @@ EnabledMethods | `[]` | Array
8787
### References
8888

8989
* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods)
90+
91+
## Capybara/VisibilityMatcher
92+
93+
Enabled by default | Supports autocorrection
94+
--- | ---
95+
Enabled | No
96+
97+
Checks for boolean visibility in capybara finders.
98+
99+
Capybara lets you find elements that match a certain visibility using
100+
the `:visible` option. `:visible` accepts both boolean and symbols as
101+
values, however using booleans can have unwanted effects. `visible:
102+
false` does not find just invisible elements, but both visible and
103+
invisible elements. For expressiveness and clarity, use one of the
104+
symbol values, `:all`, `:hidden` or `:visible`.
105+
(https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all)
106+
107+
### Examples
108+
109+
```ruby
110+
# bad
111+
expect(page).to have_selector('.foo', visible: false)
112+
113+
# bad
114+
expect(page).to have_selector('.foo', visible: true)
115+
116+
# good
117+
expect(page).to have_selector('.foo', visible: :all)
118+
119+
# good
120+
expect(page).to have_selector('.foo', visible: :hidden)
121+
122+
# good
123+
expect(page).to have_selector('.foo', visible: :visible)
124+
```
125+
126+
### Configurable attributes
127+
128+
Name | Default value | Configurable values
129+
--- | --- | ---
130+
VersionAdded | `1.39` | String
131+
132+
### References
133+
134+
* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::Capybara::VisibilityMatcher do
4+
subject(:cop) { described_class.new }
5+
6+
it 'registers an offense when using `visible: true`' do
7+
expect_offense(<<-RUBY)
8+
expect(page).to have_selector('.my_element', visible: true)
9+
^^^^^^^^^^^^^ Use `:visible` instead of `true`.
10+
RUBY
11+
end
12+
13+
it 'registers an offense when using `visible: false`' do
14+
expect_offense(<<-RUBY)
15+
expect(page).to have_selector('.my_element', visible: false)
16+
^^^^^^^^^^^^^^ Use `:all` or `:hidden` instead of `false`.
17+
RUBY
18+
end
19+
20+
it 'registers an offense when using a selector`' do
21+
expect_offense(<<-RUBY)
22+
expect(page).to have_selector(:css, '.my_element', visible: false)
23+
^^^^^^^^^^^^^^ Use `:all` or `:hidden` instead of `false`.
24+
RUBY
25+
end
26+
27+
it 'registers an offense when using a using multiple options`' do
28+
expect_offense(<<-RUBY)
29+
expect(page).to have_selector('.my_element', count: 1, visible: false, normalize_ws: true)
30+
^^^^^^^^^^^^^^ Use `:all` or `:hidden` instead of `false`.
31+
RUBY
32+
end
33+
34+
it 'does not register an offense when no options are given`' do
35+
expect_no_offenses(<<~RUBY)
36+
expect(page).to have_selector('.my_element')
37+
RUBY
38+
end
39+
40+
it 'does not register an offense when using `visible: :all`' do
41+
expect_no_offenses(<<~RUBY)
42+
expect(page).to have_selector('.my_element', visible: :all)
43+
RUBY
44+
end
45+
46+
it 'does not register an offense when using `visible: :visible`' do
47+
expect_no_offenses(<<~RUBY)
48+
expect(page).to have_selector('.my_element', visible: :visible)
49+
RUBY
50+
end
51+
52+
it 'does not register an offense when using `visible: :hidden`' do
53+
expect_no_offenses(<<~RUBY)
54+
expect(page).to have_selector('.my_element', visible: :hidden)
55+
RUBY
56+
end
57+
58+
it 'does not register an offense when using other options' do
59+
expect_no_offenses(<<~RUBY)
60+
expect(page).to have_selector('.my_element', normalize_ws: true)
61+
RUBY
62+
end
63+
64+
it 'does not register an offense when using multiple options' do
65+
expect_no_offenses(<<~RUBY)
66+
expect(page).to have_selector('.my_element', count: 1, normalize_ws: true)
67+
RUBY
68+
end
69+
end

0 commit comments

Comments
 (0)