selenium-webdriver 4.1.0 → 4.28.0

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.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +446 -1
  3. data/Gemfile +4 -0
  4. data/LICENSE +1 -1
  5. data/NOTICE +1 -1
  6. data/README.md +3 -3
  7. data/bin/linux/selenium-manager +0 -0
  8. data/bin/macos/selenium-manager +0 -0
  9. data/bin/windows/selenium-manager.exe +0 -0
  10. data/lib/selenium/server.rb +41 -43
  11. data/lib/selenium/webdriver/atoms/findElements.js +52 -50
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +6 -100
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +24 -96
  14. data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
  15. data/lib/selenium/webdriver/atoms.rb +5 -3
  16. data/lib/selenium/webdriver/bidi/browsing_context.rb +100 -0
  17. data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
  18. data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
  19. data/lib/selenium/webdriver/{common/driver_extensions/has_location.rb → bidi/log/filter_by.rb} +14 -11
  20. data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
  21. data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
  22. data/lib/selenium/webdriver/bidi/log_handler.rb +65 -0
  23. data/lib/selenium/webdriver/bidi/log_inspector.rb +147 -0
  24. data/lib/selenium/webdriver/bidi/network.rb +82 -0
  25. data/lib/selenium/webdriver/bidi/session.rb +51 -0
  26. data/lib/selenium/webdriver/bidi/struct.rb +42 -0
  27. data/lib/selenium/webdriver/bidi.rb +67 -0
  28. data/lib/selenium/webdriver/chrome/driver.rb +9 -29
  29. data/lib/selenium/webdriver/chrome/features.rb +9 -67
  30. data/lib/selenium/webdriver/chrome/options.rb +3 -223
  31. data/lib/selenium/webdriver/chrome/profile.rb +3 -83
  32. data/lib/selenium/webdriver/chrome/service.rb +5 -19
  33. data/lib/selenium/webdriver/chrome.rb +0 -16
  34. data/lib/selenium/webdriver/chromium/driver.rb +61 -0
  35. data/lib/selenium/webdriver/chromium/features.rb +99 -0
  36. data/lib/selenium/webdriver/chromium/options.rb +243 -0
  37. data/lib/selenium/webdriver/chromium/profile.rb +113 -0
  38. data/lib/selenium/webdriver/chromium.rb +29 -0
  39. data/lib/selenium/webdriver/common/action_builder.rb +60 -22
  40. data/lib/selenium/webdriver/common/child_process.rb +136 -0
  41. data/lib/selenium/webdriver/common/driver.rb +54 -89
  42. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
  43. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
  44. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
  45. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
  46. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
  47. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +10 -5
  48. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -1
  49. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
  50. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -4
  51. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
  52. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
  53. data/lib/selenium/webdriver/common/driver_extensions/has_fedcm_dialog.rb +55 -0
  54. data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
  55. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
  56. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +9 -3
  57. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
  58. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +8 -68
  59. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
  60. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -3
  61. data/lib/selenium/webdriver/common/driver_finder.rb +97 -0
  62. data/lib/selenium/webdriver/common/element.rb +8 -8
  63. data/lib/selenium/webdriver/common/error.rb +29 -4
  64. data/lib/selenium/webdriver/common/fedcm/account.rb +49 -0
  65. data/lib/selenium/webdriver/common/fedcm/dialog.rb +74 -0
  66. data/lib/selenium/webdriver/common/fedcm.rb +27 -0
  67. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
  68. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  69. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  70. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  71. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  72. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  73. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  74. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  75. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -71
  76. data/lib/selenium/webdriver/common/{driver_extensions/has_network_connection.rb → interactions/pointer_cancel.rb} +19 -11
  77. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  78. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  79. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  80. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  81. data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
  82. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  83. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  84. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +114 -0
  85. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  86. data/lib/selenium/webdriver/common/keys.rb +1 -0
  87. data/lib/selenium/webdriver/common/local_driver.rb +53 -0
  88. data/lib/selenium/webdriver/common/logger.rb +93 -28
  89. data/lib/selenium/webdriver/common/manager.rb +2 -29
  90. data/lib/selenium/webdriver/common/network.rb +64 -0
  91. data/lib/selenium/webdriver/common/options.rb +19 -19
  92. data/lib/selenium/webdriver/common/platform.rb +12 -52
  93. data/lib/selenium/webdriver/common/port_prober.rb +1 -1
  94. data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
  95. data/lib/selenium/webdriver/common/proxy.rb +4 -4
  96. data/lib/selenium/webdriver/common/script.rb +45 -0
  97. data/lib/selenium/webdriver/common/search_context.rb +10 -8
  98. data/lib/selenium/webdriver/common/selenium_manager.rb +104 -0
  99. data/lib/selenium/webdriver/common/service.rb +24 -26
  100. data/lib/selenium/webdriver/common/service_manager.rb +9 -15
  101. data/lib/selenium/webdriver/common/shadow_root.rb +2 -3
  102. data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
  103. data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
  104. data/lib/selenium/webdriver/common/takes_screenshot.rb +6 -5
  105. data/lib/selenium/webdriver/common/target_locator.rb +5 -5
  106. data/lib/selenium/webdriver/common/timeouts.rb +2 -2
  107. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
  108. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
  109. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  110. data/lib/selenium/webdriver/common/wait.rb +1 -1
  111. data/lib/selenium/webdriver/common/websocket_connection.rb +176 -0
  112. data/lib/selenium/webdriver/common/window.rb +6 -6
  113. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  114. data/lib/selenium/webdriver/common.rb +27 -5
  115. data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
  116. data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
  117. data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
  118. data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
  119. data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
  120. data/lib/selenium/webdriver/devtools/request.rb +1 -3
  121. data/lib/selenium/webdriver/devtools/response.rb +1 -3
  122. data/lib/selenium/webdriver/devtools.rb +17 -114
  123. data/lib/selenium/webdriver/edge/driver.rb +9 -3
  124. data/lib/selenium/webdriver/edge/features.rb +8 -4
  125. data/lib/selenium/webdriver/edge/options.rb +17 -5
  126. data/lib/selenium/webdriver/edge/profile.rb +2 -2
  127. data/lib/selenium/webdriver/edge/service.rb +8 -7
  128. data/lib/selenium/webdriver/edge.rb +0 -2
  129. data/lib/selenium/webdriver/firefox/driver.rb +9 -2
  130. data/lib/selenium/webdriver/firefox/features.rb +11 -7
  131. data/lib/selenium/webdriver/firefox/options.rb +10 -16
  132. data/lib/selenium/webdriver/firefox/profile.rb +21 -17
  133. data/lib/selenium/webdriver/firefox/profiles_ini.rb +1 -1
  134. data/lib/selenium/webdriver/firefox/service.rb +1 -18
  135. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  136. data/lib/selenium/webdriver/firefox.rb +1 -14
  137. data/lib/selenium/webdriver/ie/driver.rb +7 -1
  138. data/lib/selenium/webdriver/ie/features.rb +34 -0
  139. data/lib/selenium/webdriver/ie/options.rb +6 -4
  140. data/lib/selenium/webdriver/ie/service.rb +1 -22
  141. data/lib/selenium/webdriver/ie.rb +4 -17
  142. data/lib/selenium/webdriver/remote/bidi_bridge.rb +66 -0
  143. data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +22 -9
  144. data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
  145. data/lib/selenium/webdriver/remote/bridge.rb +158 -100
  146. data/lib/selenium/webdriver/remote/capabilities.rb +5 -55
  147. data/lib/selenium/webdriver/remote/driver.rb +35 -14
  148. data/lib/selenium/webdriver/remote/features.rb +75 -0
  149. data/lib/selenium/webdriver/remote/http/common.rb +26 -6
  150. data/lib/selenium/webdriver/remote/http/curb.rb +10 -6
  151. data/lib/selenium/webdriver/remote/http/default.rb +8 -14
  152. data/lib/selenium/webdriver/remote/response.rb +14 -22
  153. data/lib/selenium/webdriver/remote/server_error.rb +2 -2
  154. data/lib/selenium/webdriver/remote.rb +3 -2
  155. data/lib/selenium/webdriver/safari/driver.rb +7 -1
  156. data/lib/selenium/webdriver/safari/features.rb +5 -3
  157. data/lib/selenium/webdriver/safari/options.rb +5 -1
  158. data/lib/selenium/webdriver/safari/service.rb +10 -4
  159. data/lib/selenium/webdriver/safari.rb +1 -15
  160. data/lib/selenium/webdriver/support/color.rb +22 -22
  161. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  162. data/lib/selenium/webdriver/support/guards/guard.rb +14 -14
  163. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
  164. data/lib/selenium/webdriver/support/guards.rb +4 -4
  165. data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
  166. data/lib/selenium/webdriver/support/select.rb +3 -1
  167. data/lib/selenium/webdriver/version.rb +1 -1
  168. data/lib/selenium/webdriver.rb +7 -5
  169. data/selenium-webdriver.gemspec +22 -16
  170. metadata +137 -47
  171. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  172. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
  173. data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed to the Software Freedom Conservancy (SFC) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The SFC licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+
