selenium-webdriver 4.0.0.alpha2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +338 -4
  3. data/Gemfile +3 -1
  4. data/LICENSE +1 -1
  5. data/NOTICE +2 -0
  6. data/README.md +4 -5
  7. data/lib/selenium/server.rb +21 -29
  8. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +100 -7
  10. data/lib/selenium/webdriver/atoms/isDisplayed.js +76 -78
  11. data/lib/selenium/webdriver/atoms/mutationListener.js +55 -0
  12. data/lib/selenium/webdriver/chrome/driver.rb +26 -56
  13. data/lib/selenium/webdriver/chrome/features.rb +106 -0
  14. data/lib/selenium/webdriver/chrome/options.rb +127 -52
  15. data/lib/selenium/webdriver/chrome/profile.rb +8 -5
  16. data/lib/selenium/webdriver/chrome/service.rb +4 -6
  17. data/lib/selenium/webdriver/chrome.rb +10 -9
  18. data/lib/selenium/webdriver/common/driver.rb +110 -19
  19. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +43 -0
  20. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +51 -0
  21. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +89 -0
  22. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +77 -0
  23. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +38 -0
  24. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +45 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +43 -0
  26. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +38 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -8
  28. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +144 -0
  29. data/lib/selenium/webdriver/common/driver_extensions/has_logs.rb +30 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +17 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
  32. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +136 -0
  33. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -11
  34. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +77 -0
  35. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +1 -0
  36. data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
  37. data/lib/selenium/webdriver/common/element.rb +79 -16
  38. data/lib/selenium/webdriver/common/error.rb +12 -0
  39. data/lib/selenium/webdriver/common/interactions/interaction.rb +4 -1
  40. data/lib/selenium/webdriver/common/log_entry.rb +2 -2
  41. data/lib/selenium/webdriver/common/logger.rb +50 -15
  42. data/lib/selenium/webdriver/common/manager.rb +14 -14
  43. data/lib/selenium/webdriver/common/options.rb +186 -0
  44. data/lib/selenium/webdriver/common/platform.rb +6 -1
  45. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  46. data/lib/selenium/webdriver/common/profile_helper.rb +10 -2
  47. data/lib/selenium/webdriver/common/proxy.rb +6 -3
  48. data/lib/selenium/webdriver/common/search_context.rb +7 -3
  49. data/lib/selenium/webdriver/common/service.rb +23 -113
  50. data/lib/selenium/webdriver/common/service_manager.rb +151 -0
  51. data/lib/selenium/webdriver/common/shadow_root.rb +87 -0
  52. data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
  53. data/lib/selenium/webdriver/common/takes_screenshot.rb +66 -0
  54. data/lib/selenium/webdriver/common/target_locator.rb +32 -4
  55. data/lib/selenium/webdriver/common/timeouts.rb +31 -4
  56. data/lib/selenium/webdriver/common/wait.rb +1 -1
  57. data/lib/selenium/webdriver/common/window.rb +0 -4
  58. data/lib/selenium/webdriver/common.rb +17 -2
  59. data/lib/selenium/webdriver/devtools/console_event.rb +38 -0
  60. data/lib/selenium/webdriver/devtools/exception_event.rb +36 -0
  61. data/lib/selenium/webdriver/devtools/mutation_event.rb +37 -0
  62. data/lib/selenium/webdriver/{chrome/bridge.rb → devtools/pinned_script.rb} +26 -17
  63. data/lib/selenium/webdriver/devtools/request.rb +67 -0
  64. data/lib/selenium/webdriver/devtools/response.rb +66 -0
  65. data/lib/selenium/webdriver/devtools.rb +182 -0
  66. data/lib/selenium/webdriver/edge/driver.rb +5 -31
  67. data/lib/selenium/webdriver/edge/features.rb +44 -0
  68. data/lib/selenium/webdriver/edge/options.rb +11 -48
  69. data/lib/selenium/webdriver/edge/profile.rb +33 -0
  70. data/lib/selenium/webdriver/edge/service.rb +9 -24
  71. data/lib/selenium/webdriver/edge.rb +11 -13
  72. data/lib/selenium/webdriver/firefox/driver.rb +20 -30
  73. data/lib/selenium/webdriver/firefox/extension.rb +8 -0
  74. data/lib/selenium/webdriver/firefox/{bridge.rb → features.rb} +23 -4
  75. data/lib/selenium/webdriver/firefox/options.rb +70 -49
  76. data/lib/selenium/webdriver/firefox/profile.rb +16 -77
  77. data/lib/selenium/webdriver/firefox/service.rb +1 -5
  78. data/lib/selenium/webdriver/firefox.rb +22 -16
  79. data/lib/selenium/webdriver/ie/driver.rb +1 -34
  80. data/lib/selenium/webdriver/ie/options.rb +13 -44
  81. data/lib/selenium/webdriver/ie/service.rb +9 -11
  82. data/lib/selenium/webdriver/ie.rb +8 -7
  83. data/lib/selenium/webdriver/remote/bridge.rb +112 -86
  84. data/lib/selenium/webdriver/remote/capabilities.rb +120 -62
  85. data/lib/selenium/webdriver/remote/commands.rb +7 -0
  86. data/lib/selenium/webdriver/remote/driver.rb +15 -12
  87. data/lib/selenium/webdriver/remote/http/common.rb +0 -5
  88. data/lib/selenium/webdriver/remote/http/default.rb +17 -11
  89. data/lib/selenium/webdriver/remote/http/persistent.rb +11 -6
  90. data/lib/selenium/webdriver/remote.rb +15 -9
  91. data/lib/selenium/webdriver/safari/driver.rb +3 -34
  92. data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +6 -6
  93. data/lib/selenium/webdriver/safari/options.rb +10 -29
  94. data/lib/selenium/webdriver/safari/service.rb +0 -4
  95. data/lib/selenium/webdriver/safari.rb +16 -8
  96. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
  97. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +63 -0
  98. data/lib/selenium/webdriver/support/cdp_client_generator.rb +108 -0
  99. data/lib/selenium/webdriver/support/color.rb +2 -2
  100. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  101. data/lib/selenium/webdriver/support/guards/guard.rb +89 -0
  102. data/lib/selenium/webdriver/support/guards/guard_condition.rb +52 -0
  103. data/lib/selenium/webdriver/support/guards.rb +95 -0
  104. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  105. data/lib/selenium/webdriver/support/select.rb +2 -2
  106. data/lib/selenium/webdriver/support.rb +1 -0
  107. data/lib/selenium/webdriver/version.rb +1 -1
  108. data/lib/selenium/webdriver.rb +10 -8
  109. data/selenium-webdriver.gemspec +29 -13
  110. metadata +125 -51
  111. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
  112. data/lib/selenium/webdriver/firefox/binary.rb +0 -110
  113. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
