rubocop-capybara 2.18.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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