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
@@ -46,6 +46,8 @@ module Watir
46
46
  # @return [$1] value of $3 property
47
47
  #
48
48
  def attribute(type, method, attr)
49
+ return if method_defined?(method)
50
+
49
51
  typed_attributes[type] << [method, attr]
50
52
  define_attribute(type, method, attr)
51
53
  end
data/lib/watir/browser.rb CHANGED
@@ -55,16 +55,21 @@ module Watir
55
55
  @default_context = true
56
56
  end
57
57
 
58
+ # rubocop:disable Metrics/AbcSize
59
+ # TODO: w3c default behavior does not like checking if alert exists
58
60
  def inspect
59
61
  if alert.exists?
60
62
  format('#<%s:0x%x alert=true>', self.class, hash * 2)
61
63
  else
62
64
  format('#<%s:0x%x url=%s title=%s>', self.class, hash * 2, url.inspect, title.inspect)
63
65
  end
66
+ rescue Selenium::WebDriver::Error::NoSuchWindowError
67
+ format('#<%s:0x%x closed=%s>', self.class, hash * 2, closed?)
64
68
  rescue Errno::ECONNREFUSED
65
69
  format('#<%s:0x%x closed=true>', self.class, hash * 2)
66
70
  end
67
71
  alias selector_string inspect
72
+ # rubocop:enable Metrics/AbcSize
68
73
 
69
74
  #
70
75
  # Returns URL of current page.
@@ -101,13 +106,23 @@ module Watir
101
106
  #
102
107
 
103
108
  def close
104
- return if @closed
109
+ return if closed?
105
110
 
106
111
  @driver.quit
107
112
  @closed = true
108
113
  end
109
114
  alias quit close
110
115
 
116
+ #
117
+ # Returns true if browser is closed and false otherwise.
118
+ #
119
+ # @return [Boolean]
120
+ #
121
+
122
+ def closed?
123
+ @closed
124
+ end
125
+
111
126
  #
112
127
  # Handles cookies.
113
128
  #
@@ -213,11 +228,11 @@ module Watir
213
228
  # @param args Arguments will be available in the given script in the 'arguments' pseudo-array
214
229
  #
215
230
 
216
- def execute_script(script, *args)
231
+ def execute_script(script, *args, function_name: nil)
217
232
  args.map! do |e|
218
233
  e.is_a?(Element) ? e.wait_until(&:exists?).wd : e
219
234
  end
220
-
235
+ Watir.logger.info "Executing Script on Browser: #{function_name}" if function_name
221
236
  wrap_elements_in(self, @driver.execute_script(script, *args))
222
237
  end
223
238
 
@@ -252,12 +267,12 @@ module Watir
252
267
  #
253
268
 
254
269
  def exist?
255
- !@closed && window.present?
270
+ !closed? && window.present?
256
271
  end
257
272
  alias exists? exist?
258
273
 
259
274
  def locate
260
- raise Error, 'browser was closed' if @closed
275
+ raise Error, 'browser was closed' if closed?
261
276
 
262
277
  ensure_context
263
278
  end
@@ -267,7 +282,6 @@ module Watir
267
282
 
268
283
  driver.switch_to.default_content
269
284
  @default_context = true
270
- after_hooks.run
271
285
  end
272
286
 
273
287
  def browser
@@ -2,19 +2,23 @@ module Watir
2
2
  class Capabilities
3
3
  attr_reader :options
4
4
 
5
- def initialize(browser, options = {})
5
+ def initialize(browser = nil, options = {})
6
+ if browser.is_a?(Hash)
7
+ options = browser
8
+ browser = nil
9
+ end
10
+
6
11
  @options = options.dup
7
12
  Watir.logger.info "Creating Browser instance of #{browser} with user provided options: #{@options.inspect}"
8
- @browser = if browser == :remote && @options.key?(:browser)
9
- @options.delete(:browser)
10
- elsif browser == :remote && @options.key?(:desired_capabilities)
11
- @options[:desired_capabilities].browser_name.to_sym
12
- else
13
- browser.to_sym
14
- end
15
- @selenium_browser = browser == :remote || options[:url] ? :remote : browser
16
-
17
- @selenium_opts = {}
13
+
14
+ if @options.key?(:capabilities) && @options.key?(:options)
15
+ raise(ArgumentError, ':capabilities and :options are not both allowed')
16
+ end
17
+ raise(ArgumentError, ':url and :service are not both allowed') if @options.key?(:service) && @options.key?(:url)
18
+
19
+ @browser = browser.nil? && infer_browser || browser
20
+
21
+ @selenium_browser = @options.key?(:url) ? :remote : @browser
18
22
  end
19
23
 