20
+ module Selenium
21
+ module WebDriver
22
+ module Remote
23
+ class Bridge
24
+ class LocatorConverter
25
+ ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
26
+ UNICODE_CODE_POINT = 30
27
+
28
+ #
29
+ # Converts a locator to a specification compatible one.
30
+ # @param [String, Symbol] how
31
+ # @param [String] what
32
+ #
33
+
34
+ def convert(how, what)
35
+ how = SearchContext.finders[how.to_sym] || how
36
+
37
+ case how
38
+ when 'class name'
39
+ how = 'css selector'
40
+ what = ".#{escape_css(what.to_s)}"
41
+ when 'id'
42
+ how = 'css selector'
43
+ what = "##{escape_css(what.to_s)}"
44
+ when 'name'
45
+ how = 'css selector'
46
+ what = "*[name='#{escape_css(what.to_s)}']"
47
+ end
48
+
49
+ if what.is_a?(Hash)
50
+ what = what.each_with_object({}) do |(h, w), hash|
51
+ h, w = convert(h.to_s, w)
52
+ hash[h] = w
53
+ end
54
+ end
55
+
56
+ [how, what]
57
+ end
58
+
59
+ private
60
+
61
+ #
62
+ # Escapes invalid characters in CSS selector.
63
+ # @see https://mathiasbynens.be/notes/css-escapes
64
+ #
65
+
66
+ def escape_css(string)
67
+ string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
68
+ string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
69
+
70
+ string
71
+ end
72
+ end # LocatorConverter
73
+ end # Bridge
74
+ end # Remote
75
+ end # WebDriver
76
+ end # Selenium
@@ -21,6 +21,9 @@ module Selenium
21
21
  module WebDriver
