watir 6.17.0 → 7.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.github/actions/enable-safari/action.yml +11 -0
  3. data/.github/actions/install-chrome/action.yml +12 -0
  4. data/.github/actions/setup-linux/action.yml +8 -0
  5. data/.github/workflows/tests.yml +104 -0
  6. data/.rubocop.yml +2 -7
  7. data/.rubocop_todo.yml +36 -0
  8. data/CHANGES.md +59 -0
  9. data/LICENSE +2 -2
  10. data/README.md +6 -10
  11. data/Rakefile +2 -2
  12. data/lib/watir.rb +4 -45
  13. data/lib/watir/adjacent.rb +1 -1
  14. data/lib/watir/alert.rb +4 -8
  15. data/lib/watir/attribute_helper.rb +2 -0
  16. data/lib/watir/browser.rb +20 -6
  17. data/lib/watir/capabilities.rb +79 -110
  18. data/lib/watir/cell_container.rb +4 -4
  19. data/lib/watir/container.rb +4 -26
  20. data/lib/watir/cookies.rb +2 -0
  21. data/lib/watir/element_collection.rb +21 -6
  22. data/lib/watir/elements/checkbox.rb +4 -4
  23. data/lib/watir/elements/date_field.rb +4 -4
  24. data/lib/watir/elements/date_time_field.rb +4 -4
  25. data/lib/watir/elements/element.rb +51 -59
  26. data/lib/watir/elements/file_field.rb +4 -4
  27. data/lib/watir/elements/font.rb +5 -4
  28. data/lib/watir/elements/hidden.rb +4 -4
  29. data/lib/watir/elements/html_elements.rb +444 -446
  30. data/lib/watir/elements/iframe.rb +6 -6
  31. data/lib/watir/elements/radio.rb +6 -6
  32. data/lib/watir/elements/select.rb +62 -90
  33. data/lib/watir/elements/svg_elements.rb +96 -96
  34. data/lib/watir/elements/text_field.rb +4 -4
  35. data/lib/watir/generator/base/generator.rb +4 -4
  36. data/lib/watir/generator/base/visitor.rb +0 -29
  37. data/lib/watir/generator/html/generator.rb +2 -1
  38. data/lib/watir/has_window.rb +22 -18
  39. data/lib/watir/http_client.rb +9 -0
  40. data/lib/watir/js_execution.rb +2 -2
  41. data/lib/watir/js_snippets.rb +2 -2
  42. data/lib/watir/locators.rb +6 -8
  43. data/lib/watir/locators/button/matcher.rb +0 -23
  44. data/lib/watir/locators/button/selector_builder/xpath.rb +4 -15
  45. data/lib/watir/locators/element/matcher.rb +4 -19
  46. data/lib/watir/locators/element/selector_builder.rb +3 -41
  47. data/lib/watir/locators/element/selector_builder/xpath.rb +34 -41
  48. data/lib/watir/locators/option/matcher.rb +24 -0
  49. data/lib/watir/locators/option/selector_builder.rb +8 -0
  50. data/lib/watir/locators/option/selector_builder/xpath.rb +37 -0
  51. data/lib/watir/logger.rb +4 -91
  52. data/lib/watir/radio_set.rb +5 -4
  53. data/lib/watir/row_container.rb +4 -4
  54. data/lib/watir/screenshot.rb +2 -8
  55. data/lib/watir/user_editable.rb +13 -2
  56. data/lib/watir/version.rb +1 -1
  57. data/lib/watir/wait.rb +6 -74
  58. data/lib/watir/wait/timer.rb +1 -1
  59. data/lib/watir/window.rb +24 -25
  60. data/lib/watir/window_collection.rb +79 -0
  61. data/lib/watirspec.rb +5 -2
  62. data/lib/watirspec/guards.rb +1 -1
  63. data/lib/watirspec/implementation.rb +7 -5
  64. data/lib/watirspec/runner.rb +2 -2
  65. data/lib/watirspec/server.rb +2 -2
  66. data/spec/spec_helper.rb +1 -21
  67. data/spec/unit/capabilities_spec.rb +504 -21
  68. data/spec/unit/match_elements/button_spec.rb +0 -13
  69. data/spec/unit/match_elements/element_spec.rb +55 -51
  70. data/spec/unit/match_elements/text_field_spec.rb +6 -6
  71. data/spec/unit/selector_builder/element_spec.rb +6 -23
  72. data/spec/unit/selector_builder/text_field_spec.rb +6 -7
  73. data/spec/unit/unit_helper.rb +2 -4
  74. data/spec/watirspec/after_hooks_spec.rb +23 -42
  75. data/spec/watirspec/alert_spec.rb +4 -21
  76. data/spec/watirspec/browser_spec.rb +186 -206
  77. data/spec/watirspec/cookies_spec.rb +47 -52
  78. data/spec/watirspec/drag_and_drop_spec.rb +5 -7
  79. data/spec/watirspec/elements/area_spec.rb +1 -5
  80. data/spec/watirspec/elements/button_spec.rb +4 -18
  81. data/spec/watirspec/elements/checkbox_spec.rb +10 -24
  82. data/spec/watirspec/elements/date_field_spec.rb +13 -16
  83. data/spec/watirspec/elements/date_time_field_spec.rb +14 -13
  84. data/spec/watirspec/elements/dd_spec.rb +3 -4
  85. data/spec/watirspec/elements/del_spec.rb +10 -12
  86. data/spec/watirspec/elements/div_spec.rb +45 -84
  87. data/spec/watirspec/elements/divs_spec.rb +2 -2
  88. data/spec/watirspec/elements/dl_spec.rb +4 -12
  89. data/spec/watirspec/elements/element_spec.rb +204 -181
  90. data/spec/watirspec/elements/elements_spec.rb +8 -9
  91. data/spec/watirspec/elements/filefield_spec.rb +5 -7
  92. data/spec/watirspec/elements/form_spec.rb +3 -5
  93. data/spec/watirspec/elements/forms_spec.rb +3 -5
  94. data/spec/watirspec/elements/frame_spec.rb +17 -22
  95. data/spec/watirspec/elements/iframe_spec.rb +25 -33
  96. data/spec/watirspec/elements/ins_spec.rb +10 -12
  97. data/spec/watirspec/elements/link_spec.rb +23 -23
  98. data/spec/watirspec/elements/links_spec.rb +8 -9
  99. data/spec/watirspec/elements/radio_spec.rb +11 -14
  100. data/spec/watirspec/elements/select_list_spec.rb +358 -209
  101. data/spec/watirspec/elements/span_spec.rb +12 -14
  102. data/spec/watirspec/elements/spans_spec.rb +1 -1
  103. data/spec/watirspec/elements/strong_spec.rb +1 -1
  104. data/spec/watirspec/elements/table_nesting_spec.rb +31 -34
  105. data/spec/watirspec/elements/table_spec.rb +11 -13
  106. data/spec/watirspec/elements/tbody_spec.rb +10 -12
  107. data/spec/watirspec/elements/td_spec.rb +4 -6
  108. data/spec/watirspec/elements/text_field_spec.rb +10 -12
  109. data/spec/watirspec/elements/tr_spec.rb +5 -7
  110. data/spec/watirspec/html/non_control_elements.html +8 -3
  111. data/spec/watirspec/html/special_chars.html +3 -0
  112. data/spec/watirspec/html/wait.html +5 -5
  113. data/spec/watirspec/html/window_switching.html +10 -0
  114. data/spec/watirspec/special_chars_spec.rb +10 -0
  115. data/spec/watirspec/support/rspec_matchers.rb +11 -24
  116. data/spec/watirspec/user_editable_spec.rb +26 -28
  117. data/spec/watirspec/wait_spec.rb +154 -201
  118. data/spec/watirspec/window_switching_spec.rb +359 -270
  119. data/spec/watirspec_helper.rb +52 -49
  120. data/support/doctest_helper.rb +0 -2
  121. data/watir.gemspec +4 -5
  122. metadata +30 -39
  123. data/.travis.yml +0 -87
  124. data/appveyor.yml +0 -13
  125. data/lib/watir/legacy_wait.rb +0 -123
  126. data/spec/implementation_spec.rb +0 -24
  127. data/spec/unit/container_spec.rb +0 -35
  128. data/spec/unit/logger_spec.rb +0 -81
  129. data/spec/watirspec/relaxed_locate_spec.rb +0 -109
