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 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