rubocop-capybara 2.19.0 → 2.20.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: 5af741b67ec2d02d9e27e688d553e5e1c8cbb04c845f5d9771b480b291b1d805
4
- data.tar.gz: cbef51994ffa79af69a8a7aac9aabf69ccc307b3c2c104eae2a3a4c635467d26
3
+ metadata.gz: 963a68452b58c47db1d1d9e7f9201db0a98a8b70bb8089473b406911fa5cc33e
4
+ data.tar.gz: 58e60dd5ff6097695a1029b9e37414209f7c26d2da0d16cbc6b808c8cd56efd8
5
5
  SHA512:
6
- metadata.gz: 98bcff699a8ee323a24118f26b412ed8e4d26754df9ee82eab0b107f1b96cb2a0ad7ba898925fde7683501244f1474231bd53591f450e6080321e0bd83245257
7
- data.tar.gz: 4194b54f6b12d4b168122c59fbfa18ec210e92cb4d20207665787ae870a3a92a240938818e187889f239e5ce29244db5c612c368bf761ee68f65f1617f72092f
6
+ metadata.gz: cec93e7919aeefbb1f2b2c13f1f0caeb7627c5556f85fe219f107b6520237cccd792ad4713905dfb47151f0bcb82e04d4bd0034a7a747b741e607ea9d4de5e54
7
+ data.tar.gz: 3e359ec71065b5daf64cdb080d8ddfad4f6144778ad7fb89d932aa083353547663a1316efe7f9e9fd3cb7bca6a6f012812ea7f18e00c76a8af22316f24325f7c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Edge (Unreleased)
4
4
 
5
+ ## 2.20.0 (2024-01-03)
6
+
7
+ - Change to default `EnforcedStyle: link_or_button` for `Capybara/ClickLinkOrButtonStyle` cop. ([@ydah])
8
+ - Fix a false negative for `RSpec/HaveSelector` when first argument is dstr node. ([@ydah])
9
+ - Add new `Capybara/RedundantWithinFind` cop. ([@ydah])
10
+ - Fix an invalid attributes parse when name with multiple `[]` for `Capybara/SpecificFinders` and `Capybara/SpecificActions` and `Capybara/SpecificMatcher`. ([@ydah])
11
+ - Change to default `EnforcedStyle: have_no` for `Capybara/NegationMatcher` cop. ([@ydah])
12
+ - Fix a false positive for `Capybara/SpecificMatcher` when `text:` or `exact_text:` with regexp. ([@ydah])
13
+
5
14
  ## 2.19.0 (2023-09-20)
6
15
 
7
16
  - Add new `Capybara/RSpec/PredicateMatcher` cop. ([@ydah])
data/config/default.yml CHANGED
@@ -10,13 +10,14 @@ Capybara:
10
10
  - "**/features/step_definitions/**/*"
11
11
 
12
12
  Capybara/ClickLinkOrButtonStyle:
13
- Description: Checks for click button or link style.
13
+ Description: Checks for methods of button or link clicks.
14
14
  Enabled: pending
15
15
  VersionAdded: '2.19'
16
- EnforcedStyle: strict
16
+ VersionChanged: '2.20'
17
+ EnforcedStyle: link_or_button
17
18
  SupportedStyles:
18
- - strict
19
19
  - link_or_button
20
+ - strict
20
21
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/ClickLinkOrButtonStyle
21
22
 
22
23
  Capybara/CurrentPathExpectation:
@@ -36,12 +37,19 @@ Capybara/NegationMatcher:
36
37
  Description: Enforces use of `have_no_*` or `not_to` for negated expectations.
37
38
  Enabled: pending
38
39
  VersionAdded: '2.14'
39
- EnforcedStyle: not_to
40
+ VersionChanged: '2.20'
41
+ EnforcedStyle: have_no
40
42
  SupportedStyles:
41
43
  - have_no
42
44
  - not_to
43
45
  Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/NegationMatcher
44
46
 
47
+ Capybara/RedundantWithinFind:
48
+ Description: Checks for redundant `within find(...)` calls.
49
+ Enabled: pending
50
+ VersionAdded: '2.20'
51
+ Reference: https://www.rubydoc.info/gems/rubocop-capybara/RuboCop/Cop/Capybara/RedundantWithinFind
52
+
45
53
  Capybara/SpecificActions:
