Skip to content

Commit

Permalink
Merge 990e7b1 into 2d8db09
Browse files Browse the repository at this point in the history
  • Loading branch information
p0deje committed Feb 12, 2019
2 parents 2d8db09 + 990e7b1 commit ef68a89
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 5 deletions.
11 changes: 11 additions & 0 deletions lib/watir/elements/element.rb
Expand Up @@ -18,6 +18,17 @@ class Element
attr_accessor :keyword
attr_reader :selector

# https://www.w3.org/TR/html52/single-page.html#casesensitivity
CASE_INSENSITIVE_ATTRIBUTES = %i[accept accept_charset align alink axis
bgcolor charset checked clear codetype
color compact declare defer dir direction
disabled enctype face frame hreflang
http_equiv lang language link media
method multiple nohref noresize noshade
nowrap readonly rel rev rules scope
scrolling selected shape target text
type valign valuetype vlink].freeze

#
# temporarily add :id and :class_name manually since they're no longer specified in the HTML spec.
#
Expand Down
27 changes: 22 additions & 5 deletions lib/watir/locators/element/selector_builder/xpath.rb
Expand Up @@ -12,6 +12,7 @@ class XPath

def build(selector)
@selector = selector
@valid_attributes = build_valid_attributes

@built = (@selector.keys & CAN_NOT_BUILD).each_with_object({}) do |key, hash|
hash[key] = @selector.delete(key)
Expand Down Expand Up @@ -55,10 +56,7 @@ def predicate_expression(key, val)
end

def predicate_conversion(key, regexp)
# type attributes can be upper case - downcase them
# https://github.com/watir/watir/issues/72
downcase = key == :type || regexp.casefold?

downcase = case_insensitive_attribute?(key) || regexp.casefold?
lhs = lhs_for(key, downcase)

results = RegexpDisassembler.new(regexp).substrings
Expand Down Expand Up @@ -236,9 +234,28 @@ def equal_pair(key, value)

negate_xpath ? "not(#{expression})" : expression
else
"#{lhs_for(key, key == :type)}=#{XpathSupport.escape value}"
"#{lhs_for(key, case_insensitive_attribute?(key))}=#{XpathSupport.escape value}"
end
end

def build_valid_attributes
tag_name = @selector[:tag_name]
if tag_name.is_a?(String) && Watir.tag_to_class[tag_name.to_sym]
Watir.tag_to_class[tag_name.to_sym].attribute_list
else
Watir::HTMLElement.attribute_list
end
end

def case_insensitive_attribute?(attribute)
# type attributes can be upper case - downcase them
# https://github.com/watir/watir/issues/72
return true if attribute == :type
return true if Watir::Element::CASE_INSENSITIVE_ATTRIBUTES.include?(attribute) &&
@valid_attributes.include?(attribute)

false
end
end
end
end
Expand Down
40 changes: 40 additions & 0 deletions spec/unit/selector_builder/element_spec.rb
Expand Up @@ -762,5 +762,45 @@
expect(build_selector).to eq built
end
end

context 'with case-insensitive attributes' do
it 'respects case when locating uknown element with uknown attribute' do
expect(selector_builder.build(hreflang: 'en')).to eq(xpath: ".//*[@hreflang='en']")
expect(selector_builder.build(hreflang: /en/)).to eq(xpath: ".//*[contains(@hreflang, 'en')]")
end

it 'ignores case when locating uknown element with defined attribute' do
ignore_case_str = 'translate(@lang,' \
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
expect(selector_builder.build(lang: 'en')).to eq(xpath: ".//*[#{ignore_case_str}='en']")
expect(selector_builder.build(lang: /en/)).to eq(xpath: ".//*[contains(#{ignore_case_str}, 'en')]")
expect(selector_builder.build(tag_name: /a/, lang: 'en'))
.to eq(xpath: ".//*[contains(local-name(), 'a')][#{ignore_case_str}='en']")
expect(selector_builder.build(tag_name: /a/, lang: /en/))
.to eq(xpath: ".//*[contains(local-name(), 'a')][contains(#{ignore_case_str}, 'en')]")
end

it 'ignores case when attribute is defined for element' do
ignore_case_str = 'translate(@hreflang,' \
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
expect(selector_builder.build(tag_name: 'a', hreflang: 'en'))
.to eq(xpath: ".//*[local-name()='a'][#{ignore_case_str}='en']")
expect(selector_builder.build(tag_name: 'a', hreflang: /en/))
.to eq(xpath: ".//*[local-name()='a'][contains(#{ignore_case_str}, 'en')]")
end

it 'respects case when attribute is not defined for element' do
expect(selector_builder.build(tag_name: 'table', hreflang: 'en'))
.to eq(xpath: ".//*[local-name()='table'][@hreflang='en']")
expect(selector_builder.build(tag_name: 'table', hreflang: /en/))
.to eq(xpath: ".//*[local-name()='table'][contains(@hreflang, 'en')]")
expect(selector_builder.build(tag_name: /a/, hreflang: 'en'))
.to eq(xpath: ".//*[contains(local-name(), 'a')][@hreflang='en']")
expect(selector_builder.build(tag_name: /a/, hreflang: /en/))
.to eq(xpath: ".//*[contains(local-name(), 'a')][contains(@hreflang, 'en')]")
end
end
end
end

0 comments on commit ef68a89

Please sign in to comment.