@@ -0,0 +1,24 @@
1
+ module Watir
2
+ module Locators
3
+ class Option
4
+ class Matcher < Element::Matcher
5
+ def fetch_value(element, how)
6
+ case how
7
+ when :any
8
+ [element.attribute(:value),
9
+ execute_js(:getTextContent, element).gsub(/\s+/, ' ').strip,
10
+ element.attribute(:label)]
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def matches_values?(found, expected)
17
+ return super unless found.is_a?(Array)
18
+
19
+ found.find { |possible| matches_values?(possible, expected) }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Watir
2
+ module Locators
3
+ class Option
4
+ class SelectorBuilder < Element::SelectorBuilder
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,37 @@
1
+ module Watir
2
+ module Locators
3
+ class Option
4
+ class SelectorBuilder
5
+ class XPath < Element::SelectorBuilder::XPath
6
+ private
7
+
8
+ def attribute_string
9
+ result = if @selector.key?(:any)
10
+ to_match = @selector.delete :any
11
+ value = process_attribute(:value, to_match)
12
+ text = process_attribute(:text, to_match)
13
+ label = process_attribute(:label, to_match)
14
+ "[#{value} or #{text} or #{label}]"
15
+ else
16
+ ''
17
+ end
18
+
19
+ attributes = @selector.keys.map { |key|
20
+ process_attribute(key, @selector.delete(key))
21
+ }.flatten.compact
22
+ attribute_values = attributes.empty? ? '' : "[#{attributes.join(' and ')}]"
23
+ "#{result}#{attribute_values}"
24
+ end
25
+
26
+ def add_to_matching(key, regexp, results = nil)
27
+ return unless results.nil? || requires_matching?(results, regexp)
28
+
29
+ return super unless %i[value text label].include? key
30
+
31
+ @built[:any] = regexp
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/watir/logger.rb CHANGED
@@ -2,7 +2,7 @@ require 'forwardable'
2
2
  require 'logger'
