Merge pull request #14671 from Homebrew/dependabot/bundler/Library/Homebrew/rubocop-capybara-2.17.1
build(deps): bump rubocop-capybara from 2.17.0 to 2.17.1 in /Library/Homebrew
This commit is contained in:
		
						commit
						9b063cc787
					
				@ -142,7 +142,7 @@ GEM
 | 
			
		||||
      unicode-display_width (>= 2.4.0, < 3.0)
 | 
			
		||||
    rubocop-ast (1.26.0)
 | 
			
		||||
      parser (>= 3.2.1.0)
 | 
			
		||||
    rubocop-capybara (2.17.0)
 | 
			
		||||
    rubocop-capybara (2.17.1)
 | 
			
		||||
      rubocop (~> 1.41)
 | 
			
		||||
    rubocop-performance (1.16.0)
 | 
			
		||||
      rubocop (>= 1.7.0, < 2.0)
 | 
			
		||||
 | 
			
		||||
@ -8,19 +8,74 @@ module RuboCop; end
 | 
			
		||||
module RuboCop::Cop; end
 | 
			
		||||
module RuboCop::Cop::Capybara; end
 | 
			
		||||
 | 
			
		||||
module RuboCop::Cop::Capybara::CapybaraHelp
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def common_attributes?(selector); end
 | 
			
		||||
  def include_option?(node, option); end
 | 
			
		||||
  def replaceable_attributes?(attrs); end
 | 
			
		||||
  def replaceable_element?(node, element, attrs); end
 | 
			
		||||
  def replaceable_option?(node, locator, element); end
 | 
			
		||||
  def replaceable_pseudo_class?(pseudo_class, locator); end
 | 
			
		||||
  def replaceable_pseudo_class_not?(locator); end
 | 
			
		||||
  def replaceable_pseudo_classes?(locator); end
 | 
			
		||||
  def replaceable_to_link?(node, attrs); end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
    def common_attributes?(selector); end
 | 
			
		||||
    def include_option?(node, option); end
 | 
			
		||||
    def replaceable_attributes?(attrs); end
 | 
			
		||||
    def replaceable_element?(node, element, attrs); end
 | 
			
		||||
    def replaceable_option?(node, locator, element); end
 | 
			
		||||
    def replaceable_pseudo_class?(pseudo_class, locator); end
 | 
			
		||||
    def replaceable_pseudo_class_not?(locator); end
 | 
			
		||||
    def replaceable_pseudo_classes?(locator); end
 | 
			
		||||
    def replaceable_to_link?(node, attrs); end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
RuboCop::Cop::Capybara::CapybaraHelp::COMMON_OPTIONS = T.let(T.unsafe(nil), Array)
 | 
			
		||||
RuboCop::Cop::Capybara::CapybaraHelp::SPECIFIC_OPTIONS = T.let(T.unsafe(nil), Hash)
 | 
			
		||||
RuboCop::Cop::Capybara::CapybaraHelp::SPECIFIC_PSEUDO_CLASSES = T.let(T.unsafe(nil), Array)
 | 
			
		||||
 | 
			
		||||
module RuboCop::Cop::Capybara::CssSelector
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def attribute?(selector); end
 | 
			
		||||
  def attributes(selector); end
 | 
			
		||||
  def classes(selector); end
 | 
			
		||||
  def id(selector); end
 | 
			
		||||
  def id?(selector); end
 | 
			
		||||
  def multiple_selectors?(selector); end
 | 
			
		||||
  def normalize_value(value); end
 | 
			
		||||
  def pseudo_classes(selector); end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
    def attribute?(selector); end
 | 
			
		||||
    def attributes(selector); end
 | 
			
		||||
    def classes(selector); end
 | 
			
		||||
    def id(selector); end
 | 
			
		||||
    def id?(selector); end
 | 
			
		||||
    def multiple_selectors?(selector); end
 | 
			
		||||
    def normalize_value(value); end
 | 
			
		||||
    def pseudo_classes(selector); end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class RuboCop::Cop::Capybara::CurrentPathExpectation < ::RuboCop::Cop::Base
 | 
			
		||||
  extend ::RuboCop::Cop::AutoCorrector
 | 
			
		||||
 | 
			
		||||
  def as_is_matcher(param0 = T.unsafe(nil)); end
 | 
			
		||||
  def expectation_set_on_current_path(param0 = T.unsafe(nil)); end
 | 
			
		||||
  def on_send(node); end
 | 
			
		||||
  def regexp_str_matcher(param0 = T.unsafe(nil)); end
 | 
			
		||||
  def regexp_node_matcher(param0 = T.unsafe(nil)); end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def add_ignore_query_options(corrector, node); end
 | 
			
		||||
  def autocorrect(corrector, node); end
 | 
			
		||||
  def convert_regexp_str_to_literal(corrector, matcher_node, regexp_str); end
 | 
			
		||||
  def convert_regexp_node_to_literal(corrector, matcher_node, regexp_node); end
 | 
			
		||||
  def regexp_node_to_regexp_expr(regexp_node); end
 | 
			
		||||
  def rewrite_expectation(corrector, node, to_symbol, matcher_node); end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
