rubocop-capybara 2.18.0 → 2.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -1
- data/README.md +1 -1
- data/config/default.yml +34 -1
- data/lib/rubocop/capybara/config_formatter.rb +1 -1
- data/lib/rubocop/capybara/version.rb +1 -1
- data/lib/rubocop/cop/capybara/click_link_or_button_style.rb +68 -0
- data/lib/rubocop/cop/capybara/current_path_expectation.rb +10 -17
- data/lib/rubocop/cop/capybara/rspec/have_selector.rb +87 -0
- data/lib/rubocop/cop/capybara/rspec/predicate_matcher.rb +269 -0
- data/lib/rubocop/cop/capybara/specific_finders.rb +18 -14
- data/lib/rubocop/cop/capybara_cops.rb +4 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5af741b67ec2d02d9e27e688d553e5e1c8cbb04c845f5d9771b480b291b1d805
|
4
|
+
data.tar.gz: cbef51994ffa79af69a8a7aac9aabf69ccc307b3c2c104eae2a3a4c635467d26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98bcff699a8ee323a24118f26b412ed8e4d26754df9ee82eab0b107f1b96cb2a0ad7ba898925fde7683501244f1474231bd53591f450e6080321e0bd83245257
|
7
|
+
data.tar.gz: 4194b54f6b12d4b168122c59fbfa18ec210e92cb4d20207665787ae870a3a92a240938818e187889f239e5ce29244db5c612c368bf761ee68f65f1617f72092f
|
data/CHANGELOG.md
CHANGED
@@ -2,10 +2,19 @@
|
|
2
2
|
|
3
3
|
## Edge (Unreleased)
|
4
4
|
|
5
|
+
## 2.19.0 (2023-09-20)
|
6
|
+
|
7
|
+
- Add new `Capybara/RSpec/PredicateMatcher` cop. ([@ydah])
|
8
|
+
- Add new `Capybara/RSpec/HaveSelector` cop. ([@ydah])
|
9
|
+
- Add new `Capybara/ClickLinkOrButtonStyle` cop. ([@ydah])
|
10
|
+
- Fix a false positive for `Capybara/SpecificFinders` when `find` with kind option. ([@ydah])
|
11
|
+
- Fix an incorrect autocorrect for `Capybara/CurrentPathExpectation`. ([@ydah])
|
12
|
+
- Drop Ruby 2.6 support. ([@ydah])
|
13
|
+
|
5
14
|
## 2.18.0 (2023-04-21)
|
6
15
|
|
7
16
|
- Fix an offense message for `Capybara/SpecificFinders`. ([@ydah])
|
8
|
-
- Expand `Capybara/NegationMatcher` to support `have_content
|
17
|
+
- Expand `Capybara/NegationMatcher` to support `have_content`. ([@OskarsEzerins])
|
9
18
|
- Fix an incorrect autocorrect for `Capybara/CurrentPathExpectation` when matcher's argument is a method with a argument and no parentheses. ([@ydah])
|
10
19
|
|
11
20
|
## 2.17.1 (2023-02-13)
|
data/README.md
CHANGED
data/config/default.yml
CHANGED
@@ -2,13 +2,23 @@
|
|
2
2
|
Capybara:
|
3
3
|
Enabled: true
|
4
4
|
DocumentationBaseURL: https://docs.rubocop.org/rubocop-capybara
|
5
|
-
Include:
|
5
|
+
Include: &1
|
6
6
|
- "**/*_spec.rb"
|
7
7
|
- "**/spec/**/*"
|
8
8
|
- "**/test/**/*"
|
9
9
|
- "**/*_steps.rb"
|
10
10
|
- "**/features/step_definitions/**/*"
|
11
11
|
|
12
|
+
Capybara/ClickLinkOrButtonStyle:
|
13
|
+
Description: Checks for click button or link style.
|
14
|
+
Enabled: pending
|
15
|
+
VersionAdded: '2.19'
|
16
|
+
EnforcedStyle: strict
|
17
|
+
SupportedStyles:
|
18
|
+
- strict
|
19
|
+
- link_or_button
|
20
|
+
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/ClickLinkOrButtonStyle
|
21
|
+
|
12
22
|
Capybara/CurrentPathExpectation:
|
13
23
|
Description: Checks that no expectations are set on Capybara's `current_path`.
|
14
24
|
Enabled: true
|
@@ -56,3 +66,26 @@ Capybara/VisibilityMatcher:
|
|
56
66
|
VersionAdded: '1.39'
|
57
67
|
VersionChanged: '2.0'
|
58
68
|
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/VisibilityMatcher
|
69
|
+
|
70
|
+
Capybara/RSpec:
|
71
|
+
Enabled: true
|
72
|
+
Include: *1
|
73
|
+
|
74
|
+
Capybara/RSpec/HaveSelector:
|
75
|
+
Description: Use `have_css` or `have_xpath` instead of `have_selector`.
|
76
|
+
Enabled: pending
|
77
|
+
DefaultSelector: css
|
78
|
+
VersionAdded: '2.19'
|
79
|
+
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/HaveSelector
|
80
|
+
|
81
|
+
Capybara/RSpec/PredicateMatcher:
|
82
|
+
Description: Prefer using predicate matcher over using predicate method directly.
|
83
|
+
Enabled: pending
|
84
|
+
Strict: true
|
85
|
+
EnforcedStyle: inflected
|
86
|
+
AllowedExplicitMatchers: []
|
87
|
+
SupportedStyles:
|
88
|
+
- inflected
|
89
|
+
- explicit
|
90
|
+
VersionAdded: '2.19'
|
91
|
+
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/PredicateMatcher
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
# Builds a YAML config file from two config hashes
|
8
8
|
class ConfigFormatter
|
9
9
|
EXTENSION_ROOT_DEPARTMENT = %r{^(Capybara/)}.freeze
|
10
|
-
SUBDEPARTMENTS =
|
10
|
+
SUBDEPARTMENTS = %(Capybara/RSpec)
|
11
11
|
AMENDMENTS = [].freeze
|
12
12
|
COP_DOC_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/'
|
13
13
|
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Capybara
|
6
|
+
# Checks for click button or link style.
|
7
|
+
#
|
8
|
+
# @example EnforcedStyle: strict (default)
|
9
|
+
# # bad
|
10
|
+
# click_link_or_button('foo')
|
11
|
+
# click_on('foo')
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# click_link('foo')
|
15
|
+
# click_button('foo')
|
16
|
+
#
|
17
|
+
# @example EnforcedStyle: link_or_button
|
18
|
+
# # bad
|
19
|
+
# click_link('foo')
|
20
|
+
# click_button('foo')
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# click_link_or_button('foo')
|
24
|
+
# click_on('foo')
|
25
|
+
#
|
26
|
+
class ClickLinkOrButtonStyle < ::RuboCop::Cop::Base
|
27
|
+
include ConfigurableEnforcedStyle
|
28
|
+
|
29
|
+
MSG_STRICT =
|
30
|
+
'Use `click_link` or `click_button` instead of `%<method>s`.'
|
31
|
+
MSG_CLICK_LINK_OR_BUTTON =
|
32
|
+
'Use `click_link_or_button` or `click_on` instead of `%<method>s`.'
|
33
|
+
STRICT_METHODS = %i[click_link click_button].freeze
|
34
|
+
CLICK_LINK_OR_BUTTON = %i[click_link_or_button click_on].freeze
|
35
|
+
RESTRICT_ON_SEND = (STRICT_METHODS + CLICK_LINK_OR_BUTTON).freeze
|
36
|
+
|
37
|
+
def on_send(node)
|
38
|
+
return unless offense?(node)
|
39
|
+
|
40
|
+
add_offense(node, message: offense_message(node))
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def offense?(node)
|
46
|
+
style == :strict && !strict_method?(node) ||
|
47
|
+
style == :link_or_button && !link_or_button_method?(node)
|
48
|
+
end
|
49
|
+
|
50
|
+
def offense_message(node)
|
51
|
+
if style == :strict
|
52
|
+
format(MSG_STRICT, method: node.method_name)
|
53
|
+
elsif style == :link_or_button
|
54
|
+
format(MSG_CLICK_LINK_OR_BUTTON, method: node.method_name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def strict_method?(node)
|
59
|
+
STRICT_METHODS.include?(node.method_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def link_or_button_method?(node)
|
63
|
+
CLICK_LINK_OR_BUTTON.include?(node.method_name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -18,15 +18,13 @@ module RuboCop
|
|
18
18
|
# @example
|
19
19
|
# # bad
|
20
20
|
# expect(current_path).to eq('/callback')
|
21
|
+
# expect(page.current_path).to eq('/callback')
|
21
22
|
#
|
22
23
|
# # good
|
23
|
-
# expect(page).to have_current_path('/callback')
|
24
|
+
# expect(page).to have_current_path('/callback', ignore_query: true)
|
24
25
|
#
|
25
|
-
# # bad (does not support autocorrection)
|
26
|
-
# expect(page
|
27
|
-
#
|
28
|
-
# # good
|
29
|
-
# expect(page).to have_current_path('/callback')
|
26
|
+
# # bad (does not support autocorrection when `match` with a variable)
|
27
|
+
# expect(page).to match(variable)
|
30
28
|
#
|
31
29
|
class CurrentPathExpectation < ::RuboCop::Cop::Base
|
32
30
|
extend AutoCorrector
|
@@ -95,7 +93,7 @@ module RuboCop
|
|
95
93
|
end
|
96
94
|
corrector.replace(matcher_node.loc.selector, matcher_method)
|
97
95
|
add_argument_parentheses(corrector, matcher_node.first_argument)
|
98
|
-
add_ignore_query_options(corrector, node)
|
96
|
+
add_ignore_query_options(corrector, node, matcher_node)
|
99
97
|
end
|
100
98
|
|
101
99
|
def convert_regexp_node_to_literal(corrector, matcher_node, regexp_node)
|
@@ -129,18 +127,13 @@ module RuboCop
|
|
129
127
|
# `have_current_path` with no options will include the querystring
|
130
128
|
# while `page.current_path` does not.
|
131
129
|
# This ensures the option `ignore_query: true` is added
|
132
|
-
# except when
|
133
|
-
def add_ignore_query_options(corrector, node)
|
130
|
+
# except when `match` matcher.
|
131
|
+
def add_ignore_query_options(corrector, node, matcher_node)
|
132
|
+
return if matcher_node.method?(:match)
|
133
|
+
|
134
134
|
expectation_node = node.parent.last_argument
|
135
135
|
expectation_last_child = expectation_node.children.last
|
136
|
-
|
137
|
-
regexp str dstr xstr
|
138
|
-
].include?(expectation_last_child.type)
|
139
|
-
|
140
|
-
corrector.insert_after(
|
141
|
-
expectation_last_child,
|
142
|
-
', ignore_query: true'
|
143
|
-
)
|
136
|
+
corrector.insert_after(expectation_last_child, ', ignore_query: true')
|
144
137
|
end
|
145
138
|
end
|
146
139
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Capybara
|
6
|
+
module RSpec
|
7
|
+
# Use `have_css` or `have_xpath` instead of `have_selector`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# expect(foo).to have_selector(:css, 'bar')
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# expect(foo).to have_css('bar')
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# expect(foo).to have_selector(:xpath, 'bar')
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# expect(foo).to have_xpath('bar')
|
21
|
+
#
|
22
|
+
# @example DefaultSelector: css (default)
|
23
|
+
# # bad
|
24
|
+
# expect(foo).to have_selector('bar')
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# expect(foo).to have_css('bar')
|
28
|
+
#
|
29
|
+
# @example DefaultSelector: xpath
|
30
|
+
# # bad
|
31
|
+
# expect(foo).to have_selector('bar')
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# expect(foo).to have_xpath('bar')
|
35
|
+
#
|
36
|
+
class HaveSelector < ::RuboCop::Cop::Base
|
37
|
+
extend AutoCorrector
|
38
|
+
include RangeHelp
|
39
|
+
|
40
|
+
MSG = 'Use `%<good>s` instead of `have_selector`.'
|
41
|
+
RESTRICT_ON_SEND = %i[have_selector].freeze
|
42
|
+
SELECTORS = %i[css xpath].freeze
|
43
|
+
|
44
|
+
def on_send(node)
|
45
|
+
argument = node.first_argument
|
46
|
+
on_select_with_type(node, argument) if argument.sym_type?
|
47
|
+
on_select_without_type(node) if argument.str_type?
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def on_select_with_type(node, type)
|
53
|
+
return unless SELECTORS.include?(type.value)
|
54
|
+
|
55
|
+
add_offense(node, message: message_typed(type)) do |corrector|
|
56
|
+
corrector.remove(deletion_range(type, node.arguments[1]))
|
57
|
+
corrector.replace(node.loc.selector, "have_#{type.value}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def message_typed(type)
|
62
|
+
format(MSG, good: "have_#{type.value}")
|
63
|
+
end
|
64
|
+
|
65
|
+
def deletion_range(first_argument, second_argument)
|
66
|
+
range_between(first_argument.source_range.begin_pos,
|
67
|
+
second_argument.source_range.begin_pos)
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_select_without_type(node)
|
71
|
+
add_offense(node, message: message_untyped) do |corrector|
|
72
|
+
corrector.replace(node.loc.selector, "have_#{default_selector}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def message_untyped
|
77
|
+
format(MSG, good: "have_#{default_selector}")
|
78
|
+
end
|
79
|
+
|
80
|
+
def default_selector
|
81
|
+
cop_config.fetch('DefaultSelector', 'css')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,269 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Capybara
|
6
|
+
module RSpec
|
7
|
+
# A helper for `inflected` style
|
8
|
+
module InflectedHelper
|
9
|
+
extend NodePattern::Macros
|
10
|
+
|
11
|
+
EXPLICIT_MATCHER = %w[css selector style xpath].map do |suffix|
|
12
|
+
"matches_#{suffix}?".to_sym
|
13
|
+
end.freeze
|
14
|
+
MSG_INFLECTED = 'Prefer using `%<matcher_name>s` matcher over ' \
|
15
|
+
'`%<predicate_name>s`.'
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def check_inflected(node)
|
20
|
+
predicate_in_actual?(node) do |predicate, to, matcher|
|
21
|
+
msg = message_inflected(predicate)
|
22
|
+
add_offense(node, message: msg) do |corrector|
|
23
|
+
remove_predicate(corrector, predicate)
|
24
|
+
corrector.replace(node.loc.selector,
|
25
|
+
true?(to, matcher) ? 'to' : 'not_to')
|
26
|
+
rewrite_matcher(corrector, predicate, matcher)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!method predicate_in_actual?(node)
|
32
|
+
def_node_matcher :predicate_in_actual?, <<~PATTERN
|
33
|
+
(send
|
34
|
+
(send nil? :expect
|
35
|
+
{
|
36
|
+
(block $(send !nil? #predicate? ...) ...)
|
37
|
+
$(send !nil? #predicate? ...)
|
38
|
+
}
|
39
|
+
)
|
40
|
+
${:to :to_not :not_to}
|
41
|
+
$#boolean_matcher?
|
42
|
+
)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
# @!method be_bool?(node)
|
46
|
+
def_node_matcher :be_bool?, <<~PATTERN
|
47
|
+
(send nil? {:be :eq :eql :equal} {true false})
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
# @!method be_boolthy?(node)
|
51
|
+
def_node_matcher :be_boolthy?, <<~PATTERN
|
52
|
+
(send nil? {:be_truthy :be_falsey :be_falsy :a_truthy_value :a_falsey_value :a_falsy_value})
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
def boolean_matcher?(node)
|
56
|
+
if cop_config['Strict']
|
57
|
+
be_boolthy?(node)
|
58
|
+
else
|
59
|
+
be_bool?(node) || be_boolthy?(node)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def predicate?(sym)
|
64
|
+
EXPLICIT_MATCHER.include?(sym)
|
65
|
+
end
|
66
|
+
|
67
|
+
def message_inflected(predicate)
|
68
|
+
format(MSG_INFLECTED,
|
69
|
+
predicate_name: predicate.method_name,
|
70
|
+
matcher_name: to_predicate_matcher(predicate.method_name))
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_predicate_matcher(name)
|
74
|
+
name.to_s.sub('matches_', 'match_')[0..-2]
|
75
|
+
end
|
76
|
+
|
77
|
+
def remove_predicate(corrector, predicate)
|
78
|
+
range = predicate.loc.dot.with(
|
79
|
+
end_pos: predicate.source_range.end_pos
|
80
|
+
)
|
81
|
+
|
82
|
+
corrector.remove(range)
|
83
|
+
end
|
84
|
+
|
85
|
+
def rewrite_matcher(corrector, predicate, matcher)
|
86
|
+
args = args_loc(predicate).source
|
87
|
+
|
88
|
+
corrector.replace(
|
89
|
+
matcher,
|
90
|
+
to_predicate_matcher(predicate.method_name) + args
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def true?(to_symbol, matcher)
|
95
|
+
result = case matcher.method_name
|
96
|
+
when :be, :eq
|
97
|
+
matcher.first_argument.true_type?
|
98
|
+
when :be_truthy, :a_truthy_value
|
99
|
+
true
|
100
|
+
when :be_falsey, :be_falsy, :a_falsey_value, :a_falsy_value
|
101
|
+
false
|
102
|
+
end
|
103
|
+
to_symbol == :to ? result : !result
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# A helper for `explicit` style
|
108
|
+
module ExplicitHelper
|
109
|
+
extend NodePattern::Macros
|
110
|
+
|
111
|
+
MSG_EXPLICIT = 'Prefer using `%<predicate_name>s` over ' \
|
112
|
+
'`%<matcher_name>s` matcher.'
|
113
|
+
BUILT_IN_MATCHERS = %w[
|
114
|
+
be_truthy be_falsey be_falsy
|
115
|
+
have_attributes have_received
|
116
|
+
be_between be_within
|
117
|
+
].freeze
|
118
|
+
INFLECTED_MATCHER = %w[css selector style xpath].each.map do |suffix|
|
119
|
+
"match_#{suffix}"
|
120
|
+
end.freeze
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def allowed_explicit_matchers
|
125
|
+
cop_config.fetch('AllowedExplicitMatchers', []) + BUILT_IN_MATCHERS
|
126
|
+
end
|
127
|
+
|
128
|
+
def check_explicit(node) # rubocop:disable Metrics/MethodLength
|
129
|
+
predicate_matcher?(node) do |actual, matcher|
|
130
|
+
add_offense(node,
|
131
|
+
message: message_explicit(matcher)) do |corrector|
|
132
|
+
corrector_explicit(corrector, node, actual, matcher)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# @!method predicate_matcher?(node)
|
138
|
+
def_node_matcher :predicate_matcher?, <<~PATTERN
|
139
|
+
(send
|
140
|
+
(send nil? :expect $!nil?)
|
141
|
+
{:to :to_not :not_to}
|
142
|
+
{
|
143
|
+
$(send nil? #predicate_matcher_name? ...)
|
144
|
+
(block $(send nil? #predicate_matcher_name? ...) ...)
|
145
|
+
}
|
146
|
+
)
|
147
|
+
PATTERN
|
148
|
+
|
149
|
+
def predicate_matcher_name?(name)
|
150
|
+
name = name.to_s
|
151
|
+
return false if allowed_explicit_matchers.include?(name)
|
152
|
+
|
153
|
+
INFLECTED_MATCHER.include?(name)
|
154
|
+
end
|
155
|
+
|
156
|
+
def message_explicit(matcher)
|
157
|
+
format(MSG_EXPLICIT,
|
158
|
+
predicate_name: to_predicate_method(matcher.method_name),
|
159
|
+
matcher_name: matcher.method_name)
|
160
|
+
end
|
161
|
+
|
162
|
+
def corrector_explicit(corrector, to_node, actual, matcher)
|
163
|
+
replacement_matcher = replacement_matcher(to_node)
|
164
|
+
corrector.replace(matcher, replacement_matcher)
|
165
|
+
move_predicate(corrector, actual, matcher)
|
166
|
+
corrector.replace(to_node.loc.selector, 'to')
|
167
|
+
end
|
168
|
+
|
169
|
+
def move_predicate(corrector, actual, matcher)
|
170
|
+
predicate = to_predicate_method(matcher.method_name)
|
171
|
+
args = args_loc(matcher).source
|
172
|
+
corrector.insert_after(actual,
|
173
|
+
".#{predicate}" + args)
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_predicate_method(matcher)
|
177
|
+
"#{matcher.to_s.sub('match_', 'matches_')}?"
|
178
|
+
end
|
179
|
+
|
180
|
+
def replacement_matcher(node)
|
181
|
+
case [cop_config['Strict'], node.method?(:to)]
|
182
|
+
when [true, true]
|
183
|
+
'be(true)'
|
184
|
+
when [true, false]
|
185
|
+
'be(false)'
|
186
|
+
when [false, true]
|
187
|
+
'be_truthy'
|
188
|
+
when [false, false]
|
189
|
+
'be_falsey'
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Prefer using predicate matcher over using predicate method directly.
|
195
|
+
#
|
196
|
+
# Capybara defines magic matchers for predicate methods.
|
197
|
+
# This cop recommends to use the predicate matcher instead of using
|
198
|
+
# predicate method directly.
|
199
|
+
#
|
200
|
+
# @example Strict: true, EnforcedStyle: inflected (default)
|
201
|
+
# # bad
|
202
|
+
# expect(foo.matches_css?(bar: 'baz')).to be_truthy
|
203
|
+
# expect(foo.matches_selector?(bar: 'baz')).to be_truthy
|
204
|
+
# expect(foo.matches_style?(bar: 'baz')).to be_truthy
|
205
|
+
# expect(foo.matches_xpath?(bar: 'baz')).to be_truthy
|
206
|
+
#
|
207
|
+
# # good
|
208
|
+
# expect(foo).to match_css(bar: 'baz')
|
209
|
+
# expect(foo).to match_selector(bar: 'baz')
|
210
|
+
# expect(foo).to match_style(bar: 'baz')
|
211
|
+
# expect(foo).to match_xpath(bar: 'baz')
|
212
|
+
#
|
213
|
+
# # also good - It checks "true" strictly.
|
214
|
+
# expect(foo.matches_style?(bar: 'baz')).to be(true)
|
215
|
+
#
|
216
|
+
# @example Strict: false, EnforcedStyle: inflected
|
217
|
+
# # bad
|
218
|
+
# expect(foo.matches_style?(bar: 'baz')).to be_truthy
|
219
|
+
# expect(foo.matches_style?(bar: 'baz')).to be(true)
|
220
|
+
#
|
221
|
+
# # good
|
222
|
+
# expect(foo).to match_style(bar: 'baz')
|
223
|
+
#
|
224
|
+
# @example Strict: true, EnforcedStyle: explicit
|
225
|
+
# # bad
|
226
|
+
# expect(foo).to match_style(bar: 'baz')
|
227
|
+
#
|
228
|
+
# # good - the above code is rewritten to it by this cop
|
229
|
+
# expect(foo.matches_style?(bar: 'baz')).to be(true)
|
230
|
+
#
|
231
|
+
# @example Strict: false, EnforcedStyle: explicit
|
232
|
+
# # bad
|
233
|
+
# expect(foo).to match_style(bar: 'baz')
|
234
|
+
#
|
235
|
+
# # good - the above code is rewritten to it by this cop
|
236
|
+
# expect(foo.matches_style?(bar: 'baz')).to be_truthy
|
237
|
+
#
|
238
|
+
class PredicateMatcher < ::RuboCop::Cop::Base
|
239
|
+
extend AutoCorrector
|
240
|
+
include ConfigurableEnforcedStyle
|
241
|
+
include InflectedHelper
|
242
|
+
include ExplicitHelper
|
243
|
+
|
244
|
+
RESTRICT_ON_SEND = %i[to to_not not_to].freeze
|
245
|
+
|
246
|
+
def on_send(node)
|
247
|
+
if style == :inflected
|
248
|
+
check_inflected(node)
|
249
|
+
elsif style == :explicit
|
250
|
+
check_explicit(node)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
# returns args location with whitespace
|
257
|
+
# @example
|
258
|
+
# foo 1, 2
|
259
|
+
# ^^^^^
|
260
|
+
def args_loc(send_node)
|
261
|
+
send_node.loc.selector.end.with(
|
262
|
+
end_pos: send_node.source_range.end_pos
|
263
|
+
)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
@@ -8,15 +8,14 @@ module RuboCop
|
|
8
8
|
# @example
|
9
9
|
# # bad
|
10
10
|
# find('#some-id')
|
11
|
-
# find('[
|
11
|
+
# find('[id=some-id]')
|
12
|
+
# find(:css, '#some-id')
|
12
13
|
#
|
13
14
|
# # good
|
14
15
|
# find_by_id('some-id')
|
15
|
-
# find_by_id('some-id', visible: true)
|
16
16
|
#
|
17
17
|
class SpecificFinders < ::RuboCop::Cop::Base
|
18
18
|
extend AutoCorrector
|
19
|
-
|
20
19
|
include RangeHelp
|
21
20
|
|
22
21
|
MSG = 'Prefer `find_by_id` over `find`.'
|
@@ -24,7 +23,7 @@ module RuboCop
|
|
24
23
|
|
25
24
|
# @!method find_argument(node)
|
26
25
|
def_node_matcher :find_argument, <<~PATTERN
|
27
|
-
(send _ :find (str $_) ...)
|
26
|
+
(send _ :find $(sym :css)? (str $_) ...)
|
28
27
|
PATTERN
|
29
28
|
|
30
29
|
# @!method class_options(node)
|
@@ -33,30 +32,30 @@ module RuboCop
|
|
33
32
|
PATTERN
|
34
33
|
|
35
34
|
def on_send(node)
|
36
|
-
find_argument(node) do |arg|
|
35
|
+
find_argument(node) do |sym, arg|
|
37
36
|
next if CssSelector.pseudo_classes(arg).any?
|
38
37
|
next if CssSelector.multiple_selectors?(arg)
|
39
38
|
|
40
|
-
on_attr(node, arg) if attribute?(arg)
|
41
|
-
on_id(node, arg) if CssSelector.id?(arg)
|
39
|
+
on_attr(node, sym, arg) if attribute?(arg)
|
40
|
+
on_id(node, sym, arg) if CssSelector.id?(arg)
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
45
44
|
private
|
46
45
|
|
47
|
-
def on_attr(node, arg)
|
46
|
+
def on_attr(node, sym, arg)
|
48
47
|
attrs = CssSelector.attributes(arg)
|
49
48
|
return unless (id = attrs['id'])
|
50
49
|
return if attrs['class']
|
51
50
|
|
52
|
-
register_offense(node, replaced_arguments(arg, id))
|
51
|
+
register_offense(node, sym, replaced_arguments(arg, id))
|
53
52
|
end
|
54
53
|
|
55
|
-
def on_id(node, arg)
|
54
|
+
def on_id(node, sym, arg)
|
56
55
|
return if CssSelector.attributes(arg).any?
|
57
56
|
|
58
57
|
id = CssSelector.id(arg)
|
59
|
-
register_offense(node, "'#{id}'",
|
58
|
+
register_offense(node, sym, "'#{id}'",
|
60
59
|
CssSelector.classes(arg.sub("##{id}", '')))
|
61
60
|
end
|
62
61
|
|
@@ -65,17 +64,22 @@ module RuboCop
|
|
65
64
|
CapybaraHelp.common_attributes?(arg)
|
66
65
|
end
|
67
66
|
|
68
|
-
def register_offense(node, id, classes = [])
|
67
|
+
def register_offense(node, sym, id, classes = [])
|
69
68
|
add_offense(offense_range(node)) do |corrector|
|
70
69
|
corrector.replace(node.loc.selector, 'find_by_id')
|
71
|
-
corrector.replace(node.first_argument,
|
72
|
-
id.delete('\\'))
|
70
|
+
corrector.replace(node.first_argument, id.delete('\\'))
|
73
71
|
unless classes.compact.empty?
|
74
72
|
autocorrect_classes(corrector, node, classes)
|
75
73
|
end
|
74
|
+
corrector.remove(deletion_range(node)) unless sym.empty?
|
76
75
|
end
|
77
76
|
end
|
78
77
|
|
78
|
+
def deletion_range(node)
|
79
|
+
range_between(node.arguments[0].source_range.end_pos,
|
80
|
+
node.arguments[1].source_range.end_pos)
|
81
|
+
end
|
82
|
+
|
79
83
|
def autocorrect_classes(corrector, node, classes)
|
80
84
|
if (options = class_options(node).first)
|
81
85
|
append_options(classes, options)
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'capybara/rspec/have_selector'
|
4
|
+
require_relative 'capybara/rspec/predicate_matcher'
|
5
|
+
|
6
|
+
require_relative 'capybara/click_link_or_button_style'
|
3
7
|
require_relative 'capybara/current_path_expectation'
|
4
8
|
require_relative 'capybara/match_style'
|
5
9
|
require_relative 'capybara/negation_matcher'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-capybara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.19.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yudai Takada
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -43,11 +43,14 @@ files:
|
|
43
43
|
- lib/rubocop/capybara/config_formatter.rb
|
44
44
|
- lib/rubocop/capybara/description_extractor.rb
|
45
45
|
- lib/rubocop/capybara/version.rb
|
46
|
+
- lib/rubocop/cop/capybara/click_link_or_button_style.rb
|
46
47
|
- lib/rubocop/cop/capybara/current_path_expectation.rb
|
47
48
|
- lib/rubocop/cop/capybara/match_style.rb
|
48
49
|
- lib/rubocop/cop/capybara/mixin/capybara_help.rb
|
49
50
|
- lib/rubocop/cop/capybara/mixin/css_selector.rb
|
50
51
|
- lib/rubocop/cop/capybara/negation_matcher.rb
|
52
|
+
- lib/rubocop/cop/capybara/rspec/have_selector.rb
|
53
|
+
- lib/rubocop/cop/capybara/rspec/predicate_matcher.rb
|
51
54
|
- lib/rubocop/cop/capybara/specific_actions.rb
|
52
55
|
- lib/rubocop/cop/capybara/specific_finders.rb
|
53
56
|
- lib/rubocop/cop/capybara/specific_matcher.rb
|
@@ -68,14 +71,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
71
|
requirements:
|
69
72
|
- - ">="
|
70
73
|
- !ruby/object:Gem::Version
|
71
|
-
version: 2.
|
74
|
+
version: 2.7.0
|
72
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
76
|
requirements:
|
74
77
|
- - ">="
|
75
78
|
- !ruby/object:Gem::Version
|
76
79
|
version: '0'
|
77
80
|
requirements: []
|
78
|
-
rubygems_version: 3.
|
81
|
+
rubygems_version: 3.3.7
|
79
82
|
signing_key:
|
80
83
|
specification_version: 4
|
81
84
|
summary: Code style checking for Capybara test files
|