rubocop-capybara 2.22.1 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/config/default.yml +59 -43
- data/config/obsoletion.yml +18 -0
- data/lib/rubocop/capybara/version.rb +1 -1
- data/lib/rubocop/cop/capybara/assert_style.rb +30 -0
- data/lib/rubocop/cop/capybara/find_all_first.rb +16 -2
- data/lib/rubocop/cop/capybara/mixin/css_selector.rb +12 -0
- data/lib/rubocop/cop/capybara/redundant_within_find.rb +17 -4
- data/lib/rubocop/cop/capybara/rspec/current_path_expectation.rb +146 -0
- data/lib/rubocop/cop/capybara/rspec/have_content.rb +51 -0
- data/lib/rubocop/cop/capybara/rspec/have_selector.rb +2 -1
- data/lib/rubocop/cop/capybara/rspec/match_style.rb +53 -0
- data/lib/rubocop/cop/capybara/rspec/negation_matcher.rb +106 -0
- data/lib/rubocop/cop/capybara/rspec/negation_matcher_after_visit.rb +53 -0
- data/lib/rubocop/cop/capybara/rspec/specific_matcher.rb +96 -0
- data/lib/rubocop/cop/capybara/rspec/visibility_matcher.rb +72 -0
- data/lib/rubocop/cop/capybara_cops.rb +8 -6
- data/lib/rubocop-capybara.rb +1 -1
- metadata +14 -17
- data/lib/rubocop/cop/capybara/current_path_expectation.rb +0 -141
- data/lib/rubocop/cop/capybara/match_style.rb +0 -58
- data/lib/rubocop/cop/capybara/negation_matcher.rb +0 -104
- data/lib/rubocop/cop/capybara/negation_matcher_after_visit.rb +0 -51
- data/lib/rubocop/cop/capybara/specific_matcher.rb +0 -94
- data/lib/rubocop/cop/capybara/visibility_matcher.rb +0 -71
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Capybara
|
|
6
|
-
# Enforces use of `have_no_*` or `not_to` for negated expectations.
|
|
7
|
-
#
|
|
8
|
-
# @example EnforcedStyle: have_no (default)
|
|
9
|
-
# # bad
|
|
10
|
-
# expect(page).not_to have_selector 'a'
|
|
11
|
-
# expect(page).not_to have_css('a')
|
|
12
|
-
#
|
|
13
|
-
# # good
|
|
14
|
-
# expect(page).to have_no_selector 'a'
|
|
15
|
-
# expect(page).to have_no_css('a')
|
|
16
|
-
#
|
|
17
|
-
# @example EnforcedStyle: not_to
|
|
18
|
-
# # bad
|
|
19
|
-
# expect(page).to have_no_selector 'a'
|
|
20
|
-
# expect(page).to have_no_css('a')
|
|
21
|
-
#
|
|
22
|
-
# # good
|
|
23
|
-
# expect(page).not_to have_selector 'a'
|
|
24
|
-
# expect(page).not_to have_css('a')
|
|
25
|
-
#
|
|
26
|
-
class NegationMatcher < ::RuboCop::Cop::Base
|
|
27
|
-
extend AutoCorrector
|
|
28
|
-
include ConfigurableEnforcedStyle
|
|
29
|
-
include CapybaraHelp
|
|
30
|
-
|
|
31
|
-
MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
|
|
32
|
-
RESTRICT_ON_SEND = (POSITIVE_MATCHERS + NEGATIVE_MATCHERS).freeze
|
|
33
|
-
|
|
34
|
-
# @!method not_to?(node)
|
|
35
|
-
def_node_matcher :not_to?, <<~PATTERN
|
|
36
|
-
(send ... {:not_to :to_not}
|
|
37
|
-
(send nil? %POSITIVE_MATCHERS ...))
|
|
38
|
-
PATTERN
|
|
39
|
-
|
|
40
|
-
# @!method have_no?(node)
|
|
41
|
-
def_node_matcher :have_no?, <<~PATTERN
|
|
42
|
-
(send ... :to
|
|
43
|
-
(send nil? %NEGATIVE_MATCHERS ...))
|
|
44
|
-
PATTERN
|
|
45
|
-
|
|
46
|
-
def on_send(node)
|
|
47
|
-
return unless offense?(node)
|
|
48
|
-
|
|
49
|
-
matcher = node.method_name.to_s
|
|
50
|
-
add_offense(offense_range(node),
|
|
51
|
-
message: message(matcher)) do |corrector|
|
|
52
|
-
corrector.replace(node.parent.loc.selector, replaced_runner)
|
|
53
|
-
corrector.replace(node.loc.selector,
|
|
54
|
-
replaced_matcher(matcher))
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
def offense?(node)
|
|
61
|
-
node.arguments? &&
|
|
62
|
-
((style == :have_no && not_to?(node.parent)) ||
|
|
63
|
-
(style == :not_to && have_no?(node.parent)))
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def offense_range(node)
|
|
67
|
-
node.parent.loc.selector.with(end_pos: node.loc.selector.end_pos)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def message(matcher)
|
|
71
|
-
format(MSG,
|
|
72
|
-
runner: replaced_runner,
|
|
73
|
-
matcher: replaced_matcher(matcher))
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def replaced_runner
|
|
77
|
-
case style
|
|
78
|
-
when :have_no
|
|
79
|
-
'to'
|
|
80
|
-
when :not_to
|
|
81
|
-
'not_to'
|
|
82
|
-
else
|
|
83
|
-
# :nocov:
|
|
84
|
-
:noop
|
|
85
|
-
# :nocov:
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def replaced_matcher(matcher)
|
|
90
|
-
case style
|
|
91
|
-
when :have_no
|
|
92
|
-
matcher.sub('have_', 'have_no_')
|
|
93
|
-
when :not_to
|
|
94
|
-
matcher.sub('have_no_', 'have_')
|
|
95
|
-
else
|
|
96
|
-
# :nocov:
|
|
97
|
-
:noop
|
|
98
|
-
# :nocov:
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Capybara
|
|
6
|
-
# Do not allow negative matchers to be used immediately after `visit`.
|
|
7
|
-
#
|
|
8
|
-
# @example
|
|
9
|
-
# # bad
|
|
10
|
-
# visit foo_path
|
|
11
|
-
# expect(page).to have_no_link('bar')
|
|
12
|
-
# expect(page).to have_css('a')
|
|
13
|
-
#
|
|
14
|
-
# # good
|
|
15
|
-
# visit foo_path
|
|
16
|
-
# expect(page).to have_css('a')
|
|
17
|
-
# expect(page).to have_no_link('bar')
|
|
18
|
-
#
|
|
19
|
-
# # bad
|
|
20
|
-
# visit foo_path
|
|
21
|
-
# expect(page).not_to have_link('bar')
|
|
22
|
-
# expect(page).to have_css('a')
|
|
23
|
-
#
|
|
24
|
-
# # good
|
|
25
|
-
# visit foo_path
|
|
26
|
-
# expect(page).to have_css('a')
|
|
27
|
-
# expect(page).not_to have_link('bar')
|
|
28
|
-
#
|
|
29
|
-
class NegationMatcherAfterVisit < ::RuboCop::Cop::Base
|
|
30
|
-
include CapybaraHelp
|
|
31
|
-
|
|
32
|
-
MSG = 'Do not use negation matcher immediately after visit.'
|
|
33
|
-
RESTRICT_ON_SEND = %i[visit].freeze
|
|
34
|
-
|
|
35
|
-
# @!method negation_matcher?(node)
|
|
36
|
-
def_node_matcher :negation_matcher?, <<~PATTERN
|
|
37
|
-
{
|
|
38
|
-
(send (send nil? :expect _) :to (send nil? %NEGATIVE_MATCHERS ...))
|
|
39
|
-
(send (send nil? :expect _) :not_to (send nil? %POSITIVE_MATCHERS ...))
|
|
40
|
-
}
|
|
41
|
-
PATTERN
|
|
42
|
-
|
|
43
|
-
def on_send(node)
|
|
44
|
-
negation_matcher?(node.right_sibling) do
|
|
45
|
-
add_offense(node.right_sibling)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Capybara
|
|
6
|
-
# Checks for there is a more specific matcher offered by Capybara.
|
|
7
|
-
#
|
|
8
|
-
# @example
|
|
9
|
-
#
|
|
10
|
-
# # bad
|
|
11
|
-
# expect(page).to have_selector('button')
|
|
12
|
-
# expect(page).to have_no_selector('button.cls')
|
|
13
|
-
# expect(page).to have_css('button')
|
|
14
|
-
# expect(page).to have_no_css('a.cls', href: 'http://example.com')
|
|
15
|
-
# expect(page).to have_css('table.cls')
|
|
16
|
-
# expect(page).to have_css('select')
|
|
17
|
-
# expect(page).to have_css('input', exact_text: 'foo')
|
|
18
|
-
#
|
|
19
|
-
# # good
|
|
20
|
-
# expect(page).to have_button
|
|
21
|
-
# expect(page).to have_no_button(class: 'cls')
|
|
22
|
-
# expect(page).to have_button
|
|
23
|
-
# expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com')
|
|
24
|
-
# expect(page).to have_table(class: 'cls')
|
|
25
|
-
# expect(page).to have_select
|
|
26
|
-
# expect(page).to have_field(with: 'foo')
|
|
27
|
-
#
|
|
28
|
-
class SpecificMatcher < ::RuboCop::Cop::Base
|
|
29
|
-
MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
|
|
30
|
-
RESTRICT_ON_SEND = %i[have_selector have_no_selector have_css
|
|
31
|
-
have_no_css].freeze
|
|
32
|
-
SPECIFIC_MATCHER = {
|
|
33
|
-
'button' => 'button',
|
|
34
|
-
'a' => 'link',
|
|
35
|
-
'table' => 'table',
|
|
36
|
-
'select' => 'select',
|
|
37
|
-
'input' => 'field'
|
|
38
|
-
}.freeze
|
|
39
|
-
|
|
40
|
-
# @!method first_argument(node)
|
|
41
|
-
def_node_matcher :first_argument, <<~PATTERN
|
|
42
|
-
(send nil? _ (str $_) ... )
|
|
43
|
-
PATTERN
|
|
44
|
-
|
|
45
|
-
# @!method text_with_regexp?(node)
|
|
46
|
-
def_node_search :text_with_regexp?, <<~PATTERN
|
|
47
|
-
(pair (sym {:text :exact_text}) (regexp ...))
|
|
48
|
-
PATTERN
|
|
49
|
-
|
|
50
|
-
def on_send(node)
|
|
51
|
-
first_argument(node) do |arg|
|
|
52
|
-
next unless (matcher = specific_matcher(arg))
|
|
53
|
-
next if CssSelector.multiple_selectors?(arg)
|
|
54
|
-
next unless replaceable?(node, arg, matcher)
|
|
55
|
-
|
|
56
|
-
add_offense(node, message: message(node, matcher))
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
def specific_matcher(arg)
|
|
63
|
-
splitted_arg = arg[/^\w+/, 0]
|
|
64
|
-
SPECIFIC_MATCHER[splitted_arg]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def replaceable?(node, arg, matcher)
|
|
68
|
-
replaceable_attributes?(arg) &&
|
|
69
|
-
!text_with_regexp?(node) &&
|
|
70
|
-
CapybaraHelp.replaceable_option?(node, arg, matcher) &&
|
|
71
|
-
CapybaraHelp.replaceable_pseudo_classes?(arg)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def replaceable_attributes?(selector)
|
|
75
|
-
CapybaraHelp.replaceable_attributes?(
|
|
76
|
-
CssSelector.attributes(selector)
|
|
77
|
-
)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def message(node, matcher)
|
|
81
|
-
format(MSG,
|
|
82
|
-
good_matcher: good_matcher(node, matcher),
|
|
83
|
-
bad_matcher: node.method_name)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def good_matcher(node, matcher)
|
|
87
|
-
node.method_name
|
|
88
|
-
.to_s
|
|
89
|
-
.gsub(/selector|css/, matcher.to_s)
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Capybara
|
|
6
|
-
# Checks for boolean visibility in Capybara finders.
|
|
7
|
-
#
|
|
8
|
-
# Capybara lets you find elements that match a certain visibility using
|
|
9
|
-
# the `:visible` option. `:visible` accepts both boolean and symbols as
|
|
10
|
-
# values, however using booleans can have unwanted effects. `visible:
|
|
11
|
-
# false` does not find just invisible elements, but both visible and
|
|
12
|
-
# invisible elements. For expressiveness and clarity, use one of the
|
|
13
|
-
# symbol values, `:all`, `:hidden` or `:visible`.
|
|
14
|
-
# Read more in
|
|
15
|
-
# https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all[the documentation].
|
|
16
|
-
#
|
|
17
|
-
# @example
|
|
18
|
-
# # bad
|
|
19
|
-
# expect(page).to have_selector('.foo', visible: false)
|
|
20
|
-
# expect(page).to have_css('.foo', visible: true)
|
|
21
|
-
# expect(page).to have_link('my link', visible: false)
|
|
22
|
-
#
|
|
23
|
-
# # good
|
|
24
|
-
# expect(page).to have_selector('.foo', visible: :visible)
|
|
25
|
-
# expect(page).to have_css('.foo', visible: :all)
|
|
26
|
-
# expect(page).to have_link('my link', visible: :hidden)
|
|
27
|
-
#
|
|
28
|
-
class VisibilityMatcher < ::RuboCop::Cop::Base
|
|
29
|
-
MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
|
|
30
|
-
MSG_TRUE = 'Use `:visible` instead of `true`.'
|
|
31
|
-
CAPYBARA_MATCHER_METHODS = %w[
|
|
32
|
-
button
|
|
33
|
-
checked_field
|
|
34
|
-
css
|
|
35
|
-
field
|
|
36
|
-
link
|
|
37
|
-
select
|
|
38
|
-
selector
|
|
39
|
-
table
|
|
40
|
-
unchecked_field
|
|
41
|
-
xpath
|
|
42
|
-
].flat_map do |element|
|
|
43
|
-
["have_#{element}".to_sym, "have_no_#{element}".to_sym]
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
RESTRICT_ON_SEND = CAPYBARA_MATCHER_METHODS
|
|
47
|
-
|
|
48
|
-
# @!method visible_true?(node)
|
|
49
|
-
def_node_matcher :visible_true?, <<~PATTERN
|
|
50
|
-
(send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) true) ...>))
|
|
51
|
-
PATTERN
|
|
52
|
-
|
|
53
|
-
# @!method visible_false?(node)
|
|
54
|
-
def_node_matcher :visible_false?, <<~PATTERN
|
|
55
|
-
(send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) false) ...>))
|
|
56
|
-
PATTERN
|
|
57
|
-
|
|
58
|
-
def on_send(node)
|
|
59
|
-
visible_false?(node) { |arg| add_offense(arg, message: MSG_FALSE) }
|
|
60
|
-
visible_true?(node) { |arg| add_offense(arg, message: MSG_TRUE) }
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
|
|
65
|
-
def capybara_matcher?(method_name)
|
|
66
|
-
CAPYBARA_MATCHER_METHODS.include? method_name
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|