selenium-webdriver 4.0.0.alpha2 → 4.0.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 (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