@@ -21,42 +21,48 @@ require 'timeout'
21
21
  require 'socket'
22
22
  require 'rexml/document'
23
23
 
24
- require 'selenium/webdriver/firefox/driver'
25
-
26
- require 'selenium/webdriver/firefox/extension'
27
- require 'selenium/webdriver/firefox/binary'
28
- require 'selenium/webdriver/firefox/profiles_ini'
29
- require 'selenium/webdriver/firefox/profile'
30
- require 'selenium/webdriver/firefox/bridge'
31
- require 'selenium/webdriver/firefox/driver'
32
- require 'selenium/webdriver/firefox/options'
33
-
34
24
  module Selenium
35
25
  module WebDriver
36
26
  module Firefox
27
+ autoload :Extension, 'selenium/webdriver/firefox/extension'
28
+ autoload :ProfilesIni, 'selenium/webdriver/firefox/profiles_ini'
29
+ autoload :Profile, 'selenium/webdriver/firefox/profile'
30
+ autoload :Features, 'selenium/webdriver/firefox/features'
31
+ autoload :Driver, 'selenium/webdriver/firefox/driver'
32
+ autoload :Options, 'selenium/webdriver/firefox/options'
33
+ autoload :Service, 'selenium/webdriver/firefox/service'
34
+
37
35
  DEFAULT_PORT = 7055
38
- DEFAULT_ENABLE_NATIVE_EVENTS = Platform.os == :windows
39
36
  DEFAULT_SECURE_SSL = false
40
37
  DEFAULT_ASSUME_UNTRUSTED_ISSUER = true
41
38
  DEFAULT_LOAD_NO_FOCUS_LIB = false
42
39
 