46
54
  Description: Checks for there is a more specific actions offered by Capybara.
47
55
  Enabled: pending
@@ -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.19.0'
7
+ STRING = '2.20.0'
8
8
  end
9
9
  end
10
10
  end
@@ -3,18 +3,16 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Capybara
6
- # Checks for click button or link style.
6
+ # Checks for methods of button or link clicks.
7
7
  #
8
- # @example EnforcedStyle: strict (default)
9
- # # bad
10
- # click_link_or_button('foo')
11
- # click_on('foo')
8
+ # By default, prefer to use `click_link_or_button` or `click_on`.
9
+ # These methods offer a weaker coupling between the test and HTML,
10
+ # allowing for a more faithful reflection of how the user behaves.
12
11
  #
13
- # # good
14
- # click_link('foo')
15
- # click_button('foo')
12
+ # You can set `EnforcedStyle: strict` to prefer the use of
13
+ # `click_link` and `click_button`, but this is a deprecated setting.
16
14
  #
17
- # @example EnforcedStyle: link_or_button
15
+ # @example EnforcedStyle: link_or_button (default)
18
16
  # # bad
19
17
  # click_link('foo')
20
18
  # click_button('foo')
@@ -23,6 +21,15 @@ module RuboCop
23
21
  # click_link_or_button('foo')
24
22
  # click_on('foo')
25
23
  #
24
+ # @example EnforcedStyle: strict
25
+ # # bad
26
+ # click_link_or_button('foo')
27
+ # click_on('foo')
28
+ #
29
+ # # good
30
+ # click_link('foo')
31
+ # click_button('foo')
32
+ #
26
33
  class ClickLinkOrButtonStyle < ::RuboCop::Cop::Base
27
34
  include ConfigurableEnforcedStyle
28
35
 
@@ -43,8 +50,8 @@ module RuboCop
43
50
  private
44
51
 
45
52
  def offense?(node)
46
- style == :strict && !strict_method?(node) ||
47
- style == :link_or_button && !link_or_button_method?(node)
53
+ (style == :strict && !strict_method?(node)) ||
54
+ (style == :link_or_button && !link_or_button_method?(node))
48
55
  end
49
56
 
50
57
  def offense_message(node)
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ # Css selector parser.
7
+ # @api private
8
+ class CssAttributesParser
9
+ def initialize(selector)
10
+ @selector = selector
11
+ @state = :initial
12
+ @temp = ''
13
+ @results = {}
14
+ @bracket_count = 0
15
+ end
16
+
17
+ # @return [Array<String>]
18
+ def parse # rubocop:disable Metrics/MethodLength
19
+ @selector.chars do |char|
20
+ if char == '['
21
+ on_bracket_start
22
+ elsif char == ']'
23
+ on_bracket_end
24
+ elsif @state == :inside_attr
25
+ @temp += char
26
+ end
27
+ end
28
+ @results
29
+ end
30
+
31
+ private
32
+
33
+ def on_bracket_start
34
+ @bracket_count += 1
35
+ if @state == :initial
36
+ @state = :inside_attr
37
+ else
38
+ @temp += '['
39
+ end
40
+ end
41
+
42
+ def on_bracket_end
43
+ @bracket_count -= 1
44
+ if @bracket_count.zero?
45
+ @state = :initial
46
+ key, value = @temp.split('=')
47
+ @results[key] = normalize_value(value)
48
+ @temp.clear
49
+ else
50
+ @temp += ']'
51
+ end
52
+ end
53
+
54
+ # @param value [String]
55
+ # @return [Boolean, String]
56
+ # @example
57
+ # normalize_value('true') # => true
58
+ # normalize_value('false') # => false
59
+ # normalize_value(nil) # => nil
60
+ # normalize_value("foo") # => "'foo'"
61
+ def normalize_value(value)
62
+ case value
63
+ when 'true' then true
64
+ when 'false' then false
65
+ when nil then nil
66
+ else "'#{value.gsub(/"|'/, '')}'"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -55,15 +55,9 @@ module RuboCop
55
55
  # attributes('a[foo-bar_baz]') # => {"foo-bar_baz=>nil}
