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