3
3
 
4
4
  # Code adapted from Selenium Implementation
5
- # https://github.com/SeleniumHQ/selenium/blob/master/rb/lib/selenium/webdriver/common/logger.rb
5
+ # https://github.com/SeleniumHQ/selenium/blob/trunk/rb/lib/selenium/webdriver/common/logger.rb
6
6
 
7
7
  module Watir
8
8
  #
@@ -16,100 +16,13 @@ module Watir
16
16
  # Watir.logger.info('This is info message')
17
17
  # Watir.logger.warn('This is warning message')
18
18
  #
19
- class Logger
20
- extend Forwardable
21
- include ::Logger::Severity
22
-
23
- def_delegators :@logger, :debug, :debug?,
24
- :info, :info?,
25
- :warn?,
26
- :error, :error?,
27
- :fatal, :fatal?,
28
- :level
29
-
30
- def initialize(progname = 'Watir')
31
- @logger = create_logger($stdout)
32
- @logger.progname = progname
33
- @ignored = []
34
- end
35
-
36
- def ignore(ids)
37
- @ignored.concat Array(ids).map(&:to_s)
38
- end
39
-
40
- def output=(io)
41
- @logger.reopen(io)
42
- end
43
-
44
- #
45
- # Only log a warn message if it is not set to be ignored.
46
- #
47
- def warn(message, ids: [], &block)
48
- msg = ids.empty? ? '' : "[#{ids.map!(&:to_s).map(&:inspect).join(', ')}] "
49
- msg += message
50
- @logger.warn(msg, &block) unless (@ignored & ids).any?
51
- end
52
-
53
- #
54
- # For Ruby < 2.4 compatibility
55
- # Based on https://github.com/ruby/ruby/blob/ruby_2_3/lib/logger.rb#L250
56
- #
57
-
58
- def level=(severity)
59
- if severity.is_a?(Integer)
60
- @logger.level = severity
61
- else
62
- levels = %w[debug info warn error fatal unknown]
63
- raise ArgumentError, "invalid log level: #{severity}" unless levels.include? severity.to_s.downcase
64
-
65
- @logger.level = severity.to_s.upcase
66
- end
67
- end
68
-
69
- #
70
- # Returns IO object used by logger internally.
71
- #
72
- # Normally, we would have never needed it, but we want to
73
- # use it as IO object for all child processes to ensure their
74
- # output is redirected there.
75
- #
76
- # It is only used in debug level, in other cases output is suppressed.
77
- #
78
- # @api private
79
- #
80
- def io
81
- @logger.instance_variable_get(:@logdev).instance_variable_get(:@dev)
82
- end
83
-
84
- #
85
- # Marks code as deprecated with replacement.
86
- #
87
- # @param [String] old
88
- # @param [String] new
89
- #
90
- def deprecate(old, new, reference: '', ids: [])
91
- return if @ignored.include?('deprecations') || (@ignored & ids.map!(&:to_s)).any?
92
-
93
- msg = ids.empty? ? '' : "[#{ids.map(&:inspect).join(', ')}] "
94
- ref_msg = reference.empty? ? '.' : "; see explanation for this deprecation: #{reference}."
95
- warn "[DEPRECATION] #{msg}#{old} is deprecated. Use #{new} instead#{ref_msg}"
19
+ class Logger < Selenium::WebDriver::Logger
20
+ def initialize
21
+ super('Watir')
96
22
  end
