rubocop-capybara 2.22.0 → 2.23.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: 03bbb0abff1aee334bbe60b255688a098b5713f6f7d675de12ede6298c6ddb87
4
- data.tar.gz: 78faf4416bd058d5ba3442a20ffd5ac34f7f406c6e1fe5eb5fcb64ef39b61aab
3
+ metadata.gz: 4d1c9d6e62bd38cf7ef34d42dcf3eabcc49cbb2ad1cb23ea96ddfb6d7b964ef0
4
+ data.tar.gz: 5fe88f1d98cb8b1068c9604648f1ef18517fcd27b3552a7525913f5bcd30490d
5
5
  SHA512:
6
- metadata.gz: 29bede0e47aba00c65533df9696d675927830b50f954f3ec3ca6f7417bb036057c3089cdd986cad0327bcc73081226991acb265e8c99d6d915d5b2fdc5942ae5
7
- data.tar.gz: bda139c9e52f5dbbac07d95304baeca8186ce23ec2cb2f9099fab7af4d2488f586026232560480eacd2fa502502a3c15742df2e0a94f57a5f548cd81c1502a65
6
+ metadata.gz: 5f1652a43814b536fd98975128b51468b453f2665f32f97e8eb8ba9940ff92b7dd03093d29a738356bf50e11349d6b11c7e225cc23e4306bac1d94da48fd0948
7
+ data.tar.gz: 9045ef2ed4d00354ca9888158c6dc6088361f476aef5105dfb120421951252d101470cdd68bdcaf71b0ae2fbebe265fab86f6b1ca0fb863603b5465f8a5c3ccb
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## Edge (Unreleased)
4
4
 
5
+ ## 2.23.0 (2026-04-30)
6
+
7
+ - Bump RuboCop requirement to +1.81. ([@ydah])
8
+ - Add new `Capybara/RSpec/HaveContent` cop. ([@nzlaura])
9
+ - Move the department associated with `Capybara::RSpecMatchers` to `Capybara/RSpec/*`. ([@ydah])
10
+ - Rename `Capybara/CurrentPathExpectation` to `Capybara/RSpec/CurrentPathExpectation`
11
+ - Rename `Capybara/SpecificMatcher` to `Capybara/RSpec/SpecificMatcher`
12
+ - Rename `Capybara/NegationMatcher` to `Capybara/RSpec/NegationMatcher`
13
+ - Rename `Capybara/NegationMatcherAfterVisit` to `Capybara/RSpec/NegationMatcherAfterVisit`
14
+ - Rename `Capybara/VisibilityMatcher` to `Capybara/RSpec/VisibilityMatcher`
15
+ - Split `Capybara/MatchStyle` into `Capybara/AssertStyle` and `Capybara/RSpec/MatchStyle`. ([@ydah])
16
+ - Fix a false positive for `Capybara/FindAllFirst` when using logical operators with `all('...')[0]`. ([@ydah])
17
+ - Fix an incorrect autocorrect for `Capybara/FindAllFirst` when `find` or `all` with `match: :first` uses a receiver and mark autocorrection as unsafe. ([@ydah])
18
+ - Fix an error for `Capybara/RSpec/HaveSelector` when passing only a selector type. ([@ydah])
19
+
20
+ ## 2.22.1 (2025-03-12)
21
+
22
+ - Fix incorrect plugin version. ([@koic])
23
+
5
24
  ## 2.22.0 (2025-03-10)
6
25
 
7
26
  - Add `Capybara/AmbiguousClick` cop and make soft-deprecated `Capybara/ClickLinkOrButtonStyle` cop. If you want to use `EnforcedStyle: strict`, use `Capybara/AmbiguousClick` cop instead. ([@ydah])
@@ -9,6 +28,7 @@
9
28
  - Add a new `Capybara/NegationMatcherAfterVisit` cop. ([@ydah])
10
29
  - Fix an error for `Capybara/RSpec/HaveSelector` when passing no arguments. ([@earlopain])
11
30
  - Make RuboCop Capybara work as a RuboCop plugin. ([@bquorning])
31
+ - Fix an incorrect autocorrect for `Capybara/RedundantWithinFind` when escape required css selector. ([@ydah])
12
32
 
13
33
  ## 2.21.0 (2024-06-08)