40
+ # Mozilla Automation Team asked to only support 85
41
+ # until WebDriver Bidi is available.
42
+ DEVTOOLS_VERSION = 85
43
+
43
44
  def self.driver_path=(path)
44
45
  WebDriver.logger.deprecate 'Selenium::WebDriver::Firefox#driver_path=',
45
- 'Selenium::WebDriver::Firefox::Service#driver_path='
46
+ 'Selenium::WebDriver::Firefox::Service#driver_path=',
47
+ id: :driver_path
46
48
  Selenium::WebDriver::Firefox::Service.driver_path = path
47
49
  end
48
50
 
49
51
  def self.driver_path
50
52
  WebDriver.logger.deprecate 'Selenium::WebDriver::Firefox#driver_path',
51
- 'Selenium::WebDriver::Firefox::Service#driver_path'
53
+ 'Selenium::WebDriver::Firefox::Service#driver_path',
54
+ id: :driver_path
52
55
  Selenium::WebDriver::Firefox::Service.driver_path
53
56
  end
54
57
 
55
58
  def self.path=(path)
56
- Binary.path = path
59
+ Platform.assert_executable path
60
+ @path = path
61
+ end
62
+
63
+ def self.path
64
+ @path ||= nil
57
65
  end
58
66
  end # Firefox
59
67
  end # WebDriver
60
68
  end # Selenium
61
-
62
- require 'selenium/webdriver/firefox/service'
@@ -28,44 +28,11 @@ module Selenium
28
28
  #
29
29
 
30
30
  class Driver < WebDriver::Driver
31
- include DriverExtensions::HasWebStorage
32
- include DriverExtensions::TakesScreenshot
33
-
34
- def initialize(opts = {})
35
- opts[:desired_capabilities] = create_capabilities(opts)
36
-
37
- opts[:url] ||= service_url(opts)
38
-
39
- listener = opts.delete(:listener)
40
- desired_capabilities = opts.delete(:desired_capabilities)
41
-
42
- @bridge = Remote::Bridge.new(opts)
43
- @bridge.create_session(desired_capabilities)
44
-
45
- super(@bridge, listener: listener)
46
- end
31
+ EXTENSIONS = [DriverExtensions::HasWebStorage].freeze
47
32
 
48
33
  def browser
49
34
  :internet_explorer
50
35
  end
51
-
52
- def quit
53
- super
54
- ensure
55
- @service&.stop
56
- end
57
-
58
- private
59
-
60
- def create_capabilities(opts)
61
- caps = opts.delete(:desired_capabilities) { Remote::Capabilities.internet_explorer }
62
- options = opts.delete(:options) { Options.new }
63
- options = options.as_json
64
- caps.merge!(options) unless options.empty?
65
-
66
- caps
67
- end
68
-
69
36
  end # Driver
70
37
  end # IE
71
38
  end # WebDriver
@@ -20,7 +20,7 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module IE
23
- class Options
23
+ class Options < WebDriver::Options
24
24
  KEY = 'se:ieOptions'
25
25
  SCROLL_TOP = 0
26
26
  SCROLL_BOTTOM = 1
@@ -39,20 +39,13 @@ module Selenium
39
39
  persistent_hover: 'enablePersistentHover',
40
40
  require_window_focus: 'requireWindowFocus',
41
41
  use_per_process_proxy: 'ie.usePerProcessProxy',
42
- validate_cookie_document_type: 'ie.validateCookieDocumentType'
42
+ use_legacy_file_upload_dialog_handling: 'ie.useLegacyFileUploadDialogHandling',
43
+ attach_to_edge_chrome: 'ie.edgechromium',
44
+ edge_executable_path: 'ie.edgepath'
43
45
  }.freeze
46
+ BROWSER = 'internet explorer'
44
47
 
45
- CAPABILITIES.each_key do |key|
46
- define_method key do
47
- @options[key]
48
- end
49
-
50
- define_method "#{key}=" do |value|
51
- @options[key] = value
52
- end
53
- end
54
-
55
- attr_reader :args, :options
48
+ attr_reader :args
56
49
 
57
50
  #
58
51
  # Create a new Options instance
@@ -86,8 +79,9 @@ module Selenium
86
79
  #
87
80
 
88
81
  def initialize(**opts)
89
- @args = Set.new(opts.delete(:args) || [])
90
- @options = opts
82
+ @args = (opts.delete(:args) || []).to_set
83
+ super(**opts)
84
+
91
85
  @options[:native_events] = true if @options[:native_events].nil?
