rubocop-capybara 2.18.0 → 2.19.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 +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
|