rubocop-capybara 2.20.0 → 2.22.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: 963a68452b58c47db1d1d9e7f9201db0a98a8b70bb8089473b406911fa5cc33e
4
- data.tar.gz: 58e60dd5ff6097695a1029b9e37414209f7c26d2da0d16cbc6b808c8cd56efd8
3
+ metadata.gz: 03bbb0abff1aee334bbe60b255688a098b5713f6f7d675de12ede6298c6ddb87
4
+ data.tar.gz: 78faf4416bd058d5ba3442a20ffd5ac34f7f406c6e1fe5eb5fcb64ef39b61aab
5
5
  SHA512:
6
- metadata.gz: cec93e7919aeefbb1f2b2c13f1f0caeb7627c5556f85fe219f107b6520237cccd792ad4713905dfb47151f0bcb82e04d4bd0034a7a747b741e607ea9d4de5e54
7
- data.tar.gz: 3e359ec71065b5daf64cdb080d8ddfad4f6144778ad7fb89d932aa083353547663a1316efe7f9e9fd3cb7bca6a6f012812ea7f18e00c76a8af22316f24325f7c
6
+ metadata.gz: 29bede0e47aba00c65533df9696d675927830b50f954f3ec3ca6f7417bb036057c3089cdd986cad0327bcc73081226991acb265e8c99d6d915d5b2fdc5942ae5
7
+ data.tar.gz: bda139c9e52f5dbbac07d95304baeca8186ce23ec2cb2f9099fab7af4d2488f586026232560480eacd2fa502502a3c15742df2e0a94f57a5f548cd81c1502a65
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## Edge (Unreleased)
4
4
 
5
+ ## 2.22.0 (2025-03-10)
6
+
7
+ - Add `Capybara/AmbiguousClick` cop and make soft-deprecated `Capybara/ClickLinkOrButtonStyle` cop. If you want to use `EnforcedStyle: strict`, use `Capybara/AmbiguousClick` cop instead. ([@ydah])
8
+ - Add new `Capybara/FindAllFirst` cop. ([@ydah])
9
+ - Add a new `Capybara/NegationMatcherAfterVisit` cop. ([@ydah])
10
+ - Fix an error for `Capybara/RSpec/HaveSelector` when passing no arguments. ([@earlopain])
11
+ - Make RuboCop Capybara work as a RuboCop plugin. ([@bquorning])
12
+
13
+ ## 2.21.0 (2024-06-08)
14
+
15
+ - Fix a false negative for `Capybara/NegationMatcher` when using `to_not`. ([@ydah])
16
+ - Fix a false negative for `Capybara/SpecificFinders` when using `find(:id, 'some-id')`. ([@ydah])
17
+
5
18
  ## 2.20.0 (2024-01-03)
6
19
 
7
20
  - Change to default `EnforcedStyle: link_or_button` for `Capybara/ClickLinkOrButtonStyle` cop. ([@ydah])
@@ -70,6 +83,7 @@
70
83
  [@aried3r]: https://github.com/aried3r
71
84
  [@bquorning]: https://github.com/bquorning
72
85
  [@darhazer]: https://github.com/Darhazer
86
+ [@earlopain]: https://github.com/earlopain
73
87
  [@onumis]: https://github.com/onumis
74
88
  [@oskarsezerins]: https://github.com/OskarsEzerins
75
89
  [@pirj]: https://github.com/pirj
data/README.md CHANGED
@@ -31,13 +31,13 @@ ways to do this:
31
31
  Put this into your `.rubocop.yml`.
32
32
 
33
33
  ```yaml
34
- require: rubocop-capybara
34
+ plugins: rubocop-capybara
35
35
  ```
36
36
 
37
37
  Alternatively, use the following array notation when specifying multiple extensions.
38
38
 
39
39
  ```yaml
40
- require:
40
+ plugins:
41
41
  - rubocop-other-extension
42
42
  - rubocop-capybara
43
43
  ```
