rubocop-capybara 2.18.0 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb7f795cc80f072b94781f2339e781cccf9922ad2303c09111c0e34a4af264f8
4
- data.tar.gz: d2ae47b522208177b7c93338973c8a144dfbccca2f817f0797b531f8c89e50b9
3
+ metadata.gz: 5af741b67ec2d02d9e27e688d553e5e1c8cbb04c845f5d9771b480b291b1d805
4
+ data.tar.gz: cbef51994ffa79af69a8a7aac9aabf69ccc307b3c2c104eae2a3a4c635467d26
5
5
  SHA512:
6
- metadata.gz: 36b58f29500840c7dc34c2c2ed55856ff08cdd7ca41822209a9388204948903be89cf13c3065da7cfed3e8332f3433b3293087c8de1cf1266f093fadef2fa69d
7
- data.tar.gz: 5b961abac7ae8dd46708ec9649718a23596bde9e45bcc48f7d9e818c2a116d5fda3de0653cf3aa8e56d11ed11ebcb94e24ef49ee0653e739a26d43d2383c2152
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` ([@OskarsEzerins])
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
@@ -17,7 +17,7 @@ gem install rubocop-capybara
17
17
 
18
18
  or if you use bundler put this in your `Gemfile`
19
19
 
20
- ```
20
+ ```ruby
21
21
  gem 'rubocop-capybara', require: false
22
22
  ```
23
23
 
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 = [].freeze
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
 
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Capybara
5
5
  # Version information for the Capybara RuboCop plugin.
6
6
  module Version
7
- STRING = '2.18.0'
7
+ STRING = '2.19.0'
8
8
  end
9
9
  end
10
10
  end
@@ -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.current_path).to match(variable)
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 the expectation is a regexp or string
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
- return if %i[
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('[visible][id=some-id]')
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.18.0
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-04-21 00:00:00.000000000 Z
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.6.0
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.4.5
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