20
24
  def to_args
@@ -24,129 +28,94 @@ module Watir
24
28
  private
25
29
 
26
30
  def process_arguments
27
- url = @options.delete(:url)
28
- if url
29
- @selenium_opts[:url] = url
30
- elsif @options.key?(:service)
31
- @selenium_opts[:service] = options.delete(:service)
32
- end
33
-
34
- create_http_client
31
+ selenium_opts = {}
32
+ selenium_opts[:listener] = @options.delete(:listener) if @options.key?(:listener)
35
33
 
36
- @selenium_opts[:port] = @options.delete(:port) if @options.key?(:port)
37
- @selenium_opts[:driver_opts] = @options.delete(:driver_opts) if @options.key?(:driver_opts)
38
- @selenium_opts[:listener] = @options.delete(:listener) if @options.key?(:listener)
34
+ if @options.key?(:url)
35
+ selenium_opts[:url] = @options.delete(:url)
36
+ else
37
+ service = process_service
38
+ selenium_opts[:service] = service if service
39
+ end
39
40
 
40
- process_browser_options
41
- process_capabilities
42
- Watir.logger.info "Creating Browser instance with Watir processed options: #{@selenium_opts.inspect}"
41
+ selenium_opts[:http_client] = process_http_client
42
+ selenium_opts[:capabilities] = @options.delete(:capabilities) || process_browser_options
43
43
 
44
- @selenium_opts
44
+ Watir.logger.info "Creating Browser instance with Watir processed options: #{selenium_opts.inspect}"
45
+ selenium_opts
45
46
  end
46
47
 
47
- def create_http_client
48
- client_timeout = @options.delete(:client_timeout)
49
- open_timeout = @options.delete(:open_timeout)
50
- read_timeout = @options.delete(:read_timeout)
51
-
52
- http_client = @options.delete(:http_client)
53
-
54
- %i[open_timeout read_timeout client_timeout].each do |t|
55
- next if http_client.nil? || !respond_to?(t)
56
-
57
- msg = "You can pass #{t} value directly into Watir::Browser opt without needing to use :http_client"
58
- Watir.logger.warn msg, ids: %i[http_client use_capabilities]
48
+ def process_http_client
49
+ http_client = @options.delete(:http_client) || Watir::HttpClient.new
50
+
51
+ case http_client
52
+ when Hash
53
+ Watir::HttpClient.new(**http_client)
54
+ when Watir::HttpClient
55
+ http_client
56
+ when Selenium::WebDriver::Remote::Http::Common
57
+ Watir.logger.warn 'Check out the new Watir::HttpClient and let us know if there are missing features you need',
58
+ id: [:watir_client]
59
+ http_client
60
+ else
61
+ raise TypeError, ':http_client must be a Hash or a Selenium HTTP Client instance'
59
62
  end
60
-
61
- http_client ||= Selenium::WebDriver::Remote::Http::Default.new
62
-
63
- http_client.timeout = client_timeout if client_timeout
64
- http_client.open_timeout = open_timeout if open_timeout
65
- http_client.read_timeout = read_timeout if read_timeout
66
- @selenium_opts[:http_client] = http_client
67
63
  end
68
64
 
69
- # TODO: - this will get addressed with Capabilities Update
70
- # rubocop:disable Metrics/MethodLength
71
- # rubocop:disable Metrics/PerceivedComplexity:
72
- # rubocop:disable Metrics/CyclomaticComplexity::
73
65
  def process_browser_options
74
66
  browser_options = @options.delete(:options) || {}
75
67
 
76
- case @selenium_browser
77
- when :chrome
78
- if @options.key?(:args) || @options.key?(:switches)
79
- browser_options ||= {}
80
- browser_options[:args] = (@options.delete(:args) || @options.delete(:switches)).dup
81
- end
68
+ options = browser_options if browser_options.is_a? Selenium::WebDriver::Options
69
+ options ||= Selenium::WebDriver::Options.send(@browser, **browser_options)
70
+
71
+ browser_specific_options(options)
72
+ raise ArgumentError, "#{@options} are unrecognized arguments for Browser constructor" unless @options.empty?
73
+
74
+ options
75
+ end
76
+
77
+ def browser_specific_options(options)
78
+ case @browser
79
+ when :chrome, :edge, :microsoftedge
82
80
  if @options.delete(:headless)
83
- browser_options ||= {}
84
- browser_options[:args] ||= []
85
- browser_options[:args] += ['--headless', '--disable-gpu']
81
+ options.args << '--headless'
82
+ options.args << '--disable-gpu'
83
+ options.args << '--no-sandbox'
86
84
  end