22
22
  module Remote
23
23
  class Bridge
24
+ autoload :COMMANDS, 'selenium/webdriver/remote/bridge/commands'
25
+ autoload :LocatorConverter, 'selenium/webdriver/remote/bridge/locator_converter'
26
+
24
27
  include Atoms
25
28
 
26
29
  PORT = 4444
@@ -28,10 +31,29 @@ module Selenium
28
31
  attr_accessor :http, :file_detector
29
32
  attr_reader :capabilities
30
33
 
34
+ class << self
35
+ attr_reader :extra_commands
36
+ attr_writer :element_class, :locator_converter
37
+
38
+ def add_command(name, verb, url, &)
39
+ @extra_commands ||= {}
40
+ @extra_commands[name] = [verb, url]
41
+ define_method(name, &)
42
+ end
43
+
44
+ def locator_converter
45
+ @locator_converter ||= LocatorConverter.new
46
+ end
47
+
48
+ def element_class
49
+ @element_class ||= Element
50
+ end
51
+ end
52
+
31
53
  #
32
54
  # Initializes the bridge with the given server URL
33
- # @param [String, URI] :url url for the remote server
34
- # @param [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
55
+ # @param [String, URI] url url for the remote server
56
+ # @param [Object] http_client an HTTP client instance that implements the same protocol as Http::Default
35
57
  # @api private