92
86
  end
93
87
 
@@ -101,36 +95,11 @@ module Selenium
101
95
  @args << arg
102
96
  end
103
97
 
104
- #
105
- # Add a new option not yet handled by these bindings.
106
- #
107
- # @example
108
- # options = Selenium::WebDriver::IE::Options.new
109
- # options.add_option(:foo, 'bar')
110
- #
111
- # @param [String, Symbol] name Name of the option
112
- # @param [Boolean, String, Integer] value Value of the option
113
- #
114
-
115
- def add_option(name, value)
116
- @options[name] = value
117
- end
118
-
119
- #
120
- # @api private
121
- #
122
-
123
- def as_json(*)
124
- opts = {}
125
-
126
- CAPABILITIES.each do |capability_alias, capability_name|
127
- capability_value = @options.delete(capability_alias)
128
- opts[capability_name] = capability_value unless capability_value.nil?
129
- end
130
- opts['ie.browserCommandLineSwitches'] = @args.to_a.join(' ') if @args.any?
131
- opts.merge!(@options)
98
+ private
132
99
 
133
- {KEY => opts}
100
+ def process_browser_options(browser_options)
101
+ options = browser_options[KEY]
102
+ options['ie.browserCommandLineSwitches'] = @args.to_a.join(' ') if @args.any?
134
103
  end
135
104
  end # Options
136
105
  end # IE
@@ -20,31 +20,29 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module IE
23
- #
24
- # @api private
25
- #
26
-
27
23
  class Service < WebDriver::Service
28
24
  DEFAULT_PORT = 5555
29
25
  EXECUTABLE = 'IEDriverServer'
30
26
  MISSING_TEXT = <<~ERROR
31
27
  Unable to find IEDriverServer. Please download the server from
32
- http://selenium-release.storage.googleapis.com/index.html and place it somewhere on your PATH.
28
+ https://www.selenium.dev/downloads/ and place it somewhere on your PATH.
33
29
  More info at https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver.
34
30
  ERROR
35
31
  SHUTDOWN_SUPPORTED = true
36
32
 
37
33
  private
38
34
 
39
- # Note: This processing is deprecated
35
+ # NOTE: This processing is deprecated
40
36
  def extract_service_args(driver_opts)
41
37
  driver_args = super
42
38
  driver_opts = driver_opts.dup
43
- driver_args << "--log-level=#{driver_opts.delete(:log_level).to_s.upcase}" if driver_opts.key?(:log_level)
44
- driver_args << "--log-file=#{driver_opts.delete(:log_file)}" if driver_opts.key?(:log_file)
45
- driver_args << "--implementation=#{driver_opts.delete(:implementation).to_s.upcase}" if driver_opts.key?(:implementation)
46
- driver_args << "--host=#{driver_opts.delete(:host)}" if driver_opts.key?(:host)
47
- driver_args << "--extract_path=#{driver_opts.delete(:extract_path)}" if driver_opts.key?(:extract_path)
39
+ driver_args << "--log-level=#{driver_opts[:log_level].to_s.upcase}" if driver_opts.key?(:log_level)
40
+ driver_args << "--log-file=#{driver_opts[:log_file]}" if driver_opts.key?(:log_file)
41
+ if driver_opts.key?(:implementation)
42
+ driver_args << "--implementation=#{driver_opts[:implementation].to_s.upcase}"
43
+ end
44
+ driver_args << "--host=#{driver_opts[:host]}" if driver_opts.key?(:host)
45
+ driver_args << "--extract_path=#{driver_opts[:extract_path]}" if driver_opts.key?(:extract_path)
48
46
  driver_args << "--silent" if driver_opts[:silent] == true
49
47
  driver_args
50
48
  end
@@ -17,25 +17,26 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- require 'selenium/webdriver/ie/driver'
21
- require 'selenium/webdriver/ie/options'
22
-
23
20
  module Selenium
24
21
  module WebDriver
25
22
  module IE
23
+ autoload :Driver, 'selenium/webdriver/ie/driver'
24
+ autoload :Options, 'selenium/webdriver/ie/options'
25
+ autoload :Service, 'selenium/webdriver/ie/service'
26
+
26
27
  def self.driver_path=(path)