14
34
 
@@ -84,6 +104,8 @@
84
104
  [@bquorning]: https://github.com/bquorning
85
105
  [@darhazer]: https://github.com/Darhazer
86
106
  [@earlopain]: https://github.com/earlopain
107
+ [@koic]: https://github.com/koic
108
+ [@nzlaura]: https://github.com/nzlaura
87
109
  [@onumis]: https://github.com/onumis
88
110
  [@oskarsezerins]: https://github.com/OskarsEzerins
89
111
  [@pirj]: https://github.com/pirj
data/config/default.yml CHANGED
@@ -15,6 +15,12 @@ Capybara/AmbiguousClick:
15
15
  VersionAdded: '2.22'
16
16
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/AmbiguousClick
17
17
 
18
+ Capybara/AssertStyle:
19
+ Description: Checks for usage of deprecated assert style method.
20
+ Enabled: pending
21
+ VersionAdded: '2.23'
22
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/AssertStyle
23
+
18
24
  Capybara/ClickLinkOrButtonStyle:
19
25
  Description: Checks for methods of button or link clicks.
20
26
  Enabled: false
@@ -26,42 +32,14 @@ Capybara/ClickLinkOrButtonStyle:
26
32
  - strict
27
33
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/ClickLinkOrButtonStyle
28
34
 
29
- Capybara/CurrentPathExpectation:
30
- Description: Checks that no expectations are set on Capybara's `current_path`.
31
- Enabled: true
32
- VersionAdded: '1.18'
33
- VersionChanged: '2.0'
34
- Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/CurrentPathExpectation
35
-
36
35
  Capybara/FindAllFirst:
37
36
  Description: Enforces use of `first` instead of `all` with `first` or `[0]`.
38
37
  Enabled: pending
38
+ SafeAutoCorrect: false
39
39
  VersionAdded: '2.22'
40
+ VersionChanged: '2.23'
40
41
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/FindAllFirst
41
42
 
42
- Capybara/MatchStyle:
43
- Description: Checks for usage of deprecated style methods.
44
- Enabled: pending
45
- VersionAdded: '2.17'
46
- Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/MatchStyle
47
-
48
- Capybara/NegationMatcher:
49
- Description: Enforces use of `have_no_*` or `not_to` for negated expectations.
50
- Enabled: pending
51
- VersionAdded: '2.14'
52
- VersionChanged: '2.20'
53
- EnforcedStyle: have_no
54
- SupportedStyles:
55
- - have_no
56
- - not_to
57
- Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcher
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
-
65
43
  Capybara/RedundantWithinFind:
66
44
  Description: Checks for redundant `within find(...)` calls.
67
45
  Enabled: pending
@@ -80,23 +58,23 @@ Capybara/SpecificFinders:
80
58
  VersionAdded: '2.13'
81
59
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/SpecificFinders
82
60
 
83
- Capybara/SpecificMatcher:
84
- Description: Checks for there is a more specific matcher offered by Capybara.
85
- Enabled: pending
86
- VersionAdded: '2.12'
87
- Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/SpecificMatcher
88
-
89
- Capybara/VisibilityMatcher:
90
- Description: Checks for boolean visibility in Capybara finders.
91
- Enabled: true
92
- VersionAdded: '1.39'
93
- VersionChanged: '2.0'
94
- Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/VisibilityMatcher
95
-
96
61
  Capybara/RSpec:
97
62
  Enabled: true
98
63
  Include: *1
99
64
 
65
+ Capybara/RSpec/CurrentPathExpectation:
66
+ Description: Checks that no expectations are set on Capybara's `current_path`.
67
+ Enabled: pending
68
+ VersionAdded: '1.18'
69
+ VersionChanged: '2.23'
70
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/CurrentPathExpectation
71
+
72
+ Capybara/RSpec/HaveContent:
73
+ Description: Checks for usage of `have_content` and `have_no_content`.
74
+ Enabled: pending
75
+ VersionAdded: '2.23'
76
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/HaveContent
77
+
100
78
  Capybara/RSpec/HaveSelector:
101
79
  Description: Use `have_css` or `have_xpath` instead of `have_selector`.
102
80
  Enabled: pending
@@ -104,6 +82,30 @@ Capybara/RSpec/HaveSelector:
104
82
  VersionAdded: '2.19'