@@ -45,17 +45,20 @@ require:
45
45
  Now you can run `rubocop` and it will automatically load the RuboCop Capybara
46
46
  cops together with the standard cops.
47
47
 
48
+ > [!NOTE]
49
+ > The plugin system is supported in RuboCop 1.72+. In earlier versions, use `require` instead of `plugins`.
50
+
48
51
  ### Command line
49
52
 
50
53
  ```bash
51
- rubocop --require rubocop-capybara
54
+ rubocop --plugin rubocop-capybara
52
55
  ```
53
56
 
54
57
  ### Rake task
55
58
 
56
59
  ```ruby
57
60
  RuboCop::RakeTask.new do |task|
58
- task.requires << 'rubocop-capybara'
61
+ task.plugins << 'rubocop-capybara'
59
62
  end
60
63
  ```
61
64
 
data/config/default.yml CHANGED
@@ -9,11 +9,17 @@ Capybara:
9
9
  - "**/*_steps.rb"
10
10
  - "**/features/step_definitions/**/*"
11
11
 
12
+ Capybara/AmbiguousClick:
13
+ Description: Specify the exact target to click on.
14
+ Enabled: false
15
+ VersionAdded: '2.22'
16
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/AmbiguousClick
17
+
12
18
  Capybara/ClickLinkOrButtonStyle:
13
19
  Description: Checks for methods of button or link clicks.
14
- Enabled: pending
20
+ Enabled: false
15
21
  VersionAdded: '2.19'
16
- VersionChanged: '2.20'
22
+ VersionChanged: '2.22'
17
23
  EnforcedStyle: link_or_button
18
24
  SupportedStyles:
19
25
  - link_or_button
@@ -27,6 +33,12 @@ Capybara/CurrentPathExpectation:
27
33
  VersionChanged: '2.0'
28
34
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/CurrentPathExpectation
29
35
 
36
+ Capybara/FindAllFirst:
37
+ Description: Enforces use of `first` instead of `all` with `first` or `[0]`.
38
+ Enabled: pending
39
+ VersionAdded: '2.22'
40
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/FindAllFirst
41
+
30
42
  Capybara/MatchStyle:
31
43
  Description: Checks for usage of deprecated style methods.
32
44
  Enabled: pending
@@ -44,6 +56,12 @@ Capybara/NegationMatcher:
44
56
  - not_to
45
57
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcher
46
58
 
59
+ Capybara/NegationMatcherAfterVisit:
60
+ Description: Do not allow negative matchers to be used immediately after `visit`.
61
+ Enabled: pending
62
+ VersionAdded: '2.22'
63
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcherAfterVisit
64
+
47
65
  Capybara/RedundantWithinFind:
48
66
  Description: Checks for redundant `within find(...)` calls.