@ -78,6 +133,8 @@ class RuboCop::Cop::Capybara::SpecificActions < ::RuboCop::Cop::Base
 | 
			
		||||
  def last_selector(arg); end
 | 
			
		||||
  def message(action, selector); end
 | 
			
		||||
  def offense_range(node, receiver); end
 | 
			
		||||
  def replaceable?(node, arg, action); end
 | 
			
		||||
  def replaceable_attributes?(selector); end
 | 
			
		||||
  def specific_action(selector); end
 | 
			
		||||
  def supported_selector?(selector); end
 | 
			
		||||
end
 | 
			
		||||
@ -90,17 +147,21 @@ class RuboCop::Cop::Capybara::SpecificFinders < ::RuboCop::Cop::Base
 | 
			
		||||
  include ::RuboCop::Cop::RangeHelp
 | 
			
		||||
  extend ::RuboCop::Cop::AutoCorrector
 | 
			
		||||
 | 
			
		||||
  def class_options(param0); end
 | 
			
		||||
  def find_argument(param0 = T.unsafe(nil)); end
 | 
			
		||||
  def on_send(node); end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def append_options(classes, options); end
 | 
			
		||||
  def attribute?(arg); end
 | 
			
		||||
  def autocorrect_classes(corrector, node, classes); end
 | 
			
		||||
  def end_pos(node); end
 | 
			
		||||
  def keyword_argument_class(classes); end
 | 
			
		||||
  def offense_range(node); end
 | 
			
		||||
  def on_attr(node, arg); end
 | 
			
		||||
  def on_id(node, arg); end
 | 
			
		||||
  def register_offense(node, arg_replacement); end
 | 
			
		||||
  def register_offense(node, id, classes = T.unsafe(nil)); end
 | 
			
		||||
  def replaced_arguments(arg, id); end
 | 
			
		||||
  def to_options(attrs); end
 | 
			
		||||
end
 | 
			
		||||
