watir 6.14.0 → 6.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -6
- data/CHANGES.md +10 -0
- data/Gemfile +0 -2
- data/README.md +1 -1
- data/Rakefile +2 -2
- data/lib/watir.rb +2 -3
- data/lib/watir/adjacent.rb +2 -2
- data/lib/watir/alert.rb +5 -9
- data/lib/watir/attribute_helper.rb +2 -3
- data/lib/watir/browser.rb +5 -17
- data/lib/watir/capabilities.rb +11 -0
- data/lib/watir/cell_container.rb +2 -2
- data/lib/watir/container.rb +7 -8
- data/lib/watir/cookies.rb +2 -12
- data/lib/watir/element_collection.rb +2 -2
- data/lib/watir/elements/button.rb +2 -11
- data/lib/watir/elements/element.rb +43 -25
- data/lib/watir/elements/hidden.rb +1 -1
- data/lib/watir/elements/iframe.rb +7 -5
- data/lib/watir/elements/option.rb +2 -13
- data/lib/watir/elements/select.rb +6 -22
- data/lib/watir/elements/table.rb +2 -2
- data/lib/watir/exception.rb +1 -2
- data/lib/watir/generator/base/idl_sorter.rb +1 -1
- data/lib/watir/generator/base/spec_extractor.rb +2 -2
- data/lib/watir/generator/base/visitor.rb +1 -1
- data/lib/watir/generator/html/generator.rb +0 -1
- data/lib/watir/generator/html/spec_extractor.rb +1 -1
- data/lib/watir/generator/html/visitor.rb +1 -1
- data/lib/watir/generator/svg/spec_extractor.rb +1 -1
- data/lib/watir/generator/svg/visitor.rb +1 -1
- data/lib/watir/js_execution.rb +11 -0
- data/lib/watir/js_snippets.rb +1 -1
- data/lib/watir/js_snippets/elementObscured.js +14 -0
- data/lib/watir/js_snippets/selectedText.js +17 -0
- data/lib/watir/legacy_wait.rb +5 -5
- data/lib/watir/locators.rb +16 -5
- data/lib/watir/locators/anchor/selector_builder.rb +38 -0
- data/lib/watir/locators/button/locator.rb +14 -12
- data/lib/watir/locators/button/selector_builder.rb +0 -22
- data/lib/watir/locators/button/selector_builder/xpath.rb +67 -12
- data/lib/watir/locators/button/validator.rb +2 -1
- data/lib/watir/locators/cell/selector_builder.rb +0 -14
- data/lib/watir/locators/cell/selector_builder/xpath.rb +21 -0
- data/lib/watir/locators/element/locator.rb +60 -153
- data/lib/watir/locators/element/selector_builder.rb +103 -84
- data/lib/watir/locators/element/selector_builder/regexp_disassembler.rb +66 -0
- data/lib/watir/locators/element/selector_builder/xpath.rb +195 -82
- data/lib/watir/locators/element/selector_builder/xpath_support.rb +27 -0
- data/lib/watir/locators/element/validator.rb +2 -9
- data/lib/watir/locators/row/selector_builder.rb +5 -22
- data/lib/watir/locators/row/selector_builder/xpath.rb +53 -0
- data/lib/watir/locators/text_area/selector_builder.rb +1 -1
- data/lib/watir/locators/text_area/selector_builder/xpath.rb +19 -0
- data/lib/watir/locators/text_field/locator.rb +11 -8
- data/lib/watir/locators/text_field/selector_builder.rb +0 -23
- data/lib/watir/locators/text_field/selector_builder/xpath.rb +33 -4
- data/lib/watir/locators/text_field/validator.rb +4 -4
- data/lib/watir/radio_set.rb +5 -5
- data/lib/watir/row_container.rb +2 -2
- data/lib/watir/user_editable.rb +2 -2
- data/lib/watir/version.rb +1 -1
- data/lib/watir/wait.rb +24 -37
- data/lib/watir/window.rb +11 -8
- data/lib/watirspec/remote_server.rb +3 -1
- data/spec/locator_spec_helper.rb +1 -1
- data/spec/spec_helper.rb +25 -1
- data/spec/unit/anchor_locator_spec.rb +68 -0
- data/spec/unit/capabilities_spec.rb +27 -0
- data/spec/unit/element_locator_spec.rb +184 -101
- data/spec/unit/logger_spec.rb +5 -0
- data/spec/watirspec/adjacent_spec.rb +34 -34
- data/spec/watirspec/after_hooks_spec.rb +78 -35
- data/spec/watirspec/alert_spec.rb +10 -0
- data/spec/watirspec/browser_spec.rb +27 -1
- data/spec/watirspec/element_hidden_spec.rb +6 -0
- data/spec/watirspec/elements/button_spec.rb +5 -11
- data/spec/watirspec/elements/buttons_spec.rb +1 -1
- data/spec/watirspec/elements/checkbox_spec.rb +2 -15
- data/spec/watirspec/elements/date_time_field_spec.rb +6 -1
- data/spec/watirspec/elements/dd_spec.rb +0 -17
- data/spec/watirspec/elements/del_spec.rb +0 -14
- data/spec/watirspec/elements/div_spec.rb +0 -18
- data/spec/watirspec/elements/dl_spec.rb +0 -17
- data/spec/watirspec/elements/dt_spec.rb +0 -17
- data/spec/watirspec/elements/element_spec.rb +177 -17
- data/spec/watirspec/elements/elements_spec.rb +7 -6
- data/spec/watirspec/elements/em_spec.rb +0 -13
- data/spec/watirspec/elements/filefield_spec.rb +0 -11
- data/spec/watirspec/elements/form_spec.rb +6 -0
- data/spec/watirspec/elements/hn_spec.rb +0 -14
- data/spec/watirspec/elements/iframe_spec.rb +15 -0
- data/spec/watirspec/elements/ins_spec.rb +0 -14
- data/spec/watirspec/elements/labels_spec.rb +1 -1
- data/spec/watirspec/elements/li_spec.rb +0 -14
- data/spec/watirspec/elements/link_spec.rb +22 -14
- data/spec/watirspec/elements/links_spec.rb +13 -0
- data/spec/watirspec/elements/list_spec.rb +15 -0
- data/spec/watirspec/elements/ol_spec.rb +0 -14
- data/spec/watirspec/elements/option_spec.rb +0 -10
- data/spec/watirspec/elements/p_spec.rb +0 -14
- data/spec/watirspec/elements/pre_spec.rb +0 -14
- data/spec/watirspec/elements/radio_spec.rb +0 -14
- data/spec/watirspec/elements/select_list_spec.rb +0 -10
- data/spec/watirspec/elements/span_spec.rb +4 -15
- data/spec/watirspec/elements/strong_spec.rb +4 -15
- data/spec/watirspec/elements/table_nesting_spec.rb +1 -1
- data/spec/watirspec/elements/table_spec.rb +7 -0
- data/spec/watirspec/elements/text_field_spec.rb +10 -2
- data/spec/watirspec/elements/text_fields_spec.rb +1 -1
- data/spec/watirspec/elements/tr_spec.rb +1 -1
- data/spec/watirspec/elements/ul_spec.rb +0 -14
- data/spec/watirspec/html/closeable.html +8 -0
- data/spec/watirspec/html/forms_with_input_elements.html +28 -23
- data/spec/watirspec/html/nested_elements.html +9 -9
- data/spec/watirspec/html/obscured.html +34 -0
- data/spec/watirspec/html/tables.html +13 -13
- data/spec/watirspec/radio_set_spec.rb +5 -0
- data/spec/watirspec/selector_builder/button_spec.rb +254 -0
- data/spec/watirspec/selector_builder/cell_spec.rb +93 -0
- data/spec/watirspec/selector_builder/element_spec.rb +639 -0
- data/spec/watirspec/selector_builder/row_spec.rb +150 -0
- data/spec/watirspec/selector_builder/text_spec.rb +170 -0
- data/spec/watirspec/support/rspec_matchers.rb +6 -1
- data/spec/watirspec/user_editable_spec.rb +4 -0
- data/spec/watirspec/wait_spec.rb +65 -14
- data/spec/watirspec/window_switching_spec.rb +54 -1
- data/spec/watirspec_helper.rb +2 -0
- data/watir.gemspec +7 -1
- metadata +86 -8
- data/lib/watir/locators/text_area/locator.rb +0 -13
- data/lib/watir/xpath_support.rb +0 -18
data/lib/watir/window.rb
CHANGED
@@ -2,6 +2,9 @@ module Watir
|
|
2
2
|
class Window
|
3
3
|
include EventuallyPresent
|
4
4
|
include Waitable
|
5
|
+
include Exception
|
6
|
+
|
7
|
+
attr_reader :browser
|
5
8
|
|
6
9
|
def initialize(browser, selector)
|
7
10
|
@browser = browser
|
@@ -15,7 +18,7 @@ module Watir
|
|
15
18
|
else
|
16
19
|
return if selector.keys.all? { |k| %i[title url index].include? k }
|
17
20
|
|
18
|
-
raise ArgumentError, "invalid window selector: #{
|
21
|
+
raise ArgumentError, "invalid window selector: #{selector_string}"
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
@@ -99,7 +102,7 @@ module Watir
|
|
99
102
|
def exists?
|
100
103
|
assert_exists
|
101
104
|
true
|
102
|
-
rescue
|
105
|
+
rescue NoMatchingWindowFoundException
|
103
106
|
false
|
104
107
|
end
|
105
108
|
|
@@ -194,8 +197,6 @@ module Watir
|
|
194
197
|
@selector.inspect
|
195
198
|
end
|
196
199
|
|
197
|
-
protected
|
198
|
-
|
199
200
|
def handle
|
200
201
|
@handle ||= locate
|
201
202
|
end
|
@@ -213,7 +214,9 @@ module Watir
|
|
213
214
|
end
|
214
215
|
|
215
216
|
def assert_exists
|
216
|
-
|
217
|
+
return if @driver.window_handles.include?(handle)
|
218
|
+
|
219
|
+
raise(NoMatchingWindowFoundException, selector_string)
|
217
220
|
end
|
218
221
|
|
219
222
|
# return a handle to the currently active window if it is still open; otherwise nil
|
@@ -225,8 +228,8 @@ module Watir
|
|
225
228
|
|
226
229
|
def matches?(handle)
|
227
230
|
@driver.switch_to.window(handle) do
|
228
|
-
matches_title = @selector[:title].nil? || @
|
229
|
-
matches_url = @selector[:url].nil? || @
|
231
|
+
matches_title = @selector[:title].nil? || @browser.title =~ /#{@selector[:title]}/
|
232
|
+
matches_url = @selector[:url].nil? || @browser.url =~ /#{@selector[:url]}/
|
230
233
|
|
231
234
|
matches_title && matches_url
|
232
235
|
end
|
@@ -241,7 +244,7 @@ module Watir
|
|
241
244
|
begin
|
242
245
|
wait_until(&:exists?)
|
243
246
|
rescue Wait::TimeoutError
|
244
|
-
raise
|
247
|
+
raise NoMatchingWindowFoundException, selector_string
|
245
248
|
end
|
246
249
|
end
|
247
250
|
end # Window
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module WatirSpec
|
2
2
|
class RemoteServer
|
3
|
+
include Watir::Exception
|
4
|
+
|
3
5
|
attr_reader :server
|
4
6
|
|
5
7
|
def start(port = 4444, args: [])
|
@@ -33,7 +35,7 @@ module WatirSpec
|
|
33
35
|
end
|
34
36
|
rescue SocketError
|
35
37
|
# not connected to internet
|
36
|
-
raise
|
38
|
+
raise Error, 'unable to find or download selenium-server-standalone jar'
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
data/spec/locator_spec_helper.rb
CHANGED
@@ -10,7 +10,7 @@ module LocatorSpecHelper
|
|
10
10
|
def locator(selector, attrs)
|
11
11
|
attrs ||= Watir::HTMLElement.attributes
|
12
12
|
element_validator = Watir::Locators::Element::Validator.new
|
13
|
-
selector_builder = Watir::Locators::Element::SelectorBuilder.new(
|
13
|
+
selector_builder = Watir::Locators::Element::SelectorBuilder.new(attrs)
|
14
14
|
Watir::Locators::Element::Locator.new(browser, selector, selector_builder, element_validator)
|
15
15
|
end
|
16
16
|
|
data/spec/spec_helper.rb
CHANGED
@@ -2,13 +2,37 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
3
|
|
4
4
|
require 'coveralls'
|
5
|
-
|
5
|
+
require 'simplecov'
|
6
|
+
require 'simplecov-console'
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new [
|
8
|
+
Coveralls::SimpleCov::Formatter,
|
9
|
+
SimpleCov::Formatter::HTMLFormatter,
|
10
|
+
SimpleCov::Formatter::Console
|
11
|
+
]
|
12
|
+
SimpleCov.start do
|
13
|
+
add_filter %r{/spec/}
|
14
|
+
add_filter 'lib/watir/elements/html_elements.rb'
|
15
|
+
add_filter 'lib/watir/elements/svg_elements.rb'
|
16
|
+
add_filter 'lib/watir/legacy_wait.rb'
|
17
|
+
add_filter 'lib/watirspec'
|
18
|
+
refuse_coverage_drop
|
19
|
+
end
|
6
20
|
|
7
21
|
require 'watir'
|
8
22
|
require 'webdrivers'
|
9
23
|
require 'locator_spec_helper'
|
10
24
|
require 'rspec'
|
11
25
|
|
26
|
+
if ENV['TRAVIS']
|
27
|
+
require 'rspec/retry'
|
28
|
+
RSpec.configure do |config|
|
29
|
+
config.verbose_retry = true
|
30
|
+
config.display_try_failure_messages = true
|
31
|
+
config.default_retry_count = 3
|
32
|
+
config.exceptions_to_retry = [IOError, Net::ReadTimeout]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
12
36
|
SELENIUM_SELECTORS = %i[css tag_name xpath link_text partial_link_text link].freeze
|
13
37
|
|
14
38
|
Watir.relaxed_locate = false if ENV['RELAXED_LOCATE'] == 'false'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'unit_helper'
|
2
|
+
|
3
|
+
describe Watir::Locators::Element::Locator do
|
4
|
+
include LocatorSpecHelper
|
5
|
+
|
6
|
+
def locator(selector, attrs)
|
7
|
+
attrs ||= Watir::HTMLElement.attributes
|
8
|
+
element_validator = Watir::Locators::Element::Validator.new
|
9
|
+
selector_builder = Watir::Locators::Anchor::SelectorBuilder.new(attrs)
|
10
|
+
Watir::Locators::Element::Locator.new(browser, selector, selector_builder, element_validator)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'converts :visible_text with String to :link_text' do
|
14
|
+
link = element(tag_name: 'a')
|
15
|
+
expect_one(:link_text, 'exact text').and_return(link)
|
16
|
+
expect(link).not_to receive(:text) # matching does not validate text
|
17
|
+
|
18
|
+
locate_one(tag_name: 'a', visible_text: 'exact text')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'converts :visible_text with basic Regexp to :partial_link_text' do
|
22
|
+
link = element(tag_name: 'a')
|
23
|
+
expect_one(:partial_link_text, 'partial text').and_return(link)
|
24
|
+
expect(link).not_to receive(:text) # matching does not validate text
|
25
|
+
|
26
|
+
locate_one(tag_name: 'a', visible_text: /partial text/)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'does not convert :visible_text with complex Regexp' do
|
30
|
+
elements = [
|
31
|
+
element(tag_name: 'a', text: 'other text'),
|
32
|
+
element(tag_name: 'a', text: 'matching complex!text')
|
33
|
+
]
|
34
|
+
expect_all(:xpath, ".//*[local-name()='a']").and_return(elements)
|
35
|
+
|
36
|
+
expect(locate_one(tag_name: 'a', visible_text: /complex.text/)).to eq(elements[1])
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'does not convert :visible_text with casefold Regexp' do
|
40
|
+
elements = [
|
41
|
+
element(tag_name: 'a', text: 'other text'),
|
42
|
+
element(tag_name: 'a', text: 'partial text')
|
43
|
+
]
|
44
|
+
expect_all(:xpath, ".//*[local-name()='a']").and_return(elements)
|
45
|
+
|
46
|
+
expect(locate_one(tag_name: 'a', visible_text: /partial text/i)).to eq(elements[1])
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'does not convert :visible_text with String and other locators' do
|
50
|
+
els = [
|
51
|
+
element(tag_name: 'a', attributes: {class: 'klass', name: 'abc'}, text: 'other text'),
|
52
|
+
element(tag_name: 'a', attributes: {class: 'klass', name: 'abc'}, text: 'exact text')
|
53
|
+
]
|
54
|
+
expect_all(:xpath, ".//*[local-name()='a'][contains(concat(' ', @class, ' '), ' klass ')][@name]").and_return(els)
|
55
|
+
|
56
|
+
expect(locate_one(tag_name: 'a', class: 'klass', name: /a|.c/, visible_text: 'exact text')).to eq(els[1])
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'does not convert :visible_text with Regexp and other locators' do
|
60
|
+
els = [
|
61
|
+
element(tag_name: 'a', attributes: {class: 'klass', name: 'abc'}, text: 'other text'),
|
62
|
+
element(tag_name: 'a', attributes: {class: 'klass', name: 'abc'}, text: 'partial text')
|
63
|
+
]
|
64
|
+
expect_all(:xpath, ".//*[local-name()='a'][contains(concat(' ', @class, ' '), ' klass ')][@name]").and_return(els)
|
65
|
+
|
66
|
+
expect(locate_one(tag_name: 'a', class: 'klass', name: /a|.c/, visible_text: /partial text/)).to eq(els[1])
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'unit_helper'
|
2
|
+
|
3
|
+
describe Watir::Capabilities do
|
4
|
+
describe '#ie' do
|
5
|
+
it 'processes options' do
|
6
|
+
options = {browser_attach_timeout: 1, full_page_screenshot: true}
|
7
|
+
caps = Watir::Capabilities.new(:ie, options: options)
|
8
|
+
opts = caps.to_args.last[:options]
|
9
|
+
expect(opts.browser_attach_timeout).to eq 1
|
10
|
+
expect(opts.full_page_screenshot).to be true
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'processes args' do
|
14
|
+
caps = Watir::Capabilities.new(:ie, args: %w[foo bar])
|
15
|
+
opts = caps.to_args.last[:options]
|
16
|
+
expect(opts.args).to eq Set.new(%w[foo bar])
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'processes options class' do
|
20
|
+
options = Selenium::WebDriver::IE::Options.new(browser_attach_timeout: 1, full_page_screenshot: true)
|
21
|
+
caps = Watir::Capabilities.new(:ie, options: options)
|
22
|
+
opts = caps.to_args.last[:options]
|
23
|
+
expect(opts.browser_attach_timeout).to eq 1
|
24
|
+
expect(opts.full_page_screenshot).to be true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -13,6 +13,14 @@ describe Watir::Locators::Element::Locator do
|
|
13
13
|
expect { locate_one loc => 'bar' }.send(match, output(msg).to_stdout_from_any_process)
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
it 'raises exception if locating a non-link element by link locator' do
|
18
|
+
selector = {tag_name: 'div', link_text: 'foo'}
|
19
|
+
msg = 'Can not use link_text locator to find a foo element'
|
20
|
+
expect {
|
21
|
+
expect { locate_one(selector) }.to raise_exception(StandardError, msg)
|
22
|
+
}.to have_deprecated_link_text
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
26
|
describe 'with selectors not supported by Selenium' do
|
@@ -69,7 +77,7 @@ describe Watir::Locators::Element::Locator do
|
|
69
77
|
end
|
70
78
|
|
71
79
|
it 'handles selector with multiple classes in array' do
|
72
|
-
xpath = ".//*[
|
80
|
+
xpath = ".//*[contains(concat(' ', @class, ' '), ' a ') and contains(concat(' ', @class, ' '), ' b ')]"
|
73
81
|
expect_one :xpath, xpath
|
74
82
|
|
75
83
|
locate_one class: %w[a b]
|
@@ -81,7 +89,24 @@ describe Watir::Locators::Element::Locator do
|
|
81
89
|
expect { locate_one class: 'a b' }.to have_deprecated_class_array
|
82
90
|
end
|
83
91
|
|
84
|
-
it 'handles selector with
|
92
|
+
it 'handles selector with xpath and tag_name String' do
|
93
|
+
elements = [
|
94
|
+
element(tag_name: 'div', attributes: {class: 'foo'}),
|
95
|
+
element(tag_name: 'span', attributes: {class: 'foo'}),
|
96
|
+
element(tag_name: 'div', attributes: {class: 'foo'})
|
97
|
+
]
|
98
|
+
|
99
|
+
expect_all(:xpath, './/*[@class="foo"]').and_return(elements)
|
100
|
+
|
101
|
+
selector = {
|
102
|
+
xpath: './/*[@class="foo"]',
|
103
|
+
tag_name: 'span'
|
104
|
+
}
|
105
|
+
|
106
|
+
expect(locate_one(selector).tag_name).to eq 'span'
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'handles selector with xpath and tag_name Symbol' do
|
85
110
|
elements = [
|
86
111
|
element(tag_name: 'div', attributes: {class: 'foo'}),
|
87
112
|
element(tag_name: 'span', attributes: {class: 'foo'}),
|
@@ -123,6 +148,7 @@ describe Watir::Locators::Element::Locator do
|
|
123
148
|
text: 'foo'
|
124
149
|
end
|
125
150
|
|
151
|
+
# TODO: This is deprecated by 'text_string'
|
126
152
|
it "handles 'text' key when it's a string" do
|
127
153
|
expect_one :xpath, ".//*[local-name()='div'][normalize-space()='foo']"
|
128
154
|
locate_one tag_name: 'div',
|
@@ -169,7 +195,8 @@ describe Watir::Locators::Element::Locator do
|
|
169
195
|
end
|
170
196
|
|
171
197
|
it 'wraps :type attribute with translate() for upper case values' do
|
172
|
-
translated_type = "translate(@type,'
|
198
|
+
translated_type = "translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
199
|
+
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
|
173
200
|
expect_one :xpath, ".//*[local-name()='input'][#{translated_type}='file']"
|
174
201
|
|
175
202
|
selector = [
|
@@ -181,7 +208,8 @@ describe Watir::Locators::Element::Locator do
|
|
181
208
|
end
|
182
209
|
|
183
210
|
it "uses the corresponding <label>'s @for attribute or parent::label when locating by label" do
|
184
|
-
translated_type = "translate(@type,'
|
211
|
+
translated_type = "translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
212
|
+
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ')"
|
185
213
|
xpath = ".//*[local-name()='input'][#{translated_type}='text' and " \
|
186
214
|
"(@id=//label[normalize-space()='foo']/@for or " \
|
187
215
|
"parent::label[normalize-space()='foo'])]"
|
@@ -212,30 +240,22 @@ describe Watir::Locators::Element::Locator do
|
|
212
240
|
}
|
213
241
|
|
214
242
|
locate_one selector, Watir::Meta.attributes
|
215
|
-
|
216
|
-
# TODO: check edge cases
|
217
243
|
end
|
218
244
|
end
|
219
245
|
|
220
|
-
describe 'with regexp selectors' do
|
221
|
-
it 'handles selector with tag name and a
|
222
|
-
|
223
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
224
|
-
element(tag_name: 'div', attributes: {class: 'foob'})
|
225
|
-
]
|
246
|
+
describe 'with simple regexp selectors' do
|
247
|
+
it 'handles selector with tag name and a simple regexp attribute' do
|
248
|
+
element = element(tag_name: 'div', attributes: {class: 'foob'})
|
226
249
|
|
227
|
-
|
250
|
+
expect_one(:xpath, ".//*[local-name()='div'][contains(@class, 'oob')]").and_return(element)
|
228
251
|
|
229
|
-
expect(locate_one(tag_name: 'div', class: /oob/)).to eq
|
252
|
+
expect(locate_one(tag_name: 'div', class: /oob/)).to eq element
|
230
253
|
end
|
231
254
|
|
232
|
-
it 'handles :tag_name, :index and a
|
233
|
-
|
234
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
235
|
-
element(tag_name: 'div', attributes: {class: 'foo'})
|
236
|
-
]
|
255
|
+
it 'handles :tag_name, :index and a simple regexp attribute' do
|
256
|
+
element = element(tag_name: 'div', attributes: {class: 'foo'})
|
237
257
|
|
238
|
-
|
258
|
+
expect_one(:xpath, "(.//*[local-name()='div'][contains(@class, 'foo')])[2]").and_return(element)
|
239
259
|
|
240
260
|
selector = {
|
241
261
|
tag_name: 'div',
|
@@ -243,7 +263,7 @@ describe Watir::Locators::Element::Locator do
|
|
243
263
|
index: 1
|
244
264
|
}
|
245
265
|
|
246
|
-
expect(locate_one(selector)).to eq
|
266
|
+
expect(locate_one(selector)).to eq element
|
247
267
|
end
|
248
268
|
|
249
269
|
it 'handles :xpath and :index selectors' do
|
@@ -279,12 +299,9 @@ describe Watir::Locators::Element::Locator do
|
|
279
299
|
end
|
280
300
|
|
281
301
|
it 'handles mix of string and regexp attributes' do
|
282
|
-
|
283
|
-
element(tag_name: 'div', attributes: {dir: 'foo', title: 'bar'}),
|
284
|
-
element(tag_name: 'div', attributes: {dir: 'foo', title: 'baz'})
|
285
|
-
]
|
302
|
+
element = element(tag_name: 'div', attributes: {dir: 'foo', title: 'baz'})
|
286
303
|
|
287
|
-
|
304
|
+
expect_one(:xpath, ".//*[local-name()='div'][@dir='foo' and contains(@title, 'baz')]").and_return(element)
|
288
305
|
|
289
306
|
selector = {
|
290
307
|
tag_name: 'div',
|
@@ -292,23 +309,20 @@ describe Watir::Locators::Element::Locator do
|
|
292
309
|
title: /baz/
|
293
310
|
}
|
294
311
|
|
295
|
-
expect(locate_one(selector)).to eq
|
312
|
+
expect(locate_one(selector)).to eq element
|
296
313
|
end
|
297
314
|
|
298
315
|
it 'handles data-* attributes with regexp' do
|
299
|
-
|
300
|
-
element(tag_name: 'div', attributes: {'data-automation-id': 'foo'}),
|
301
|
-
element(tag_name: 'div', attributes: {'data-automation-id': 'bar'})
|
302
|
-
]
|
316
|
+
element = element(tag_name: 'div', attributes: {'data-automation-id': 'bar'})
|
303
317
|
|
304
|
-
|
318
|
+
expect_one(:xpath, ".//*[local-name()='div'][contains(@data-automation-id, 'bar')]").and_return(element)
|
305
319
|
|
306
320
|
selector = {
|
307
321
|
tag_name: 'div',
|
308
322
|
data_automation_id: /bar/
|
309
323
|
}
|
310
324
|
|
311
|
-
expect(locate_one(selector)).to eq
|
325
|
+
expect(locate_one(selector)).to eq element
|
312
326
|
end
|
313
327
|
|
314
328
|
it 'handles :label => /regexp/ selector' do
|
@@ -341,28 +355,28 @@ describe Watir::Locators::Element::Locator do
|
|
341
355
|
|
342
356
|
allow(elements1.first).to receive(:attribute).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
|
343
357
|
|
344
|
-
expect_all(:xpath, "
|
358
|
+
expect_all(:xpath, ".//*[contains(@class, 'foo')]").and_return(elements1, elements2)
|
345
359
|
|
346
|
-
expect(locate_one(class: /foo
|
360
|
+
expect(locate_one(class: /foo$/)).to eq elements2[0]
|
347
361
|
end
|
348
|
-
end
|
349
362
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
element(tag_name: 'div'),
|
354
|
-
element(tag_name: 'div')
|
355
|
-
]
|
363
|
+
it 'raises error if too many attempts to relocate a stale element during filtering' do
|
364
|
+
element1 = element(tag_name: 'div', attributes: {class: 'foo'})
|
365
|
+
element2 = element(tag_name: 'div', attributes: {class: 'foob'})
|
356
366
|
|
357
|
-
|
367
|
+
elements1 = [element1.clone, element2.clone]
|
368
|
+
elements2 = [element1.clone, element2.clone]
|
369
|
+
elements3 = [element1.clone, element2.clone]
|
358
370
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
371
|
+
allow(elements1.first).to receive(:attribute).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
|
372
|
+
allow(elements2.first).to receive(:attribute).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
|
373
|
+
allow(elements3.first).to receive(:attribute).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
|
374
|
+
|
375
|
+
expect_all(:xpath, ".//*[contains(@class, 'foo')]").and_return(elements1, elements2, elements3)
|
364
376
|
|
365
|
-
|
377
|
+
msg = 'Unable to locate element from {:class=>[/foo$/]} due to changing page'
|
378
|
+
expect { locate_one(class: /foo$/) }.to raise_exception(Watir::Exception::LocatorException, msg)
|
379
|
+
end
|
366
380
|
end
|
367
381
|
|
368
382
|
it "returns nil if found element didn't match the selector tag_name" do
|
@@ -376,6 +390,17 @@ describe Watir::Locators::Element::Locator do
|
|
376
390
|
expect(locate_one(selector, Watir::Input.attributes)).to be_nil
|
377
391
|
end
|
378
392
|
|
393
|
+
it 'allows tag_name values to be Symbols when combined with xpath' do
|
394
|
+
expect_all(:xpath, '//div').and_return([element(tag_name: 'div')])
|
395
|
+
|
396
|
+
selector = {
|
397
|
+
tag_name: :input,
|
398
|
+
xpath: '//div'
|
399
|
+
}
|
400
|
+
|
401
|
+
expect(locate_one(selector, Watir::Input.attributes)).to be_nil
|
402
|
+
end
|
403
|
+
|
379
404
|
describe 'errors' do
|
380
405
|
it 'raises a TypeError if :index is not a Integer' do
|
381
406
|
expect { locate_one(tag_name: 'div', index: 'bar') }.to \
|
@@ -383,10 +408,54 @@ describe Watir::Locators::Element::Locator do
|
|
383
408
|
end
|
384
409
|
|
385
410
|
it 'raises a TypeError if selector value is not a String, Regexp or Boolean' do
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
411
|
+
msg = /expected one of \[String, Regexp, TrueClass, FalseClass\], got 123:(Integer|Fixnum)/
|
412
|
+
expect { locate_one(foo: 123) }.to raise_error TypeError, msg
|
413
|
+
end
|
414
|
+
|
415
|
+
it 'raises a Error if selector key is not a String or a Symbol' do
|
416
|
+
msg = /Unable to build XPath using 7:(Integer|Fixnum)/
|
417
|
+
expect { locate_one(7 => 'bad') }.to raise_exception(Watir::Exception::Error, msg)
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'raises a Error if selector key is not a String or a Symbol' do
|
421
|
+
msg = /Unable to build XPath using 7:(Integer|Fixnum)/
|
422
|
+
expect { locate_one(7 => 'bad') }.to raise_exception(Watir::Exception::Error, msg)
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'raises an Error if unable to build selector' do
|
426
|
+
module Foo
|
427
|
+
class SelectorBuilder < Watir::Locators::Element::SelectorBuilder
|
428
|
+
def build(*_args)
|
429
|
+
nil
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
selector = {name: 'foo'}
|
435
|
+
element_validator = Watir::Locators::Element::Validator.new
|
436
|
+
selector_builder = Foo::SelectorBuilder.new(Watir::HTMLElement.attributes)
|
437
|
+
locator = Watir::Locators::Element::Locator.new(browser, selector, selector_builder, element_validator)
|
438
|
+
|
439
|
+
msg = 'Foo::SelectorBuilder was unable to build selector from {:name=>"foo"}'
|
440
|
+
expect { locator.locate }.to raise_exception(Watir::Exception::LocatorException, msg)
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'raises an Error if unable to build values to match' do
|
444
|
+
module Foo
|
445
|
+
class SelectorBuilder < Watir::Locators::Element::SelectorBuilder
|
446
|
+
def build(*_args)
|
447
|
+
{}
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
selector = {name: 'foo'}
|
453
|
+
element_validator = Watir::Locators::Element::Validator.new
|
454
|
+
selector_builder = Foo::SelectorBuilder.new(Watir::HTMLElement.attributes)
|
455
|
+
locator = Watir::Locators::Element::Locator.new(browser, selector, selector_builder, element_validator)
|
456
|
+
|
457
|
+
msg = 'Foo::SelectorBuilder#build is not returning expected responses for the current version of Watir'
|
458
|
+
expect { locator.locate }.to raise_exception(Watir::Exception::LocatorException, msg)
|
390
459
|
end
|
391
460
|
end
|
392
461
|
end
|
@@ -431,7 +500,7 @@ describe Watir::Locators::Element::Locator do
|
|
431
500
|
end
|
432
501
|
|
433
502
|
it 'handles selector with multiple classes in array' do
|
434
|
-
xpath = ".//*[
|
503
|
+
xpath = ".//*[contains(concat(' ', @class, ' '), ' a ') and contains(concat(' ', @class, ' '), ' b ')]"
|
435
504
|
expect_all :xpath, xpath
|
436
505
|
|
437
506
|
locate_all class: %w[a b]
|
@@ -447,24 +516,22 @@ describe Watir::Locators::Element::Locator do
|
|
447
516
|
describe 'with regexp selectors' do
|
448
517
|
it 'handles selector with tag name and a single regexp attribute' do
|
449
518
|
elements = [
|
450
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
451
519
|
element(tag_name: 'div', attributes: {class: 'foob'}),
|
452
520
|
element(tag_name: 'div', attributes: {class: 'doob'}),
|
453
521
|
element(tag_name: 'div', attributes: {class: 'noob'})
|
454
522
|
]
|
455
523
|
|
456
|
-
expect_all(:xpath, "
|
524
|
+
expect_all(:xpath, ".//*[local-name()='div'][contains(@class, 'oob')]").and_return(elements)
|
457
525
|
expect(locate_all(tag_name: 'div', class: /oob/)).to eq elements.last(3)
|
458
526
|
end
|
459
527
|
|
460
528
|
it 'handles mix of string and regexp attributes' do
|
461
529
|
elements = [
|
462
|
-
element(tag_name: 'div', attributes: {dir: 'foo', title: 'bar'}),
|
463
530
|
element(tag_name: 'div', attributes: {dir: 'foo', title: 'baz'}),
|
464
531
|
element(tag_name: 'div', attributes: {dir: 'foo', title: 'bazt'})
|
465
532
|
]
|
466
533
|
|
467
|
-
expect_all(:xpath, "
|
534
|
+
expect_all(:xpath, ".//*[local-name()='div'][@dir='foo' and contains(@title, 'baz')]").and_return(elements)
|
468
535
|
|
469
536
|
selector = {
|
470
537
|
tag_name: 'div',
|
@@ -474,64 +541,80 @@ describe Watir::Locators::Element::Locator do
|
|
474
541
|
|
475
542
|
expect(locate_all(selector)).to eq elements.last(2)
|
476
543
|
end
|
544
|
+
end
|
477
545
|
|
478
|
-
|
479
|
-
|
480
|
-
elements = [
|
481
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
482
|
-
element(tag_name: 'div', attributes: {class: 'foob'}),
|
483
|
-
element(tag_name: 'div', attributes: {class: 'bar'})
|
484
|
-
]
|
546
|
+
it 'with :index' do
|
547
|
+
element = element(tag_name: 'div')
|
485
548
|
|
486
|
-
|
549
|
+
expect_one(:xpath, "(.//*[local-name()='div'][@dir='foo'])[2]").and_return(element)
|
487
550
|
|
488
|
-
|
489
|
-
|
551
|
+
selector = {
|
552
|
+
tag_name: 'div',
|
553
|
+
dir: 'foo',
|
554
|
+
index: 1
|
555
|
+
}
|
490
556
|
|
491
|
-
|
492
|
-
|
493
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
494
|
-
element(tag_name: 'div', attributes: {class: 'foob'})
|
495
|
-
]
|
557
|
+
expect(locate_one(selector)).to eq element
|
558
|
+
end
|
496
559
|
|
497
|
-
|
560
|
+
context 'and xpath' do
|
561
|
+
it 'converts a leading run of regexp literals to a contains() expression' do
|
562
|
+
elements = [
|
563
|
+
element(tag_name: 'div', attributes: {foo: 'foo'}),
|
564
|
+
element(tag_name: 'div', attributes: {foo: 'foob'}),
|
565
|
+
element(tag_name: 'div', attributes: {foo: 'bar'})
|
566
|
+
]
|
498
567
|
|
499
|
-
|
500
|
-
|
568
|
+
expect_all(:xpath, ".//*[local-name()='div'][contains(@foo, 'fo') and contains(@foo, 'b')]")
|
569
|
+
.and_return(elements.first(2))
|
501
570
|
|
502
|
-
|
503
|
-
|
504
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
505
|
-
element(tag_name: 'div', attributes: {class: 'foob'})
|
506
|
-
]
|
571
|
+
expect(locate_one(tag_name: 'div', foo: /fo.b$/)).to eq elements[1]
|
572
|
+
end
|
507
573
|
|
508
|
-
|
509
|
-
|
574
|
+
it 'converts a trailing run of regexp literals to a contains() expression' do
|
575
|
+
elements = [
|
576
|
+
element(tag_name: 'div', attributes: {foo: 'foo'}),
|
577
|
+
element(tag_name: 'div', attributes: {foo: 'foob'})
|
578
|
+
]
|
510
579
|
|
511
|
-
|
512
|
-
|
580
|
+
expect_all(:xpath, ".//*[local-name()='div'][contains(@foo, 'fo') and contains(@foo, 'b')]")
|
581
|
+
.and_return(elements.last(1))
|
513
582
|
|
514
|
-
|
515
|
-
|
516
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
517
|
-
element(tag_name: 'div', attributes: {class: 'foob'})
|
518
|
-
]
|
583
|
+
expect(locate_one(tag_name: 'div', foo: /^fo.b/)).to eq elements[1]
|
584
|
+
end
|
519
585
|
|
520
|
-
|
586
|
+
it 'converts a leading and a trailing run of regexp literals to a contains() expression' do
|
587
|
+
elements = [
|
588
|
+
element(tag_name: 'div', attributes: {foo: 'foo'}),
|
589
|
+
element(tag_name: 'div', attributes: {foo: 'foob'})
|
590
|
+
]
|
521
591
|
|
522
|
-
|
523
|
-
|
592
|
+
expect_all(:xpath, ".//*[local-name()='div'][contains(@foo, 'fo') and contains(@foo, 'b')]")
|
593
|
+
.and_return(elements.last(1))
|
524
594
|
|
525
|
-
|
526
|
-
|
527
|
-
element(tag_name: 'div', attributes: {class: 'foo'}),
|
528
|
-
element(tag_name: 'div', attributes: {class: 'foob'})
|
529
|
-
]
|
595
|
+
expect(locate_one(tag_name: 'div', foo: /fo.b/)).to eq elements[1]
|
596
|
+
end
|
530
597
|
|
531
|
-
|
598
|
+
it 'does not try to convert case insensitive expressions' do
|
599
|
+
element = element(tag_name: 'div', attributes: {foo: 'foo'})
|
532
600
|
|
533
|
-
|
534
|
-
|
601
|
+
xpath = ".//*[local-name()='div'][contains(translate" \
|
602
|
+
"(@foo,'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
603
|
+
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'), 'foob')]"
|
604
|
+
expect_one(:xpath, xpath).and_return(element)
|
605
|
+
|
606
|
+
expect(locate_one(tag_name: 'div', foo: /FOOB/i)).to eq element
|
607
|
+
end
|
608
|
+
|
609
|
+
it "does not try to convert expressions containing '|'" do
|
610
|
+
elements = [
|
611
|
+
element(tag_name: 'div', attributes: {foo: 'foo'}),
|
612
|
+
element(tag_name: 'div', attributes: {foo: 'foob'})
|
613
|
+
]
|
614
|
+
|
615
|
+
expect_all(:xpath, ".//*[local-name()='div'][@foo]").and_return(elements.last(1))
|
616
|
+
|
617
|
+
expect(locate_one(tag_name: 'div', foo: /x|b/)).to eq elements[1]
|
535
618
|
end
|
536
619
|
end
|
537
620
|
|