49
67
  Enabled: pending
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Capybara
5
+ module Cop
6
+ # Source and spec generator for new cops
7
+ #
8
+ # This generator will take a cop name and generate a source file
9
+ # and spec file when given a valid qualified cop name.
10
+ # @api private
11
+ class Generator < RuboCop::Cop::Generator
12
+ def todo
13
+ <<~TODO
14
+ Do 4 steps:
15
+ 1. Modify the description of #{badge} in config/default.yml
16
+ 2. Implement your new cop in the generated file!
17
+ 3. Add an entry about new cop to CHANGELOG.md
18
+ 4. Commit your new cop with a message such as
19
+ e.g. "Add new `#{badge}` cop"
20
+ TODO
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lint_roller'
4
+
5
+ module RuboCop
6
+ module Capybara
7
+ # A plugin that integrates RuboCop Capybara with RuboCop's plugin system.
8
+ class Plugin < LintRoller::Plugin
9
+ # :nocov:
10
+ def about
11
+ LintRoller::About.new(
12
+ name: 'rubocop-capybara',
13
+ version: Version::STRING,
14
+ homepage: 'https://github.com/rubocop/rubocop-capybara',
15
+ description: 'Code style checking for Capybara test files.'
16
+ )
17
+ end
18
+ # :nocov:
19
+
20
+ def supported?(context)
21
+ context.engine == :rubocop
22
+ end
23
+
24
+ def rules(_context)
25
+ project_root = Pathname.new(__dir__).join('../../..')
26
+
27
+ obsoletion = project_root.join('config', 'obsoletion.yml')
28
+ ConfigObsoletion.files << obsoletion if obsoletion.exist?
29
+
30
+ LintRoller::Rules.new(
31
+ type: :path,
32
+ config_format: :rubocop,
33
+ value: project_root.join('config/default.yml')
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -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.20.0'
7
+ STRING = '2.22.0'
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ # Specify the exact target to click on.
7
+ #
8
+ # In projects where accessibility needs to be considered,
9
+ # it is crucial to specify the click target precisely.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # click_link_or_button('foo')
14
+ # click_on('foo')
15
+ #
16
+ # # good
17
+ # click_link('foo')
18
+ # click_button('foo')
19
+ #
20
+ class AmbiguousClick < ::RuboCop::Cop::Base
21
+ MSG = 'Use `click_link` or `click_button` instead of `%<method>s`.'
22
+ RESTRICT_ON_SEND = %i[click_link_or_button click_on].freeze
23
+
24
+ def on_send(node)
25
+ add_offense(node, message: format(MSG, method: node.method_name))
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -5,6 +5,14 @@ module RuboCop
5
5
  module Capybara
6
6
  # Checks for methods of button or link clicks.
7
7
  #
8
+ # This cop is deprecated.
9
+ # We plan to remove this in the next major version update to 3.0.
10
+ #
11
+ # The migration target is `Capybara/AmbiguousClick`.
12
+ # It is only migration target when `EnforcedStyle: strict`.
13
+ # If you are using this cop, please plan for migration.
14
+ # There is no migration target when `EnforcedStyle: link_or_button`.
15
+ #
8
16
  # By default, prefer to use `click_link_or_button` or `click_on`.
9
17
  # These methods offer a weaker coupling between the test and HTML,
10
18
  # allowing for a more faithful reflection of how the user behaves.
@@ -59,6 +67,10 @@ module RuboCop
59
67
  format(MSG_STRICT, method: node.method_name)
60
68
  elsif style == :link_or_button
61
69
  format(MSG_CLICK_LINK_OR_BUTTON, method: node.method_name)
70
+ else
71
+ # :nocov:
72
+ :noop
73
+ # :nocov:
62
74
  end
63
75
  end
64
76
 
@@ -37,20 +37,20 @@ module RuboCop
37
37
  RESTRICT_ON_SEND = %i[expect].freeze
38
38
 
39
39
  # @!method expectation_set_on_current_path(node)
40
- def_node_matcher :expectation_set_on_current_path, <<-PATTERN
40
+ def_node_matcher :expectation_set_on_current_path, <<~PATTERN
41
41
  (send nil? :expect (send {(send nil? :page) nil?} :current_path))
42
42
  PATTERN
43
43
 
44
44
  # Supported matchers: eq(...) / match(/regexp/) / match('regexp')
45
45
  # @!method as_is_matcher(node)
46
- def_node_matcher :as_is_matcher, <<-PATTERN
46
+ def_node_matcher :as_is_matcher, <<~PATTERN
47
47
  (send
48
48
  #expectation_set_on_current_path ${:to :to_not :not_to}
49
49
  ${(send nil? :eq ...) (send nil? :match (regexp ...))})
50
50
  PATTERN
51
51
 
52
52
  # @!method regexp_node_matcher(node)
53
- def_node_matcher :regexp_node_matcher, <<-PATTERN
53
+ def_node_matcher :regexp_node_matcher, <<~PATTERN
54
54
  (send
55
55
  #expectation_set_on_current_path ${:to :to_not :not_to}
56
56
  $(send nil? :match ${str dstr xstr}))
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ # Enforces use of `first` instead of `all` with `first` or `[0]`.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # all('a').first
12
+ # all('a')[0]
13
+ # find('a', match: :first)
14
+ # all('a', match: :first)
15
+ #
16
+ # # good
17
+ # first('a')
18
+ #
19
+ class FindAllFirst < ::RuboCop::Cop::Base
20
+ extend AutoCorrector
21
+ include RangeHelp
22
+
23
+ MSG = 'Use `first(%<selector>s)`.'
24
+ RESTRICT_ON_SEND = %i[all find].freeze
25
+
26
+ # @!method find_all_first?(node)
27
+ def_node_matcher :find_all_first?, <<~PATTERN
28
+ {
29
+ (send (send _ :all _ ...) :first)
30
+ (send (send _ :all _ ...) :[] (int 0))
31
+ }
32
+ PATTERN
33
+
34
+ # @!method include_match_first?(node)
35
+ def_node_matcher :include_match_first?, <<~PATTERN
36
+ (send _ {:find :all} _ $(hash <(pair (sym :match) (sym :first)) ...>))
37
+ PATTERN
38
+
39
+ def on_send(node)
40
+ on_all_first(node)
41
+ on_match_first(node)
42
+ end
43
+
44
+ private
45
+
46
+ def on_all_first(node)
47
+ return unless (parent = node.parent)
48
+ return unless find_all_first?(parent)
49
+
50
+ range = range_between(node.loc.selector.begin_pos,
51
+ parent.loc.selector.end_pos)
52
+ selector = node.arguments.map(&:source).join(', ')
53
+ add_offense(range,
54
+ message: format(MSG, selector: selector)) do |corrector|
55
+ corrector.replace(range, "first(#{selector})")
56
+ end
57
+ end
58
+
59
+ def on_match_first(node)
60
+ include_match_first?(node) do |hash|
61
+ selector = ([node.first_argument.source] + replaced_hash(hash))
62
+ .join(', ')
63
+ add_offense(node,
64
+ message: format(MSG, selector: selector)) do |corrector|
65
+ corrector.replace(node, "first(#{selector})")
66
+ end
67
+ end
68
+ end
69
+
70
+ def replaced_hash(hash)
71
+ hash.child_nodes.flat_map(&:source).reject do |arg|
72
+ arg == 'match: :first'
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -6,6 +6,16 @@ module RuboCop
6
6
  # Help methods for capybara.
7
7
  # @api private
8
8
  module CapybaraHelp
9
+ CAPYBARA_MATCHERS = %w[
10
+ selector css xpath text title current_path link button
11
+ field checked_field unchecked_field select table
12
+ sibling ancestor content
13
+ ].freeze
14
+ POSITIVE_MATCHERS =
15
+ Set.new(CAPYBARA_MATCHERS) { |element| :"have_#{element}" }.freeze
16
+ NEGATIVE_MATCHERS =
17
+ Set.new(CAPYBARA_MATCHERS) { |element| :"have_no_#{element}" }
18
+ .freeze
9
19
  COMMON_OPTIONS = %w[
10
20
  id class style
11
21
  ].freeze
@@ -16,7 +16,7 @@ module RuboCop
16
16
 
17
17
  # @return [Array<String>]
18
18
  def parse # rubocop:disable Metrics/MethodLength
19
- @selector.chars do |char|
19
+ @selector.each_char do |char|
20
20
  if char == '['
21
21
  on_bracket_start
22
22
  elsif char == ']'
@@ -7,42 +7,33 @@ module RuboCop
7
7
  #
8
8
  # @example EnforcedStyle: have_no (default)
9
9
  # # bad
10
- # expect(page).not_to have_selector
10
+ # expect(page).not_to have_selector 'a'
11
11
  # expect(page).not_to have_css('a')
12
12
  #
13
13
  # # good
14
- # expect(page).to have_no_selector
14
+ # expect(page).to have_no_selector 'a'
15
15
  # expect(page).to have_no_css('a')
16
16
  #
17
17
  # @example EnforcedStyle: not_to
18
18
  # # bad
19
- # expect(page).to have_no_selector
19
+ # expect(page).to have_no_selector 'a'
20
20
  # expect(page).to have_no_css('a')
21
21
  #
22
22
  # # good
23
- # expect(page).not_to have_selector
23
+ # expect(page).not_to have_selector 'a'
24
24
  # expect(page).not_to have_css('a')
25
25
  #
26
26
  class NegationMatcher < ::RuboCop::Cop::Base
27
27
  extend AutoCorrector
28
28
  include ConfigurableEnforcedStyle
29
+ include CapybaraHelp
29
30
 
30
31
  MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
31
- CAPYBARA_MATCHERS = %w[
32
- selector css xpath text title current_path link button
33
- field checked_field unchecked_field select table
34
- sibling ancestor content
35
- ].freeze
36
- POSITIVE_MATCHERS =
37
- Set.new(CAPYBARA_MATCHERS) { |element| :"have_#{element}" }.freeze
38
- NEGATIVE_MATCHERS =
39
- Set.new(CAPYBARA_MATCHERS) { |element| :"have_no_#{element}" }
40
- .freeze
41
32
  RESTRICT_ON_SEND = (POSITIVE_MATCHERS + NEGATIVE_MATCHERS).freeze
42
33
 
43
34
  # @!method not_to?(node)
44
35
  def_node_matcher :not_to?, <<~PATTERN
45
- (send ... :not_to
36
+ (send ... {:not_to :to_not}
46
37
  (send nil? %POSITIVE_MATCHERS ...))
47
38
  PATTERN
48
39
 
@@ -53,7 +44,7 @@ module RuboCop
53
44
  PATTERN
54
45
 
55
46
  def on_send(node)
56
- return unless offense?(node.parent)
47
+ return unless offense?(node)
57
48
 
58
49
  matcher = node.method_name.to_s
59
50
  add_offense(offense_range(node),
@@ -67,8 +58,9 @@ module RuboCop
67
58
  private
68
59
 
69
60
  def offense?(node)
70
- (style == :have_no && not_to?(node)) ||
71
- (style == :not_to && have_no?(node))
61
+ node.arguments? &&
62
+ ((style == :have_no && not_to?(node.parent)) ||
63
+ (style == :not_to && have_no?(node.parent)))
72
64
  end
73
65
 
74
66
  def offense_range(node)
@@ -87,6 +79,10 @@ module RuboCop
87
79
  'to'
88
80
  when :not_to
89
81
  'not_to'
82
+ else
83
+ # :nocov:
84
+ :noop
85
+ # :nocov:
90
86
  end
91
87
  end
92
88
 
@@ -96,6 +92,10 @@ module RuboCop
96
92
  matcher.sub('have_', 'have_no_')
97
93
  when :not_to
98
94
  matcher.sub('have_no_', 'have_')
95
+ else
96
+ # :nocov:
97
+ :noop
98
+ # :nocov:
99
99
  end
100
100
  end
101
101
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ # Do not allow negative matchers to be used immediately after `visit`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # visit foo_path
11
+ # expect(page).to have_no_link('bar')
12
+ # expect(page).to have_css('a')
13
+ #
14
+ # # good
15
+ # visit foo_path
16
+ # expect(page).to have_css('a')
17
+ # expect(page).to have_no_link('bar')
18
+ #
19
+ # # bad
20
+ # visit foo_path
21
+ # expect(page).not_to have_link('bar')
22
+ # expect(page).to have_css('a')
23
+ #
24
+ # # good
25
+ # visit foo_path
26
+ # expect(page).to have_css('a')
27
+ # expect(page).not_to have_link('bar')
28
+ #
29
+ class NegationMatcherAfterVisit < ::RuboCop::Cop::Base
30
+ include CapybaraHelp
31
+
32
+ MSG = 'Do not use negation matcher immediately after visit.'
33
+ RESTRICT_ON_SEND = %i[visit].freeze
34
+
35
+ # @!method negation_matcher?(node)
36
+ def_node_matcher :negation_matcher?, <<~PATTERN
37
+ {
38
+ (send (send nil? :expect _) :to (send nil? %NEGATIVE_MATCHERS ...))
39
+ (send (send nil? :expect _) :not_to (send nil? %POSITIVE_MATCHERS ...))
40
+ }
41
+ PATTERN
42
+
43
+ def on_send(node)
44
+ negation_matcher?(node.right_sibling) do
45
+ add_offense(node.right_sibling)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -42,7 +42,8 @@ module RuboCop
42
42
  SELECTORS = %i[css xpath].freeze
43
43
 
44
44
  def on_send(node)
45
- argument = node.first_argument
45
+ return unless (argument = node.first_argument)
46
+
46
47
  on_select_with_type(node, argument) if argument.sym_type?
47
48
  on_select_without_type(node) if %i[str dstr].include?(argument.type)
48
49
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
 
45
45
  # @!method be_bool?(node)
46
46
  def_node_matcher :be_bool?, <<~PATTERN
47
- (send nil? {:be :eq :eql :equal} {true false})
47
+ (send nil? {:be :eq :eql :equal} boolean)
48
48
  PATTERN
49
49
 
50
50
  # @!method be_boolthy?(node)
@@ -177,6 +177,7 @@ module RuboCop
177
177
  "#{matcher.to_s.sub('match_', 'matches_')}?"
178
178
  end
179
179
 
180
+ # rubocop:disable Metrics/MethodLength
180
181
  def replacement_matcher(node)
181
182
  case [cop_config['Strict'], node.method?(:to)]
182
183
  when [true, true]
@@ -187,8 +188,13 @@ module RuboCop
187
188
  'be_truthy'
188
189
  when [false, false]
189
190
  'be_falsey'
191
+ else
192
+ # :nocov:
193
+ :noop
194
+ # :nocov:
190
195
  end
191
196
  end
197
+ # rubocop:enable Metrics/MethodLength
192
198
  end
193
199
 
194
200
  # Prefer using predicate matcher over using predicate method directly.
@@ -248,6 +254,10 @@ module RuboCop
248
254
  check_inflected(node)
249
255
  elsif style == :explicit
250
256
  check_explicit(node)
257
+ else
258
+ # :nocov:
259
+ :noop
260
+ # :nocov:
251
261
  end
252
262
  end
253
263
 
@@ -28,7 +28,7 @@ module RuboCop
28
28
  }.freeze
29
29
 
30
30
  # @!method click_on_selector(node)
31
- def_node_matcher :click_on_selector, <<-PATTERN
31
+ def_node_matcher :click_on_selector, <<~PATTERN
32
32
  (send _ :find (str $_) ...)
33
33
  PATTERN
34
34
 
@@ -10,6 +10,7 @@ module RuboCop
10
10
  # find('#some-id')
11
11
  # find('[id=some-id]')
12
12
  # find(:css, '#some-id')
13
+ # find(:id, 'some-id')
13
14
  #
14
15
  # # good
15
16
  # find_by_id('some-id')
@@ -23,7 +24,7 @@ module RuboCop
23
24
 
24
25
  # @!method find_argument(node)
25
26
  def_node_matcher :find_argument, <<~PATTERN
26
- (send _ :find $(sym :css)? (str $_) ...)
27
+ (send _ :find $(sym {:css :id})? (str $_) ...)
27
28
  PATTERN
28
29
 
29
30
  # @!method class_options(node)
@@ -38,6 +39,7 @@ module RuboCop
38
39
 
39
40
  on_attr(node, sym, arg) if attribute?(arg)
40
41
  on_id(node, sym, arg) if CssSelector.id?(arg)
42
+ on_sym_id(node, sym, arg) if sym.first&.value == :id
41
43
  end
42
44
  end
43
45
 
@@ -59,6 +61,10 @@ module RuboCop
59
61
  CssSelector.classes(arg.sub("##{id}", '')))
60
62
  end
61
63
 
64
+ def on_sym_id(node, sym, id)
65
+ register_offense(node, sym, "'#{id}'")
66
+ end
67
+
62
68
  def attribute?(arg)
63
69
  CssSelector.attribute?(arg) &&
64
70
  CapybaraHelp.common_attributes?(arg)
@@ -106,11 +112,11 @@ module RuboCop
106
112
  end
107
113
 
108
114
  def to_options(attrs)
109
- attrs.each.map do |key, value|
115
+ attrs.each.filter_map do |key, value|
110
116
  next if key == 'id'
111
117
 
112
118
  "#{key}: #{value}"
113
- end.compact.join(', ')
119
+ end.join(', ')
114
120
  end
115
121
 
116
122
  def offense_range(node)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # expect(page).to have_no_link('foo', class: 'cls', href: 'http://example.com')
24
24
  # expect(page).to have_table(class: 'cls')
25
25
  # expect(page).to have_select
26
- # expect(page).to have_field('foo')
26
+ # expect(page).to have_field(with: 'foo')
27
27
  #
28
28
  class SpecificMatcher < ::RuboCop::Cop::Base
29
29
  MSG = 'Prefer `%<good_matcher>s` over `%<bad_matcher>s`.'
@@ -38,12 +38,12 @@ module RuboCop
38
38
  }.freeze
