lib/rubocop/cop/capybara/mixin/css_selector.rb in rubocop-capybara-2.17.0 vs lib/rubocop/cop/capybara/mixin/css_selector.rb in rubocop-capybara-2.17.1
- old
+ new
@@ -1,143 +1,109 @@
# 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 Capybara
+ # Helps parsing css selector.
+ module CssSelector
+ module_function
- 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)
- # @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
+ selector.delete('#').gsub(selector.scan(/[^\\]([>,+~.].*)/).join, '')
+ 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-cls') # => false
+ def id?(selector)
+ selector.start_with?('#')
+ 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 [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 [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)]
+ # @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
- 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 [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 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}'"
+ # @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