rubocop-capybara 2.18.0 → 2.20.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 +19 -1
- data/README.md +1 -1
- data/config/default.yml +43 -2
- 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 +75 -0
- data/lib/rubocop/cop/capybara/current_path_expectation.rb +10 -17
- data/lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb +72 -0
- data/lib/rubocop/cop/capybara/mixin/css_selector.rb +2 -24
- data/lib/rubocop/cop/capybara/negation_matcher.rb +10 -10
- data/lib/rubocop/cop/capybara/redundant_within_find.rb +66 -0
- 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/specific_matcher.rb +6 -0
- data/lib/rubocop/cop/capybara_cops.rb +5 -0
- data/lib/rubocop-capybara.rb +1 -0
- metadata +9 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 963a68452b58c47db1d1d9e7f9201db0a98a8b70bb8089473b406911fa5cc33e
|
|
4
|
+
data.tar.gz: 58e60dd5ff6097695a1029b9e37414209f7c26d2da0d16cbc6b808c8cd56efd8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cec93e7919aeefbb1f2b2c13f1f0caeb7627c5556f85fe219f107b6520237cccd792ad4713905dfb47151f0bcb82e04d4bd0034a7a747b741e607ea9d4de5e54
|
|
7
|
+
data.tar.gz: 3e359ec71065b5daf64cdb080d8ddfad4f6144778ad7fb89d932aa083353547663a1316efe7f9e9fd3cb7bca6a6f012812ea7f18e00c76a8af22316f24325f7c
|
data/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Edge (Unreleased)
|
|
4
4
|
|
|
5
|
+
## 2.20.0 (2024-01-03)
|
|
6
|
+
|
|
7
|
+
- Change to default `EnforcedStyle: link_or_button` for `Capybara/ClickLinkOrButtonStyle` cop. ([@ydah])
|
|
8
|
+
- Fix a false negative for `RSpec/HaveSelector` when first argument is dstr node. ([@ydah])
|
|
9
|
+
- Add new `Capybara/RedundantWithinFind` cop. ([@ydah])
|
|
10
|
+
- Fix an invalid attributes parse when name with multiple `[]` for `Capybara/SpecificFinders` and `Capybara/SpecificActions` and `Capybara/SpecificMatcher`. ([@ydah])
|
|
11
|
+
- Change to default `EnforcedStyle: have_no` for `Capybara/NegationMatcher` cop. ([@ydah])
|
|
12
|
+
- Fix a false positive for `Capybara/SpecificMatcher` when `text:` or `exact_text:` with regexp. ([@ydah])
|
|
13
|
+
|
|
14
|
+
## 2.19.0 (2023-09-20)
|
|
15
|
+
|
|
16
|
+
- Add new `Capybara/RSpec/PredicateMatcher` cop. ([@ydah])
|
|
17
|
+
- Add new `Capybara/RSpec/HaveSelector` cop. ([@ydah])
|
|
18
|
+
- Add new `Capybara/ClickLinkOrButtonStyle` cop. ([@ydah])
|
|
19
|
+
- Fix a false positive for `Capybara/SpecificFinders` when `find` with kind option. ([@ydah])
|
|
20
|
+
- Fix an incorrect autocorrect for `Capybara/CurrentPathExpectation`. ([@ydah])
|
|
21
|
+
- Drop Ruby 2.6 support. ([@ydah])
|
|
22
|
+
|
|
5
23
|
## 2.18.0 (2023-04-21)
|
|
6
24
|
|
|
7
25
|
- Fix an offense message for `Capybara/SpecificFinders`. ([@ydah])
|
|
8
|
-
- Expand `Capybara/NegationMatcher` to support `have_content
|
|
26
|
+
- Expand `Capybara/NegationMatcher` to support `have_content`. ([@OskarsEzerins])
|
|
9
27
|
- Fix an incorrect autocorrect for `Capybara/CurrentPathExpectation` when matcher's argument is a method with a argument and no parentheses. ([@ydah])
|
|
10
28
|
|
|
11
29
|
## 2.17.1 (2023-02-13)
|
data/README.md
CHANGED
data/config/default.yml
CHANGED
|
@@ -2,13 +2,24 @@
|
|
|
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 methods of button or link clicks.
|
|
14
|
+
Enabled: pending
|
|
15
|
+
VersionAdded: '2.19'
|
|
16
|
+
VersionChanged: '2.20'
|
|
17
|
+
EnforcedStyle: link_or_button
|
|
18
|
+
SupportedStyles:
|
|
19
|
+
- link_or_button
|
|
20
|
+
- strict
|
|
21
|
+
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/ClickLinkOrButtonStyle
|
|
22
|
+
|
|
12
23
|
Capybara/CurrentPathExpectation:
|
|
13
24
|
Description: Checks that no expectations are set on Capybara's `current_path`.
|
|
14
25
|
Enabled: true
|
|
@@ -26,12 +37,19 @@ Capybara/NegationMatcher:
|
|
|
26
37
|
Description: Enforces use of `have_no_*` or `not_to` for negated expectations.
|
|
27
38
|
Enabled: pending
|
|
28
39
|
VersionAdded: '2.14'
|
|
29
|
-
|
|
40
|
+
VersionChanged: '2.20'
|
|
41
|
+
EnforcedStyle: have_no
|
|
30
42
|
SupportedStyles:
|
|
31
43
|
- have_no
|
|
32
44
|
- not_to
|
|
33
45
|
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcher
|
|
34
46
|
|
|
47
|
+
Capybara/RedundantWithinFind:
|
|
48
|
+
Description: Checks for redundant `within find(...)` calls.
|
|
49
|
+
Enabled: pending
|
|
50
|
+
VersionAdded: '2.20'
|
|
51
|
+
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RedundantWithinFind
|
|
52
|
+
|
|
35
53
|
Capybara/SpecificActions:
|
|
36
54
|
Description: Checks for there is a more specific actions offered by Capybara.
|
|
37
55
|
Enabled: pending
|
|
@@ -56,3 +74,26 @@ Capybara/VisibilityMatcher:
|
|
|
56
74
|
VersionAdded: '1.39'
|
|
57
75
|
VersionChanged: '2.0'
|
|
58
76
|
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/VisibilityMatcher
|
|
77
|
+
|
|
78
|
+
Capybara/RSpec:
|
|
79
|
+
Enabled: true
|
|
80
|
+
Include: *1
|
|
81
|
+
|
|
82
|
+
Capybara/RSpec/HaveSelector:
|
|
83
|
+
Description: Use `have_css` or `have_xpath` instead of `have_selector`.
|
|
84
|
+
Enabled: pending
|
|
85
|
+
DefaultSelector: css
|
|
86
|
+
VersionAdded: '2.19'
|
|
87
|
+
Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/HaveSelector
|
|
88
|
+
|
|
89
|
+
Capybara/RSpec/PredicateMatcher:
|
|
90
|
+
Description: Prefer using predicate matcher over using predicate method directly.
|
|
91
|
+
Enabled: pending
|
|
92
|
+
Strict: true
|
|
93
|
+
EnforcedStyle: inflected
|
|
94
|
+
AllowedExplicitMatchers: []
|
|
95
|
+
SupportedStyles:
|
|
96
|
+
- inflected
|
|
97
|
+
- explicit
|
|
98
|
+
VersionAdded: '2.19'
|
|
99
|
+
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,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Capybara
|
|
6
|
+
# Checks for methods of button or link clicks.
|
|
7
|
+
#
|
|
8
|
+
# By default, prefer to use `click_link_or_button` or `click_on`.
|
|
9
|
+
# These methods offer a weaker coupling between the test and HTML,
|
|
10
|
+
# allowing for a more faithful reflection of how the user behaves.
|
|
11
|
+
#
|
|
12
|
+
# You can set `EnforcedStyle: strict` to prefer the use of
|
|
13
|
+
# `click_link` and `click_button`, but this is a deprecated setting.
|
|
14
|
+
#
|
|
15
|
+
# @example EnforcedStyle: link_or_button (default)
|
|
16
|
+
# # bad
|
|
17
|
+
# click_link('foo')
|
|
18
|
+
# click_button('foo')
|
|
19
|
+
#
|
|
20
|
+
# # good
|
|
21
|
+
# click_link_or_button('foo')
|
|
22
|
+
# click_on('foo')
|
|
23
|
+
#
|
|
24
|
+
# @example EnforcedStyle: strict
|
|
25
|
+
# # bad
|
|
26
|
+
# click_link_or_button('foo')
|
|
27
|
+
# click_on('foo')
|
|
28
|
+
#
|
|
29
|
+
# # good
|
|
30
|
+
# click_link('foo')
|
|
31
|
+
# click_button('foo')
|
|
32
|
+
#
|
|
33
|
+
class ClickLinkOrButtonStyle < ::RuboCop::Cop::Base
|
|
34
|
+
include ConfigurableEnforcedStyle
|
|
35
|
+
|
|
36
|
+
MSG_STRICT =
|
|
37
|
+
'Use `click_link` or `click_button` instead of `%<method>s`.'
|
|
38
|
+
MSG_CLICK_LINK_OR_BUTTON =
|
|
39
|
+
'Use `click_link_or_button` or `click_on` instead of `%<method>s`.'
|
|
40
|
+
STRICT_METHODS = %i[click_link click_button].freeze
|
|
41
|
+
CLICK_LINK_OR_BUTTON = %i[click_link_or_button click_on].freeze
|
|
42
|
+
RESTRICT_ON_SEND = (STRICT_METHODS + CLICK_LINK_OR_BUTTON).freeze
|
|
43
|
+
|
|
44
|
+
def on_send(node)
|
|
45
|
+
return unless offense?(node)
|
|
46
|
+
|
|
47
|
+
add_offense(node, message: offense_message(node))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def offense?(node)
|
|
53
|
+
(style == :strict && !strict_method?(node)) ||
|
|
54
|
+
(style == :link_or_button && !link_or_button_method?(node))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def offense_message(node)
|
|
58
|
+
if style == :strict
|
|
59
|
+
format(MSG_STRICT, method: node.method_name)
|
|
60
|
+
elsif style == :link_or_button
|
|
61
|
+
format(MSG_CLICK_LINK_OR_BUTTON, method: node.method_name)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def strict_method?(node)
|
|
66
|
+
STRICT_METHODS.include?(node.method_name)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def link_or_button_method?(node)
|
|
70
|
+
CLICK_LINK_OR_BUTTON.include?(node.method_name)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
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,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Capybara
|
|
6
|
+
# Css selector parser.
|
|
7
|
+
# @api private
|
|
8
|
+
class CssAttributesParser
|
|
9
|
+
def initialize(selector)
|
|
10
|
+
@selector = selector
|
|
11
|
+
@state = :initial
|
|
12
|
+
@temp = ''
|
|
13
|
+
@results = {}
|
|
14
|
+
@bracket_count = 0
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [Array<String>]
|
|
18
|
+
def parse # rubocop:disable Metrics/MethodLength
|
|
19
|
+
@selector.chars do |char|
|
|
20
|
+
if char == '['
|
|
21
|
+
on_bracket_start
|
|
22
|
+
elsif char == ']'
|
|
23
|
+
on_bracket_end
|
|
24
|
+
elsif @state == :inside_attr
|
|
25
|
+
@temp += char
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
@results
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def on_bracket_start
|
|
34
|
+
@bracket_count += 1
|
|
35
|
+
if @state == :initial
|
|
36
|
+
@state = :inside_attr
|
|
37
|
+
else
|
|
38
|
+
@temp += '['
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def on_bracket_end
|
|
43
|
+
@bracket_count -= 1
|
|
44
|
+
if @bracket_count.zero?
|
|
45
|
+
@state = :initial
|
|
46
|
+
key, value = @temp.split('=')
|
|
47
|
+
@results[key] = normalize_value(value)
|
|
48
|
+
@temp.clear
|
|
49
|
+
else
|
|
50
|
+
@temp += ']'
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @param value [String]
|
|
55
|
+
# @return [Boolean, String]
|
|
56
|
+
# @example
|
|
57
|
+
# normalize_value('true') # => true
|
|
58
|
+
# normalize_value('false') # => false
|
|
59
|
+
# normalize_value(nil) # => nil
|
|
60
|
+
# normalize_value("foo") # => "'foo'"
|
|
61
|
+
def normalize_value(value)
|
|
62
|
+
case value
|
|
63
|
+
when 'true' then true
|
|
64
|
+
when 'false' then false
|
|
65
|
+
when nil then nil
|
|
66
|
+
else "'#{value.gsub(/"|'/, '')}'"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -55,15 +55,9 @@ module RuboCop
|
|
|
55
55
|
# attributes('a[foo-bar_baz]') # => {"foo-bar_baz=>nil}
|
|
56
56
|
# attributes('button[foo][bar=baz]') # => {"foo"=>nil, "bar"=>"'baz'"}
|
|
57
57
|
# attributes('table[foo=bar]') # => {"foo"=>"'bar'"}
|
|
58
|
+
# attributes('[foo="bar[baz][qux]"]') # => {"foo"=>"'bar[baz][qux]'"}
|
|
58
59
|
def attributes(selector)
|
|
59
|
-
|
|
60
|
-
# For example, extract the following:
|
|
61
|
-
# 'button[foo][bar=baz]' => 'foo][bar=baz'
|
|
62
|
-
inside_attributes = selector.scan(/\[(.*)\]/).flatten.join
|
|
63
|
-
inside_attributes.split('][').to_h do |attr|
|
|
64
|
-
key, value = attr.split('=')
|
|
65
|
-
[key, normalize_value(value)]
|
|
66
|
-
end
|
|
60
|
+
CssAttributesParser.new(selector).parse
|
|
67
61
|
end
|
|
68
62
|
|
|
69
63
|
# @param selector [String]
|
|
@@ -89,22 +83,6 @@ module RuboCop
|
|
|
89
83
|
normalize = selector.gsub(/(\\[>,+~]|\(.*\))/, '')
|
|
90
84
|
normalize.match?(/[ >,+~]/)
|
|
91
85
|
end
|
|
92
|
-
|
|
93
|
-
# @param value [String]
|
|
94
|
-
# @return [Boolean, String]
|
|
95
|
-
# @example
|
|
96
|
-
# normalize_value('true') # => true
|
|
97
|
-
# normalize_value('false') # => false
|
|
98
|
-
# normalize_value(nil) # => nil
|
|
99
|
-
# normalize_value("foo") # => "'foo'"
|
|
100
|
-
def normalize_value(value)
|
|
101
|
-
case value
|
|
102
|
-
when 'true' then true
|
|
103
|
-
when 'false' then false
|
|
104
|
-
when nil then nil
|
|
105
|
-
else "'#{value.gsub(/"|'/, '')}'"
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
86
|
end
|
|
109
87
|
end
|
|
110
88
|
end
|
|
@@ -5,16 +5,7 @@ module RuboCop
|
|
|
5
5
|
module Capybara
|
|
6
6
|
# Enforces use of `have_no_*` or `not_to` for negated expectations.
|
|
7
7
|
#
|
|
8
|
-
# @example EnforcedStyle:
|
|
9
|
-
# # bad
|
|
10
|
-
# expect(page).to have_no_selector
|
|
11
|
-
# expect(page).to have_no_css('a')
|
|
12
|
-
#
|
|
13
|
-
# # good
|
|
14
|
-
# expect(page).not_to have_selector
|
|
15
|
-
# expect(page).not_to have_css('a')
|
|
16
|
-
#
|
|
17
|
-
# @example EnforcedStyle: have_no
|
|
8
|
+
# @example EnforcedStyle: have_no (default)
|
|
18
9
|
# # bad
|
|
19
10
|
# expect(page).not_to have_selector
|
|
20
11
|
# expect(page).not_to have_css('a')
|
|
@@ -23,6 +14,15 @@ module RuboCop
|
|
|
23
14
|
# expect(page).to have_no_selector
|
|
24
15
|
# expect(page).to have_no_css('a')
|
|
25
16
|
#
|
|
17
|
+
# @example EnforcedStyle: not_to
|
|
18
|
+
# # bad
|
|
19
|
+
# expect(page).to have_no_selector
|
|
20
|
+
# expect(page).to have_no_css('a')
|
|
21
|
+
#
|
|
22
|
+
# # good
|
|
23
|
+
# expect(page).not_to have_selector
|
|
24
|
+
# expect(page).not_to have_css('a')
|
|
25
|
+
#
|
|
26
26
|
class NegationMatcher < ::RuboCop::Cop::Base
|
|
27
27
|
extend AutoCorrector
|
|
28
28
|
include ConfigurableEnforcedStyle
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Capybara
|
|
6
|
+
# Checks for redundant `within find(...)` calls.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# within find('foo.bar') do
|
|
11
|
+
# # ...
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# within 'foo.bar' do
|
|
16
|
+
# # ...
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# # bad
|
|
20
|
+
# within find_by_id('foo') do
|
|
21
|
+
# # ...
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# # good
|
|
25
|
+
# within '#foo' do
|
|
26
|
+
# # ...
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
class RedundantWithinFind < ::RuboCop::Cop::Base
|
|
30
|
+
extend AutoCorrector
|
|
31
|
+
MSG = 'Redundant `within %<method>s(...)` call detected.'
|
|
32
|
+
RESTRICT_ON_SEND = %i[within].freeze
|
|
33
|
+
FIND_METHODS = Set.new(%i[find find_by_id]).freeze
|
|
34
|
+
|
|
35
|
+
# @!method within_find(node)
|
|
36
|
+
def_node_matcher :within_find, <<~PATTERN
|
|
37
|
+
(send nil? :within
|
|
38
|
+
$(send nil? %FIND_METHODS ...))
|
|
39
|
+
PATTERN
|
|
40
|
+
|
|
41
|
+
def on_send(node)
|
|
42
|
+
within_find(node) do |find_node|
|
|
43
|
+
add_offense(find_node, message: msg(find_node)) do |corrector|
|
|
44
|
+
corrector.replace(find_node, replaced(find_node))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def msg(node)
|
|
52
|
+
format(MSG, method: node.method_name)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def replaced(node)
|
|
56
|
+
replaced = node.arguments.map(&:source).join(', ')
|
|
57
|
+
if node.method?(:find_by_id)
|
|
58
|
+
replaced.sub(/\A(["'])/, '\1#')
|
|
59
|
+
else
|
|
60
|
+
replaced
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
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 %i[str dstr].include?(argument.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.first_argument.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)
|
|
@@ -42,6 +42,11 @@ module RuboCop
|
|
|
42
42
|
(send nil? _ (str $_) ... )
|
|
43
43
|
PATTERN
|
|
44
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
|
+
|
|
45
50
|
def on_send(node)
|
|
46
51
|
first_argument(node) do |arg|
|
|
47
52
|
next unless (matcher = specific_matcher(arg))
|
|
@@ -61,6 +66,7 @@ module RuboCop
|
|
|
61
66
|
|
|
62
67
|
def replaceable?(node, arg, matcher)
|
|
63
68
|
replaceable_attributes?(arg) &&
|
|
69
|
+
!text_with_regexp?(node) &&
|
|
64
70
|
CapybaraHelp.replaceable_option?(node, arg, matcher) &&
|
|
65
71
|
CapybaraHelp.replaceable_pseudo_classes?(arg)
|
|
66
72
|
end
|
|
@@ -1,8 +1,13 @@
|
|
|
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'
|
|
10
|
+
require_relative 'capybara/redundant_within_find'
|
|
6
11
|
require_relative 'capybara/specific_actions'
|
|
7
12
|
require_relative 'capybara/specific_finders'
|
|
8
13
|
require_relative 'capybara/specific_matcher'
|
data/lib/rubocop-capybara.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'yaml'
|
|
|
6
6
|
require 'rubocop'
|
|
7
7
|
|
|
8
8
|
require_relative 'rubocop/cop/capybara/mixin/capybara_help'
|
|
9
|
+
require_relative 'rubocop/cop/capybara/mixin/css_attributes_parser'
|
|
9
10
|
require_relative 'rubocop/cop/capybara/mixin/css_selector'
|
|
10
11
|
|
|
11
12
|
require_relative 'rubocop/cop/capybara_cops'
|
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.20.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:
|
|
11
|
+
date: 2024-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubocop
|
|
@@ -43,11 +43,16 @@ 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
|
|
50
|
+
- lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb
|
|
49
51
|
- lib/rubocop/cop/capybara/mixin/css_selector.rb
|
|
50
52
|
- lib/rubocop/cop/capybara/negation_matcher.rb
|
|
53
|
+
- lib/rubocop/cop/capybara/redundant_within_find.rb
|
|
54
|
+
- lib/rubocop/cop/capybara/rspec/have_selector.rb
|
|
55
|
+
- lib/rubocop/cop/capybara/rspec/predicate_matcher.rb
|
|
51
56
|
- lib/rubocop/cop/capybara/specific_actions.rb
|
|
52
57
|
- lib/rubocop/cop/capybara/specific_finders.rb
|
|
53
58
|
- lib/rubocop/cop/capybara/specific_matcher.rb
|
|
@@ -68,14 +73,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
68
73
|
requirements:
|
|
69
74
|
- - ">="
|
|
70
75
|
- !ruby/object:Gem::Version
|
|
71
|
-
version: 2.
|
|
76
|
+
version: 2.7.0
|
|
72
77
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
78
|
requirements:
|
|
74
79
|
- - ">="
|
|
75
80
|
- !ruby/object:Gem::Version
|
|
76
81
|
version: '0'
|
|
77
82
|
requirements: []
|
|
78
|
-
rubygems_version: 3.4.
|
|
83
|
+
rubygems_version: 3.4.22
|
|
79
84
|
signing_key:
|
|
80
85
|
specification_version: 4
|
|
81
86
|
summary: Code style checking for Capybara test files
|