lib/rubocop/cop/capybara/mixin/capybara_help.rb in rubocop-capybara-2.17.0 vs lib/rubocop/cop/capybara/mixin/capybara_help.rb in rubocop-capybara-2.17.1

- old
+ new

@@ -1,78 +1,130 @@ # frozen_string_literal: true module RuboCop module Cop - # Help methods for capybara. - module CapybaraHelp - module_function + 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 - # @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) + module_function - attrs.all? do |attr| - CssSelector.specific_options?(element, attr) + # @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 - 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) + # @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 - 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) + # @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 - case pseudo_class - when 'not()' then replaceable_pseudo_class_not?(locator) - else true + # @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 - 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) + # @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 - 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 + # @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 - 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 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 option [Symbol] - # @return [Boolean] - def include_option?(node, option) - node.each_descendant(:sym).find { |opt| opt.value == option } + # @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