56
56
  # attributes('button[foo][bar=baz]') # => {"foo"=>nil, "bar"=>"'baz'"}
57
57
  # attributes('table[foo=bar]') # => {"foo"=>"'bar'"}
58
+ # attributes('[foo="bar[baz][qux]"]') # => {"foo"=>"'bar[baz][qux]'"}
58
59
  def attributes(selector)
59
- # Extract the inner strings of attributes.
60
- # For example, extract the following:
61
- # 'button[foo][bar=baz]' => 'foo][bar=baz'
62
- inside_attributes = selector.scan(/\[(.*)\]/).flatten.join
63
- inside_attributes.split('][').to_h do |attr|
64
- key, value = attr.split('=')
65
- [key, normalize_value(value)]
66
- end
60
+ CssAttributesParser.new(selector).parse
67
61
  end
68
62
 
69
63
  # @param selector [String]
@@ -89,22 +83,6 @@ module RuboCop
89
83
  normalize = selector.gsub(/(\\[>,+~]|\(.*\))/, '')
90
84
  normalize.match?(/[ >,+~]/)
91
85
  end
92
-
93
- # @param value [String]
94
- # @return [Boolean, String]
95
- # @example
96
- # normalize_value('true') # => true
97
- # normalize_value('false') # => false
98
- # normalize_value(nil) # => nil
99
- # normalize_value("foo") # => "'foo'"
100
- def normalize_value(value)
101
- case value
102
- when 'true' then true
103
- when 'false' then false
104
- when nil then nil
105
- else "'#{value.gsub(/"|'/, '')}'"
106
- end
107
- end
108
86
  end
109
87
  end
110
88
  end
@@ -5,16 +5,7 @@ module RuboCop
5
5
  module Capybara
6
6
  # Enforces use of `have_no_*` or `not_to` for negated expectations.
7
7
  #
8
- # @example EnforcedStyle: not_to (default)
9
- # # bad
10
- # expect(page).to have_no_selector
11
- # expect(page).to have_no_css('a')
12
- #
13
- # # good
14
- # expect(page).not_to have_selector
15
- # expect(page).not_to have_css('a')
16
- #
17
- # @example EnforcedStyle: have_no
8
+ # @example EnforcedStyle: have_no (default)
18
9
  # # bad
19
10
  # expect(page).not_to have_selector
20
11
  # expect(page).not_to have_css('a')
@@ -23,6 +14,15 @@ module RuboCop
23
14
  # expect(page).to have_no_selector
24
15
  # expect(page).to have_no_css('a')
25
16
  #
17
+ # @example EnforcedStyle: not_to
18
+ # # bad
19
+ # expect(page).to have_no_selector
20
+ # expect(page).to have_no_css('a')
21
+ #
22
+ # # good
23
+ # expect(page).not_to have_selector
24
+ # expect(page).not_to have_css('a')
25
+ #
26
26
  class NegationMatcher < ::RuboCop::Cop::Base
27
27
  extend AutoCorrector