39
39
 
40
40
  # @!method first_argument(node)
41
- def_node_matcher :first_argument, <<-PATTERN
41
+ def_node_matcher :first_argument, <<~PATTERN
42
42
  (send nil? _ (str $_) ... )
43
43
  PATTERN
44
44
 
45
45
  # @!method text_with_regexp?(node)
46
- def_node_search :text_with_regexp?, <<-PATTERN
46
+ def_node_search :text_with_regexp?, <<~PATTERN
47
47
  (pair (sym {:text :exact_text}) (regexp ...))
48
48
  PATTERN
49
49
 
@@ -3,10 +3,13 @@
3
3
  require_relative 'capybara/rspec/have_selector'
4
4
  require_relative 'capybara/rspec/predicate_matcher'
5
5
 
6
+ require_relative 'capybara/ambiguous_click'
6
7
  require_relative 'capybara/click_link_or_button_style'
7
8
  require_relative 'capybara/current_path_expectation'
9
+ require_relative 'capybara/find_all_first'
8
10
  require_relative 'capybara/match_style'
9
11
  require_relative 'capybara/negation_matcher'
12
+ require_relative 'capybara/negation_matcher_after_visit'
10
13
  require_relative 'capybara/redundant_within_find'
11
14
  require_relative 'capybara/specific_actions'