97
23
 
98
24
  def selenium=(val)
99
25
  Selenium::WebDriver.logger.level = val
100
26
  end
101
-
102
- private
103
-
104
- def create_logger(output)
105
- logger = ::Logger.new(output)
106
- logger.progname = 'Watir'
107
- logger.level = ($DEBUG ? DEBUG : WARN)
108
- logger.formatter = proc do |severity, time, progname, msg|
109
- "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
110
- end
111
-
112
- logger
113
- end
114
27
  end
115
28
  end
@@ -4,7 +4,7 @@ module Watir
4
4
  include Exception
5
5
  include Enumerable
6
6
 
7
- delegate %i[exists? present? visible? browser] => :source
7
+ delegate %i[exist? exists? present? visible? browser] => :source
8
8
 
9
9
  attr_reader :source, :frame
10
10
 
@@ -139,6 +139,7 @@ module Watir
139
139
  end
140
140
  raise UnknownObjectException, "Unable to locate radio matching #{str_or_rx.inspect}"
141
141
  end
142
+ alias set select
142
143
 
143
144
  #
144
145
  # Returns true if any of the radio button label matches the given value.
@@ -201,7 +202,7 @@ module Watir
201
202
  end
202
203
  alias eql? ==
203
204
 
204
- # Ruby 2.4+ complains about using #delegate to do this
205
+ # Delegating to Private Methods
205
206
  %i[assert_exists element_call].each do |method|
206
207
  define_method(method) do |*args, &blk|
207
208
  source.send(method, *args, &blk)
@@ -218,8 +219,8 @@ module Watir
218
219
  end # RadioSet
219
220
 
220
221
  module Container
221
- def radio_set(*args)
222
- RadioSet.new(self, extract_selector(args).merge(tag_name: 'input', type: 'radio'))
222
+ def radio_set(opts = {})
223
+ RadioSet.new(self, opts.merge(tag_name: 'input', type: 'radio'))
223
224
  end
224
225
 
225
226
  Watir.tag_to_class[:radio_set] = RadioSet
@@ -4,16 +4,16 @@ module Watir
4
4
  # Returns table row.
5
5
  #
6
6
 
7
- def row(*args)
8
- Row.new(self, extract_selector(args))
7
+ def row(opts)
8
+ Row.new(self, opts)
9
9
  end
10
10
 
11
11
  #
12
12
  # Returns table rows collection.
13
13
  #
14
14
 
15
- def rows(*args)
16
- RowCollection.new(self, extract_selector(args))
15
+ def rows(opts = {})
16
+ RowCollection.new(self, opts)
17
17
  end
