watir 6.14.0 → 6.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|