105
83
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/HaveSelector
106
84
 
85
+ Capybara/RSpec/MatchStyle:
86
+ Description: Checks for usage of deprecated style methods in RSpec matchers.
87
+ Enabled: pending
88
+ VersionAdded: '2.23'
89
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/MatchStyle
90
+
91
+ Capybara/RSpec/NegationMatcher:
92
+ Description: Enforces use of `have_no_*` or `not_to` for negated expectations.
93
+ Enabled: pending
94
+ VersionAdded: '2.14'
95
+ VersionChanged: '2.23'
96
+ EnforcedStyle: have_no
97
+ SupportedStyles:
98
+ - have_no
99
+ - not_to
100
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/NegationMatcher
101
+
102
+ Capybara/RSpec/NegationMatcherAfterVisit:
103
+ Description: Do not allow negative matchers to be used immediately after `visit`.
104
+ Enabled: pending
105
+ VersionAdded: '2.22'
106
+ VersionChanged: '2.23'
107
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/NegationMatcherAfterVisit
108
+
107
109
  Capybara/RSpec/PredicateMatcher:
108
110
  Description: Prefer using predicate matcher over using predicate method directly.
109
111
  Enabled: pending
@@ -115,3 +117,17 @@ Capybara/RSpec/PredicateMatcher:
115
117
  - explicit
116
118
  VersionAdded: '2.19'
117
119
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/PredicateMatcher
120
+
121
+ Capybara/RSpec/SpecificMatcher:
122
+ Description: Checks for there is a more specific matcher offered by Capybara.
123
+ Enabled: pending
124
+ VersionAdded: '2.12'
125
+ VersionChanged: '2.23'
126
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/SpecificMatcher
127
+
128
+ Capybara/RSpec/VisibilityMatcher:
129
+ Description: Checks for boolean visibility in Capybara finders.
130
+ Enabled: pending
131
+ VersionAdded: '1.39'
132
+ VersionChanged: '2.23'
133
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RSpec/VisibilityMatcher
@@ -0,0 +1,18 @@
1
+ #
2
+ # Configuration of obsolete/deprecated cops used by `ConfigObsoletion`.
3
+ #
4
+ # See: https://docs.rubocop.org/rubocop/extensions.html#config-obsoletions
5
+ #
6
+
7
+ renamed:
8
+ Capybara/CurrentPathExpectation: Capybara/RSpec/CurrentPathExpectation
9
+ Capybara/NegationMatcher: Capybara/RSpec/NegationMatcher
10
+ Capybara/NegationMatcherAfterVisit: Capybara/RSpec/NegationMatcherAfterVisit
11
+ Capybara/SpecificMatcher: Capybara/RSpec/SpecificMatcher
12
+ Capybara/VisibilityMatcher: Capybara/RSpec/VisibilityMatcher
13
+
14
+ split:
15
+ Capybara/MatchStyle:
16
+ alternatives:
17
+ - Capybara/AssertStyle
18
+ - Capybara/RSpec/MatchStyle
@@ -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.22.0'
7
+ STRING = '2.23.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
+ # Checks for usage of deprecated assert style method.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # page.find(:css, '#first').assert_style(display: 'block')
11
+ #
12
+ # # good
13
+ # page.find(:css, '#first').assert_matches_style(display: 'block')
14
+ #
15
+ class AssertStyle < ::RuboCop::Cop::Base
16
+ extend AutoCorrector
17
+
18
+ MSG = 'Use `assert_matches_style` instead of `assert_style`.'
19
+ RESTRICT_ON_SEND = %i[assert_style].freeze
20
+
21
+ def on_send(node)
22
+ method_node = node.loc.selector
23
+ add_offense(method_node) do |corrector|
24
+ corrector.replace(method_node, 'assert_matches_style')
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -5,6 +5,13 @@ module RuboCop
5
5
  module Capybara
6
6
  # Enforces use of `first` instead of `all` with `first` or `[0]`.
7
7
  #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because `all` returns a
10
+ # `Capybara::Result` (an enumerable collection), while `first`
11
+ # returns a single `Capybara::Node::Element`. Replacing `all`
12
+ # with `first` may break code that depends on the return value
13
+ # being a collection (e.g. calling `.each` on the result).
14
+ #
8
15
  # @example