18
18
 
19
19
  #
@@ -1,14 +1,8 @@
1
1
  module Watir
2
2
  class Screenshot
3
3
  def initialize(browser)
4
- if browser.is_a? Selenium::WebDriver::Driver
5
- msg = 'Initializing `Watir::Screenshot` with a `Selenium::Driver` instance', 'a `Watir::Browser` instance'
6
- Watir.logger.deprecate msg, ids: [:screenshot_driver]
7
- @driver = browser
8
- else
9
- @browser = browser
10
- @driver = browser.wd
11
- end
4
+ @browser = browser
5
+ @driver = browser.wd
12
6
  end
13
7
 
14
8
  #
@@ -14,9 +14,20 @@ module Watir
14
14
  end
15
15
  alias value= set
16
16
 
17
+ #
18
+ # Returns true if element is user_editable because it has a content_editable attribute set
19
+ #
20
+ # @return [Boolean]
21
+ #
22
+
23
+ def content_editable
24
+ defined?(@content_editable) && content_editable?
25
+ end
26
+
17
27
  #
18
28
  # Uses JavaScript to enter most of the given value.
19
29
  # Selenium is used to enter the first and last characters
30
+ # This might provide a performance improvement when entering a lot of text on a local machine
20
31
  #
21
32
  # @param [String, Symbol] args
22
33
  #
@@ -27,7 +38,7 @@ module Watir
27
38
 
28
39
  input_value = args.join
29
40
  set input_value[0]
30
- return content_editable_set!(*args) if @content_editable
41
+ return content_editable_set!(*args) if content_editable
31
42
 
32
43
  element_call { execute_js(:setValue, @element, input_value[0..-2]) }
33
44
  append(input_value[-1])
@@ -43,7 +54,7 @@ module Watir
43
54
  #
44
55
 
45
56
  def append(*args)
46
- raise NotImplementedError, '#append method is not supported with contenteditable element' if @content_editable
57
+ raise NotImplementedError, '#append method is not supported with contenteditable element' if content_editable
47
58
 
48
59
  send_keys(*args)
49
60
  end
data/lib/watir/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Watir
2
- VERSION = '6.17.0'.freeze
2
+ VERSION = '7.0.0.beta2'.freeze
3
3
  end
data/lib/watir/wait.rb CHANGED
@@ -32,12 +32,7 @@ module Watir
32
32
  # @raise [TimeoutError] if timeout is exceeded
33
33
  #
34
34
 
35
- def until(depr_timeout = nil, depr_message = nil, timeout: nil, message: nil, interval: nil, object: nil)
36
- if depr_message || depr_timeout
37
- Watir.logger.deprecate 'Using arguments for Wait#until', 'keywords', ids: %i[until timeout_arguments]
38
- timeout = depr_timeout
39
- message = depr_message
40
- end
35
+ def until(timeout: nil, message: nil, interval: nil, object: nil)
41
36
  timeout ||= Watir.default_timeout
42
37
  run_with_timer(timeout, interval) do
43
38
  result = yield(object)
@@ -58,12 +53,7 @@ module Watir
58
53
  # @raise [TimeoutError] if timeout is exceeded
59
54
  #
60
55
 
61
- def while(depr_timeout = nil, depr_message = nil, timeout: nil, message: nil, interval: nil, object: nil)
62
- if depr_message || depr_timeout
63
- Watir.logger.deprecate 'Using arguments for Wait#while', 'keywords', ids: %i[while timeout_arguments]
64
- timeout = depr_timeout
65
- message = depr_message
66
- end
56
+ def while(timeout: nil, message: nil, interval: nil, object: nil)
67
57
  timeout ||= Watir.default_timeout
68
58
  run_with_timer(timeout, interval) { return unless yield(object) }
69
59
  raise TimeoutError, message_for(timeout, object, message)
@@ -111,12 +101,7 @@ module Watir
111
101
  # @param [String] message error message for when times out
112
102
  #
113
103
 