27
28
  WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path=',
28
- 'Selenium::WebDriver::IE::Service#driver_path='
29
+ 'Selenium::WebDriver::IE::Service#driver_path=',
30
+ id: :driver_path
29
31
  Selenium::WebDriver::IE::Service.driver_path = path
30
32
  end
31
33
 
32
34
  def self.driver_path
33
35
  WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path',
34
- 'Selenium::WebDriver::IE::Service#driver_path'
36
+ 'Selenium::WebDriver::IE::Service#driver_path',
37
+ id: :driver_path
35
38
  Selenium::WebDriver::IE::Service.driver_path
36
39
  end
37
40
  end # IE
38
41
  end # WebDriver
39
42
  end # Selenium
40
-
41
- require 'selenium/webdriver/ie/service'
@@ -25,35 +25,22 @@ module Selenium
25
25
 
26
26
  PORT = 4444
27
27
 
28
- attr_accessor :context, :http, :file_detector
28
+ attr_accessor :http, :file_detector
29
29
  attr_reader :capabilities
30
30
 
31
31
  #
32
32
  # Initializes the bridge with the given server URL
33
- # @param [Hash] opts options for the driver
34
- # @option opts [String] :url url for the remote server
35
- # @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
36
- # @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
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
37
35
  # @api private
38
36
  #
39
37
 
40
- def initialize(opts = {})
41
- opts = opts.dup
42
-
43
- http_client = opts.delete(:http_client) { Http::Default.new }
44
- url = opts.delete(:url) { "http://#{Platform.localhost}:#{PORT}/wd/hub" }
45
- opts.delete(:options)
46
-
47
- unless opts.empty?
48
- raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
49
- end
50
-
38
+ def initialize(url:, http_client: nil)
51
39
  uri = url.is_a?(URI) ? url : URI.parse(url)
52
- uri.path += '/' unless %r{\/$}.match?(uri.path)
40
+ uri.path += '/' unless uri.path.end_with?('/')
53
41
 
54
- http_client.server_url = uri
55
-
56
- @http = http_client
42
+ @http = http_client || Http::Default.new
43
+ @http.server_url = uri
57
44
  @file_detector = nil
58
45
  end
59
46
 
@@ -61,8 +48,8 @@ module Selenium
61
48
  # Creates session.
62
49
  #
63
50
 
64
- def create_session(desired_capabilities, options = nil)
65
- response = execute(:new_session, {}, merged_capabilities(desired_capabilities, options))
51
+ def create_session(capabilities)
52
+ response = execute(:new_session, {}, prepare_capabilities_payload(capabilities))
66
53
 
67
54
  @session_id = response['sessionId']
68
55
  capabilities = response['capabilities']
@@ -70,6 +57,17 @@ module Selenium
70
57
  raise Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
71
58
 
72
59
  @capabilities = Capabilities.json_create(capabilities)
60
+
61
+ case @capabilities[:browser_name]
62
+ when 'chrome'
63
+ extend(WebDriver::Chrome::Features)
64
+ when 'firefox'
65
+ extend(WebDriver::Firefox::Features)
66
+ when 'msedge'
67
+ extend(WebDriver::Edge::Features)
68
+ when 'Safari', 'Safari Technology Preview'
69
+ extend(WebDriver::Safari::Features)
70
+ end
73
71
  end
74
72
 
75
73
  #
@@ -83,7 +81,7 @@ module Selenium
83
81
  def browser
84
82
  @browser ||= begin
85
83
  name = @capabilities.browser_name
86
- name ? name.tr(' ', '_').to_sym : 'unknown'
84
+ name ? name.tr(' ', '_').downcase.to_sym : 'unknown'
87
85
  end
88
86
  end
89
87
 
@@ -95,17 +93,16 @@ module Selenium
95
93
  execute :get, {}, {url: url}
96
94
  end
97
95
 
98
- def implicit_wait_timeout=(milliseconds)
99
- timeout('implicit', milliseconds)
100
- end
96
+ #
97
+ # timeouts
98
+ #
101
99
 
102
- def script_timeout=(milliseconds)
103
- timeout('script', milliseconds)
100
+ def timeouts
101
+ execute :get_timeouts, {}
104
102
  end
105
103
 
