watir 6.11.0.beta1 → 6.11.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +5 -0
- data/lib/watir/element_collection.rb +3 -8
- data/lib/watir/elements/element.rb +3 -12
- data/lib/watir/locators.rb +8 -0
- data/lib/watir/locators/element/locator.rb +81 -76
- data/spec/element_locator_spec.rb +0 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/watirspec/relaxed_locate_spec.rb +32 -0
- data/watir.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37e5bd05d8b6ed813503264d89bcaf6aceb4b67d5ad2217569c4aef4b7762ca5
|
4
|
+
data.tar.gz: 49c34f4a6e37dec3788fdc5c91ae3d06368b6b0d34c5365387e46d2ee07fe922
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b654a96f43a8d9dc84645cc595c018a6263f180f3bd2e8dc20f03ed8ff2d43c135a267d0cf65f7c87c5cfe1ac9d3524cd438a77d13830ebfb179c9cbb4802693
|
7
|
+
data.tar.gz: d358cfee80f3afae74f39f8aa1d91ba9e98ed6d231735ab19d535ccb3932a0ef273e29e5749dc3ca51778c6220a58e84e5774a26e44403ab0c1592db5fc1ade3
|
data/CHANGES.md
CHANGED
@@ -45,7 +45,7 @@ module Watir
|
|
45
45
|
#
|
46
46
|
|
47
47
|
def [](idx)
|
48
|
-
|
48
|
+
element_class.new(@query_scope, @selector.merge(index: idx))
|
49
49
|
end
|
50
50
|
|
51
51
|
#
|
@@ -121,13 +121,8 @@ module Watir
|
|
121
121
|
private
|
122
122
|
|
123
123
|
def elements
|
124
|
-
@
|
125
|
-
|
126
|
-
element_validator = element_validator_class.new
|
127
|
-
selector_builder = selector_builder_class.new(@query_scope, @selector, element_class.attribute_list)
|
128
|
-
locator = locator_class.new(@query_scope, @selector, selector_builder, element_validator)
|
129
|
-
|
130
|
-
@elements ||= locator.locate_all
|
124
|
+
@locator ||= build_locator
|
125
|
+
@elements ||= @locator.locate_all
|
131
126
|
end
|
132
127
|
|
133
128
|
def element_class
|
@@ -389,8 +389,8 @@ module Watir
|
|
389
389
|
#
|
390
390
|
|
391
391
|
def wd
|
392
|
-
return driver if @element.is_a? FramedDriver
|
393
392
|
assert_exists if @element.nil?
|
393
|
+
return driver if @element.is_a? FramedDriver
|
394
394
|
@element
|
395
395
|
end
|
396
396
|
|
@@ -577,21 +577,12 @@ module Watir
|
|
577
577
|
# Ensure that the element exists, making sure that it is not stale and located if necessary
|
578
578
|
def assert_exists
|
579
579
|
locate unless @element
|
580
|
-
assert_element_found
|
581
|
-
end
|
582
|
-
|
583
|
-
def assert_element_found
|
584
580
|
return if @element
|
585
581
|
raise unknown_exception, "unable to locate element: #{inspect}"
|
586
582
|
end
|
587
583
|
|
588
584
|
def locate
|
589
|
-
@
|
590
|
-
|
591
|
-
element_validator = element_validator_class.new
|
592
|
-
selector_builder = selector_builder_class.new(@query_scope, @selector.dup, self.class.attribute_list)
|
593
|
-
@locator = locator_class.new(@query_scope, @selector.dup, selector_builder, element_validator)
|
594
|
-
|
585
|
+
@locator = build_locator
|
595
586
|
@element = @locator.locate
|
596
587
|
end
|
597
588
|
|
@@ -662,7 +653,7 @@ module Watir
|
|
662
653
|
end
|
663
654
|
msg = ex.message
|
664
655
|
msg += "; Maybe look in an iframe?" if @query_scope.ensure_context && @query_scope.iframes.count > 0
|
665
|
-
custom_attributes = @locator.selector_builder.custom_attributes
|
656
|
+
custom_attributes = @locator.nil? ? [] : @locator.selector_builder.custom_attributes
|
666
657
|
msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended" unless custom_attributes.empty?
|
667
658
|
raise unknown_exception, msg
|
668
659
|
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
data/lib/watir/locators.rb
CHANGED
@@ -55,6 +55,14 @@ module Watir
|
|
55
55
|
def element_class_name
|
56
56
|
element_class.to_s.split('::').last
|
57
57
|
end
|
58
|
+
|
59
|
+
def build_locator
|
60
|
+
@query_scope.send :ensure_context
|
61
|
+
|
62
|
+
element_validator = element_validator_class.new
|
63
|
+
selector_builder = selector_builder_class.new(@query_scope, @selector.dup, element_class.attribute_list)
|
64
|
+
locator_class.new(@query_scope, @selector.dup, selector_builder, element_validator)
|
65
|
+
end
|
58
66
|
end
|
59
67
|
end
|
60
68
|
end
|
@@ -5,14 +5,10 @@ module Watir
|
|
5
5
|
attr_reader :selector_builder
|
6
6
|
attr_reader :element_validator
|
7
7
|
|
8
|
-
|
9
|
-
:class,
|
10
|
-
:class_name,
|
8
|
+
W3C_FINDERS = [
|
11
9
|
:css,
|
12
|
-
:id,
|
13
10
|
:link,
|
14
11
|
:link_text,
|
15
|
-
:name,
|
16
12
|
:partial_link_text,
|
17
13
|
:tag_name,
|
18
14
|
:xpath
|
@@ -49,53 +45,34 @@ module Watir
|
|
49
45
|
private
|
50
46
|
|
51
47
|
def using_selenium(filter = :first)
|
52
|
-
|
53
|
-
|
54
|
-
selector.delete(:tag_name) if selector.size > 1
|
55
|
-
|
56
|
-
WD_FINDERS.each do |sel|
|
57
|
-
next unless (value = selector.delete(sel))
|
58
|
-
return unless selector.empty? && wd_supported?(sel, value)
|
59
|
-
if filter == :all
|
60
|
-
found = locate_elements(sel, value)
|
61
|
-
return found if sel == :tag_name
|
62
|
-
filter_selector = tag_name ? {tag_name: tag_name} : {}
|
63
|
-
return filter_elements(found, filter_selector, filter: filter).compact
|
64
|
-
else
|
65
|
-
found = locate_element(sel, value)
|
66
|
-
return sel != :tag_name && tag_name && !validate([found], tag_name) ? nil : found
|
67
|
-
end
|
68
|
-
end
|
69
|
-
nil
|
70
|
-
end
|
48
|
+
tag = @selector[:tag_name].is_a?(::Symbol) ? @selector.delete(:tag_name).to_s : @selector.delete(:tag_name)
|
49
|
+
return if @selector.size > 1
|
71
50
|
|
72
|
-
|
73
|
-
|
74
|
-
selector = selector_builder.normalized_selector
|
51
|
+
how = @selector.keys.first || :tag_name
|
52
|
+
what = @selector.values.first || tag
|
75
53
|
|
76
|
-
|
77
|
-
query_scope = convert_label_to_scope_or_selector(query_scope, selector)
|
78
|
-
return unless query_scope # stop, label not found
|
79
|
-
end
|
54
|
+
return unless wd_supported?(how, what, tag)
|
80
55
|
|
81
|
-
|
82
|
-
|
83
|
-
|
56
|
+
filter == :all ? locate_elements(how, what) : locate_element(how, what)
|
57
|
+
end
|
58
|
+
|
59
|
+
def using_watir(filter = :first)
|
60
|
+
create_normalized_selector(filter)
|
61
|
+
return unless @normalized_selector
|
84
62
|
|
85
|
-
|
63
|
+
create_filter_selector
|
86
64
|
|
87
|
-
how, what = selector_builder.build(
|
65
|
+
how, what = selector_builder.build(@normalized_selector.dup)
|
88
66
|
unless how
|
89
|
-
raise Error, "internal error: unable to build Selenium selector from #{
|
67
|
+
raise Error, "internal error: unable to build Selenium selector from #{@normalized_selector.inspect}"
|
90
68
|
end
|
91
|
-
what = add_regexp_predicates(what
|
69
|
+
what = add_regexp_predicates(what) if how == :xpath
|
92
70
|
|
93
|
-
|
94
|
-
|
95
|
-
elements
|
96
|
-
filter_elements(elements, filter_selector, filter: filter)
|
71
|
+
if filter == :all || !@filter_selector.empty?
|
72
|
+
elements = locate_elements(how, what, @driver_scope) || []
|
73
|
+
filter_elements(elements, filter: filter)
|
97
74
|
else
|
98
|
-
locate_element(how, what,
|
75
|
+
locate_element(how, what, @driver_scope)
|
99
76
|
end
|
100
77
|
end
|
101
78
|
|
@@ -125,7 +102,8 @@ module Watir
|
|
125
102
|
end
|
126
103
|
end
|
127
104
|
|
128
|
-
def filter_elements(elements,
|
105
|
+
def filter_elements(elements, filter: :first)
|
106
|
+
selector = @filter_selector.dup
|
129
107
|
if filter == :first
|
130
108
|
idx = selector.delete(:index) || 0
|
131
109
|
if idx < 0
|
@@ -141,57 +119,79 @@ module Watir
|
|
141
119
|
end
|
142
120
|
end
|
143
121
|
|
144
|
-
def
|
145
|
-
|
122
|
+
def create_normalized_selector(filter)
|
123
|
+
return @normalized_selector if @normalized_selector
|
124
|
+
@driver_scope = ensure_scope_context
|
125
|
+
|
126
|
+
@normalized_selector = selector_builder.normalized_selector
|
127
|
+
|
128
|
+
if @normalized_selector[:label]
|
129
|
+
process_label
|
130
|
+
return if @normalized_selector.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
if @normalized_selector.key?(:index) && filter == :all
|
134
|
+
raise ArgumentError, "can't locate all elements by :index"
|
135
|
+
end
|
136
|
+
@normalized_selector
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_filter_selector
|
140
|
+
return @filter_selector if @filter_selector
|
141
|
+
@filter_selector = {}
|
146
142
|
|
147
143
|
# Remove selectors that can never be used in XPath builder
|
148
144
|
[:visible, :visible_text].each do |how|
|
149
|
-
next unless
|
150
|
-
filter_selector[how] =
|
145
|
+
next unless @normalized_selector.key?(how)
|
146
|
+
@filter_selector[how] = @normalized_selector.delete(how)
|
151
147
|
end
|
152
148
|
|
153
|
-
if tag_validation_required?(
|
154
|
-
tag_name =
|
155
|
-
filter_selector[:tag_name] = tag_name
|
149
|
+
if tag_validation_required?(@normalized_selector)
|
150
|
+
tag_name = @normalized_selector[:tag_name].is_a?(::Symbol) ? @normalized_selector[:tag_name].to_s : @normalized_selector[:tag_name]
|
151
|
+
@filter_selector[:tag_name] = tag_name
|
156
152
|
end
|
157
153
|
|
158
154
|
# Regexp locators currently need to be validated even if they are included in the XPath builder
|
159
155
|
# TODO: Identify Regexp that can have an exact equivalent using XPath contains (ie would not require
|
160
156
|
# filtering) vs approximations (ie would still requiring filtering)
|
161
|
-
|
157
|
+
@normalized_selector.each do |how, what|
|
162
158
|
next unless what.is_a?(Regexp)
|
163
|
-
filter_selector[how] =
|
159
|
+
@filter_selector[how] = @normalized_selector.delete(how)
|
164
160
|
end
|
165
161
|
|
166
|
-
if
|
167
|
-
idx =
|
162
|
+
if @normalized_selector[:index] && !@normalized_selector[:adjacent]
|
163
|
+
idx = @normalized_selector.delete(:index)
|
168
164
|
|
169
165
|
# Do not add {index: 0} filter if the only filter. This will allow using #find_element instead of #find_elements.
|
170
|
-
implicit_idx_filter = filter_selector.empty? && idx == 0
|
171
|
-
filter_selector[:index] = idx unless implicit_idx_filter
|
166
|
+
implicit_idx_filter = @filter_selector.empty? && idx == 0
|
167
|
+
@filter_selector[:index] = idx unless implicit_idx_filter
|
172
168
|
end
|
173
169
|
|
174
|
-
filter_selector
|
170
|
+
@filter_selector
|
175
171
|
end
|
176
172
|
|
177
|
-
def
|
178
|
-
return
|
173
|
+
def process_label
|
174
|
+
return unless @normalized_selector[:label].kind_of?(Regexp) && selector_builder.should_use_label_element?
|
179
175
|
|
180
|
-
label = label_from_text
|
181
|
-
|
176
|
+
label = label_from_text
|
177
|
+
unless label # label not found, stop looking for element
|
178
|
+
@normalized_selector = nil
|
179
|
+
return
|
180
|
+
end
|
182
181
|
|
183
182
|
if (id = label.attribute('for'))
|
184
|
-
|
185
|
-
query_scope
|
183
|
+
@normalized_selector[:id] = id
|
186
184
|
else
|
187
|
-
label
|
185
|
+
@driver_scope = label
|
188
186
|
end
|
189
187
|
end
|
190
188
|
|
191
|
-
def label_from_text
|
192
|
-
# TODO: this won't work correctly if @wd is a sub-element
|
193
|
-
|
194
|
-
|
189
|
+
def label_from_text
|
190
|
+
# TODO: this won't work correctly if @wd is a sub-element, write spec
|
191
|
+
# TODO: Figure out how to do this with find_element
|
192
|
+
label_text = @normalized_selector.delete(:label)
|
193
|
+
locate_elements(:tag_name, 'label', @driver_scope).find do |el|
|
194
|
+
matches_selector?(el, text: label_text)
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
@@ -209,10 +209,10 @@ module Watir
|
|
209
209
|
true
|
210
210
|
end
|
211
211
|
|
212
|
-
def add_regexp_predicates(what
|
212
|
+
def add_regexp_predicates(what)
|
213
213
|
return what unless can_convert_regexp_to_contains?
|
214
214
|
|
215
|
-
filter_selector.each do |key, value|
|
215
|
+
@filter_selector.each do |key, value|
|
216
216
|
next if [:tag_name, :text, :visible_text, :visible, :index].include?(key)
|
217
217
|
|
218
218
|
predicates = regexp_selector_to_predicates(key, value)
|
@@ -251,12 +251,17 @@ module Watir
|
|
251
251
|
scope.find_elements(how, what)
|
252
252
|
end
|
253
253
|
|
254
|
-
def wd_supported?(how, what)
|
254
|
+
def wd_supported?(how, what, tag)
|
255
|
+
return false unless W3C_FINDERS.include? how
|
255
256
|
return false unless what.kind_of?(String)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
257
|
+
if %i[partial_link_text link_text link].include?(how)
|
258
|
+
Watir.logger.deprecate(":#{how} locator", ':visible_text')
|
259
|
+
return true if [:a, :link, nil].include?(tag)
|
260
|
+
raise StandardError, "Can not use #{how} locator to find a #{what} element"
|
261
|
+
elsif how == :tag_name
|
262
|
+
return true
|
263
|
+
else
|
264
|
+
return false unless tag.nil?
|
260
265
|
end
|
261
266
|
true
|
262
267
|
end
|
@@ -88,7 +88,6 @@ describe Watir::Locators::Element::Locator do
|
|
88
88
|
element(tag_name: "div", attributes: { class: "foo" })
|
89
89
|
]
|
90
90
|
|
91
|
-
expect_one(:xpath, './/*[@class="foo"]').and_return(elements.first)
|
92
91
|
expect_all(:xpath, './/*[@class="foo"]').and_return(elements)
|
93
92
|
|
94
93
|
selector = {
|
@@ -337,7 +336,6 @@ describe Watir::Locators::Element::Locator do
|
|
337
336
|
end
|
338
337
|
|
339
338
|
it "returns nil if found element didn't match the selector tag_name" do
|
340
|
-
expect_one(:xpath, "//div").and_return(element(tag_name: "div"))
|
341
339
|
expect_all(:xpath, "//div").and_return([element(tag_name: "div")])
|
342
340
|
|
343
341
|
selector = {
|
data/spec/spec_helper.rb
CHANGED
@@ -9,7 +9,7 @@ require 'webdrivers'
|
|
9
9
|
require 'locator_spec_helper'
|
10
10
|
require 'rspec'
|
11
11
|
|
12
|
-
SELENIUM_SELECTORS = %i(
|
12
|
+
SELENIUM_SELECTORS = %i(css tag_name xpath link_text partial_link_text link)
|
13
13
|
|
14
14
|
if ENV['RELAXED_LOCATE'] == "false"
|
15
15
|
Watir.relaxed_locate = false
|
@@ -23,6 +23,38 @@ describe 'Watir#relaxed_locate?' do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
context 'when acting on an element whose parent is never present' do
|
27
|
+
it 'raises exception after timing out' do
|
28
|
+
begin
|
29
|
+
time_out = 2
|
30
|
+
Watir.default_timeout = time_out
|
31
|
+
element = browser.link(id: 'not_there')
|
32
|
+
start_time = ::Time.now
|
33
|
+
allow($stderr).to receive(:write).twice
|
34
|
+
expect { element.element.click }.to raise_exception(Watir::Exception::UnknownObjectException)
|
35
|
+
expect(::Time.now - start_time).to be > time_out
|
36
|
+
ensure
|
37
|
+
Watir.default_timeout = 30
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when acting on an element from a collection whose parent is never present' do
|
43
|
+
it 'raises exception after timing out' do
|
44
|
+
begin
|
45
|
+
time_out = 2
|
46
|
+
Watir.default_timeout = time_out
|
47
|
+
element = browser.link(id: 'not_there')
|
48
|
+
start_time = ::Time.now
|
49
|
+
allow($stderr).to receive(:write).twice
|
50
|
+
expect { element.elements[2].click }.to raise_exception(Watir::Exception::UnknownObjectException)
|
51
|
+
expect(::Time.now - start_time).to be > time_out
|
52
|
+
ensure
|
53
|
+
Watir.default_timeout = 30
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
26
58
|
context 'when acting on an element that is already present' do
|
27
59
|
it 'does not wait' do
|
28
60
|
begin
|
data/watir.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: watir
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.11.0.
|
4
|
+
version: 6.11.0.beta2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Rodionov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-05-
|
12
|
+
date: 2018-05-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: selenium-webdriver
|