watir 6.0.0.beta4 → 6.0.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +6 -0
- data/lib/watir/browser.rb +0 -5
- data/lib/watir/element_collection.rb +3 -1
- data/lib/watir/elements/element.rb +24 -27
- data/lib/watir/elements/iframe.rb +6 -22
- data/lib/watir/elements/image.rb +0 -26
- data/lib/watir/elements/row.rb +2 -2
- data/lib/watir/elements/text_field.rb +7 -2
- data/lib/watir/locators/element/locator.rb +20 -13
- data/lib/watir/locators/element/selector_builder.rb +5 -1
- data/lib/watir/window.rb +2 -2
- data/spec/element_spec.rb +36 -16
- data/watir.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 935a787298648718688deca84d844924d69dc5b0
|
4
|
+
data.tar.gz: d6e4f016435c83c21ab6045eac35365c2fb0b370
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e215494da9556e98fe9542a042ff80a64bedbef8071bc920a9653806b629e4876a0708f0b06cb318f69b8cdbc16855dc0704b630df0de83fac3b36225f406a5
|
7
|
+
data.tar.gz: 4b77b7a781336fc2534b5a91051e1b66e590eabdc85162dd8ee6aebf4ba1cecb79d29a766d284d0bc849b42b82e025c51e627b543d59981a0756f5e0c31d8065
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
### 6.0.0.beta5 (2016-09-25)
|
2
|
+
|
3
|
+
* Elements in collections can now be relocated after going stale
|
4
|
+
* Added visible locator to filter out matching elements that are hidden
|
5
|
+
* Element not found error messages now include selector information from parent elements
|
6
|
+
|
1
7
|
### 6.0.0.beta4 (2016-09-12)
|
2
8
|
|
3
9
|
* Deprecate Watir#prefer_css setting
|
data/lib/watir/browser.rb
CHANGED
@@ -81,7 +81,9 @@ module Watir
|
|
81
81
|
|
82
82
|
def to_a
|
83
83
|
# TODO: optimize - lazy element_class instance?
|
84
|
-
@to_a ||= elements.map
|
84
|
+
@to_a ||= elements.map.with_index do |e, idx|
|
85
|
+
element_class.new(@query_scope, @selector.merge(element: e, index: idx))
|
86
|
+
end
|
85
87
|
end
|
86
88
|
|
87
89
|
private
|
@@ -24,12 +24,13 @@ module Watir
|
|
24
24
|
|
25
25
|
def initialize(query_scope, selector)
|
26
26
|
@query_scope = query_scope
|
27
|
-
@selector = selector
|
28
|
-
@element = nil
|
29
27
|
|
30
|
-
unless
|
28
|
+
unless selector.kind_of? Hash
|
31
29
|
raise ArgumentError, "invalid argument: #{selector.inspect}"
|
32
30
|
end
|
31
|
+
|
32
|
+
@element = selector.delete(:element)
|
33
|
+
@selector = selector
|
33
34
|
end
|
34
35
|
|
35
36
|
#
|
@@ -47,7 +48,7 @@ module Watir
|
|
47
48
|
alias_method :exist?, :exists?
|
48
49
|
|
49
50
|
def inspect
|
50
|
-
if @selector.
|
51
|
+
if @selector.empty?
|
51
52
|
'#<%s:0x%x located=%s selector=%s>' % [self.class, hash*2, !!@element, '{element: (selenium element)}']
|
52
53
|
else
|
53
54
|
'#<%s:0x%x located=%s selector=%s>' % [self.class, hash*2, !!@element, selector_string]
|
@@ -513,10 +514,11 @@ module Watir
|
|
513
514
|
|
514
515
|
# Ensure that the element exists, making sure that it is not stale and located if necessary
|
515
516
|
def assert_exists
|
516
|
-
@element
|
517
|
-
|
518
|
-
|
519
|
-
|
517
|
+
if @element && @selector.empty?
|
518
|
+
ensure_context
|
519
|
+
@element = nil if stale?
|
520
|
+
elsif @element && !stale?
|
521
|
+
return
|
520
522
|
else
|
521
523
|
@element = locate
|
522
524
|
end
|
@@ -524,27 +526,14 @@ module Watir
|
|
524
526
|
assert_element_found
|
525
527
|
end
|
526
528
|
|
527
|
-
# Ensure that the element isn't stale, by relocating if it is
|
528
|
-
def ensure_not_stale
|
529
|
-
ensure_context
|
530
|
-
|
531
|
-
# Performance shortcut; only need recursive call to ensure context if stale in current context
|
532
|
-
return unless stale?
|
533
|
-
@element = @selector.key?(:element) ? nil : locate
|
534
|
-
assert_element_found
|
535
|
-
end
|
536
|
-
|
537
529
|
def assert_element_found
|
538
530
|
unless @element
|
539
531
|
raise UnknownObjectException, "unable to locate element, using #{selector_string}"
|
540
532
|
end
|
541
533
|
end
|
542
534
|
|
543
|
-
def reset!
|
544
|
-
@element = nil
|
545
|
-
end
|
546
|
-
|
547
535
|
def locate
|
536
|
+
return if @selector.empty?
|
548
537
|
ensure_context
|
549
538
|
|
550
539
|
element_validator = element_validator_class.new
|
@@ -554,6 +543,18 @@ module Watir
|
|
554
543
|
locator.locate
|
555
544
|
end
|
556
545
|
|
546
|
+
protected
|
547
|
+
|
548
|
+
def selector_string
|
549
|
+
return @selector.inspect if @query_scope.is_a?(Browser)
|
550
|
+
query_scope = if @query_scope.is_a?(IFrame)
|
551
|
+
@query_scope.element.instance_variable_get("@query_scope")
|
552
|
+
else
|
553
|
+
@query_scope
|
554
|
+
end
|
555
|
+
"#{query_scope.selector_string} --> #{@selector.inspect}"
|
556
|
+
end
|
557
|
+
|
557
558
|
private
|
558
559
|
|
559
560
|
def locator_class
|
@@ -578,10 +579,6 @@ module Watir
|
|
578
579
|
self.class.name.split('::').last
|
579
580
|
end
|
580
581
|
|
581
|
-
def selector_string
|
582
|
-
@selector.inspect
|
583
|
-
end
|
584
|
-
|
585
582
|
# Ensure the driver is in the desired browser context
|
586
583
|
def ensure_context
|
587
584
|
@query_scope.is_a?(IFrame) ? @query_scope.switch_to! : @query_scope.assert_exists
|
@@ -620,7 +617,7 @@ module Watir
|
|
620
617
|
def element_call
|
621
618
|
yield
|
622
619
|
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
623
|
-
@element =
|
620
|
+
@element = locate
|
624
621
|
assert_element_found
|
625
622
|
retry
|
626
623
|
end
|
@@ -2,6 +2,7 @@ module Watir
|
|
2
2
|
class IFrame < HTMLElement
|
3
3
|
|
4
4
|
def locate
|
5
|
+
return if @selector.empty?
|
5
6
|
@query_scope.assert_exists
|
6
7
|
|
7
8
|
selector = @selector.merge(tag_name: frame_tag)
|
@@ -15,6 +16,11 @@ module Watir
|
|
15
16
|
FramedDriver.new(element, driver)
|
16
17
|
end
|
17
18
|
|
19
|
+
def ==(other)
|
20
|
+
return false unless other.is_a?(self.class)
|
21
|
+
wd == other.wd.is_a?(FramedDriver) ? other.wd.send(:wd) : other.wd
|
22
|
+
end
|
23
|
+
|
18
24
|
def switch_to!
|
19
25
|
locate.send :switch!
|
20
26
|
end
|
@@ -62,28 +68,6 @@ module Watir
|
|
62
68
|
|
63
69
|
|
64
70
|
class IFrameCollection < ElementCollection
|
65
|
-
|
66
|
-
def to_a
|
67
|
-
# In case `#all_elements` returns empty array, but `#elements`
|
68
|
-
# returns non-empty array (i.e. any frame has loaded between these two calls),
|
69
|
-
# index will return nil. That's why `#all_elements` should always
|
70
|
-
# be called after `#elements.`
|
71
|
-
element_indexes = elements.map { |el| all_elements.index(el) }
|
72
|
-
element_indexes.map { |idx| element_class.new(@query_scope, tag_name: @selector[:tag_name], index: idx) }
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def all_elements
|
78
|
-
selector = { tag_name: @selector[:tag_name] }
|
79
|
-
|
80
|
-
element_validator = element_validator_class.new
|
81
|
-
selector_builder = selector_builder_class.new(@query_scope, selector, element_class.attribute_list)
|
82
|
-
locator = locator_class.new(@query_scope, selector, selector_builder, element_validator)
|
83
|
-
|
84
|
-
locator.locate_all
|
85
|
-
end
|
86
|
-
|
87
71
|
end # IFrameCollection
|
88
72
|
|
89
73
|
|
data/lib/watir/elements/image.rb
CHANGED
@@ -27,32 +27,6 @@ module Watir
|
|
27
27
|
driver.execute_script "return arguments[0].width", @element
|
28
28
|
end
|
29
29
|
|
30
|
-
#
|
31
|
-
# Returns the image's height in pixels.
|
32
|
-
#
|
33
|
-
# @return [Fixnum] width
|
34
|
-
#
|
35
|
-
|
36
|
-
def height
|
37
|
-
assert_exists
|
38
|
-
driver.execute_script "return arguments[0].height", @element
|
39
|
-
end
|
40
|
-
|
41
|
-
def file_created_date
|
42
|
-
assert_exists
|
43
|
-
raise NotImplementedError, "not currently supported by Selenium"
|
44
|
-
end
|
45
|
-
|
46
|
-
def file_size
|
47
|
-
assert_exists
|
48
|
-
raise NotImplementedError, "not currently supported by Selenium"
|
49
|
-
end
|
50
|
-
|
51
|
-
def save(path)
|
52
|
-
assert_exists
|
53
|
-
raise NotImplementedError, "not currently supported by Selenium"
|
54
|
-
end
|
55
|
-
|
56
30
|
end # Image
|
57
31
|
|
58
32
|
module Container
|
data/lib/watir/elements/row.rb
CHANGED
@@ -8,10 +8,10 @@ module Watir
|
|
8
8
|
end # Row
|
9
9
|
|
10
10
|
class RowCollection < TableRowCollection
|
11
|
-
def
|
11
|
+
def to_a
|
12
12
|
# we do this craziness since the xpath used will find direct child rows
|
13
13
|
# before any rows inside thead/tbody/tfoot...
|
14
|
-
super.sort_by { |e| e.
|
14
|
+
super.sort_by { |e| e.attribute_value(:rowIndex).to_i }
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end # Watir
|
@@ -7,13 +7,18 @@ module Watir
|
|
7
7
|
inherit_attributes_from Watir::TextArea
|
8
8
|
remove_method :type # we want Input#type here, which was overriden by TextArea's attributes
|
9
9
|
|
10
|
-
|
10
|
+
protected
|
11
11
|
|
12
12
|
def selector_string
|
13
13
|
selector = @selector.dup
|
14
14
|
selector[:type] = '(any text type)'
|
15
15
|
selector[:tag_name] = "input or textarea"
|
16
|
-
|
16
|
+
|
17
|
+
if @query_scope.is_a?(Browser) || @query_scope.is_a?(IFrame)
|
18
|
+
super
|
19
|
+
else
|
20
|
+
"#{@query_scope.selector_string} --> #{selector.inspect}"
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end # TextField
|
19
24
|
|
@@ -38,11 +38,11 @@ module Watir
|
|
38
38
|
def locate
|
39
39
|
e = by_id and return e # short-circuit if :id is given
|
40
40
|
|
41
|
-
if @selector.size == 1
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
element = if @selector.size == 1
|
42
|
+
find_first_by_one
|
43
|
+
else
|
44
|
+
find_first_by_multiple
|
45
|
+
end
|
46
46
|
|
47
47
|
# This actually only applies when finding by xpath/css - browser.text_field(:xpath, "//input[@type='radio']")
|
48
48
|
# We don't need to validate the element if we built the xpath ourselves.
|
@@ -64,16 +64,15 @@ module Watir
|
|
64
64
|
private
|
65
65
|
|
66
66
|
def by_id
|
67
|
-
return unless id = @selector[:id] and id.is_a? String
|
68
|
-
|
69
67
|
selector = @selector.dup
|
70
|
-
selector.delete(:id)
|
68
|
+
id = selector.delete(:id)
|
69
|
+
return unless id.is_a? String
|
71
70
|
|
72
71
|
tag_name = selector.delete(:tag_name)
|
73
72
|
return unless selector.empty? # multiple attributes
|
74
73
|
|
75
74
|
element = @query_scope.wd.find_element(:id, id)
|
76
|
-
return if tag_name && !element_validator.validate(element,
|
75
|
+
return if tag_name && !element_validator.validate(element, {tag_name: tag_name})
|
77
76
|
|
78
77
|
element
|
79
78
|
end
|
@@ -93,19 +92,27 @@ module Watir
|
|
93
92
|
selector = selector_builder.normalized_selector
|
94
93
|
|
95
94
|
idx = selector.delete(:index)
|
95
|
+
visible = selector.delete(:visible)
|
96
|
+
|
96
97
|
how, what = selector_builder.build(selector)
|
97
98
|
|
98
99
|
if how
|
99
100
|
# could build xpath/css for selector
|
100
|
-
if idx
|
101
|
-
|
101
|
+
if idx || !visible.nil?
|
102
|
+
idx ||= 0
|
103
|
+
elements = @query_scope.wd.find_elements(how, what)
|
104
|
+
elements = elements.select { |el| visible == el.displayed? } unless visible.nil?
|
105
|
+
elements[idx] unless elements.nil?
|
102
106
|
else
|
103
107
|
@query_scope.wd.find_element(how, what)
|
104
108
|
end
|
105
109
|
else
|
106
110
|
# can't use xpath, probably a regexp in there
|
107
|
-
if idx
|
108
|
-
|
111
|
+
if idx || !visible.nil?
|
112
|
+
idx ||= 0
|
113
|
+
elements = wd_find_by_regexp_selector(selector, :select)
|
114
|
+
elements = elements.select { |el| visible == el.displayed? } unless visible.nil?
|
115
|
+
elements[idx] unless elements.nil?
|
109
116
|
else
|
110
117
|
wd_find_by_regexp_selector(selector, :find)
|
111
118
|
end
|
@@ -30,6 +30,10 @@ module Watir
|
|
30
30
|
unless what.is_a?(Fixnum)
|
31
31
|
raise TypeError, "expected Fixnum, got #{what.inspect}:#{what.class}"
|
32
32
|
end
|
33
|
+
when :visible
|
34
|
+
unless what.is_a?(TrueClass) || what.is_a?(FalseClass)
|
35
|
+
raise TypeError, "expected TrueClass or FalseClass, got #{what.inspect}:#{what.class}"
|
36
|
+
end
|
33
37
|
else
|
34
38
|
unless VALID_WHATS.any? { |t| what.is_a? t }
|
35
39
|
raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
|
@@ -53,7 +57,7 @@ module Watir
|
|
53
57
|
|
54
58
|
def normalize_selector(how, what)
|
55
59
|
case how
|
56
|
-
when :tag_name, :text, :xpath, :index, :class, :label, :css
|
60
|
+
when :tag_name, :text, :xpath, :index, :class, :label, :css, :visible
|
57
61
|
# include :class since the valid attribute is 'class_name'
|
58
62
|
# include :for since the valid attribute is 'html_for'
|
59
63
|
[how, what]
|
data/lib/watir/window.rb
CHANGED
data/spec/element_spec.rb
CHANGED
@@ -49,34 +49,24 @@ describe Watir::Element do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
describe "#reset!" do
|
53
|
-
it "successfully relocates collection elements after a reset!" do
|
54
|
-
browser.goto(WatirSpec.url_for("wait.html"))
|
55
|
-
element = browser.div(id: 'foo')
|
56
|
-
expect(element).to exist
|
57
|
-
browser.refresh
|
58
|
-
browser.div(id: 'foo').wait_until_present
|
59
|
-
|
60
|
-
expect(element.exist?).to be false unless Watir.always_locate?
|
61
|
-
element.send :reset!
|
62
|
-
expect(element).to exist
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
52
|
describe "#exists?" do
|
67
53
|
before do
|
68
54
|
browser.goto WatirSpec.url_for('removed_element.html')
|
69
55
|
end
|
70
56
|
|
71
|
-
it "
|
57
|
+
it "relocates element from a collection when it becomes stale" do
|
72
58
|
watir_element = browser.divs(id: "text").first
|
73
59
|
expect(watir_element).to exist
|
74
60
|
|
75
61
|
browser.refresh
|
76
62
|
|
77
|
-
expect(watir_element).
|
63
|
+
expect(watir_element).to exist
|
78
64
|
end
|
79
65
|
|
66
|
+
it "returns false when tag name does not match id" do
|
67
|
+
watir_element = browser.span(id: "text")
|
68
|
+
expect(watir_element).to_not exist
|
69
|
+
end
|
80
70
|
end
|
81
71
|
|
82
72
|
describe "#element_call" do
|
@@ -129,4 +119,34 @@ describe Watir::Element do
|
|
129
119
|
end.to output.to_stderr
|
130
120
|
end
|
131
121
|
end
|
122
|
+
|
123
|
+
describe "#selector_string" do
|
124
|
+
it "displays selector string for regular element" do
|
125
|
+
browser.goto(WatirSpec.url_for("wait.html"))
|
126
|
+
delegator = browser.div(:id, 'foo').when_present
|
127
|
+
message = delegator.instance_variable_get('@message')
|
128
|
+
expect(message).to eq 'waiting for {:id=>"foo", :tag_name=>"div"} to become present'
|
129
|
+
end
|
130
|
+
|
131
|
+
it "displays selector string for element from colection" do
|
132
|
+
browser.goto(WatirSpec.url_for("wait.html"))
|
133
|
+
delegator = browser.divs.last.when_present
|
134
|
+
message = delegator.instance_variable_get('@message')
|
135
|
+
expect(message).to match /waiting for {:tag_name=>\"div\", :index=>3} to become present/
|
136
|
+
end
|
137
|
+
|
138
|
+
it "displays selector string for nested element" do
|
139
|
+
browser.goto(WatirSpec.url_for("wait.html"))
|
140
|
+
delegator = browser.div(index: -1).div(:id, 'foo').when_present
|
141
|
+
message = delegator.instance_variable_get('@message')
|
142
|
+
expect(message).to eq 'waiting for {:index=>-1, :tag_name=>"div"} --> {:id=>"foo", :tag_name=>"div"} to become present'
|
143
|
+
end
|
144
|
+
|
145
|
+
it "displays selector string for nested element under frame" do
|
146
|
+
browser.goto(WatirSpec.url_for("nested_iframes.html"))
|
147
|
+
delegator = browser.iframe(id: 'one').iframe(:id, 'three').when_present
|
148
|
+
message = delegator.instance_variable_get('@message')
|
149
|
+
expect(message).to eq 'waiting for {:id=>"one", :tag_name=>"iframe"} --> {:id=>"three", :tag_name=>"iframe"} to become present'
|
150
|
+
end
|
151
|
+
end
|
132
152
|
end
|
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.0.0.
|
4
|
+
version: 6.0.0.beta5
|
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: 2016-09-
|
12
|
+
date: 2016-09-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: selenium-webdriver
|