106
- def timeout(type, milliseconds)
107
- type = 'pageLoad' if type == 'page load'
108
- execute :set_timeout, {}, {type => milliseconds}
104
+ def timeouts=(timeouts)
105
+ execute :set_timeout, {}, timeouts
109
106
  end
110
107
 
111
108
  #
@@ -220,7 +217,10 @@ module Selenium
220
217
  end
221
218
 
222
219
  def window_size(handle = :current)
223
- raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size' unless handle == :current
220
+ unless handle == :current
221
+ raise Error::UnsupportedOperationError,
222
+ 'Switch to desired window before getting its size'
223
+ end
224
224
 
225
225
  data = execute :get_window_rect
226
226
  Dimension.new data['width'], data['height']
@@ -231,7 +231,10 @@ module Selenium
231
231
  end
232
232
 
233
233
  def maximize_window(handle = :current)
234
- raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size' unless handle == :current
234
+ unless handle == :current
235
+ raise Error::UnsupportedOperationError,
236
+ 'Switch to desired window before changing its size'
237
+ end
235
238
 
236
239
  execute :maximize_window
237
240
  end
@@ -264,6 +267,10 @@ module Selenium
264
267
  execute :take_screenshot
265
268
  end
266
269
 
270
+ def element_screenshot(element)
271
+ execute :take_element_screenshot, id: element
272
+ end
273
+
267
274
  #
268
275
  # HTML 5
269
276
  #
@@ -316,22 +323,6 @@ module Selenium
316
323
  execute_script('return sessionStorage.length')
317
324
  end
318
325
 
319
- def location
320
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
321
- end
322
-
323
- def set_location(_lat, _lon, _alt)
324
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
325
- end
326
-
327
- def network_connection
328
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
329
- end
330
-
331
- def network_connection=(_type)
332
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
333
- end
334
-
335
326
  #
336
327
  # javascript execution
337
328
  #
@@ -402,6 +393,10 @@ module Selenium
402
393
  execute :release_actions
403
394
  end
404
395
 
396
+ def print_page(options = {})
397
+ execute :print_page, {}, {options: options}
398
+ end
399
+
405
400
  def click_element(element)
406
401
  execute :element_click, id: element
407
402
  end
@@ -435,20 +430,12 @@ module Selenium
435
430
  end
436
431
 
437
432
  def submit_element(element)
438
- form = find_element_by('xpath', "./ancestor-or-self::form", element)
433
+ form = find_element_by('xpath', "./ancestor-or-self::form", [:element, element])
439
434
  execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
440
435
  "e.initEvent('submit', true, true);" \
441
436
  'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
442
437
  end
443
438
 
444
- def screen_orientation=(orientation)
445
- execute :set_screen_orientation, {}, {orientation: orientation}
446
- end
447
-
448
- def screen_orientation
449
- execute :get_screen_orientation
450
- end
451
-
452
439
  #
453
440
  # element properties
454
441
  #
@@ -462,8 +449,20 @@ module Selenium
462
449
  execute_atom :getAttribute, element, name
463
450
  end
464
451
 
452
+ def element_dom_attribute(element, name)
453
+ execute :get_element_attribute, id: element, name: name
454
+ end
455
+
465
456
  def element_property(element, name)
466
- execute :get_element_property, id: element.ref, name: name
457
+ execute :get_element_property, id: element, name: name
458
+ end
459
+
460
+ def element_aria_role(element)
461
+ execute :get_element_aria_role, id: element
462
+ end
463
+
464
+ def element_aria_label(element)
465
+ execute :get_element_aria_label, id: element
467
466
  end
468
467
 
469
468
  def element_value(element)
@@ -524,29 +523,47 @@ module Selenium
524
523
 
525
524
  alias_method :switch_to_active_element, :active_element
526
525
 
527
- def find_element_by(how, what, parent = nil)
528
- how, what = convert_locators(how, what)
526
+ def find_element_by(how, what, parent_ref = [])
527
+ how, what = convert_locator(how, what)
528
+
529
+ return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
529
530
 
530
- id = if parent
531
- execute :find_child_element, {id: parent}, {using: how, value: what}
531
+ parent_type, parent_id = parent_ref
532
+ id = case parent_type
533
+ when :element
534
+ execute :find_child_element, {id: parent_id}, {using: how, value: what.to_s}
535
+ when :shadow_root
536
+ execute :find_shadow_child_element, {id: parent_id}, {using: how, value: what.to_s}
532
537
  else