36
58
  #
37
59
 
@@ -42,6 +64,8 @@ module Selenium
42
64
  @http = http_client || Http::Default.new
43
65
  @http.server_url = uri
44
66
  @file_detector = nil
67
+
68
+ @locator_converter = self.class.locator_converter
45
69
  end
46
70
 
47
71
  #
@@ -59,14 +83,16 @@ module Selenium
59
83
  @capabilities = Capabilities.json_create(capabilities)
60
84
 
61
85
  case @capabilities[:browser_name]
62
- when 'chrome'
86
+ when 'chrome', 'chrome-headless-shell'
63
87
  extend(WebDriver::Chrome::Features)
64
88
  when 'firefox'
65
89
  extend(WebDriver::Firefox::Features)
66
- when 'msedge'
90
+ when 'msedge', 'MicrosoftEdge'
67
91
  extend(WebDriver::Edge::Features)
68
92
  when 'Safari', 'Safari Technology Preview'
69
93
  extend(WebDriver::Safari::Features)
94
+ when 'internet explorer'
95
+ extend(WebDriver::IE::Features)
70
96
  end
71
97
  end
72
98
 
@@ -81,7 +107,7 @@ module Selenium
81
107
  def browser
82
108
  @browser ||= begin
83
109
  name = @capabilities.browser_name
84
- name ? name.tr(' ', '_').downcase.to_sym : 'unknown'
110
+ name ? name.tr(' -', '_').downcase.to_sym : 'unknown'
85
111
  end
86
112
  end
87
113
 
@@ -118,7 +144,7 @@ module Selenium
118
144
  end
119
145
 
120
146
  def alert=(keys)
