watir 6.10.3 → 6.11.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35bf21c910e97a772d03a1ec9711edebd7bfd9231bc2beb911542fd302a4b6ef
4
- data.tar.gz: bc8595b914786c02087e0b7689885e1b0ed0c7e77487ff0b4796a00d5f381b69
3
+ metadata.gz: 656aad05e0cbe7226f3576b88f5a65ed0959c3672b322a5252d73ac3856f0acd
4
+ data.tar.gz: 8112b892320b500fdf4cb113ea2b420b8f24b90509edc00367fcb20c264ca2ef
5
5
  SHA512:
6
- metadata.gz: 119768b890d1fc7daaff5b690619abbf66ac32e35b35fcde533df812d156815c8b8d4dbc742875ebb164f0002d44962c1b6b803c9914f1b9794af469ce5ac034
7
- data.tar.gz: 352c1f1f616e64cdac24e1b98d771a96be911a932b3e5c9f490f1d8dcac6644b623a72705b4c4f81cbfcd0c4f3a1a296a734674d2a1c3733c2417b6fe07987a9
6
+ metadata.gz: 79f71a31ac314000ed7d1633290a808824881c690c6e05bbb161dfa55e77ee38dc63b2a2ebaae5f2c0f6e68f951e3646450db998fa623f34f6ac7cf6ef525470
7
+ data.tar.gz: dba7348f6003bd93ec9ccf83a3d158f6c289a86ee80e09dac7a1487183583f2052762b5ba009da4c0eeaf9995a9b4fe9ed7d2027dc3d35d46836edeac5bac68c
@@ -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.6
23
+ rvm: 2.3.7
26
24
  four: &four
27
25
  language: ruby
28
- rvm: 2.4.3
26
+ rvm: 2.4.4
29
27
  five: &five
30
28
  language: ruby
31
- rvm: 2.5.0
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
- <<: *four
42
+ <<: *five
45
43
  <<: *firefox-latest
46
44
  - env: RAKE_TASK=spec:remote_chrome
47
- <<: *four
45
+ <<: *five
48
46
  <<: *chrome
49
47
  - env: RAKE_TASK=spec:firefox RELAXED_LOCATE=false
50
- <<: *four
48
+ <<: *five
51
49
  <<: *firefox-latest
52
50
  - env: RAKE_TASK=spec:firefox
53
- <<: *four
51
+ <<: *five
54
52
  <<: *firefox-latest
55
53
  - env: RAKE_TASK=spec:firefox
56
- <<: *three
54
+ <<: *four
57
55
  <<: *firefox-latest
58
56
  - env: RAKE_TASK=spec:firefox
59
- <<: *two
57
+ <<: *three
60
58
  <<: *firefox-latest
61
59
  - env: RAKE_TASK=spec:chrome
62
- <<: *four
63
- <<: *chrome
64
- - env: RAKE_TASK=spec:chrome
65
- <<: *three
60
+ <<: *five
66
61
  <<: *chrome
67
62
  - env: RAKE_TASK=spec:chrome
68
- <<: *five
63
+ <<: *four
69
64
  <<: *chrome
70
65
  - env: RAKE_TASK=spec:chrome
71
- <<: *two
66
+ <<: *three
72
67
  <<: *chrome
73
68
  - env: RAKE_TASK=spec:chrome RELAXED_LOCATE=false
74
- <<: *four
69
+ <<: *five
75
70
  <<: *chrome
76
71
  - env: RAKE_TASK=spec:chrome HEADLESS=true
77
- <<: *four
72
+ <<: *five
78
73
  <<: *chrome
79
74
  - env: RAKE_TASK=yard:doctest
80
- <<: *four
75
+ <<: *five
81
76
  <<: *chrome
data/CHANGES.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### 6.11.0.beta1 (2018-05-04)
2
+
3
+ * Significant performance updates
4
+
1
5
  ### 6.10.3 (2018-01-26)
2
6
 
3
7
  * Add special handling for `date_field` and `date_time_field` input types
@@ -58,11 +58,13 @@ module Watir
58
58
  end
59
59
 
60
60
  def inspect
61
- '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
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
 
@@ -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.KEY] = {'args' => args + ['--headless']}
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
- element = element.to_subtype
74
- hash[element.class] ||= []
75
- hash[element.class] << element
76
- element.class.new(@query_scope, @selector.merge(element: e,
77
- tag_name: element.tag_name,
78
- index: hash[element.class].size - 1))
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
- element_call(:assert_exists) { @element.displayed? }
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
- if @element && @selector.empty?
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
- assert_exists
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(exist_check = :wait_for_exists)
648
+ def element_call(precondition = nil, &block)
649
+ caller = caller_locations(1, 1)[0].label
647
650
  already_locked = Wait.timer.locked?
