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