12
15
  require_relative 'capybara/specific_finders'
@@ -5,17 +5,14 @@ require 'yaml'
5
5
 
6
6
  require 'rubocop'
7
7
 
8
+ require_relative 'rubocop/capybara/plugin'
9
+
8
10
  require_relative 'rubocop/cop/capybara/mixin/capybara_help'
9
11
  require_relative 'rubocop/cop/capybara/mixin/css_attributes_parser'
10
12
  require_relative 'rubocop/cop/capybara/mixin/css_selector'
11
13
 
12
14
  require_relative 'rubocop/cop/capybara_cops'
13
15
 
14
- project_root = File.join(__dir__, '..')
15
- RuboCop::ConfigLoader.inject_defaults!(project_root)
16
- obsoletion = File.join(project_root, 'config', 'obsoletion.yml')
17
- RuboCop::ConfigObsoletion.files << obsoletion if File.exist?(obsoletion)
18
-
19
16
  RuboCop::Cop::Style::TrailingCommaInArguments.singleton_class.prepend(
20
17
  Module.new do
21
18
  def autocorrect_incompatible_with
metadata CHANGED
@@ -1,33 +1,51 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.20.0
4
+ version: 2.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-01-02 00:00:00.000000000 Z
10
+ date: 2025-03-10 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: lint_roller
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.1'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: rubocop
15
28
  requirement: !ruby/object:Gem::Requirement
16
29
  requirements:
17
30
  - - "~>"
18
31
  - !ruby/object:Gem::Version
19
- version: '1.41'
32
+ version: '1.72'
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.72.1
20
36
  type: :runtime
21
37
  prerelease: false
22
38
  version_requirements: !ruby/object:Gem::Requirement
23
39
  requirements:
24
40
  - - "~>"
25
41
  - !ruby/object:Gem::Version
26
- version: '1.41'
27
- description: |2
28
- Code style checking for Capybara test files (RSpec, Cucumber, Minitest).
29
- A plugin for the RuboCop code style enforcing & linting tool.
30
- email:
42
+ version: '1.72'
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 1.72.1
46
+ description: |
47
+ Code style checking for Capybara test files (RSpec, Cucumber, Minitest).
48
+ A plugin for the RuboCop code style enforcing & linting tool.
31
49
  executables: []
32
50
  extensions: []
33
51
  extra_rdoc_files:
@@ -41,15 +59,20 @@ files:
41
59
  - config/default.yml
42
60
  - lib/rubocop-capybara.rb
43
61
  - lib/rubocop/capybara/config_formatter.rb
62
+ - lib/rubocop/capybara/cop/generator.rb
44
63
  - lib/rubocop/capybara/description_extractor.rb
64
+ - lib/rubocop/capybara/plugin.rb
45
65
  - lib/rubocop/capybara/version.rb
66
+ - lib/rubocop/cop/capybara/ambiguous_click.rb
46
67
  - lib/rubocop/cop/capybara/click_link_or_button_style.rb
47
68
  - lib/rubocop/cop/capybara/current_path_expectation.rb
69
+ - lib/rubocop/cop/capybara/find_all_first.rb
48
70
  - lib/rubocop/cop/capybara/match_style.rb
49
71
  - lib/rubocop/cop/capybara/mixin/capybara_help.rb
50
72
  - lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb
51
73
  - lib/rubocop/cop/capybara/mixin/css_selector.rb
52
74
  - lib/rubocop/cop/capybara/negation_matcher.rb
75
+ - lib/rubocop/cop/capybara/negation_matcher_after_visit.rb
53
76
  - lib/rubocop/cop/capybara/redundant_within_find.rb
54
77
  - lib/rubocop/cop/capybara/rspec/have_selector.rb
55
78
  - lib/rubocop/cop/capybara/rspec/predicate_matcher.rb
@@ -65,7 +88,7 @@ metadata:
65
88
  changelog_uri: https://github.com/rubocop/rubocop-capybara/blob/main/CHANGELOG.md
66
89
  documentation_uri: https://docs.rubocop.org/rubocop-capybara/
67
90
  rubygems_mfa_required: 'true'
68
- post_install_message:
91
+ default_lint_roller_plugin: RuboCop::Capybara::Plugin
69
92
  rdoc_options: []
70
93
  require_paths:
71
94
  - lib
@@ -80,8 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
103
  - !ruby/object:Gem::Version
81
104
  version: '0'
82
105
  requirements: []
83
- rubygems_version: 3.4.22
84
- signing_key:
106
+ rubygems_version: 3.6.2
85
107
  specification_version: 4
86
108
  summary: Code style checking for Capybara test files
87
109
  test_files: []