648
- caller = caller_locations(1,1)[0].label
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
- send exist_check
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 exist_check == :wait_for_present || exist_check == :wait_for_enabled
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 exist_check == :wait_for_writable || exist_check == :wait_for_enabled
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 "<- `#{inspect}##{caller}` has been completed"
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
- return unless %w[input button].include?(element.tag_name.downcase)
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 element.tag_name.downcase == "input" && !Watir::Button::VALID_TYPES.include?(element.attribute(:type).downcase)
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
- return filter_elements_by_locator(found, tag_name: tag_name, filter: filter).compact
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
- visible = selector.delete(:visible)
74
- visible_text = selector.delete(:visible_text)
75
- tag_name = selector[:tag_name].is_a?(::Symbol) ? selector[:tag_name].to_s : selector[:tag_name]
76
- validation_required = (selector.key?(:css) || selector.key?(:xpath)) && tag_name
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
- how, what = selector_builder.build(selector)
85
+ filter_selector = delete_filters_from(selector)
84
86
 
85
- needs_filtering = idx && idx != 0 || !visible.nil? || !visible_text.nil? || validation_required || filter == :all
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
- matching = matching_elements(how, what, selector)
89
- return filter_elements_by_locator(matching, visible, visible_text, idx, tag_name: tag_name, filter: filter)
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
- wd_find_by_regexp_selector(selector, :first)
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 all_elements
127
- locate_elements(:xpath, ".//*")
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 wd_find_by_regexp_selector(selector, filter)
131
- query_scope = ensure_scope_context
132
- rx_selector = delete_regexps_from(selector)
144
+ def delete_filters_from(selector)
145
+ filter_selector = {}
133
146
 
134
- if rx_selector.key?(:label) && selector_builder.should_use_label_element?
135
- label = label_from_text(rx_selector.delete(:label)) || return
136
- if (id = label.attribute('for'))
137
- selector[:id] = id
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
- how, what = selector_builder.build(selector)
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
- unless how
146
- raise Error, "internal error: unable to build Selenium selector from #{selector.inspect}"
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 how == :xpath && can_convert_regexp_to_contains?
150
- rx_selector.each do |key, value|
151
- next if key == :tag_name || key == :text
166
+ if selector[:index] && !selector[:adjacent]
167
+ idx = selector.delete(:index)
152
168
 
153
- predicates = regexp_selector_to_predicates(key, value)
154
- what = "(#{what})[#{predicates.join(' and ')}]" unless predicates.empty?
155
- end
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
- elements = locate_elements(how, what, query_scope)
159
- filter_elements_by_regex(elements, rx_selector, filter)
174
+ filter_selector
160
175
  end
161
176
 
162
- def filter_elements_by_locator(elements, visible = nil, visible_text = nil, idx = nil, tag_name: nil, filter: :first)
163
- elements.select! { |el| visible == el.displayed? } unless visible.nil?
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
- def filter_elements_by_regex(elements, selector, filter)
170
- method = filter == :first ? :find : :select
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
- def delete_regexps_from(selector)
175
- rx_selector = {}
176
-
177
- selector.dup.each do |how, what|
178
- next unless what.is_a?(Regexp)
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
- what === fetch_value(element, how)
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?(@query_scope.tag_name.downcase)
11
+ unless %w[tbody tfoot thead].include?(tag_name)
11
12
  expressions += %w[./tbody/tr ./thead/tr ./tfoot/tr]
12
13
  end
13
14
 
@@ -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
- it "sends keys to a frame" do
210
- browser.goto WatirSpec.url_for "frames.html"
211
- tf = browser.frame.text_field(id: "senderElement")
212
- tf.clear
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
- browser.frame.send_keys "hello"
215
+ browser.frame.send_keys "hello"
215
216
 
216
- expect(tf.value).to eq "hello"
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
- expect_all(:xpath, ".//div[@id='baz']").ordered.and_return(div_elements)
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')
@@ -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
- wd_element = browser.div(id: "foo").wd
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(browser.div(:id, 'foo')).to_not be_present
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 "relocates element from a collection when it becomes stale" do
58
- watir_element = browser.divs(id: "text").first
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(watir_element).to exist
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 an element that goes stale during execution' do
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
- watir_element = browser.div(id: "text")
76
+ element = browser.div(id: "text").tap(&:exists?)
78
77
 
79
- # simulate element going stale after assert_exists and before action taken, but not when block retried
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 { watir_element.text }.to_not raise_error
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
- wd_element = browser.text_field(id: "new_user_email").wd
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 { browser.text_field(css: '#new_user_email').visible? }.to raise_unknown_object_exception
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
@@ -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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'watir'
5
- s.version = '6.10.3'
5
+ s.version = '6.11.0.beta1'
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.authors = ['Alex Rodionov', 'Titus Fortner']
8
8
  s.email = ['p0deje@gmail.com', 'titusfortner@gmail.com']
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.10.3
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-01-26 00:00:00.000000000 Z
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: '0'
525
+ version: 1.3.1
526
526
  requirements: []
527
527
  rubyforge_project: watir
528
528
  rubygems_version: 2.7.3