114
- def wait_until(depr_timeout = nil, depr_message = nil, timeout: nil, message: nil, interval: nil, **opt, &blk)
115
- if depr_message || depr_timeout
116
- Watir.logger.deprecate 'Using arguments for #wait_until', 'keywords', ids: [:timeout_arguments]
117
- timeout = depr_timeout
118
- message = depr_message
119
- end
104
+ def wait_until(timeout: nil, message: nil, interval: nil, **opt, &blk)
120
105
  message ||= proc { |obj| "waiting for true condition on #{obj.inspect}" }
121
106
 
122
107
  # TODO: Consider throwing argument error for mixing block & options
@@ -142,12 +127,7 @@ module Watir
142
127
  # @param [String] message error message for when times out
143
128
  #
144
129
 
145
- def wait_while(depr_timeout = nil, depr_message = nil, timeout: nil, message: nil, interval: nil, **opt, &blk)
146
- if depr_message || depr_timeout
147
- Watir.logger.deprecate 'Using arguments for #wait_while', 'keywords', ids: [:timeout_arguments]
148
- timeout = depr_timeout
149
- message = depr_message
150
- end
130
+ def wait_while(timeout: nil, message: nil, interval: nil, **opt, &blk)
151
131
  message ||= proc { |obj| "waiting for false condition on #{obj.inspect}" }
152
132
 
153
133
  # TODO: Consider throwing argument error for mixing block & options
@@ -158,56 +138,6 @@ module Watir
158
138
  self
159
139
  end
160
140
 
161
- #
162
- # Waits until the element is present.
163
- # Element is always relocated, so this can be used in the case of an element going away and returning
164
- #
165
- # @example
166
- # browser.text_field(name: "new_user_first_name").wait_until_present
167
- #
168
- # @param [Integer] timeout seconds to wait before timing out
169
- # @param [Float] interval seconds to wait before each try
170
- # @param [String] message error message for when times out
171
- #
172
- # @see Watir::Wait
173
- # @see Watir::Element#present?
174
- #
175
-
176
- def wait_until_present(depr_timeout = nil, timeout: nil, interval: nil, message: nil)
177
- timeout = depr_timeout if depr_timeout
178
- Watir.logger.deprecate "#{self.class}#wait_until_present",
179
- "#{self.class}#wait_until(&:present?)",
180
- ids: [:wait_until_present]
181
-
182
- message ||= proc { |obj| "waiting for #{obj.inspect} to become present" }
183
- wait_until(timeout: timeout, interval: interval, message: message, element_reset: true, &:present?)
184
- end
185
-
186
- #
187
- # Waits while the element is present.
188
- # Element is always relocated, so this can be used in the case of the element changing attributes
189
- #
190
- # @example
191
- # browser.text_field(name: "abrakadbra").wait_while_present
192
- #
193
- # @param [Integer] timeout seconds to wait before timing out
194
- # @param [Float] interval seconds to wait before each try
195
- # @param [String] message error message for when times out
196
- #
197
- # @see Watir::Wait
198
- # @see Watir::Element#present?
199
- #
200
-
201
- def wait_while_present(depr_timeout = nil, timeout: nil, interval: nil, message: nil)
202
- timeout = depr_timeout if depr_timeout
203
- Watir.logger.deprecate "#{self.class}#wait_while_present",
204
- "#{self.class}#wait_while(&:present?)",
205
- ids: [:wait_while_present]
206
-
207
- message ||= proc { |obj| "waiting for #{obj.inspect} not to be present" }
208
- wait_while(timeout: timeout, interval: interval, message: message, element_reset: true, &:present?)
209
- end
210
-
211
141
  private
212
142
 
213
143
  def create_proc(opt)
@@ -229,6 +159,8 @@ module Watir
229
159
  case expected
230
160
  when Regexp
231
161
  expected =~ actual
162
+ when Numeric
163
+ expected == actual
232
164
  else
233
165
  expected.to_s == actual
234
166
  end
@@ -2,7 +2,7 @@ module Watir
2
2
  module Wait
3
3
  class Timer
4
4
  def initialize(timeout: nil)
5
- @end_time = current_time + timeout if timeout
5
+ @end_time = timeout ? current_time + timeout : nil
6
6
  @remaining_time = @end_time - current_time if @end_time