87
- @selenium_opts[:options] = browser_options if browser_options.is_a? Selenium::WebDriver::Chrome::Options
88
- @selenium_opts[:options] ||= Selenium::WebDriver::Chrome::Options.new(**browser_options)
89
85
  when :firefox
90
- profile = @options.delete(:profile)
91
- if browser_options.is_a? Selenium::WebDriver::Firefox::Options
92
- @selenium_opts[:options] = browser_options
93
- if profile
94
- msg = 'Initializing Browser with both :profile and :option', ':profile as a key inside :option'
95
- Watir.logger.deprecate msg, ids: [:firefox_profile]
96
- end
97
- end
98
- if @options.delete(:headless)
99
- browser_options ||= {}
100
- browser_options[:args] ||= []
101
- browser_options[:args] += ['--headless']
102
- end
103
- @selenium_opts[:options] ||= Selenium::WebDriver::Firefox::Options.new(**browser_options)
104
- @selenium_opts[:options].profile = profile if profile
86
+ options.headless! if @options.delete(:headless)
105
87
  when :safari
106
88
  Selenium::WebDriver::Safari.technology_preview! if @options.delete(:technology_preview)
107
- when :remote
108
- if @browser == :chrome && @options.delete(:headless)
109
- args = @options.delete(:args) || @options.delete(:switches) || []
110
- @options['chromeOptions'] = {'args' => args + ['--headless', '--disable-gpu']}
111
- end
112
- if @browser == :firefox && @options.delete(:headless)
113
- args = @options.delete(:args) || @options.delete(:switches) || []
114
- @options[Selenium::WebDriver::Firefox::Options::KEY] = {'args' => args + ['--headless']}
115
- end
116
- if @browser == :safari && @options.delete(:technology_preview)
117
- @options['safari.options'] = {'technologyPreview' => true}
118
- end
119
- when :ie
120
- if @options.key?(:args)
121
- browser_options ||= {}
122
- browser_options[:args] = @options.delete(:args).dup
123
- end
124
- unless browser_options.is_a? Selenium::WebDriver::IE::Options
125
- ie_caps = browser_options.select { |k| Selenium::WebDriver::IE::Options::CAPABILITIES.include?(k) }
126
- browser_options = Selenium::WebDriver::IE::Options.new(**browser_options)
127
- ie_caps.each { |k, v| browser_options.add_option(k, v) }
128
- end
129
- @selenium_opts[:options] = browser_options
130
89
  end
131
90
  end
132
91
 
133
- # rubocop:enable Metrics/MethodLength
134
- # rubocop:enable Metrics/PerceivedComplexity:
135
- # rubocop:enable Metrics/CyclomaticComplexity::
92
+ def process_service
93
+ service = @options.delete(:service)
136
94
 
137
- def process_capabilities
138
- caps = @options.delete(:desired_capabilities)
95
+ case service
96
+ when nil
97
+ nil
98
+ when Hash
99
+ return if service.empty?
139
100
 
140
- if caps
141
- msg = 'You can pass values directly into Watir::Browser opt without needing to use :desired_capabilities'
142
- Watir.logger.warn msg,
143
- ids: [:use_capabilities]
144
- @selenium_opts.merge!(@options)
101
+ Selenium::WebDriver::Service.send(@browser, **service)
102
+ when Selenium::WebDriver::Service
103
+ service
145
104
  else
146
- caps = Selenium::WebDriver::Remote::Capabilities.send @browser, @options
105
+ raise TypeError, "#{service} needs to be Selenium Service or Hash instance"
147
106
  end
107
+ end
148
108
 
149
- @selenium_opts[:desired_capabilities] = caps
109
+ def infer_browser
110
+ if @options.key?(:browser)
111
+ @options.delete(:browser)
112
+ elsif @options.key?(:capabilities)
113
+ @options[:capabilities].browser_name.tr(' ', '_').downcase.to_sym
114
+ elsif @options.key?(:options)
115
+ @options[:options].class.to_s.split('::')[-2].downcase.to_sym
116
+ else
117
+ :chrome
118
+ end
150
119
  end
151
120
  end
152
121
  end
@@ -6,8 +6,8 @@ module Watir
6
6
  # @return [Cell]
7
7
  #
8
8
 
9
- def cell(*args)
10
- Cell.new(self, extract_selector(args))
9
+ def cell(opts = {})
10
+ Cell.new(self, opts)
11
11
  end
12
12
 
13
13
  #
@@ -16,8 +16,8 @@ module Watir
16
16
  # @return [Cell]
17
17
  #
18
18
 
19
- def cells(*args)
20
- CellCollection.new(self, extract_selector(args))
19
+ def cells(opts = {})
20
+ CellCollection.new(self, opts)
21
21
  end