28
28
  include ConfigurableEnforcedStyle
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Capybara
6
+ # Checks for redundant `within find(...)` calls.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # within find('foo.bar') do
11
+ # # ...
12
+ # end
13
+ #
14
+ # # good
15
+ # within 'foo.bar' do
16
+ # # ...
17
+ # end
18
+ #
19
+ # # bad
20
+ # within find_by_id('foo') do
21
+ # # ...
22
+ # end
23
+ #
24
+ # # good
25
+ # within '#foo' do
26
+ # # ...
27
+ # end
28
+ #
29
+ class RedundantWithinFind < ::RuboCop::Cop::Base
30
+ extend AutoCorrector
31
+ MSG = 'Redundant `within %<method>s(...)` call detected.'
32
+ RESTRICT_ON_SEND = %i[within].freeze
33
+ FIND_METHODS = Set.new(%i[find find_by_id]).freeze
34
+
35
+ # @!method within_find(node)
36
+ def_node_matcher :within_find, <<~PATTERN
37
+ (send nil? :within
38
+ $(send nil? %FIND_METHODS ...))
39
+ PATTERN
40
+
41
+ def on_send(node)
42
+ within_find(node) do |find_node|
43
+ add_offense(find_node, message: msg(find_node)) do |corrector|
44
+ corrector.replace(find_node, replaced(find_node))
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def msg(node)
52
+ format(MSG, method: node.method_name)
53
+ end
54
+
55
+ def replaced(node)
56
+ replaced = node.arguments.map(&:source).join(', ')
57
+ if node.method?(:find_by_id)
58
+ replaced.sub(/\A(["'])/, '\1#')
59
+ else
60
+ replaced
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -44,7 +44,7 @@ module RuboCop
44
44
  def on_send(node)
45
45
  argument = node.first_argument
46
46
  on_select_with_type(node, argument) if argument.sym_type?
47
- on_select_without_type(node) if argument.str_type?
47
+ on_select_without_type(node) if %i[str dstr].include?(argument.type)
48
48
  end
49
49
 
50
50
  private
@@ -76,7 +76,7 @@ module RuboCop
76
76
  end
77
77
 
78
78
  def deletion_range(node)
79
- range_between(node.arguments[0].source_range.end_pos,
79
+ range_between(node.first_argument.source_range.end_pos,
80
80
  node.arguments[1].source_range.end_pos)
81
81
  end
82
82
 
@@ -42,6 +42,11 @@ module RuboCop
42
42
  (send nil? _ (str $_) ... )
43
43
  PATTERN
44
44
 
45
+ # @!method text_with_regexp?(node)
46
+ def_node_search :text_with_regexp?, <<-PATTERN
47
+ (pair (sym {:text :exact_text}) (regexp ...))
48
+ PATTERN
49
+
45
50
  def on_send(node)
46
51
  first_argument(node) do |arg|
47
52
  next unless (matcher = specific_matcher(arg))
@@ -61,6 +66,7 @@ module RuboCop
61
66
 
62
67
  def replaceable?(node, arg, matcher)
63
68
  replaceable_attributes?(arg) &&
69
+ !text_with_regexp?(node) &&
64
70
  CapybaraHelp.replaceable_option?(node, arg, matcher) &&
65
71
  CapybaraHelp.replaceable_pseudo_classes?(arg)
66
72
  end
@@ -7,6 +7,7 @@ require_relative 'capybara/click_link_or_button_style'
7
7
  require_relative 'capybara/current_path_expectation'
8
8
  require_relative 'capybara/match_style'
9
9
  require_relative 'capybara/negation_matcher'
10
+ require_relative 'capybara/redundant_within_find'
10
11
  require_relative 'capybara/specific_actions'
11
12
  require_relative 'capybara/specific_finders'
12
13
  require_relative 'capybara/specific_matcher'
@@ -6,6 +6,7 @@ require 'yaml'
6
6
  require 'rubocop'
7
7
 
8
8
  require_relative 'rubocop/cop/capybara/mixin/capybara_help'
9
+ require_relative 'rubocop/cop/capybara/mixin/css_attributes_parser'
9
10
  require_relative 'rubocop/cop/capybara/mixin/css_selector'
10
11
 
11
12
  require_relative 'rubocop/cop/capybara_cops'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-capybara
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.19.0
4
+ version: 2.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yudai Takada
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-19 00:00:00.000000000 Z
11
+ date: 2024-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -47,8 +47,10 @@ files:
47
47
  - lib/rubocop/cop/capybara/current_path_expectation.rb
48
48
  - lib/rubocop/cop/capybara/match_style.rb
49
49
  - lib/rubocop/cop/capybara/mixin/capybara_help.rb
50
+ - lib/rubocop/cop/capybara/mixin/css_attributes_parser.rb
50
51
  - lib/rubocop/cop/capybara/mixin/css_selector.rb
51
52
  - lib/rubocop/cop/capybara/negation_matcher.rb
53
+ - lib/rubocop/cop/capybara/redundant_within_find.rb
52
54
  - lib/rubocop/cop/capybara/rspec/have_selector.rb
53
55
  - lib/rubocop/cop/capybara/rspec/predicate_matcher.rb
54
56
  - lib/rubocop/cop/capybara/specific_actions.rb
@@ -78,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
80
  - !ruby/object:Gem::Version
79
81
  version: '0'
80
82
  requirements: []
81
- rubygems_version: 3.3.7
83
+ rubygems_version: 3.4.22
82
84
  signing_key:
83
85
  specification_version: 4
84
86
  summary: Code style checking for Capybara test files