121
- execute :send_alert_text, {}, {value: keys.split(//), text: keys}
147
+ execute :send_alert_text, {}, {value: keys.chars, text: keys}
122
148
  end
123
149
 
124
150
  def alert_text
@@ -146,9 +172,7 @@ module Selenium
146
172
  end
147
173
 
148
174
  def page_source
149
- execute_script('var source = document.documentElement.outerHTML;' \
150
- 'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
151
- 'return source;')
175
+ execute :get_page_source
152
176
  end
153
177
 
154
178
  #
@@ -188,6 +212,7 @@ module Selenium
188
212
  execute :delete_session
189
213
  http.close
190
214
  rescue *QUIT_ERRORS
215
+ nil
191
216
  end
192
217
 
193
218
  def close
@@ -276,6 +301,7 @@ module Selenium
276
301
  #
277
302
 
278
303
  def local_storage_item(key, value = nil)
304
+ WebDriver.logger.deprecate('local_storage_item(key, value)', id: :local_storage_item)
279
305
  if value
280
306
  execute_script("localStorage.setItem('#{key}', '#{value}')")
281
307
  else
@@ -284,22 +310,27 @@ module Selenium
284
310
  end
285
311
 
286
312
  def remove_local_storage_item(key)
313
+ WebDriver.logger.deprecate('remove_local_storage_item(key)', id: :remove_local_storage_item)
287
314
  execute_script("localStorage.removeItem('#{key}')")
288
315
  end
289
316
 
290
317
  def local_storage_keys
318
+ WebDriver.logger.deprecate('local_storage_keys', id: :local_storage_keys)
291
319
  execute_script('return Object.keys(localStorage)')
292
320
  end
293
321
 
294
322
  def clear_local_storage
323
+ WebDriver.logger.deprecate('clear_local_storage', id: :clear_local_storage)
295
324
  execute_script('localStorage.clear()')
296
325
  end
297
326
 
298
327
  def local_storage_size
328
+ WebDriver.logger.deprecate('local_storage_size', id: :local_storage_size)
299
329
  execute_script('return localStorage.length')
300
330
  end
301
331
 
302
332
  def session_storage_item(key, value = nil)
333
+ WebDriver.logger.deprecate('session_storage_item(key, value)', id: :session_storage_item)
303
334
  if value
304
335
  execute_script("sessionStorage.setItem('#{key}', '#{value}')")
305
336
  else
@@ -308,18 +339,22 @@ module Selenium
308
339
  end
309
340
 
310
341
  def remove_session_storage_item(key)
342
+ WebDriver.logger.deprecate('remove_session_storage_item(key)', id: :remove_session_storage_item)
311
343
  execute_script("sessionStorage.removeItem('#{key}')")
312
344
  end
313
345
 
314
346
  def session_storage_keys
347
+ WebDriver.logger.deprecate('session_storage_keys', id: :session_storage_keys)
315
348
  execute_script('return Object.keys(sessionStorage)')
316
349
  end
317
350
 
318
351
  def clear_session_storage
352
+ WebDriver.logger.deprecate('clear_session_storage', id: :clear_session_storage)
319
353
  execute_script('sessionStorage.clear()')
320
354
  end
321
355
 
322
356
  def session_storage_size
357
+ WebDriver.logger.deprecate('session_storage_size', id: :session_storage_size)
323
358
  execute_script('return sessionStorage.length')
324
359
  end
325
360
 
@@ -369,21 +404,10 @@ module Selenium
369
404
  # actions
370
405
  #
371
406
 
372
- def action(async = false)
373
- ActionBuilder.new self,
374
- Interactions.pointer(:mouse, name: 'mouse'),
375
- Interactions.key('keyboard'),
376
- async
377
- end
378
- alias_method :actions, :action
379
-
380
- def mouse
381
- raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
382
- end
383
-
384
- def keyboard
385
- raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
407
+ def action(async: false, devices: [], duration: 250)
408
+ ActionBuilder.new self, async: async, devices: devices, duration: duration
386
409
  end
410
+ alias actions action
387
411
 
388
412
  def send_actions(data)
389
413
  execute :actions, {}, {actions: data}
@@ -402,27 +426,9 @@ module Selenium
402
426
  end
403
427
 
404
428
  def send_keys_to_element(element, keys)
405
- # TODO: rework file detectors before Selenium 4.0
406
- if @file_detector
407
- local_files = keys.first&.split("\n")&.map { |key| @file_detector.call(Array(key)) }&.compact
408
- if local_files.any?
409
- keys = local_files.map { |local_file| upload(local_file) }
410
- keys = Array(keys.join("\n"))
411
- end
412
- end
413
-
414
- # Keep .split(//) for backward compatibility for now
415
- text = keys.join('')
416
- execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
417
- end
418
-
419
- def upload(local_file)
420
- unless File.file?(local_file)
421
- WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!")
422
- raise Error::WebDriverError, "You are trying to work with something that isn't a file."
423
- end
424
-
425
- execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
429
+ keys = upload_if_necessary(keys) if @file_detector
430
+ text = keys.join
431
+ execute :element_send_keys, {id: element}, {value: text.chars, text: text}
426
432
  end
427
433
 
428
434
  def clear_element(element)
@@ -430,10 +436,19 @@ module Selenium
430
436
  end
431
437
 
432
438
  def submit_element(element)
433
- form = find_element_by('xpath', "./ancestor-or-self::form", [:element, element])
434
- execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
435
- "e.initEvent('submit', true, true);" \
436
- 'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
439
+ script = "/* submitForm */ var form = arguments[0];\n" \
440
+ "while (form.nodeName != \"FORM\" && form.parentNode) {\n " \
441
+ "form = form.parentNode;\n" \
442
+ "}\n" \
443
+ "if (!form) { throw Error('Unable to find containing form element'); }\n" \
444
+ "if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" \
445
+ "var e = form.ownerDocument.createEvent('Event');\n" \
446
+ "e.initEvent('submit', true, true);\n" \
447
+ "if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"
448
+
449
+ execute_script(script, Bridge.element_class::ELEMENT_KEY => element)
450
+ rescue Error::JavascriptError
451
+ raise Error::UnsupportedOperationError, 'To submit an element, it must be nested inside a form element'
437
452
  end
438
453
 
439
454
  #
@@ -445,7 +460,7 @@ module Selenium
445
460
  end
446
461
 
447
462
  def element_attribute(element, name)
448
- WebDriver.logger.info "Using script for :getAttribute of #{name}"
463
+ WebDriver.logger.debug "Using script for :getAttribute of #{name}", id: :script
449
464
  execute_atom :getAttribute, element, name
450
465
  end
451
466
 
@@ -505,7 +520,7 @@ module Selenium
505
520
  end
506
521
 
507
522
  def element_displayed?(element)
508
- WebDriver.logger.info 'Using script for :isDisplayed'
523
+ WebDriver.logger.debug 'Using script for :isDisplayed', id: :script
509
524
  execute_atom :isDisplayed, element
510
525
  end
511
526
 
@@ -518,13 +533,13 @@ module Selenium
518
533
  #
519
534
 
520
535
  def active_element
521
- Element.new self, element_id_from(execute(:get_active_element))
536
+ Bridge.element_class.new self, element_id_from(execute(:get_active_element))
522
537
  end
523
538
 
524
- alias_method :switch_to_active_element, :active_element
539
+ alias switch_to_active_element active_element
525
540
 
526
541
  def find_element_by(how, what, parent_ref = [])
527
- how, what = convert_locator(how, what)
542
+ how, what = @locator_converter.convert(how, what)
528
543
 
529
544
  return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
530
545
 
@@ -538,11 +553,11 @@ module Selenium
538
553
  execute :find_element, {}, {using: how, value: what.to_s}
539
554
  end
540
555
 
541
- Element.new self, element_id_from(id)
556
+ Bridge.element_class.new self, element_id_from(id)
542
557
  end
543
558
 
544
559
  def find_elements_by(how, what, parent_ref = [])
545
- how, what = convert_locator(how, what)
560
+ how, what = @locator_converter.convert(how, what)
546
561
 
547
562
  return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
548
563
 
@@ -556,7 +571,7 @@ module Selenium
556
571
  execute :find_elements, {}, {using: how, value: what.to_s}
557
572
  end
558
573
 
559
- ids.map { |id| Element.new self, element_id_from(id) }
574
+ ids.map { |id| Bridge.element_class.new self, element_id_from(id) }
560
575
  end
561
576
 
562
577
  def shadow_root(element)
@@ -564,6 +579,88 @@ module Selenium
564
579
  ShadowRoot.new self, shadow_root_id_from(id)
565
580
  end
566
581
 
582
+ #
583
+ # virtual-authenticator
584
+ #
585
+
586
+ def add_virtual_authenticator(options)
587
+ authenticator_id = execute :add_virtual_authenticator, {}, options.as_json
588
+ VirtualAuthenticator.new(self, authenticator_id, options)
589
+ end
590
+
591
+ def remove_virtual_authenticator(id)
592
+ execute :remove_virtual_authenticator, {authenticatorId: id}
593
+ end
594
+
595
+ def add_credential(credential, id)
596
+ execute :add_credential, {authenticatorId: id}, credential
597
+ end
598
+
599
+ def credentials(authenticator_id)
600
+ execute :get_credentials, {authenticatorId: authenticator_id}
601
+ end
602
+
603
+ def remove_credential(credential_id, authenticator_id)
604
+ execute :remove_credential, {credentialId: credential_id, authenticatorId: authenticator_id}
605
+ end
606
+
607
+ def remove_all_credentials(authenticator_id)
608
+ execute :remove_all_credentials, {authenticatorId: authenticator_id}
609
+ end
610
+
611
+ def user_verified(verified, authenticator_id)
612
+ execute :set_user_verified, {authenticatorId: authenticator_id}, {isUserVerified: verified}
613
+ end
614
+
615
+ #
616
+ # federated-credential management
617
+ #
618
+
619
+ def cancel_fedcm_dialog
620
+ execute :cancel_fedcm_dialog
621
+ end
622
+
623
+ def select_fedcm_account(index)
624
+ execute :select_fedcm_account, {}, {accountIndex: index}
625
+ end
626
+
627
+ def fedcm_dialog_type
628
+ execute :get_fedcm_dialog_type
629
+ end
630
+
631
+ def fedcm_title
632
+ execute(:get_fedcm_title).fetch('title')
633
+ end
634
+
635
+ def fedcm_subtitle
636
+ execute(:get_fedcm_title).fetch('subtitle', nil)
637
+ end
638
+
639
+ def fedcm_account_list
640
+ execute :get_fedcm_account_list
641
+ end
642
+
643
+ def fedcm_delay(enabled)
644
+ execute :set_fedcm_delay, {}, {enabled: enabled}
645
+ end
646
+
647
+ def reset_fedcm_cooldown
648
+ execute :reset_fedcm_cooldown
649
+ end
650
+
651
+ def click_fedcm_dialog_button
652
+ execute :click_fedcm_dialog_button, {}, {dialogButton: 'ConfirmIdpLoginContinue'}
653
+ end
654
+
655
+ def bidi
656
+ msg = 'BiDi must be enabled by setting #web_socket_url to true in options class'
657
+ raise(WebDriver::Error::WebDriverError, msg)
658
+ end
659
+
660
+ def command_list
661
+ COMMANDS
662
+ end
663
+
567
664
  private
568
665
 
569
666
  #
@@ -584,16 +681,16 @@ module Selenium
584
681
  raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
585
682
  end
586
683
 
587
- WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
684
+ WebDriver.logger.debug("-> #{verb.to_s.upcase} #{path}", id: :command)
588
685
  http.call(verb, path, command_hash)['value']
589
686
  end
590
687
 
591
688
  def escaper
592
- @escaper ||= defined?(URI::Parser) ? URI::DEFAULT_PARSER : URI
689
+ @escaper ||= defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
593
690
  end
594
691
 
595
692
  def commands(command)
596
- COMMANDS[command]
693
+ command_list[command] || Bridge.extra_commands[command]
597
694
  end
598
695
 
599
696
  def unwrap_script_result(arg)
@@ -602,7 +699,7 @@ module Selenium
602
699
  arg.map { |e| unwrap_script_result(e) }
603
700
  when Hash
604
701
  element_id = element_id_from(arg)
605
- return Element.new(self, element_id) if element_id
702
+ return Bridge.element_class.new(self, element_id) if element_id
606
703
 
607
704
  shadow_root_id = shadow_root_id_from(arg)
608
705
  return ShadowRoot.new self, shadow_root_id if shadow_root_id
@@ -614,7 +711,7 @@ module Selenium
614
711
  end
615
712
 
616
713
  def element_id_from(id)
617
- id['ELEMENT'] || id[Element::ELEMENT_KEY]
714
+ id['ELEMENT'] || id[Bridge.element_class::ELEMENT_KEY]
618
715
  end
619
716
 
620
717
  def shadow_root_id_from(id)
@@ -625,45 +722,6 @@ module Selenium
625
722
  capabilities = {alwaysMatch: capabilities} if !capabilities['alwaysMatch'] && !capabilities['firstMatch']
626
723
  {capabilities: capabilities}
627
724
  end
628
-
629
- def convert_locator(how, what)
630
- how = SearchContext::FINDERS[how.to_sym] || how
631
-
632
- case how
633
- when 'class name'
634
- how = 'css selector'
635
- what = ".#{escape_css(what.to_s)}"
636
- when 'id'
637
- how = 'css selector'
638
- what = "##{escape_css(what.to_s)}"
639
- when 'name'
640
- how = 'css selector'
641
- what = "*[name='#{escape_css(what.to_s)}']"
642
- when 'tag name'
643
- how = 'css selector'
644
- end
645
-
646
- if what.is_a?(Hash)
647
- what = what.each_with_object({}) do |(h, w), hash|
648
- h, w = convert_locator(h.to_s, w)
649
- hash[h] = w
650
- end
651
- end
652
-
653
- [how, what]
654
- end
655
-
656
- ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/.freeze
657
- UNICODE_CODE_POINT = 30
658
-
659
- # Escapes invalid characters in CSS selector.
660
- # @see https://mathiasbynens.be/notes/css-escapes
661
- def escape_css(string)
662
- string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
663
- string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
664
-
665
- string
666
- end
667
725
  end # Bridge
668
726
  end # Remote
669
727
  end # WebDriver
@@ -20,14 +20,12 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Remote
23
-
24
23
  #
25
24
  # Specification of the desired and/or actual capabilities of the browser that the
26
25
  # server is being asked to create.
27
26
  #
28
27
 
29
28
  class Capabilities
30
-
31
29
  KNOWN = [
32
30
  :browser_name,
33
31
  :browser_version,
@@ -50,65 +48,16 @@ module Selenium
50
48
  @capabilities[key]
51
49
  end
52
50
 
53
- define_method "#{key}=" do |value|
51
+ define_method :"#{key}=" do |value|
54
52
  @capabilities[key] = value
55
53
  end
56
54
  end
57
55
 
58
- #
59
- # Backward compatibility
60
- #
61
-
62
- alias_method :version, :browser_version
63
- alias_method :version=, :browser_version=
64
- alias_method :platform, :platform_name
65
- alias_method :platform=, :platform_name=
66
-
67
56
  #
68
57
  # Convenience methods for the common choices.
69
58
  #
70
59
 
71
60
  class << self
72
- def chrome(opts = {})
73
- new({
74
- browser_name: 'chrome'
75
- }.merge(opts))
76
- end
77
-
78
- def edge(opts = {})
79
- new({
80
- browser_name: 'MicrosoftEdge'
81
- }.merge(opts))
82
- end
83
- alias_method :microsoftedge, :edge
84
-
85
- def firefox(opts = {})
86
- new({
87
- browser_name: 'firefox'
88
- }.merge(opts))
89
- end
90
- alias_method :ff, :firefox
91
-
92
- def safari(opts = {})
93
- new({
94
- browser_name: Selenium::WebDriver::Safari.technology_preview? ? "Safari Technology Preview" : 'safari'
95
- }.merge(opts))
96
- end
97
-
98
- def htmlunit(opts = {})
99
- new({
100
- browser_name: 'htmlunit'
101
- }.merge(opts))
102
- end
103
-
104
- def internet_explorer(opts = {})
105
- new({
106
- browser_name: 'internet explorer',
107
- platform_name: :windows
108
- }.merge(opts))
109
- end
110
- alias_method :ie, :internet_explorer
111
-
112
61
  def always_match(capabilities)
113
62
  new(always_match: capabilities)
114
63
  end
@@ -134,7 +83,8 @@ module Selenium
134
83
 
135
84
  # Remote Server Specific
136
85
  if data.key?('webdriver.remote.sessionid')
137
- caps[:remote_session_id] = data.delete('webdriver.remote.sessionid')
86
+ caps[:remote_session_id] =
87
+ data.delete('webdriver.remote.sessionid')
138
88
  end
139
89
 
140
90
  KNOWN.each do |cap|
@@ -149,7 +99,7 @@ module Selenium
149
99
  end
150
100
 
151
101
  def camel_case(str_or_sym)
152
- str_or_sym.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
102
+ str_or_sym.to_s.gsub(/_([a-z])/) { Regexp.last_match(1)&.upcase }
153
103
  end
154
104
 
155
105
  private
@@ -269,7 +219,7 @@ module Selenium
269
219
  as_json == other.as_json
270
220
  end
271
221
 
272
- alias_method :eql?, :==
222
+ alias eql? ==
273
223
 
274
224
  protected
275
225