spec/unit/selector_builder/element_spec.rb in watir-7.1.0 vs spec/unit/selector_builder/element_spec.rb in watir-7.2.0
- old
+ new
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
require_relative '../unit_helper'
describe Watir::Locators::Element::SelectorBuilder do
include LocatorSpecHelper
@@ -65,11 +67,11 @@
expect(selector_builder.build(selector)).to eq built
end
it 'with simple Regexp contains' do
selector = {tag_name: /div/}
- built = {xpath: ".//*[contains(local-name(), 'div')]"}
+ built = {xpath: './/*[contains(local-name(), "div")]'}
expect(selector_builder.build(selector)).to eq built
end
it 'with Symbol' do
@@ -88,55 +90,55 @@
end
context 'with class names' do
it 'class_name is converted to class' do
selector = {class_name: 'user'}
- built = {xpath: ".//*[contains(concat(' ', @class, ' '), ' user ')]"}
+ built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' user ')]"}
expect(selector_builder.build(selector)).to eq built
end
it 'single String concatenates' do
selector = {class: 'user'}
- built = {xpath: ".//*[contains(concat(' ', @class, ' '), ' user ')]"}
+ built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' user ')]"}
expect(selector_builder.build(selector)).to eq built
end
it 'Array of String concatenates with and' do
selector = {class: %w[multiple here]}
- built = {xpath: ".//*[contains(concat(' ', @class, ' '), ' multiple ') and " \
-"contains(concat(' ', @class, ' '), ' here ')]"}
+ built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' multiple ') and " \
+ "contains(concat(' ', normalize-space(@class), ' '), ' here ')]"}
expect(selector_builder.build(selector)).to eq built
end
it 'merges values when class and class_name are both used' do
selector = {class: 'foo', class_name: 'bar'}
- built = {xpath: ".//*[contains(concat(' ', @class, ' '), ' foo ') and " \
-"contains(concat(' ', @class, ' '), ' bar ')]"}
+ built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' foo ') and " \
+ "contains(concat(' ', normalize-space(@class), ' '), ' bar ')]"}
expect(selector_builder.build(selector)).to eq built
end
it 'simple Regexp contains' do
selector = {class_name: /use/}
- built = {xpath: ".//*[contains(@class, 'use')]"}
+ built = {xpath: './/*[contains(@class, "use")]'}
expect(selector_builder.build(selector)).to eq built
end
it 'Array of Regexp contains with and' do
selector = {class: [/mult/, /her/]}
- built = {xpath: ".//*[contains(@class, 'mult') and contains(@class, 'her')]"}
+ built = {xpath: './/*[contains(@class, "mult") and contains(@class, "her")]'}
expect(selector_builder.build(selector)).to eq built
end
it 'single negated String concatenates with not' do
selector = {class: '!multiple'}
- built = {xpath: ".//*[not(contains(concat(' ', @class, ' '), ' multiple '))]"}
+ built = {xpath: ".//*[not(contains(concat(' ', normalize-space(@class), ' '), ' multiple '))]"}
expect(selector_builder.build(selector)).to eq built
end
it 'single Boolean true provides the at' do
@@ -153,12 +155,13 @@
expect(selector_builder.build(selector)).to eq built
end
it 'Array of mixed String, Regexp and Boolean contains and concatenates with and and not' do
selector = {class: [/mult/, 'classes', '!here']}
- built = {xpath: ".//*[contains(@class, 'mult') and contains(concat(' ', @class, ' '), ' classes ') " \
-"and not(contains(concat(' ', @class, ' '), ' here '))]"}
+ built = {xpath: './/*[contains(@class, "mult") ' \
+ "and contains(concat(' ', normalize-space(@class), ' '), ' classes ') " \
+ "and not(contains(concat(' ', normalize-space(@class), ' '), ' here '))]"}
expect(selector_builder.build(selector)).to eq built
end
it 'empty string finds elements without class' do
@@ -249,18 +252,18 @@
end
context 'with attributes as partials' do
it 'with Regexp' do
selector = {name: /user/}
- built = {xpath: ".//*[contains(@name, 'user')]"}
+ built = {xpath: './/*[contains(@name, "user")]'}
expect(selector_builder.build(selector)).to eq built
end
it 'with multiple Regexp attributes separated by and' do
selector = {readonly: /read/, id: /good/}
- built = {xpath: ".//*[contains(@readonly, 'read') and contains(@id, 'good')]"}
+ built = {xpath: './/*[contains(@readonly, "read") and contains(@id, "good")]'}
expect(selector_builder.build(selector)).to eq built
end
end
@@ -272,11 +275,11 @@
expect(selector_builder.build(selector)).to eq built
end
it 'Regexp uses contains normalize space' do
selector = {text: /Add/}
- built = {xpath: ".//*[contains(normalize-space(), 'Add')]"}
+ built = {xpath: './/*[contains(normalize-space(), "Add")]'}
expect(selector_builder.build(selector)).to eq built
end
it 'raises exception when text is not a String or Regexp' do
@@ -325,12 +328,12 @@
end
context 'with labels' do
it 'locates the element associated with the label element located by the text of the provided label key' do
selector = {label: 'Cars'}
- built = {xpath: ".//*[@id=//label[normalize-space()='Cars']/@for "\
-"or parent::label[normalize-space()='Cars']]"}
+ built = {xpath: ".//*[@id=//label[normalize-space()='Cars']/@for " \
+ "or parent::label[normalize-space()='Cars']]"}
expect(selector_builder.build(selector)).to eq built
end
it 'returns a label_element if complex' do
@@ -388,12 +391,12 @@
expect(selector_builder.build(selector)).to eq built
end
it 'with multiple locators' do
selector = {adjacent: :ancestor, id: true, tag_name: 'div', class: 'ancestor', index: 1}
- built = {xpath: "./ancestor::*[local-name()='div']"\
-"[contains(concat(' ', @class, ' '), ' ancestor ')][@id][2]"}
+ built = {xpath: "./ancestor::*[local-name()='div']" \
+ "[contains(concat(' ', normalize-space(@class), ' '), ' ancestor ')][@id][2]"}
expect(selector_builder.build(selector)).to eq built
end
it 'raises an exception if text locator is used' do
@@ -419,12 +422,12 @@
expect(selector_builder.build(selector)).to eq built
end
it 'with multiple locators' do
selector = {adjacent: :following, tag_name: 'div', class: 'b', index: 0, id: true}
- built = {xpath: "./following-sibling::*[local-name()='div']"\
-"[contains(concat(' ', @class, ' '), ' b ')][@id][1]"}
+ built = {xpath: "./following-sibling::*[local-name()='div']" \
+ "[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
expect(selector_builder.build(selector)).to eq built
end
it 'with text' do
@@ -450,12 +453,12 @@
expect(selector_builder.build(selector)).to eq built
end
it 'with multiple locators' do
selector = {adjacent: :preceding, tag_name: 'div', class: 'b', id: true, index: 0}
- built = {xpath: "./preceding-sibling::*[local-name()='div']"\
-"[contains(concat(' ', @class, ' '), ' b ')][@id][1]"}
+ built = {xpath: "./preceding-sibling::*[local-name()='div']" \
+ "[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
expect(selector_builder.build(selector)).to eq built
end
it 'with text' do
@@ -481,12 +484,12 @@
expect(selector_builder.build(selector)).to eq built
end
it 'with multiple locators' do
selector = {adjacent: :child, tag_name: 'div', class: 'b', id: true, index: 0}
- built = {xpath: "./child::*[local-name()='div']"\
-"[contains(concat(' ', @class, ' '), ' b ')][@id][1]"}
+ built = {xpath: "./child::*[local-name()='div']" \
+ "[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
expect(selector_builder.build(selector)).to eq built
end
it 'with text' do
@@ -499,51 +502,51 @@
end
context 'with multiple locators' do
it 'locates using tag name, class, attributes and text' do
selector = {tag_name: 'div', class: 'content', contenteditable: 'true', text: 'Foo'}
- built = {xpath: ".//*[local-name()='div'][contains(concat(' ', @class, ' '), ' content ')]" \
-"[normalize-space()='Foo'][@contenteditable='true']"}
+ built = {xpath: ".//*[local-name()='div'][contains(concat(' ', normalize-space(@class), ' '), ' content ')]" \
+ "[normalize-space()='Foo'][@contenteditable='true']"}
expect(selector_builder.build(selector)).to eq built
end
end
context 'with simple Regexp' do
it 'handles spaces' do
selector = {title: /od Lu/}
- built = {xpath: ".//*[contains(@title, 'od Lu')]"}
+ built = {xpath: './/*[contains(@title, "od Lu")]'}
expect(selector_builder.build(selector)).to eq built
end
it 'handles escaped characters' do
selector = {src: %r{ages/but}}
- built = {xpath: ".//*[contains(@src, 'ages/but')]"}
+ built = {xpath: './/*[contains(@src, "ages/but")]'}
expect(selector_builder.build(selector)).to eq built
end
end
context 'with complex Regexp' do
it 'handles wildcards' do
selector = {src: /ages.*but/}
- built = {xpath: ".//*[contains(@src, 'ages') and contains(@src, 'but')]", src: /ages.*but/}
+ built = {xpath: './/*[contains(@src, "ages") and contains(@src, "but")]', src: /ages.*but/}
expect(selector_builder.build(selector)).to eq built
end
it 'handles optional characters' do
selector = {src: /ages ?but/}
- built = {xpath: ".//*[contains(@src, 'ages') and contains(@src, 'but')]", src: /ages ?but/}
+ built = {xpath: './/*[contains(@src, "ages") and contains(@src, "but")]', src: /ages ?but/}
expect(selector_builder.build(selector)).to eq built
end
it 'handles anchors' do
selector = {name: /^new_user_image$/}
- built = {xpath: ".//*[contains(@name, 'new_user_image')]", name: /^new_user_image$/}
+ built = {xpath: './/*[contains(@name, "new_user_image")]', name: /^new_user_image$/}
expect(selector_builder.build(selector)).to eq built
end
it 'handles beginning anchor' do
@@ -553,23 +556,23 @@
expect(selector_builder.build(selector)).to eq built
end
it 'does not use starts-with if visible locator used' do
selector = {id: /^vis/, visible_text: 'shown div'}
- built = {xpath: ".//*[contains(@id, 'vis')]", id: /^vis/, visible_text: 'shown div'}
+ built = {xpath: './/*[contains(@id, "vis")]', id: /^vis/, visible_text: 'shown div'}
expect(selector_builder.build(selector)).to eq built
end
it 'handles case insensitive' do
selector = {action: /ME/i}
built = {xpath: './/*[contains(translate(@action,' \
-"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
-"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'), " \
-"translate('me'," \
-"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
-"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'))]"}
+ "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
+ "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'), " \
+ 'translate("me",' \
+ "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
+ "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'))]"}
expect(selector_builder.build(selector)).to eq built
end
end
@@ -604,18 +607,18 @@
end
context 'returns locators that can not be directly translated' do
it 'attribute with complicated Regexp at end' do
selector = {action: /me$/}
- built = {xpath: ".//*[contains(@action, 'me')]", action: /me$/}
+ built = {xpath: './/*[contains(@action, "me")]', action: /me$/}
expect(selector_builder.build(selector)).to eq built
end
it 'class with complicated Regexp' do
selector = {class: /he?r/}
- built = {xpath: ".//*[contains(@class, 'h') and contains(@class, 'r')]", class: [/he?r/]}
+ built = {xpath: './/*[contains(@class, "h") and contains(@class, "r")]', class: [/he?r/]}
expect(selector_builder.build(selector)).to eq built
end
it 'visible' do
@@ -736,10 +739,11 @@
allow(selector_builder).to receive(:built).and_return(scope_built)
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
allow(query_scope).to receive(:browser).and_return(browser)
allow(query_scope).to receive(:is_a?).with(Watir::Browser).and_return(false)
+ allow(query_scope).to receive(:is_a?).with(Watir::ShadowRoot).and_return(false)
allow(query_scope).to receive(:is_a?).with(Watir::IFrame).and_return(true)
selector = {tag_name: 'div'}
built = {xpath: ".//*[local-name()='div']"}
@@ -748,51 +752,48 @@
expect(build_selector).to eq built
end
end
context 'with case-insensitive attributes' do
+ let(:upper) { "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'" }
+ let(:lower) { "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'" }
+
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')]")
+ expect(selector_builder.build(hreflang: /en/)).to eq(xpath: './/*[contains(@hreflang, "en")]')
end
it 'ignores case when locating uknown element with defined attribute' do
- lhs = 'translate(@lang,' \
- "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
- "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
- rhs = "translate('en'," \
- "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
- "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
+ lhs = "translate(@lang,#{upper},#{lower})"
+ rhs = "translate('en',#{upper},#{lower})"
+ rhs_regex = "translate(\"en\",#{upper},#{lower})"
expect(selector_builder.build(lang: 'en')).to eq(xpath: ".//*[#{lhs}=#{rhs}]")
- expect(selector_builder.build(lang: /en/)).to eq(xpath: ".//*[contains(#{lhs}, #{rhs})]")
+ expect(selector_builder.build(lang: /en/)).to eq(xpath: ".//*[contains(#{lhs}, #{rhs_regex})]")
expect(selector_builder.build(tag_name: /a/, lang: 'en'))
- .to eq(xpath: ".//*[contains(local-name(), 'a')][#{lhs}=#{rhs}]")
+ .to eq(xpath: ".//*[contains(local-name(), \"a\")][#{lhs}=#{rhs}]")
expect(selector_builder.build(tag_name: /a/, lang: /en/))
- .to eq(xpath: ".//*[contains(local-name(), 'a')][contains(#{lhs}, #{rhs})]")
+ .to eq(xpath: ".//*[contains(local-name(), \"a\")][contains(#{lhs}, #{rhs_regex})]")
end
it 'ignores case when attribute is defined for element' do
- lhs = 'translate(@hreflang,' \
- "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
- "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
- rhs = "translate('en'," \
- "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
- "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
+ lhs = "translate(@hreflang,#{upper},#{lower})"
+ rhs = "translate('en',#{upper},#{lower})"
+ rhs_regex = "translate(\"en\",#{upper},#{lower})"
expect(selector_builder.build(tag_name: 'a', hreflang: 'en'))
.to eq(xpath: ".//*[local-name()='a'][#{lhs}=#{rhs}]")
expect(selector_builder.build(tag_name: 'a', hreflang: /en/))
- .to eq(xpath: ".//*[local-name()='a'][contains(#{lhs}, #{rhs})]")
+ .to eq(xpath: ".//*[local-name()='a'][contains(#{lhs}, #{rhs_regex})]")
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')]")
+ .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']")
+ .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')]")
+ .to eq(xpath: './/*[contains(local-name(), "a")][contains(@hreflang, "en")]')
end
end
end
end