533
- execute :find_element, {}, {using: how, value: what}
538
+ execute :find_element, {}, {using: how, value: what.to_s}
534
539
  end
540
+
535
541
  Element.new self, element_id_from(id)
536
542
  end
537
543
 
538
- def find_elements_by(how, what, parent = nil)
539
- how, what = convert_locators(how, what)
544
+ def find_elements_by(how, what, parent_ref = [])
545
+ how, what = convert_locator(how, what)
540
546
 
541
- ids = if parent
542
- execute :find_child_elements, {id: parent}, {using: how, value: what}
547
+ return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
548
+
549
+ parent_type, parent_id = parent_ref
550
+ ids = case parent_type
551
+ when :element
552
+ execute :find_child_elements, {id: parent_id}, {using: how, value: what.to_s}
553
+ when :shadow_root
554
+ execute :find_shadow_child_elements, {id: parent_id}, {using: how, value: what.to_s}
543
555
  else
544
- execute :find_elements, {}, {using: how, value: what}
556
+ execute :find_elements, {}, {using: how, value: what.to_s}
545
557
  end
546
558
 
547
559
  ids.map { |id| Element.new self, element_id_from(id) }
548
560
  end
549
561
 
562
+ def shadow_root(element)
563
+ id = execute :get_element_shadow_root, id: element
564
+ ShadowRoot.new self, shadow_root_id_from(id)
565
+ end
566
+
550
567
  private
551
568
 
552
569
  #
@@ -579,16 +596,6 @@ module Selenium
579
596
  COMMANDS[command]
580
597
  end
581
598
 
582
- def merged_capabilities(capabilities, options = nil)
583
- capabilities.merge!(options.as_json) if options
584
-
585
- {
586
- capabilities: {
587
- firstMatch: [capabilities]
588
- }
589
- }
590
- end
591
-
592
599
  def unwrap_script_result(arg)
593
600
  case arg
594
601
  when Array
@@ -604,34 +611,53 @@ module Selenium
604
611
  end
605
612
 
606
613
  def element_id_from(id)
607
- id['ELEMENT'] || id['element-6066-11e4-a52e-4f735466cecf']
614
+ id['ELEMENT'] || id[Element::ELEMENT_KEY]
615
+ end
616
+
617
+ def shadow_root_id_from(id)
618
+ id[ShadowRoot::ROOT_KEY]
619
+ end
620
+
621
+ def prepare_capabilities_payload(capabilities)
622
+ capabilities = {alwaysMatch: capabilities} if !capabilities['alwaysMatch'] && !capabilities['firstMatch']
623
+ {capabilities: capabilities}
608
624
  end
609
625
 
610
- def convert_locators(how, what)
626
+ def convert_locator(how, what)
627
+ how = SearchContext::FINDERS[how.to_sym] || how
628
+
611
629
  case how
612
630
  when 'class name'
613
631
  how = 'css selector'
614
- what = ".#{escape_css(what)}"
632
+ what = ".#{escape_css(what.to_s)}"
615
633
  when 'id'
616
634
  how = 'css selector'
617
- what = "##{escape_css(what)}"
635
+ what = "##{escape_css(what.to_s)}"
618
636
  when 'name'
619
637
  how = 'css selector'
620
- what = "*[name='#{escape_css(what)}']"
638
+ what = "*[name='#{escape_css(what.to_s)}']"
621
639
  when 'tag name'
622
640
  how = 'css selector'
623
641
  end
642
+
643
+ if what.is_a?(Hash)
644
+ what = what.each_with_object({}) do |(h, w), hash|
645
+ h, w = convert_locator(h.to_s, w)
646
+ hash[h] = w
647
+ end
648
+ end
649
+
624
650
  [how, what]
625
651
  end
626
652
 
627
- ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/.freeze
653
+ ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/.freeze
628
654
  UNICODE_CODE_POINT = 30
629
655
 
630
656
  # Escapes invalid characters in CSS selector.
631
657
  # @see https://mathiasbynens.be/notes/css-escapes
632
658
  def escape_css(string)
633
659
  string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
634
- string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}" if !string.empty? && string[0].match?(/[[:digit:]]/)
660
+ string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
635
661
 
636
662
  string
637
663
  end