watir 6.10.3 → 6.11.0.beta1
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/.travis.yml +16 -21
- data/CHANGES.md +4 -0
- data/lib/watir/browser.rb +5 -3
- data/lib/watir/capabilities.rb +1 -1
- data/lib/watir/element_collection.rb +6 -6
- data/lib/watir/elements/element.rb +39 -22
- data/lib/watir/locators/button/validator.rb +3 -2
- data/lib/watir/locators/element/locator.rb +88 -61
- data/lib/watir/locators/row/selector_builder.rb +2 -1
- data/spec/browser_spec.rb +8 -6
- data/spec/element_locator_spec.rb +1 -1
- data/spec/element_spec.rb +12 -20
- data/spec/watirspec/elements/element_spec.rb +19 -6
- data/spec/watirspec/wait_spec.rb +1 -2
- data/watir.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 656aad05e0cbe7226f3576b88f5a65ed0959c3672b322a5252d73ac3856f0acd
|
|
4
|
+
data.tar.gz: 8112b892320b500fdf4cb113ea2b420b8f24b90509edc00367fcb20c264ca2ef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79f71a31ac314000ed7d1633290a808824881c690c6e05bbb161dfa55e77ee38dc63b2a2ebaae5f2c0f6e68f951e3646450db998fa623f34f6ac7cf6ef525470
|
|
7
|
+
data.tar.gz: dba7348f6003bd93ec9ccf83a3d158f6c289a86ee80e09dac7a1487183583f2052762b5ba009da4c0eeaf9995a9b4fe9ed7d2027dc3d35d46836edeac5bac68c
|
data/.travis.yml
CHANGED
|
@@ -9,6 +9,7 @@ notifications:
|
|
|
9
9
|
secure: BLsBCm33R32VNRccrLx9F0P24X6BVpVHj1OWaN4Kyn6g9qXteIwC2VKVMnKNbifpojfMkrn0OeFQFK1O1DSOsC3mgzn/udoB+DnUGcSemFUn04xhbYF5SI+3zGPKPo0JLvjjdEKSSma84YwKdrj88pGUK34p01gL8hiaqjFzWdk=
|
|
10
10
|
before_install:
|
|
11
11
|
- gem update --system
|
|
12
|
+
- gem install bundler
|
|
12
13
|
|
|
13
14
|
before_script:
|
|
14
15
|
- support/travis.sh
|
|
@@ -17,18 +18,15 @@ before_script:
|
|
|
17
18
|
script: bundle exec rake $RAKE_TASK
|
|
18
19
|
|
|
19
20
|
_version:
|
|
20
|
-
two: &two
|
|
21
|
-
language: ruby
|
|
22
|
-
rvm: 2.2.9
|
|
23
21
|
three: &three
|
|
24
22
|
language: ruby
|
|
25
|
-
rvm: 2.3.
|
|
23
|
+
rvm: 2.3.7
|
|
26
24
|
four: &four
|
|
27
25
|
language: ruby
|
|
28
|
-
rvm: 2.4.
|
|
26
|
+
rvm: 2.4.4
|
|
29
27
|
five: &five
|
|
30
28
|
language: ruby
|
|
31
|
-
rvm: 2.5.
|
|
29
|
+
rvm: 2.5.1
|
|
32
30
|
|
|
33
31
|
_browsers:
|
|
34
32
|
firefox: &firefox-latest
|
|
@@ -41,41 +39,38 @@ _browsers:
|
|
|
41
39
|
matrix:
|
|
42
40
|
include:
|
|
43
41
|
- env: RAKE_TASK=spec:remote_firefox
|
|
44
|
-
<<: *
|
|
42
|
+
<<: *five
|
|
45
43
|
<<: *firefox-latest
|
|
46
44
|
- env: RAKE_TASK=spec:remote_chrome
|
|
47
|
-
<<: *
|
|
45
|
+
<<: *five
|
|
48
46
|
<<: *chrome
|
|
49
47
|
- env: RAKE_TASK=spec:firefox RELAXED_LOCATE=false
|
|
50
|
-
<<: *
|
|
48
|
+
<<: *five
|
|
51
49
|
<<: *firefox-latest
|
|
52
50
|
- env: RAKE_TASK=spec:firefox
|
|
53
|
-
<<: *
|
|
51
|
+
<<: *five
|
|
54
52
|
<<: *firefox-latest
|
|
55
53
|
- env: RAKE_TASK=spec:firefox
|
|
56
|
-
<<: *
|
|
54
|
+
<<: *four
|
|
57
55
|
<<: *firefox-latest
|
|
58
56
|
- env: RAKE_TASK=spec:firefox
|
|
59
|
-
<<: *
|
|
57
|
+
<<: *three
|
|
60
58
|
<<: *firefox-latest
|
|
61
59
|
- env: RAKE_TASK=spec:chrome
|
|
62
|
-
<<: *
|
|
63
|
-
<<: *chrome
|
|
64
|
-
- env: RAKE_TASK=spec:chrome
|
|
65
|
-
<<: *three
|
|
60
|
+
<<: *five
|
|
66
61
|
<<: *chrome
|
|
67
62
|
- env: RAKE_TASK=spec:chrome
|
|
68
|
-
<<: *
|
|
63
|
+
<<: *four
|
|
69
64
|
<<: *chrome
|
|
70
65
|
- env: RAKE_TASK=spec:chrome
|
|
71
|
-
<<: *
|
|
66
|
+
<<: *three
|
|
72
67
|
<<: *chrome
|
|
73
68
|
- env: RAKE_TASK=spec:chrome RELAXED_LOCATE=false
|
|
74
|
-
<<: *
|
|
69
|
+
<<: *five
|
|
75
70
|
<<: *chrome
|
|
76
71
|
- env: RAKE_TASK=spec:chrome HEADLESS=true
|
|
77
|
-
<<: *
|
|
72
|
+
<<: *five
|
|
78
73
|
<<: *chrome
|
|
79
74
|
- env: RAKE_TASK=yard:doctest
|
|
80
|
-
<<: *
|
|
75
|
+
<<: *five
|
|
81
76
|
<<: *chrome
|
data/CHANGES.md
CHANGED
data/lib/watir/browser.rb
CHANGED
|
@@ -58,11 +58,13 @@ module Watir
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def inspect
|
|
61
|
-
|
|
61
|
+
if alert.exists?
|
|
62
|
+
'#<%s:0x%x alert=true>' % [self.class, hash*2]
|
|
63
|
+
else
|
|
64
|
+
'#<%s:0x%x url=%s title=%s>' % [self.class, hash * 2, url.inspect, title.inspect]
|
|
65
|
+
end
|
|
62
66
|
rescue Errno::ECONNREFUSED
|
|
63
67
|
'#<%s:0x%x closed=true>' % [self.class, hash*2]
|
|
64
|
-
rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
|
65
|
-
'#<%s:0x%x alert=true>' % [self.class, hash*2]
|
|
66
68
|
end
|
|
67
69
|
alias selector_string inspect
|
|
68
70
|
|
data/lib/watir/capabilities.rb
CHANGED
|
@@ -99,7 +99,7 @@ module Watir
|
|
|
99
99
|
end
|
|
100
100
|
if @browser == :firefox && @options.delete(:headless)
|
|
101
101
|
args = @options.delete(:args) || @options.delete(:switches) || []
|
|
102
|
-
@options[Selenium::WebDriver::Firefox::Options
|
|
102
|
+
@options[Selenium::WebDriver::Firefox::Options::KEY] = {'args' => args + ['--headless']}
|
|
103
103
|
end
|
|
104
104
|
if @browser == :safari && @options.delete(:technology_preview)
|
|
105
105
|
@options["safari.options"] = {'technologyPreview' => true}
|
|
@@ -70,12 +70,12 @@ module Watir
|
|
|
70
70
|
elements.map.with_index do |e, idx|
|
|
71
71
|
element = element_class.new(@query_scope, @selector.merge(element: e, index: idx))
|
|
72
72
|
if [Watir::HTMLElement, Watir::Input].include? element.class
|
|
73
|
-
|
|
74
|
-
hash[
|
|
75
|
-
hash[
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
tag_name = element.tag_name.to_sym
|
|
74
|
+
hash[tag_name] ||= 0
|
|
75
|
+
hash[tag_name] += 1
|
|
76
|
+
Watir.tag_to_class[tag_name].new(@query_scope, @selector.merge(element: e,
|
|
77
|
+
tag_name: tag_name,
|
|
78
|
+
index: hash[tag_name] - 1))
|
|
79
79
|
else
|
|
80
80
|
element
|
|
81
81
|
end
|
|
@@ -47,6 +47,7 @@ module Watir
|
|
|
47
47
|
#
|
|
48
48
|
|
|
49
49
|
def exists?
|
|
50
|
+
return false if @element && stale?
|
|
50
51
|
assert_exists
|
|
51
52
|
true
|
|
52
53
|
rescue UnknownObjectException, UnknownFrameException
|
|
@@ -401,7 +402,10 @@ module Watir
|
|
|
401
402
|
#
|
|
402
403
|
|
|
403
404
|
def visible?
|
|
404
|
-
|
|
405
|
+
assert_exists
|
|
406
|
+
@element.displayed?
|
|
407
|
+
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
|
408
|
+
raise unknown_exception
|
|
405
409
|
end
|
|
406
410
|
|
|
407
411
|
#
|
|
@@ -497,6 +501,12 @@ module Watir
|
|
|
497
501
|
|
|
498
502
|
def stale?
|
|
499
503
|
raise Watir::Exception::Error, "Can not check staleness of unused element" unless @element
|
|
504
|
+
return false unless stale_in_context?
|
|
505
|
+
@query_scope.ensure_context
|
|
506
|
+
stale_in_context?
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def stale_in_context?
|
|
500
510
|
@element.enabled? # any wire call will check for staleness
|
|
501
511
|
false
|
|
502
512
|
rescue Selenium::WebDriver::Error::ObsoleteElementError
|
|
@@ -566,15 +576,7 @@ module Watir
|
|
|
566
576
|
|
|
567
577
|
# Ensure that the element exists, making sure that it is not stale and located if necessary
|
|
568
578
|
def assert_exists
|
|
569
|
-
|
|
570
|
-
@query_scope.ensure_context
|
|
571
|
-
reset! if stale?
|
|
572
|
-
elsif @element && !stale?
|
|
573
|
-
return
|
|
574
|
-
else
|
|
575
|
-
@element = locate
|
|
576
|
-
end
|
|
577
|
-
|
|
579
|
+
locate unless @element
|
|
578
580
|
assert_element_found
|
|
579
581
|
end
|
|
580
582
|
|
|
@@ -600,7 +602,7 @@ module Watir
|
|
|
600
602
|
|
|
601
603
|
# Ensure the driver is in the desired browser context
|
|
602
604
|
def ensure_context
|
|
603
|
-
|
|
605
|
+
locate unless exists?
|
|
604
606
|
end
|
|
605
607
|
|
|
606
608
|
private
|
|
@@ -643,43 +645,58 @@ module Watir
|
|
|
643
645
|
end
|
|
644
646
|
end
|
|
645
647
|
|
|
646
|
-
def element_call(
|
|
648
|
+
def element_call(precondition = nil, &block)
|
|
649
|
+
caller = caller_locations(1, 1)[0].label
|
|
647
650
|
already_locked = Wait.timer.locked?
|
|
648
|
-
|
|
649
|
-
if already_locked
|
|
650
|
-
Watir.logger.info "-> `#{inspect}##{caller}` after `##{exist_check}` (as a prerequisite for a previously specified execution)"
|
|
651
|
-
else
|
|
652
|
-
Watir.logger.info "-> `#{inspect}##{caller}` after `##{exist_check}`"
|
|
651
|
+
unless already_locked
|
|
653
652
|
Wait.timer = Wait::Timer.new(timeout: Watir.default_timeout)
|
|
654
653
|
end
|
|
655
654
|
|
|
656
655
|
begin
|
|
657
|
-
|
|
656
|
+
check_condition(precondition)
|
|
657
|
+
Watir.logger.info "-> `Executing #{inspect}##{caller}`"
|
|
658
658
|
yield
|
|
659
659
|
rescue unknown_exception => ex
|
|
660
|
+
if precondition.nil?
|
|
661
|
+
element_call(:wait_for_exists, &block)
|
|
662
|
+
end
|
|
660
663
|
msg = ex.message
|
|
661
|
-
msg += "; Maybe look in an iframe?" if @query_scope.iframes.count > 0
|
|
664
|
+
msg += "; Maybe look in an iframe?" if @query_scope.ensure_context && @query_scope.iframes.count > 0
|
|
662
665
|
custom_attributes = @locator.selector_builder.custom_attributes
|
|
663
666
|
msg += "; Watir treated #{custom_attributes} as a non-HTML compliant attribute, ensure that was intended" unless custom_attributes.empty?
|
|
664
667
|
raise unknown_exception, msg
|
|
665
668
|
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
|
669
|
+
@query_scope.ensure_context
|
|
670
|
+
reset!
|
|
666
671
|
retry
|
|
667
672
|
rescue Selenium::WebDriver::Error::ElementNotVisibleError, Selenium::WebDriver::Error::ElementNotInteractableError
|
|
668
673
|
raise_present unless Wait.timer.remaining_time > 0
|
|
669
|
-
raise_present unless
|
|
674
|
+
raise_present unless precondition == :wait_for_present || precondition == :wait_for_enabled
|
|
670
675
|
retry
|
|
671
676
|
rescue Selenium::WebDriver::Error::InvalidElementStateError
|
|
672
677
|
raise_disabled unless Wait.timer.remaining_time > 0
|
|
673
|
-
raise_disabled unless
|
|
678
|
+
raise_disabled unless precondition == :wait_for_writable || precondition == :wait_for_enabled
|
|
674
679
|
retry
|
|
675
680
|
rescue Selenium::WebDriver::Error::NoSuchWindowError
|
|
676
681
|
raise Exception::NoMatchingWindowFoundException, "browser window was closed"
|
|
677
682
|
ensure
|
|
678
|
-
Watir.logger.info "<-
|
|
683
|
+
Watir.logger.info "<- `Completed #{inspect}##{caller}`"
|
|
679
684
|
Wait.timer.reset! unless already_locked
|
|
680
685
|
end
|
|
681
686
|
end
|
|
682
687
|
|
|
688
|
+
def check_condition(condition)
|
|
689
|
+
Watir.logger.info "<- `Verifying precondition #{inspect}##{condition}`"
|
|
690
|
+
begin
|
|
691
|
+
condition.nil? ? assert_exists : send(condition)
|
|
692
|
+
Watir.logger.info "<- `Verified precondition #{inspect}##{condition || 'assert_exists'}`"
|
|
693
|
+
rescue unknown_exception
|
|
694
|
+
raise unless condition.nil?
|
|
695
|
+
Watir.logger.info "<- `Unable to satisfy precondition #{inspect}##{condition}`"
|
|
696
|
+
check_condition(:wait_for_exists)
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
683
700
|
def method_missing(meth, *args, &blk)
|
|
684
701
|
method = meth.to_s
|
|
685
702
|
if method =~ Locators::Element::SelectorBuilder::WILDCARD_ATTRIBUTE
|
|
@@ -3,9 +3,10 @@ module Watir
|
|
|
3
3
|
class Button
|
|
4
4
|
class Validator < Element::Validator
|
|
5
5
|
def validate(element, _selector)
|
|
6
|
-
|
|
6
|
+
tag_name = element.tag_name.downcase
|
|
7
|
+
return unless %w[input button].include?(tag_name)
|
|
7
8
|
# TODO - Verify this is desired behavior based on https://bugzilla.mozilla.org/show_bug.cgi?id=1290963
|
|
8
|
-
return if
|
|
9
|
+
return if tag_name == "input" && !Watir::Button::VALID_TYPES.include?(element.attribute(:type).downcase)
|
|
9
10
|
|
|
10
11
|
element
|
|
11
12
|
end
|
|
@@ -59,7 +59,8 @@ module Watir
|
|
|
59
59
|
if filter == :all
|
|
60
60
|
found = locate_elements(sel, value)
|
|
61
61
|
return found if sel == :tag_name
|
|
62
|
-
|
|
62
|
+
filter_selector = tag_name ? {tag_name: tag_name} : {}
|
|
63
|
+
return filter_elements(found, filter_selector, filter: filter).compact
|
|
63
64
|
else
|
|
64
65
|
found = locate_element(sel, value)
|
|
65
66
|
return sel != :tag_name && tag_name && !validate([found], tag_name) ? nil : found
|
|
@@ -69,28 +70,32 @@ module Watir
|
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def using_watir(filter = :first)
|
|
73
|
+
query_scope = ensure_scope_context
|
|
72
74
|
selector = selector_builder.normalized_selector
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
|
|
76
|
+
if selector[:label]
|
|
77
|
+
query_scope = convert_label_to_scope_or_selector(query_scope, selector)
|
|
78
|
+
return unless query_scope # stop, label not found
|
|
79
|
+
end
|
|
77
80
|
|
|
78
81
|
if selector.key?(:index) && filter == :all
|
|
79
82
|
raise ArgumentError, "can't locate all elements by :index"
|
|
80
83
|
end
|
|
81
|
-
idx = selector.delete(:index) unless selector[:adjacent]
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
filter_selector = delete_filters_from(selector)
|
|
84
86
|
|
|
85
|
-
|
|
87
|
+
how, what = selector_builder.build(selector)
|
|
88
|
+
unless how
|
|
89
|
+
raise Error, "internal error: unable to build Selenium selector from #{selector.inspect}"
|
|
90
|
+
end
|
|
91
|
+
what = add_regexp_predicates(what, filter_selector) if how == :xpath
|
|
86
92
|
|
|
93
|
+
needs_filtering = filter == :all || !filter_selector.empty?
|
|
87
94
|
if needs_filtering
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
elsif how
|
|
91
|
-
locate_element(how, what)
|
|
95
|
+
elements = locate_elements(how, what, query_scope) || []
|
|
96
|
+
filter_elements(elements, filter_selector, filter: filter)
|
|
92
97
|
else
|
|
93
|
-
|
|
98
|
+
locate_element(how, what, query_scope)
|
|
94
99
|
end
|
|
95
100
|
end
|
|
96
101
|
|
|
@@ -98,11 +103,6 @@ module Watir
|
|
|
98
103
|
elements.compact.all? { |element| element_validator.validate(element, {tag_name: tag_name}) }
|
|
99
104
|
end
|
|
100
105
|
|
|
101
|
-
def matching_elements(how, what, selector = nil)
|
|
102
|
-
found = how ? locate_elements(how, what) : wd_find_by_regexp_selector(selector, :all)
|
|
103
|
-
found || []
|
|
104
|
-
end
|
|
105
|
-
|
|
106
106
|
def fetch_value(element, how)
|
|
107
107
|
case how
|
|
108
108
|
when :text
|
|
@@ -112,6 +112,8 @@ module Watir
|
|
|
112
112
|
Watir.logger.deprecate(':text locator with RegExp values to find elements based on only visible text', ":visible_text")
|
|
113
113
|
end
|
|
114
114
|
vis
|
|
115
|
+
when :visible
|
|
116
|
+
element.displayed?
|
|
115
117
|
when :visible_text
|
|
116
118
|
element.text
|
|
117
119
|
when :tag_name
|
|
@@ -123,64 +125,67 @@ module Watir
|
|
|
123
125
|
end
|
|
124
126
|
end
|
|
125
127
|
|
|
126
|
-
def
|
|
127
|
-
|
|
128
|
+
def filter_elements(elements, selector, filter: :first)
|
|
129
|
+
if filter == :first
|
|
130
|
+
idx = selector.delete(:index) || 0
|
|
131
|
+
if idx < 0
|
|
132
|
+
elements.reverse!
|
|
133
|
+
idx = idx.abs - 1
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Lazy evaluation to avoid fetching values for elements that will be discarded
|
|
137
|
+
matches = elements.lazy.select { |el| matches_selector?(el, selector) }
|
|
138
|
+
matches.take(idx + 1).to_a[idx]
|
|
139
|
+
else
|
|
140
|
+
elements.select { |el| matches_selector?(el, selector) }
|
|
141
|
+
end
|
|
128
142
|
end
|
|
129
143
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
rx_selector = delete_regexps_from(selector)
|
|
144
|
+
def delete_filters_from(selector)
|
|
145
|
+
filter_selector = {}
|
|
133
146
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
else
|
|
139
|
-
query_scope = label
|
|
140
|
-
end
|
|
147
|
+
# Remove selectors that can never be used in XPath builder
|
|
148
|
+
[:visible, :visible_text].each do |how|
|
|
149
|
+
next unless selector.key?(how)
|
|
150
|
+
filter_selector[how] = selector.delete(how)
|
|
141
151
|
end
|
|
142
152
|
|
|
143
|
-
|
|
153
|
+
if tag_validation_required?(selector)
|
|
154
|
+
tag_name = selector[:tag_name].is_a?(::Symbol) ? selector[:tag_name].to_s : selector[:tag_name]
|
|
155
|
+
filter_selector[:tag_name] = tag_name
|
|
156
|
+
end
|
|
144
157
|
|
|
145
|
-
|
|
146
|
-
|
|
158
|
+
# Regexp locators currently need to be validated even if they are included in the XPath builder
|
|
159
|
+
# TODO: Identify Regexp that can have an exact equivalent using XPath contains (ie would not require
|
|
160
|
+
# filtering) vs approximations (ie would still requiring filtering)
|
|
161
|
+
selector.dup.each do |how, what|
|
|
162
|
+
next unless what.is_a?(Regexp)
|
|
163
|
+
filter_selector[how] = selector.delete(how)
|
|
147
164
|
end
|
|
148
165
|
|
|
149
|
-
if
|
|
150
|
-
|
|
151
|
-
next if key == :tag_name || key == :text
|
|
166
|
+
if selector[:index] && !selector[:adjacent]
|
|
167
|
+
idx = selector.delete(:index)
|
|
152
168
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
169
|
+
# 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
|
|
156
172
|
end
|
|
157
173
|
|
|
158
|
-
|
|
159
|
-
filter_elements_by_regex(elements, rx_selector, filter)
|
|
174
|
+
filter_selector
|
|
160
175
|
end
|
|
161
176
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
elements.select! { |el| visible_text === el.text } unless visible_text.nil?
|
|
165
|
-
elements.select! { |el| element_validator.validate(el, {tag_name: tag_name}) } unless tag_name.nil?
|
|
166
|
-
filter == :first ? elements[idx || 0] : elements
|
|
167
|
-
end
|
|
177
|
+
def convert_label_to_scope_or_selector(query_scope, selector)
|
|
178
|
+
return query_scope unless selector[:label].kind_of?(Regexp) && selector_builder.should_use_label_element?
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
elements.__send__(method) { |el| matches_selector?(el, selector) }
|
|
172
|
-
end
|
|
180
|
+
label = label_from_text(selector.delete(:label))
|
|
181
|
+
return unless label # label not found, stop looking for element
|
|
173
182
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
rx_selector[how] = what
|
|
180
|
-
selector.delete how
|
|
183
|
+
if (id = label.attribute('for'))
|
|
184
|
+
selector[:id] = id
|
|
185
|
+
query_scope
|
|
186
|
+
else
|
|
187
|
+
label
|
|
181
188
|
end
|
|
182
|
-
|
|
183
|
-
rx_selector
|
|
184
189
|
end
|
|
185
190
|
|
|
186
191
|
def label_from_text(label_exp)
|
|
@@ -192,7 +197,11 @@ module Watir
|
|
|
192
197
|
|
|
193
198
|
def matches_selector?(element, selector)
|
|
194
199
|
selector.all? do |how, what|
|
|
195
|
-
|
|
200
|
+
if how == :tag_name && what.is_a?(String)
|
|
201
|
+
element_validator.validate(element, {tag_name: what})
|
|
202
|
+
else
|
|
203
|
+
what === fetch_value(element, how)
|
|
204
|
+
end
|
|
196
205
|
end
|
|
197
206
|
end
|
|
198
207
|
|
|
@@ -200,6 +209,20 @@ module Watir
|
|
|
200
209
|
true
|
|
201
210
|
end
|
|
202
211
|
|
|
212
|
+
def add_regexp_predicates(what, filter_selector)
|
|
213
|
+
return what unless can_convert_regexp_to_contains?
|
|
214
|
+
|
|
215
|
+
filter_selector.each do |key, value|
|
|
216
|
+
next if [:tag_name, :text, :visible_text, :visible, :index].include?(key)
|
|
217
|
+
|
|
218
|
+
predicates = regexp_selector_to_predicates(key, value)
|
|
219
|
+
unless predicates.empty?
|
|
220
|
+
what = "(#{what})[#{predicates.join(' and ')}]"
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
what
|
|
224
|
+
end
|
|
225
|
+
|
|
203
226
|
def regexp_selector_to_predicates(key, re)
|
|
204
227
|
return [] if re.casefold?
|
|
205
228
|
|
|
@@ -212,6 +235,10 @@ module Watir
|
|
|
212
235
|
end
|
|
213
236
|
end
|
|
214
237
|
|
|
238
|
+
def tag_validation_required?(selector)
|
|
239
|
+
(selector.key?(:css) || selector.key?(:xpath)) && selector.key?(:tag_name)
|
|
240
|
+
end
|
|
241
|
+
|
|
215
242
|
def ensure_scope_context
|
|
216
243
|
@query_scope.wd
|
|
217
244
|
end
|
|
@@ -6,8 +6,9 @@ module Watir
|
|
|
6
6
|
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
|
7
7
|
selectors.delete(:tag_name) || raise("internal error: no tag_name?!")
|
|
8
8
|
|
|
9
|
+
tag_name = @query_scope.tag_name.downcase
|
|
9
10
|
expressions = %w[./tr]
|
|
10
|
-
unless %w[tbody tfoot thead].include?(
|
|
11
|
+
unless %w[tbody tfoot thead].include?(tag_name)
|
|
11
12
|
expressions += %w[./tbody/tr ./thead/tr ./tfoot/tr]
|
|
12
13
|
end
|
|
13
14
|
|
data/spec/browser_spec.rb
CHANGED
|
@@ -206,14 +206,16 @@ describe Watir::Browser do
|
|
|
206
206
|
expect(browser.text_field(id: "new_user_first_name").value).to eq "hello"
|
|
207
207
|
end
|
|
208
208
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
not_compliant_on(:firefox) do
|
|
210
|
+
it "sends keys to a frame" do
|
|
211
|
+
browser.goto WatirSpec.url_for "frames.html"
|
|
212
|
+
tf = browser.frame.text_field(id: "senderElement")
|
|
213
|
+
tf.clear
|
|
213
214
|
|
|
214
|
-
|
|
215
|
+
browser.frame.send_keys "hello"
|
|
215
216
|
|
|
216
|
-
|
|
217
|
+
expect(tf.value).to eq "hello"
|
|
218
|
+
end
|
|
217
219
|
end
|
|
218
220
|
end
|
|
219
221
|
|
|
@@ -304,7 +304,7 @@ describe Watir::Locators::Element::Locator do
|
|
|
304
304
|
div_elements = [element(tag_name: "div")]
|
|
305
305
|
|
|
306
306
|
expect_all(:tag_name, "label").ordered.and_return(label_elements)
|
|
307
|
-
|
|
307
|
+
expect_one(:xpath, ".//div[@id='baz']").ordered.and_return(div_elements.first)
|
|
308
308
|
|
|
309
309
|
allow(browser).to receive(:ensure_context).and_return(nil)
|
|
310
310
|
allow(browser).to receive(:execute_script).and_return('foo', 'foob')
|
data/spec/element_spec.rb
CHANGED
|
@@ -20,13 +20,12 @@ describe Watir::Element do
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
it "returns false if the element is stale" do
|
|
23
|
-
|
|
23
|
+
element = browser.div(id: "foo").tap(&:exists?)
|
|
24
24
|
|
|
25
|
-
# simulate element going stale during lookup
|
|
26
|
-
allow(browser.driver).to receive(:find_element).with(:id, 'foo') { wd_element }
|
|
27
25
|
browser.refresh
|
|
28
26
|
|
|
29
|
-
expect(
|
|
27
|
+
expect(element).to be_stale
|
|
28
|
+
expect(element).to_not be_present
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
end
|
|
@@ -54,13 +53,13 @@ describe Watir::Element do
|
|
|
54
53
|
browser.goto WatirSpec.url_for('removed_element.html')
|
|
55
54
|
end
|
|
56
55
|
|
|
57
|
-
it "
|
|
58
|
-
|
|
59
|
-
expect(watir_element).to exist
|
|
56
|
+
it "element from a collection returns false when it becomes stale" do
|
|
57
|
+
element = browser.divs(id: "text").first.tap(&:exists?)
|
|
60
58
|
|
|
61
59
|
browser.refresh
|
|
62
60
|
|
|
63
|
-
expect(
|
|
61
|
+
expect(element).to be_stale
|
|
62
|
+
expect(element).to_not exist
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
it "returns false when tag name does not match id" do
|
|
@@ -71,22 +70,15 @@ describe Watir::Element do
|
|
|
71
70
|
|
|
72
71
|
describe "#element_call" do
|
|
73
72
|
|
|
74
|
-
it 'handles exceptions when taking an action on
|
|
73
|
+
it 'handles exceptions when taking an action on a stale element' do
|
|
75
74
|
browser.goto WatirSpec.url_for('removed_element.html')
|
|
76
75
|
|
|
77
|
-
|
|
76
|
+
element = browser.div(id: "text").tap(&:exists?)
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
allow(watir_element).to receive(:text) do
|
|
81
|
-
watir_element.send(:element_call) do
|
|
82
|
-
@already_stale ||= false
|
|
83
|
-
browser.refresh unless @already_stale
|
|
84
|
-
@already_stale = true
|
|
85
|
-
watir_element.instance_variable_get('@element').text
|
|
86
|
-
end
|
|
87
|
-
end
|
|
78
|
+
browser.refresh
|
|
88
79
|
|
|
89
|
-
expect
|
|
80
|
+
expect(element).to be_stale
|
|
81
|
+
expect { element.text }.to_not raise_error
|
|
90
82
|
end
|
|
91
83
|
|
|
92
84
|
end
|
|
@@ -199,15 +199,12 @@ describe "Element" do
|
|
|
199
199
|
end
|
|
200
200
|
|
|
201
201
|
it "raises UnknownObjectException exception if the element is stale" do
|
|
202
|
-
|
|
202
|
+
element = browser.text_field(id: "new_user_email").tap(&:exists?)
|
|
203
203
|
|
|
204
|
-
# simulate element going stale during lookup
|
|
205
|
-
allow(browser.driver).to receive(:find_element).with(:css, '#new_user_email') { wd_element }
|
|
206
|
-
allow(browser.driver).to receive(:find_elements).with(:css, '#new_user_email') { [wd_element] }
|
|
207
|
-
allow(browser.driver).to receive(:find_elements).with(:tag_name, 'iframe') { [] }
|
|
208
204
|
browser.refresh
|
|
209
205
|
|
|
210
|
-
expect
|
|
206
|
+
expect(element).to be_stale
|
|
207
|
+
expect { element.visible? }.to raise_unknown_object_exception
|
|
211
208
|
end
|
|
212
209
|
|
|
213
210
|
it "returns true if the element has style='visibility: visible' even if parent has style='visibility: hidden'" do
|
|
@@ -298,6 +295,22 @@ describe "Element" do
|
|
|
298
295
|
end
|
|
299
296
|
end
|
|
300
297
|
|
|
298
|
+
context ":index locator" do
|
|
299
|
+
before { browser.goto WatirSpec.url_for("data_attributes.html") }
|
|
300
|
+
|
|
301
|
+
it "finds the first element by index: 0" do
|
|
302
|
+
expect(browser.element(index: 0).tag_name).to eq "html"
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
it "finds the second element by index: 1" do
|
|
306
|
+
expect(browser.element(index: 1).tag_name).to eq "head"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it "finds the last element by index: -1" do
|
|
310
|
+
expect(browser.element(index: -1).tag_name).to eq "p"
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
301
314
|
it "doesn't raise when called on nested elements" do
|
|
302
315
|
expect(browser.div(id: 'no_such_div').link(id: 'no_such_id')).to_not exist
|
|
303
316
|
end
|
data/spec/watirspec/wait_spec.rb
CHANGED
|
@@ -247,10 +247,9 @@ describe Watir::Element do
|
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
it "does not error when element goes stale" do
|
|
250
|
-
element = browser.div(id: 'foo')
|
|
250
|
+
element = browser.div(id: 'foo').tap(&:exists?)
|
|
251
251
|
|
|
252
252
|
allow(element).to receive(:stale?).and_return(false, true)
|
|
253
|
-
allow(element.wd).to receive(:displayed?).and_raise(Selenium::WebDriver::Error::StaleElementReferenceError)
|
|
254
253
|
|
|
255
254
|
browser.a(id: 'hide_foo').click
|
|
256
255
|
expect { element.wait_while_present(timeout: 1) }.to_not raise_exception
|
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.
|
|
4
|
+
version: 6.11.0.beta1
|
|
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-
|
|
12
|
+
date: 2018-05-04 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: selenium-webdriver
|
|
@@ -520,9 +520,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
520
520
|
version: '0'
|
|
521
521
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
522
522
|
requirements:
|
|
523
|
-
- - "
|
|
523
|
+
- - ">"
|
|
524
524
|
- !ruby/object:Gem::Version
|
|
525
|
-
version:
|
|
525
|
+
version: 1.3.1
|
|
526
526
|
requirements: []
|
|
527
527
|
rubyforge_project: watir
|
|
528
528
|
rubygems_version: 2.7.3
|