7
7
  end
8
8
 
data/lib/watir/window.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  module Watir
2
2
  class Window
3
- include EventuallyPresent
4
3
  include Waitable
5
4
  include Exception
6
5
 
7
6
  attr_reader :browser
8
7
 
9
- def initialize(browser, selector)
8
+ def initialize(browser, selector = {})
10
9
  @browser = browser
11
10
  @driver = browser.driver
12
11
  @selector = selector
@@ -16,7 +15,7 @@ module Watir
16
15
  elsif selector.key? :handle
17
16
  @handle = selector.delete :handle
18
17
  else
19
- return if selector.keys.all? { |k| %i[title url index].include? k }
18
+ return if selector.keys.all? { |k| %i[title url element].include? k }
20
19
 
21
20
  raise ArgumentError, "invalid window selector: #{selector_string}"
22
21
  end
@@ -113,7 +112,7 @@ module Watir
113
112
  # Returns true if two windows are equal.
114
113
  #
115
114
  # @example
116
- # browser.window(index: 0) == browser.window(index: 1)
115
+ # browser.window(title: /window_switching/) == browser.window(/closeable/)
117
116
  # #=> false
118
117
  #
119
118
  # @param [Window] other
@@ -180,18 +179,27 @@ module Watir
180
179
  # end
181
180
  #
182
181
 
183
- def use(&blk)
184
- @browser.original_window ||= current_window
182
+ def use
185
183
  wait_for_exists
186
- @driver.switch_to.window(handle, &blk)
184
+ cache_current = current_window
185
+ @browser.original_window ||= cache_current
186
+ restore_to = unless cache_current == handle
187
+ @driver.switch_to.window(handle)
188
+ cache_current
189
+ end
190
+ if block_given?
191
+ begin
192
+ yield
193
+ ensure
194
+ @driver.switch_to.window(restore_to) if restore_to
195
+ end
196
+ end
187
197
  self
188
198
  end
189
199
 
190
200
  #
191
201
  # @api private
192
202
  #
193
- # Referenced in EventuallyPresent
194
- #
195
203
 
196
204
  def selector_string
197
205
  @selector.inspect
@@ -204,17 +212,11 @@ module Watir
204
212
  private
205
213
 
206
214
  def locate
207
- if @selector.empty?
208
- nil
209
- elsif @selector.key?(:index)
210
- @driver.window_handles[Integer(@selector[:index])]
211
- else
212
- @driver.window_handles.find { |wh| matches?(wh) }
213
- end
215
+ @selector.empty? ? nil : @driver.window_handles.find { |wh| matches?(wh) }
214
216
  end
215
217
 
216
218
  def assert_exists
217
- return if @driver.window_handles.include?(handle)
219
+ return if !handle.nil? && @driver.window_handles.include?(handle)
218
220
 
219
221
  raise(NoMatchingWindowFoundException, selector_string)
220
222
  end
@@ -230,8 +232,9 @@ module Watir
230
232
  @driver.switch_to.window(handle) do
231
233
  matches_title = @selector[:title].nil? || @browser.title =~ /#{@selector[:title]}/
232
234
  matches_url = @selector[:url].nil? || @browser.url =~ /#{@selector[:url]}/
235
+ matches_element = @selector[:element].nil? || @selector[:element].exists?
233
236
 
234
- matches_title && matches_url
237
+ matches_title && matches_url && matches_element
235
238
  end
236
239
  rescue Selenium::WebDriver::Error::NoSuchWindowError
237
240
  # the window may disappear while we're iterating.
@@ -239,13 +242,9 @@ module Watir
239
242
  end
240
243
 
241
244
  def wait_for_exists
242
- return assert_exists unless Watir.relaxed_locate?
243
-
244
- begin
245
- wait_until(&:exists?)
246
- rescue Wait::TimeoutError
247
- raise NoMatchingWindowFoundException, selector_string
248
- end
245
+ wait_until(&:exists?)
246
+ rescue Wait::TimeoutError
247
+ raise NoMatchingWindowFoundException, selector_string
249
248
  end
250
249
  end # Window
251
250
  end # Watir