9
16
  #
10
17
  # # bad
@@ -46,6 +53,7 @@ module RuboCop
46
53
  def on_all_first(node)
47
54
  return unless (parent = node.parent)
48
55
  return unless find_all_first?(parent)
56
+ return if part_of_logical_operator?(parent)
49
57
 
50
58
  range = range_between(node.loc.selector.begin_pos,
51
59
  parent.loc.selector.end_pos)
@@ -60,9 +68,11 @@ module RuboCop
60
68
  include_match_first?(node) do |hash|
61
69
  selector = ([node.first_argument.source] + replaced_hash(hash))
62
70
  .join(', ')
63
- add_offense(node,
71
+ range = range_between(node.loc.selector.begin_pos,
72
+ node.source_range.end_pos)
73
+ add_offense(range,
64
74
  message: format(MSG, selector: selector)) do |corrector|
65
- corrector.replace(node, "first(#{selector})")
75
+ corrector.replace(range, "first(#{selector})")
66
76
  end
67
77
  end
68
78
  end
@@ -72,6 +82,10 @@ module RuboCop
72
82
  arg == 'match: :first'
73
83
  end
74
84
  end
85
+
86
+ def part_of_logical_operator?(node)
87
+ node.ancestors.any?(&:operator_keyword?)
88
+ end
75
89
  end
76
90
  end
77
91
  end
@@ -83,6 +83,18 @@ module RuboCop
83
83
  normalize = selector.gsub(/(\\[>,+~]|\(.*\))/, '')
84
84
  normalize.match?(/[ >,+~]/)
85
85
  end
86
+
87
+ # @param str [String]
88
+ # @param quote [String] the quote character used (' or ")
89
+ # @return [String]
90
+ # @example
91
+ # css_escape('foo.bar', "'") # => 'foo\.bar'
92
+ # css_escape('foo.bar', '"') # => 'foo\\.bar'
93
+ # css_escape('foo', "'") # => 'foo'
94
+ def css_escape(str, quote = "'")
95
+ escaped_dot = quote == '"' ? '\\\\\\\\.' : '\\\\.'
96
+ str.gsub('.', escaped_dot)
97
+ end
86
98
  end
87
99
  end
88
100
  end
@@ -27,6 +27,7 @@ module RuboCop
27
27
  # end
28
28
  #
29
29
  class RedundantWithinFind < ::RuboCop::Cop::Base
30
+ include CssSelector
30
31
  extend AutoCorrector
31
32
  MSG = 'Redundant `within %<method>s(...)` call detected.'
32
33
  RESTRICT_ON_SEND = %i[within].freeze
@@ -53,13 +54,25 @@ module RuboCop
53
54
  end
54
55
 
55
56
  def replaced(node)
56
- replaced = node.arguments.map(&:source).join(', ')
57
- if node.method?(:find_by_id)
58
- replaced.sub(/\A(["'])/, '\1#')
57
+ unless node.method?(:find_by_id)
58
+ return node.arguments.map(&:source).join(', ')
59
+ end
60
+
61
+ if node.first_argument.str_type?
62
+ build_escaped_selector(node.first_argument, node)
59
63
  else
60
- replaced
64
+ node.arguments.map(&:source).join(', ')
65
+ .sub(/\A(["'])/, '\1#')
61
66
  end
62
67
  end
68
+
69
+ def build_escaped_selector(first_arg, node)
70
+ quote = first_arg.source[0]
71
+ escaped_id = CssSelector.css_escape(first_arg.value, quote)
72
+ rest_args = node.arguments.drop(1).map(&:source)
73
+
74
+ ["#{quote}##{escaped_id}#{quote}", *rest_args].join(', ')
75
+ end
63
76
  end
64
77
  end
65
78
  end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Checks that no expectations are set on Capybara's `current_path`.
8
+ #
9
+ # The
10
+ # https://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers#have_current_path-instance_method[`have_current_path` matcher]
11
+ # should be used on `page` to set expectations on Capybara's
12
+ # current path, since it uses
13
+ # https://github.com/teamcapybara/capybara/blob/master/README.md#asynchronous-javascript-ajax-and-friends[Capybara's waiting functionality]
14
+ # which ensures that preceding actions (like `click_link`) have
15
+ # completed.
16
+ #
17
+ # This cop does not support autocorrection in some cases.
18
+ #
19
+ # @example
20
+ # # bad
21
+ # expect(current_path).to eq('/callback')
22
+ # expect(page.current_path).to eq('/callback')
23
+ #
24
+ # # good
25
+ # expect(page).to have_current_path('/callback', ignore_query: true)
26
+ #
27
+ # # bad (does not support autocorrection when `match` with a variable)
28
+ # expect(page).to match(variable)
29
+ #
30
+ class CurrentPathExpectation < ::RuboCop::Cop::Base
31
+ extend AutoCorrector
32
+ include RangeHelp
33
+
34
+ MSG = 'Do not set an RSpec expectation on `current_path` in ' \
35
+ 'Capybara feature specs - instead, use the ' \
36
+ '`have_current_path` matcher on `page`'
37
+
38
+ RESTRICT_ON_SEND = %i[expect].freeze
39
+
40
+ # @!method expectation_set_on_current_path(node)
41
+ def_node_matcher :expectation_set_on_current_path, <<~PATTERN
42
+ (send nil? :expect (send {(send nil? :page) nil?} :current_path))
43
+ PATTERN
44
+
45
+ # Supported matchers: eq(...) / match(/regexp/) / match('regexp')
46
+ # @!method as_is_matcher(node)
47
+ def_node_matcher :as_is_matcher, <<~PATTERN
48
+ (send
49
+ #expectation_set_on_current_path ${:to :to_not :not_to}
50
+ ${(send nil? :eq ...) (send nil? :match (regexp ...))})
51
+ PATTERN
52
+
53
+ # @!method regexp_node_matcher(node)
54
+ def_node_matcher :regexp_node_matcher, <<~PATTERN
55
+ (send
56
+ #expectation_set_on_current_path ${:to :to_not :not_to}
57
+ $(send nil? :match $any_str))
58
+ PATTERN
59
+
60
+ def self.autocorrect_incompatible_with
61
+ [Style::TrailingCommaInArguments]
62
+ end
63
+
64
+ def on_send(node)
65
+ expectation_set_on_current_path(node) do
66
+ add_offense(node.loc.selector) do |corrector|
67
+ next unless node.chained?
68
+
69
+ autocorrect(corrector, node)
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def autocorrect(corrector, node)
77
+ as_is_matcher(node.parent) do |to_sym, matcher_node|
78
+ rewrite_expectation(corrector, node, to_sym, matcher_node)
79
+ end
80
+
81
+ regexp_node_matcher(node.parent) do |to_sym, matcher_node, regexp|
82
+ rewrite_expectation(corrector, node, to_sym, matcher_node)
83
+ convert_regexp_node_to_literal(corrector, matcher_node, regexp)
84
+ end
85
+ end
86
+
87
+ def rewrite_expectation(corrector, node, to_symbol, matcher_node)
88
+ corrector.replace(node.first_argument, 'page')
89
+ corrector.replace(node.parent.loc.selector, 'to')
90
+ matcher_method = if to_symbol == :to
91
+ 'have_current_path'
92
+ else
93
+ 'have_no_current_path'
94
+ end
95
+ corrector.replace(matcher_node.loc.selector, matcher_method)
96
+ add_argument_parentheses(corrector, matcher_node.first_argument)
97
+ add_ignore_query_options(corrector, node, matcher_node)
98
+ end
99
+
100
+ def convert_regexp_node_to_literal(corrector, matcher_node,
101
+ regexp_node)
102
+ str_node = matcher_node.first_argument
103
+ regexp_expr = regexp_node_to_regexp_expr(regexp_node)
104
+ corrector.replace(str_node, regexp_expr)
105
+ end
106
+
107
+ def regexp_node_to_regexp_expr(regexp_node)
108
+ if regexp_node.xstr_type?
109
+ "/\#{`#{regexp_node.value.value}`}/"
110
+ else
111
+ Regexp.new(regexp_node.value).inspect
112
+ end
113
+ end
114
+
115
+ def add_argument_parentheses(corrector, arg_node)
116
+ return unless method_call_with_no_parentheses?(arg_node)
117
+
118
+ first_argument_range = range_with_surrounding_space(
119
+ arg_node.first_argument.source_range, side: :left
120
+ )
121
+ corrector.insert_before(first_argument_range, '(')
122
+ corrector.insert_after(arg_node.last_argument, ')')
123
+ end
124
+
125
+ def method_call_with_no_parentheses?(arg_node)
126
+ arg_node.send_type? && arg_node.arguments? &&
127
+ !arg_node.parenthesized?
128
+ end
129
+
130
+ # `have_current_path` with no options will include the querystring
131
+ # while `page.current_path` does not.
132
+ # This ensures the option `ignore_query: true` is added
133
+ # except when `match` matcher.
134
+ def add_ignore_query_options(corrector, node, matcher_node)
135
+ return if matcher_node.method?(:match)
136
+
137
+ expectation_node = node.parent.last_argument
138
+ expectation_last_child = expectation_node.children.last
139
+ corrector.insert_after(expectation_last_child,
140
+ ', ignore_query: true')
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Checks for usage of `have_content` and `have_no_content`.
8
+ #
9
+ # Capybara provides `have_text` and `have_no_text` matchers that are
10
+ # more concise and preferred over their aliases `have_content` and
11
+ # `have_no_content`.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # expect(page).to have_content('capy')
16
+ # expect(page).to have_no_content('bara')
17
+ #
18
+ # # good
19
+ # expect(page).to have_text('capy')
20
+ # expect(page).to have_no_text('bara')
21
+ #
22
+ class HaveContent < ::RuboCop::Cop::Base
23
+ extend AutoCorrector
24
+
25
+ MSG = 'Prefer `%<good>s` over `%<bad>s`.'
26
+ RESTRICT_ON_SEND = %i[have_content have_no_content].freeze
27
+ PREFERRED_METHOD = {
28
+ 'have_content' => 'have_text',
29
+ 'have_no_content' => 'have_no_text'
30
+ }.freeze
31
+
32
+ def on_send(node)
33
+ method_node = node.loc.selector
34
+ add_offense(method_node,
35
+ message: message(method_node)) do |corrector|
36
+ corrector.replace(method_node,
37
+ PREFERRED_METHOD[method_node.source])
38
+ end
39
+ end
40
+ alias on_csend on_send
41
+
42
+ private
43
+
44
+ def message(node)
45
+ format(MSG, good: PREFERRED_METHOD[node.source], bad: node.source)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -52,9 +52,10 @@ module RuboCop
52
52
 
53
53
  def on_select_with_type(node, type)
54
54
  return unless SELECTORS.include?(type.value)
55
+ return unless (locator = node.arguments[1])
55
56
 
56
57
  add_offense(node, message: message_typed(type)) do |corrector|
57
- corrector.remove(deletion_range(type, node.arguments[1]))
58
+ corrector.remove(deletion_range(type, locator))
58
59
  corrector.replace(node.loc.selector, "have_#{type.value}")
59
60
  end
60
61
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ module RSpec
7
+ # Checks for usage of deprecated style methods in RSpec matchers.
8
+ #
9
+ # @example when using `has_style?`
10
+ # # bad
11
+ # expect(page.find(:css, 'first')
12
+ # .has_style?(display: 'block')).to be true
13
+ #
14
+ # # good
15
+ # expect(page.find(:css, 'first')
16
+ # .matches_style?(display: 'block')).to be true
17
+ #
18
+ # @example when using `have_style`
19
+ # # bad
20
+ # expect(page).to have_style(display: 'block')
21
+ #
22
+ # # good
23
+ # expect(page).to match_style(display: 'block')
24
+ #
25
+ class MatchStyle < ::RuboCop::Cop::Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Use `%<good>s` instead of `%<bad>s`.'
29
+ RESTRICT_ON_SEND = %i[has_style? have_style].freeze
30
+ PREFERRED_METHOD = {
31
+ 'has_style?' => 'matches_style?',
32
+ 'have_style' => 'match_style'
33
+ }.freeze
34
+
35
+ def on_send(node)
36
+ method_node = node.loc.selector
37
+ preferred = PREFERRED_METHOD[method_node.source]
38
+ add_offense(method_node,
39
+ message: message(preferred, method_node)) do |corrector|
40
+ corrector.replace(method_node, preferred)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def message(preferred, method_node)
47
+ format(MSG, good: preferred, bad: method_node.source)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end