@ -116,6 +177,8 @@ class RuboCop::Cop::Capybara::SpecificMatcher < ::RuboCop::Cop::Base
 | 
			
		||||
 | 
			
		||||
  def good_matcher(node, matcher); end
 | 
			
		||||
  def message(node, matcher); end
 | 
			
		||||
  def replaceable?(node, arg, matcher); end
 | 
			
		||||
  def replaceable_attributes?(selector); end
 | 
			
		||||
  def specific_matcher(arg); end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -137,58 +200,6 @@ RuboCop::Cop::Capybara::VisibilityMatcher::CAPYBARA_MATCHER_METHODS = T.let(T.un
 | 
			
		||||
RuboCop::Cop::Capybara::VisibilityMatcher::MSG_FALSE = T.let(T.unsafe(nil), String)
 | 
			
		||||
RuboCop::Cop::Capybara::VisibilityMatcher::MSG_TRUE = T.let(T.unsafe(nil), String)
 | 
			
		||||
RuboCop::Cop::Capybara::VisibilityMatcher::RESTRICT_ON_SEND = T.let(T.unsafe(nil), Array)
 | 
			
		||||
 | 
			
		||||
module RuboCop::Cop::CapybaraHelp
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def include_option?(node, option); end
 | 
			
		||||
  def replaceable_element?(node, element, attrs); end
 | 
			
		||||
  def replaceable_pseudo_class?(pseudo_class, locator); end
 | 
			
		||||
  def replaceable_pseudo_class_not?(locator); end
 | 
			
		||||
  def replaceable_to_link?(node, attrs); end
 | 
			
		||||
  def specific_option?(node, locator, element); end
 | 
			
		||||
  def specific_pseudo_classes?(locator); end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
    def include_option?(node, option); end
 | 
			
		||||
    def replaceable_element?(node, element, attrs); end
 | 
			
		||||
    def replaceable_pseudo_class?(pseudo_class, locator); end
 | 
			
		||||
    def replaceable_pseudo_class_not?(locator); end
 | 
			
		||||
    def replaceable_to_link?(node, attrs); end
 | 
			
		||||
    def specific_option?(node, locator, element); end
 | 
			
		||||
    def specific_pseudo_classes?(locator); end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
module RuboCop::Cop::CssSelector
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def attribute?(selector); end
 | 
			
		||||
  def attributes(selector); end
 | 
			
		||||
  def common_attributes?(selector); end
 | 
			
		||||
  def id?(selector); end
 | 
			
		||||
  def multiple_selectors?(selector); end
 | 
			
		||||
  def normalize_value(value); end
 | 
			
		||||
  def pseudo_classes(selector); end
 | 
			
		||||
  def specific_options?(element, attribute); end
 | 
			
		||||
  def specific_pesudo_classes?(pseudo_class); end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
    def attribute?(selector); end
 | 
			
		||||
    def attributes(selector); end
 | 
			
		||||
    def common_attributes?(selector); end
 | 
			
		||||
    def id?(selector); end
 | 
			
		||||
    def multiple_selectors?(selector); end
 | 
			
		||||
    def normalize_value(value); end
 | 
			
		||||
    def pseudo_classes(selector); end
 | 
			
		||||
    def specific_options?(element, attribute); end
 | 
			
		||||
    def specific_pesudo_classes?(pseudo_class); end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
RuboCop::Cop::CssSelector::COMMON_OPTIONS = T.let(T.unsafe(nil), Array)
 | 
			
		||||
RuboCop::Cop::CssSelector::SPECIFIC_OPTIONS = T.let(T.unsafe(nil), Hash)
 | 
			
		||||
RuboCop::Cop::CssSelector::SPECIFIC_PSEUDO_CLASSES = T.let(T.unsafe(nil), Array)
 | 
			
		||||
RuboCop::Cop::IgnoredMethods = RuboCop::Cop::AllowedMethods
 | 
			
		||||
RuboCop::Cop::IgnoredPattern = RuboCop::Cop::AllowedPattern
 | 
			
		||||
RuboCop::NodePattern = RuboCop::AST::NodePattern
 | 
			
		||||
@ -104,7 +104,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/ruby-progressbar-1.11.0/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/unicode-display_width-2.4.2/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-1.45.1/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-capybara-2.17.0/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-capybara-2.17.1/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-performance-1.16.0/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rails-2.17.4/lib")
 | 
			
		||||
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rspec-2.18.1/lib")
 | 
			
		||||
 | 
			
		||||
@ -1,78 +0,0 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    # Help methods for capybara.
 | 
			
		||||
    module CapybaraHelp
 | 
			
		||||
      module_function
 | 
			
		||||
 | 
			
		||||
      # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
      # @param locator [String]
 | 
			
		||||
      # @param element [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def specific_option?(node, locator, element)
 | 
			
		||||
        attrs = CssSelector.attributes(locator).keys
 | 
			
		||||
        return false unless replaceable_element?(node, element, attrs)
 | 
			
		||||
 | 
			
		||||
        attrs.all? do |attr|
 | 
			
		||||
          CssSelector.specific_options?(element, attr)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param locator [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def specific_pseudo_classes?(locator)
 | 
			
		||||
        CssSelector.pseudo_classes(locator).all? do |pseudo_class|
 | 
			
		||||
          replaceable_pseudo_class?(pseudo_class, locator)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param pseudo_class [String]
 | 
			
		||||
      # @param locator [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def replaceable_pseudo_class?(pseudo_class, locator)
 | 
			
		||||
        return false unless CssSelector.specific_pesudo_classes?(pseudo_class)
 | 
			
		||||
 | 
			
		||||
        case pseudo_class
 | 
			
		||||
        when 'not()' then replaceable_pseudo_class_not?(locator)
 | 
			
		||||
        else true
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param locator [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def replaceable_pseudo_class_not?(locator)
 | 
			
		||||
        locator.scan(/not\(.*?\)/).all? do |negation|
 | 
			
		||||
          CssSelector.attributes(negation).values.all? do |v|
 | 
			
		||||
            v.is_a?(TrueClass) || v.is_a?(FalseClass)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
      # @param element [String]
 | 
			
		||||
      # @param attrs [Array<String>]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def replaceable_element?(node, element, attrs)
 | 
			
		||||
        case element
 | 
			
		||||
        when 'link' then replaceable_to_link?(node, attrs)
 | 
			
		||||
        else true
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
      # @param attrs [Array<String>]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def replaceable_to_link?(node, attrs)
 | 
			
		||||
        include_option?(node, :href) || attrs.include?('href')
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
      # @param option [Symbol]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      def include_option?(node, option)
 | 
			
		||||
        node.each_descendant(:sym).find { |opt| opt.value == option }
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -1,144 +0,0 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    # Helps parsing css selector.
 | 
			
		||||
    module CssSelector
 | 
			
		||||
      COMMON_OPTIONS = %w[
 | 
			
		||||
        above below left_of right_of near count minimum maximum between text
 | 
			
		||||
        id class style visible obscured exact exact_text normalize_ws match
 | 
			
		||||
        wait filter_set focused
 | 
			
		||||
      ].freeze
 | 
			
		||||
      SPECIFIC_OPTIONS = {
 | 
			
		||||
        'button' => (
 | 
			
		||||
          COMMON_OPTIONS + %w[disabled name value title type]
 | 
			
		||||
        ).freeze,
 | 
			
		||||
        'link' => (
 | 
			
		||||
          COMMON_OPTIONS + %w[href alt title download]
 | 
			
		||||
        ).freeze,
 | 
			
		||||
        'table' => (
 | 
			
		||||
          COMMON_OPTIONS + %w[
 | 
			
		||||
            caption with_cols cols with_rows rows
 | 
			
		||||
          ]
 | 
			
		||||
        ).freeze,
 | 
			
		||||
        'select' => (
 | 
			
		||||
          COMMON_OPTIONS + %w[
 | 
			
		||||
            disabled name placeholder options enabled_options
 | 
			
		||||
            disabled_options selected with_selected multiple with_options
 | 
			
		||||
          ]
 | 
			
		||||
        ).freeze,
 | 
			
		||||
        'field' => (
 | 
			
		||||
          COMMON_OPTIONS + %w[
 | 
			
		||||
            checked unchecked disabled valid name placeholder
 | 
			
		||||
            validation_message readonly with type multiple
 | 
			
		||||
          ]
 | 
			
		||||
        ).freeze
 | 
			
		||||
      }.freeze
 | 
			
		||||
      SPECIFIC_PSEUDO_CLASSES = %w[
 | 
			
		||||
        not() disabled enabled checked unchecked
 | 
			
		||||
      ].freeze
 | 
			
		||||
 | 
			
		||||
      module_function
 | 
			
		||||
 | 
			
		||||
      # @param element [String]
 | 
			
		||||
      # @param attribute [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   specific_pesudo_classes?('button', 'name') # => true
 | 
			
		||||
      #   specific_pesudo_classes?('link', 'invalid') # => false
 | 
			
		||||
      def specific_options?(element, attribute)
 | 
			
		||||
        SPECIFIC_OPTIONS.fetch(element, []).include?(attribute)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param pseudo_class [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   specific_pesudo_classes?('disabled') # => true
 | 
			
		||||
      #   specific_pesudo_classes?('first-of-type') # => false
 | 
			
		||||
      def specific_pesudo_classes?(pseudo_class)
 | 
			
		||||
        SPECIFIC_PSEUDO_CLASSES.include?(pseudo_class)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param selector [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   id?('#some-id') # => true
 | 
			
		||||
      #   id?('.some-class') # => false
 | 
			
		||||
      def id?(selector)
 | 
			
		||||
        selector.start_with?('#')
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param selector [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   attribute?('[attribute]') # => true
 | 
			
		||||
      #   attribute?('attribute') # => false
 | 
			
		||||
      def attribute?(selector)
 | 
			
		||||
        selector.start_with?('[')
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param selector [String]
 | 
			
		||||
      # @return [Array<String>]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   attributes('a[foo-bar_baz]') # => {"foo-bar_baz=>true}
 | 
			
		||||
      #   attributes('button[foo][bar]') # => {"foo"=>true, "bar"=>true}
 | 
			
		||||
      #   attributes('table[foo=bar]') # => {"foo"=>"'bar'"}
 | 
			
		||||
      def attributes(selector)
 | 
			
		||||
        selector.scan(/\[(.*?)\]/).flatten.to_h do |attr|
 | 
			
		||||
          key, value = attr.split('=')
 | 
			
		||||
          [key, normalize_value(value)]
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param selector [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   common_attributes?('a[focused]') # => true
 | 
			
		||||
      #   common_attributes?('button[focused][visible]') # => true
 | 
			
		||||
      #   common_attributes?('table[id=some-id]') # => true
 | 
			
		||||
      #   common_attributes?('h1[invalid]') # => false
 | 
			
		||||
      def common_attributes?(selector)
 | 
			
		||||
        attributes(selector).keys.difference(COMMON_OPTIONS).none?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param selector [String]
 | 
			
		||||
      # @return [Array<String>]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   pseudo_classes('button:not([disabled])') # => ['not()']
 | 
			
		||||
      #   pseudo_classes('a:enabled:not([valid])') # => ['enabled', 'not()']
 | 
			
		||||
      def pseudo_classes(selector)
 | 
			
		||||
        # Attributes must be excluded or else the colon in the `href`s URL
 | 
			
		||||
        # will also be picked up as pseudo classes.
 | 
			
		||||
        # "a:not([href='http://example.com']):enabled" => "a:not():enabled"
 | 
			
		||||
        ignored_attribute = selector.gsub(/\[.*?\]/, '')
 | 
			
		||||
        # "a:not():enabled" => ["not()", "enabled"]
 | 
			
		||||
        ignored_attribute.scan(/:([^:]*)/).flatten
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param selector [String]
 | 
			
		||||
      # @return [Boolean]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   multiple_selectors?('a.cls b#id') # => true
 | 
			
		||||
      #   multiple_selectors?('a.cls') # => false
 | 
			
		||||
      def multiple_selectors?(selector)
 | 
			
		||||
        selector.match?(/[ >,+~]/)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      # @param value [String]
 | 
			
		||||
      # @return [Boolean, String]
 | 
			
		||||
      # @example
 | 
			
		||||
      #   normalize_value('true') # => true
 | 
			
		||||
      #   normalize_value('false') # => false
 | 
			
		||||
      #   normalize_value(nil) # => false
 | 
			
		||||
      #   normalize_value("foo") # => "'foo'"
 | 
			
		||||
      def normalize_value(value)
 | 
			
		||||
        case value
 | 
			
		||||
        when 'true' then true
 | 
			
		||||
        when 'false' then false
 | 
			
		||||
        when nil then true
 | 
			
		||||
        else "'#{value}'"
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -4,7 +4,7 @@ module RuboCop
 | 
			
		||||
  module Capybara
 | 
			
		||||
    # Version information for the Capybara RuboCop plugin.
 | 
			
		||||
    module Version
 | 
			
		||||
      STRING = '2.17.0'
 | 
			
		||||
      STRING = '2.17.1'
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -50,11 +50,11 @@ module RuboCop
 | 
			
		||||
            ${(send nil? :eq ...) (send nil? :match (regexp ...))})
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        # @!method regexp_str_matcher(node)
 | 
			
		||||
        def_node_matcher :regexp_str_matcher, <<-PATTERN
 | 
			
		||||
        # @!method regexp_node_matcher(node)
 | 
			
		||||
        def_node_matcher :regexp_node_matcher, <<-PATTERN
 | 
			
		||||
          (send
 | 
			
		||||
            #expectation_set_on_current_path ${:to :to_not :not_to}
 | 
			
		||||
            $(send nil? :match (str $_)))
 | 
			
		||||
            $(send nil? :match ${str dstr xstr}))
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def self.autocorrect_incompatible_with
 | 
			
		||||
@ -78,9 +78,9 @@ module RuboCop
 | 
			
		||||
            rewrite_expectation(corrector, node, to_sym, matcher_node)
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
          regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp|
 | 
			
		||||
          regexp_node_matcher(node.parent) do |to_sym, matcher_node, regexp|
 | 
			
		||||
            rewrite_expectation(corrector, node, to_sym, matcher_node)
 | 
			
		||||
            convert_regexp_str_to_literal(corrector, matcher_node, regexp)
 | 
			
		||||
            convert_regexp_node_to_literal(corrector, matcher_node, regexp)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
@ -97,12 +97,20 @@ module RuboCop
 | 
			
		||||
          add_ignore_query_options(corrector, node)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def convert_regexp_str_to_literal(corrector, matcher_node, regexp_str)
 | 
			
		||||
        def convert_regexp_node_to_literal(corrector, matcher_node, regexp_node)
 | 
			
		||||
          str_node = matcher_node.first_argument
 | 
			
		||||
          regexp_expr = Regexp.new(regexp_str).inspect
 | 
			
		||||
          regexp_expr = regexp_node_to_regexp_expr(regexp_node)
 | 
			
		||||
          corrector.replace(str_node, regexp_expr)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def regexp_node_to_regexp_expr(regexp_node)
 | 
			
		||||
          if regexp_node.xstr_type?
 | 
			
		||||
            "/\#{`#{regexp_node.value.value}`}/"
 | 
			
		||||
          else
 | 
			
		||||
            Regexp.new(regexp_node.value).inspect
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # `have_current_path` with no options will include the querystring
 | 
			
		||||
        # while `page.current_path` does not.
 | 
			
		||||
        # This ensures the option `ignore_query: true` is added
 | 
			
		||||
@ -110,7 +118,9 @@ module RuboCop
 | 
			
		||||
        def add_ignore_query_options(corrector, node)
 | 
			
		||||
          expectation_node = node.parent.last_argument
 | 
			
		||||
          expectation_last_child = expectation_node.children.last
 | 
			
		||||
          return if %i[regexp str].include?(expectation_last_child.type)
 | 
			
		||||
          return if %i[
 | 
			
		||||
            regexp str dstr xstr
 | 
			
		||||
          ].include?(expectation_last_child.type)
 | 
			
		||||
 | 
			
		||||
          corrector.insert_after(
 | 
			
		||||
            expectation_last_child,
 | 
			
		||||
@ -0,0 +1,130 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Capybara
 | 
			
		||||
      # Help methods for capybara.
 | 
			
		||||
      module CapybaraHelp
 | 
			
		||||
        COMMON_OPTIONS = %w[
 | 
			
		||||
          id class style
 | 
			
		||||
        ].freeze
 | 
			
		||||
        SPECIFIC_OPTIONS = {
 | 
			
		||||
          'button' => (
 | 
			
		||||
            COMMON_OPTIONS + %w[disabled name value title type]
 | 
			
		||||
          ).freeze,
 | 
			
		||||
          'link' => (
 | 
			
		||||
            COMMON_OPTIONS + %w[href alt title download]
 | 
			
		||||
          ).freeze,
 | 
			
		||||
          'table' => (
 | 
			
		||||
            COMMON_OPTIONS + %w[cols rows]
 | 
			
		||||
          ).freeze,
 | 
			
		||||
          'select' => (
 | 
			
		||||
            COMMON_OPTIONS + %w[
 | 
			
		||||
              disabled name placeholder
 | 
			
		||||
              selected multiple
 | 
			
		||||
            ]
 | 
			
		||||
          ).freeze,
 | 
			
		||||
          'field' => (
 | 
			
		||||
            COMMON_OPTIONS + %w[
 | 
			
		||||
              checked disabled name placeholder
 | 
			
		||||
              readonly type multiple
 | 
			
		||||
            ]
 | 
			
		||||
          ).freeze
 | 
			
		||||
        }.freeze
 | 
			
		||||
        SPECIFIC_PSEUDO_CLASSES = %w[
 | 
			
		||||
          not() disabled enabled checked unchecked
 | 
			
		||||
        ].freeze
 | 
			
		||||
 | 
			
		||||
        module_function
 | 
			
		||||
 | 
			
		||||
        # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
        # @param locator [String]
 | 
			
		||||
        # @param element [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def replaceable_option?(node, locator, element)
 | 
			
		||||
          attrs = CssSelector.attributes(locator).keys
 | 
			
		||||
          return false unless replaceable_element?(node, element, attrs)
 | 
			
		||||
 | 
			
		||||
          attrs.all? do |attr|
 | 
			
		||||
            SPECIFIC_OPTIONS.fetch(element, []).include?(attr)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   common_attributes?('a[focused]') # => true
 | 
			
		||||
        #   common_attributes?('button[focused][visible]') # => true
 | 
			
		||||
        #   common_attributes?('table[id=some-id]') # => true
 | 
			
		||||
        #   common_attributes?('h1[invalid]') # => false
 | 
			
		||||
        def common_attributes?(selector)
 | 
			
		||||
          CssSelector.attributes(selector).keys.difference(COMMON_OPTIONS).none?
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param attrs [Array<String>]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   replaceable_attributes?('table[id=some-id]') # => true
 | 
			
		||||
        #   replaceable_attributes?('a[focused]') # => false
 | 
			
		||||
        def replaceable_attributes?(attrs)
 | 
			
		||||
          attrs.values.none?(&:nil?)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param locator [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def replaceable_pseudo_classes?(locator)
 | 
			
		||||
          CssSelector.pseudo_classes(locator).all? do |pseudo_class|
 | 
			
		||||
            replaceable_pseudo_class?(pseudo_class, locator)
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param pseudo_class [String]
 | 
			
		||||
        # @param locator [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def replaceable_pseudo_class?(pseudo_class, locator)
 | 
			
		||||
          return false unless SPECIFIC_PSEUDO_CLASSES.include?(pseudo_class)
 | 
			
		||||
 | 
			
		||||
          case pseudo_class
 | 
			
		||||
          when 'not()' then replaceable_pseudo_class_not?(locator)
 | 
			
		||||
          else true
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param locator [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def replaceable_pseudo_class_not?(locator)
 | 
			
		||||
          locator.scan(/not\(.*?\)/).all? do |negation|
 | 
			
		||||
            CssSelector.attributes(negation).values.all? do |v|
 | 
			
		||||
              v.is_a?(TrueClass) || v.is_a?(FalseClass)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
        # @param element [String]
 | 
			
		||||
        # @param attrs [Array<String>]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def replaceable_element?(node, element, attrs)
 | 
			
		||||
          case element
 | 
			
		||||
          when 'link' then replaceable_to_link?(node, attrs)
 | 
			
		||||
          else true
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
        # @param attrs [Array<String>]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def replaceable_to_link?(node, attrs)
 | 
			
		||||
          include_option?(node, :href) || attrs.include?('href')
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param node [RuboCop::AST::SendNode]
 | 
			
		||||
        # @param option [Symbol]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        def include_option?(node, option)
 | 
			
		||||
          node.each_descendant(:sym).find { |opt| opt.value == option }
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -0,0 +1,110 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
module RuboCop
 | 
			
		||||
  module Cop
 | 
			
		||||
    module Capybara
 | 
			
		||||
      # Helps parsing css selector.
 | 
			
		||||
      module CssSelector
 | 
			
		||||
        module_function
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [String]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   id('#some-id') # => some-id
 | 
			
		||||
        #   id('.some-cls') # => nil
 | 
			
		||||
        #   id('#some-id.cls') # => some-id
 | 
			
		||||
        def id(selector)
 | 
			
		||||
          return unless id?(selector)
 | 
			
		||||
 | 
			
		||||
          selector.delete('#').gsub(selector.scan(/[^\\]([>,+~.].*)/).join, '')
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   id?('#some-id') # => true
 | 
			
		||||
        #   id?('.some-cls') # => false
 | 
			
		||||
        def id?(selector)
 | 
			
		||||
          selector.start_with?('#')
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Array<String>]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   classes('#some-id') # => []
 | 
			
		||||
        #   classes('.some-cls') # => ['some-cls']
 | 
			
		||||
        #   classes('#some-id.some-cls') # => ['some-cls']
 | 
			
		||||
        #   classes('#some-id.cls1.cls2') # => ['cls1', 'cls2']
 | 
			
		||||
        def classes(selector)
 | 
			
		||||
          selector.scan(/\.([\w-]*)/).flatten
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   attribute?('[attribute]') # => true
 | 
			
		||||
        #   attribute?('attribute') # => false
 | 
			
		||||
        def attribute?(selector)
 | 
			
		||||
          selector.start_with?('[')
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Array<String>]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   attributes('a[foo-bar_baz]') # => {"foo-bar_baz=>nil}
 | 
			
		||||
        #   attributes('button[foo][bar=baz]') # => {"foo"=>nil, "bar"=>"'baz'"}
 | 
			
		||||
        #   attributes('table[foo=bar]') # => {"foo"=>"'bar'"}
 | 
			
		||||
        def attributes(selector)
 | 
			
		||||
          # Extract the inner strings of attributes.
 | 
			
		||||
          # For example, extract the following:
 | 
			
		||||
          # 'button[foo][bar=baz]' => 'foo][bar=baz'
 | 
			
		||||
          inside_attributes = selector.scan(/\[(.*)\]/).flatten.join
 | 
			
		||||
          inside_attributes.split('][').to_h do |attr|
 | 
			
		||||
            key, value = attr.split('=')
 | 
			
		||||
            [key, normalize_value(value)]
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Array<String>]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   pseudo_classes('button:not([disabled])') # => ['not()']
 | 
			
		||||
        #   pseudo_classes('a:enabled:not([valid])') # => ['enabled', 'not()']
 | 
			
		||||
        def pseudo_classes(selector)
 | 
			
		||||
          # Attributes must be excluded or else the colon in the `href`s URL
 | 
			
		||||
          # will also be picked up as pseudo classes.
 | 
			
		||||
          # "a:not([href='http://example.com']):enabled" => "a:not():enabled"
 | 
			
		||||
          ignored_attribute = selector.gsub(/\[.*?\]/, '')
 | 
			
		||||
          # "a:not():enabled" => ["not()", "enabled"]
 | 
			
		||||
          ignored_attribute.scan(/:([^:]*)/).flatten
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param selector [String]
 | 
			
		||||
        # @return [Boolean]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   multiple_selectors?('a.cls b#id') # => true
 | 
			
		||||
        #   multiple_selectors?('a.cls') # => false
 | 
			
		||||
        def multiple_selectors?(selector)
 | 
			
		||||
          normalize = selector.gsub(/(\\[>,+~]|\(.*\))/, '')
 | 
			
		||||
          normalize.match?(/[ >,+~]/)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        # @param value [String]
 | 
			
		||||
        # @return [Boolean, String]
 | 
			
		||||
        # @example
 | 
			
		||||
        #   normalize_value('true') # => true
 | 
			
		||||
        #   normalize_value('false') # => false
 | 
			
		||||
        #   normalize_value(nil) # => nil
 | 
			
		||||
        #   normalize_value("foo") # => "'foo'"
 | 
			
		||||
        def normalize_value(value)
 | 
			
		||||
          case value
 | 
			
		||||
          when 'true' then true
 | 
			
		||||
          when 'false' then false
 | 
			
		||||
          when nil then nil
 | 
			
		||||
          else "'#{value.gsub(/"|'/, '')}'"
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -41,9 +41,7 @@ module RuboCop
 | 
			
		||||
            # which the last selector points.
 | 
			
		||||
            next unless (selector = last_selector(arg))
 | 
			
		||||
            next unless (action = specific_action(selector))
 | 
			
		||||
            next unless CapybaraHelp.specific_option?(node.receiver, arg,
 | 
			
		||||
                                                      action)
 | 
			
		||||
            next unless CapybaraHelp.specific_pseudo_classes?(arg)
 | 
			
		||||
            next unless replaceable?(node, arg, action)
 | 
			
		||||
 | 
			
		||||
            range = offense_range(node, node.receiver)
 | 
			
		||||
            add_offense(range, message: message(action, selector))
 | 
			
		||||
@ -56,6 +54,18 @@ module RuboCop
 | 
			
		||||
          SPECIFIC_ACTION[last_selector(selector)]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def replaceable?(node, arg, action)
 | 
			
		||||
          replaceable_attributes?(arg) &&
 | 
			
		||||
            CapybaraHelp.replaceable_option?(node.receiver, arg, action) &&
 | 
			
		||||
            CapybaraHelp.replaceable_pseudo_classes?(arg)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def replaceable_attributes?(selector)
 | 
			
		||||
          CapybaraHelp.replaceable_attributes?(
 | 
			
		||||
            CssSelector.attributes(selector)
 | 
			
		||||
          )
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def supported_selector?(selector)
 | 
			
		||||
          !selector.match?(/[>,+~]/)
 | 
			
		||||
        end
 | 
			
		||||
@ -27,8 +27,14 @@ module RuboCop
 | 
			
		||||
          (send _ :find (str $_) ...)
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        # @!method class_options(node)
 | 
			
		||||
        def_node_search :class_options, <<~PATTERN
 | 
			
		||||
          (pair (sym :class) $_ ...)
 | 
			
		||||
        PATTERN
 | 
			
		||||
 | 
			
		||||
        def on_send(node)
 | 
			
		||||
          find_argument(node) do |arg|
 | 
			
		||||
            next if CssSelector.pseudo_classes(arg).any?
 | 
			
		||||
            next if CssSelector.multiple_selectors?(arg)
 | 
			
		||||
 | 
			
		||||
            on_attr(node, arg) if attribute?(arg)
 | 
			
		||||
@ -39,28 +45,57 @@ module RuboCop
 | 
			
		||||
        private
 | 
			
		||||
 | 
			
		||||
        def on_attr(node, arg)
 | 
			
		||||
          return unless (id = CssSelector.attributes(arg)['id'])
 | 
			
		||||
          attrs = CssSelector.attributes(arg)
 | 
			
		||||
          return unless (id = attrs['id'])
 | 
			
		||||
          return if attrs['class']
 | 
			
		||||
 | 
			
		||||
          register_offense(node, replaced_arguments(arg, id))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def on_id(node, arg)
 | 
			
		||||
          register_offense(node, "'#{arg.to_s.delete('#')}'")
 | 
			
		||||
          return if CssSelector.attributes(arg).any?
 | 
			
		||||
 | 
			
		||||
          id = CssSelector.id(arg)
 | 
			
		||||
          register_offense(node, "'#{id}'",
 | 
			
		||||
                           CssSelector.classes(arg.sub("##{id}", '')))
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def attribute?(arg)
 | 
			
		||||
          CssSelector.attribute?(arg) &&
 | 
			
		||||
            CssSelector.common_attributes?(arg)
 | 
			
		||||
            CapybaraHelp.common_attributes?(arg)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def register_offense(node, arg_replacement)
 | 
			
		||||
        def register_offense(node, id, classes = [])
 | 
			
		||||
          add_offense(offense_range(node)) do |corrector|
 | 
			
		||||
            corrector.replace(node.loc.selector, 'find_by_id')
 | 
			
		||||
            corrector.replace(node.first_argument.loc.expression,
 | 
			
		||||
                              arg_replacement)
 | 
			
		||||
                              id.delete('\\'))
 | 
			
		||||
            unless classes.compact.empty?
 | 
			
		||||
              autocorrect_classes(corrector, node, classes)
 | 
			
		||||
            end
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def autocorrect_classes(corrector, node, classes)
 | 
			
		||||
          if (options = class_options(node).first)
 | 
			
		||||
            append_options(classes, options)
 | 
			
		||||
            corrector.replace(options, classes.to_s)
 | 
			
		||||
          else
 | 
			
		||||
            corrector.insert_after(node.first_argument,
 | 
			
		||||
                                   keyword_argument_class(classes))
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def append_options(classes, options)
 | 
			
		||||
          classes << options.value if options.str_type?
 | 
			
		||||
          options.each_value { |v| classes << v.value } if options.array_type?
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def keyword_argument_class(classes)
 | 
			
		||||
          value = classes.size > 1 ? classes.to_s : "'#{classes.first}'"
 | 
			
		||||
          ", class: #{value}"
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def replaced_arguments(arg, id)
 | 
			
		||||
          options = to_options(CssSelector.attributes(arg))
 | 
			
		||||
          options.empty? ? id : "#{id}, #{options}"
 | 
			
		||||
@ -46,8 +46,7 @@ module RuboCop
 | 
			
		||||
          first_argument(node) do |arg|
 | 
			
		||||
            next unless (matcher = specific_matcher(arg))
 | 
			
		||||
            next if CssSelector.multiple_selectors?(arg)
 | 
			
		||||
            next unless CapybaraHelp.specific_option?(node, arg, matcher)
 | 
			
		||||
            next unless CapybaraHelp.specific_pseudo_classes?(arg)
 | 
			
		||||
            next unless replaceable?(node, arg, matcher)
 | 
			
		||||
 | 
			
		||||
            add_offense(node, message: message(node, matcher))
 | 
			
		||||
          end
 | 
			
		||||
@ -60,6 +59,18 @@ module RuboCop
 | 
			
		||||
          SPECIFIC_MATCHER[splitted_arg]
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def replaceable?(node, arg, matcher)
 | 
			
		||||
          replaceable_attributes?(arg) &&
 | 
			
		||||
            CapybaraHelp.replaceable_option?(node, arg, matcher) &&
 | 
			
		||||
            CapybaraHelp.replaceable_pseudo_classes?(arg)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def replaceable_attributes?(selector)
 | 
			
		||||
          CapybaraHelp.replaceable_attributes?(
 | 
			
		||||
            CssSelector.attributes(selector)
 | 
			
		||||
          )
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def message(node, matcher)
 | 
			
		||||
          format(MSG,
 | 
			
		||||
                 good_matcher: good_matcher(node, matcher),
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user