22
22
  end # CellContainer
23
23
  end # Watir
@@ -11,8 +11,8 @@ module Watir
11
11
  # @return [HTMLElement]
12
12
  #
13
13
 
14
- def element(*args)
15
- HTMLElement.new(self, extract_selector(args))
14
+ def element(opts = {})
15
+ HTMLElement.new(self, opts)
16
16
  end
17
17
 
18
18
  #
@@ -24,30 +24,8 @@ module Watir
24
24
  # @return [HTMLElementCollection]
25
25
  #
26
26
 
27
- def elements(*args)
28
- HTMLElementCollection.new(self, extract_selector(args))
29
- end
30
-
31
- #
32
- # @api private
33
- #
34
-
35
- def extract_selector(selector)
36
- case selector.size
37
- when 2
38
- msg = "Using ordered parameters to locate elements (:#{selector.first}, #{selector.last.inspect})"
39
- Watir.logger.deprecate msg,
40
- "{#{selector.first}: #{selector.last.inspect}}",
41
- ids: [:selector_parameters]
42
- return {selector[0] => selector[1]}
43
- when 1
44
- obj = selector.first
45
- return obj if obj.is_a? Hash
46
- when 0
47
- return {}
48
- end
49
-
50
- raise ArgumentError, "expected Hash, got #{selector.inspect}"
27
+ def elements(opts = {})
28
+ HTMLElementCollection.new(self, opts)
51
29
  end
52
30
  end # Container
53
31
  end # Watir
data/lib/watir/cookies.rb CHANGED
@@ -105,6 +105,8 @@ module Watir
105
105
  IO.write(file, to_a.to_yaml)
106
106
  end
107
107
 
108
+ #
109
+ # TODO: Use :permitted_classes keyword when minimum supported Ruby is 2.6
108
110
  #
109
111
  # Load cookies from file
110
112
  #
@@ -7,17 +7,19 @@ module Watir
7
7
  include Enumerable
8
8
  include Exception
9
9
  include JSSnippets
10
+ include Waitable
10
11
  include Locators::ClassHelpers
11
12
 
12
13
  def initialize(query_scope, selector)
13
14
  @query_scope = query_scope
14
15
  @selector = selector
16
+ @to_a = nil
15
17
 
16
18
  build unless @selector.key?(:element)
17
19
  end
18
20
 
19
21
  #
20
- # Yields each element in collection.
22
+ # Relocates elements then yields each element in resulting collection.
21
23
  #
22
24
  # @example
23
25
  # divs = browser.divs(class: 'kls')
@@ -29,6 +31,7 @@ module Watir
29
31
  #
30
32
 
31
33
  def each(&blk)
34
+ reset!
32
35
  to_a.each(&blk)
33
36
  end
34
37
 
@@ -37,6 +40,9 @@ module Watir
37
40
 
38
41
  alias empty? none?
39
42
 
43
+ alias exist? any?
44
+ alias exists? any?
45
+
40
46
  def build
41
47
  selector_builder.build(@selector.dup)
42
48
  end
@@ -145,14 +151,19 @@ module Watir
145
151
  alias eql? ==
146
152
 
147
153
  #
148
- # Creates a Collection containing elements of two collections.
154
+ # Removes cache of previously located elements in the collection.
149
155
  #
150
156
  # @example
151
157
  # options = browser.select_list(name: "new_user_languages").options
152
- # (options + browser.select_list(id: "new_user_role").options).size
153
- # #=> 8
158
+ # options.reset!
159
+ # options[0]
160
+ # #=> nil
154
161
  #
155
162
 
163
+ def reset!
164
+ @to_a = nil
165
+ end
166
+
156
167
  private
157
168
 
158
169
  def elements
@@ -182,7 +193,11 @@ module Watir
182
193
  end
183
194
 
184
195
  def ensure_context
185
- @query_scope.locate if @query_scope.is_a?(Browser) || @query_scope.located? && @query_scope.stale?
196
+ if @query_scope.is_a?(Browser) || !@query_scope.located? && @query_scope.is_a?(IFrame)
197
+ @query_scope.browser.locate
198
+ elsif @query_scope.located? && @query_scope.stale?
199
+ @query_scope.locate
200
+ end
186
201
  @query_scope.switch_to! if @query_scope.is_a?(IFrame)
187
202
  end
188
203
 
@@ -191,7 +206,7 @@ module Watir
191
206
  end
192
207
 
193
208
  def element_class
194
- Kernel.const_get(self.class.name.sub(/Collection$/, ''))
209
+ Watir.const_get(self.class.name.sub(/Collection$/, ''))
195
210
  end
196
211
 
197
212
  def construct_subtype(element, hash, tag_name)