selenium-webdriver 4.0.0.alpha1 → 4.0.0.alpha6

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +139 -1
  3. data/LICENSE +1 -1
  4. data/lib/selenium/server.rb +3 -3
  5. data/lib/selenium/webdriver.rb +11 -7
  6. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  7. data/lib/selenium/webdriver/atoms/getAttribute.js +84 -7
  8. data/lib/selenium/webdriver/atoms/isDisplayed.js +75 -77
  9. data/lib/selenium/webdriver/chrome.rb +10 -9
  10. data/lib/selenium/webdriver/chrome/bridge.rb +20 -4
  11. data/lib/selenium/webdriver/chrome/driver.rb +3 -52
  12. data/lib/selenium/webdriver/chrome/options.rb +97 -57
  13. data/lib/selenium/webdriver/chrome/profile.rb +2 -2
  14. data/lib/selenium/webdriver/chrome/service.rb +0 -4
  15. data/lib/selenium/webdriver/common.rb +3 -0
  16. data/lib/selenium/webdriver/common/driver.rb +76 -17
  17. data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_devtools.rb} +10 -8
  18. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +2 -1
  19. data/lib/selenium/webdriver/common/logger.rb +48 -16
  20. data/lib/selenium/webdriver/common/manager.rb +5 -0
  21. data/lib/selenium/webdriver/common/options.rb +60 -121
  22. data/lib/selenium/webdriver/common/platform.rb +3 -0
  23. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  24. data/lib/selenium/webdriver/common/profile_helper.rb +10 -2
  25. data/lib/selenium/webdriver/common/proxy.rb +0 -0
  26. data/lib/selenium/webdriver/common/search_context.rb +3 -2
  27. data/lib/selenium/webdriver/common/service.rb +30 -113
  28. data/lib/selenium/webdriver/common/service_manager.rb +151 -0
  29. data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
  30. data/lib/selenium/webdriver/common/wait.rb +1 -1
  31. data/lib/selenium/webdriver/devtools.rb +118 -0
  32. data/lib/selenium/webdriver/devtools/accessibility.rb +62 -0
  33. data/lib/selenium/webdriver/devtools/animation.rb +98 -0
  34. data/lib/selenium/webdriver/devtools/application_cache.rb +64 -0
  35. data/lib/selenium/webdriver/devtools/audits.rb +61 -0
  36. data/lib/selenium/webdriver/devtools/background_service.rb +67 -0
  37. data/lib/selenium/webdriver/devtools/browser.rb +123 -0
  38. data/lib/selenium/webdriver/devtools/cache_storage.rb +73 -0
  39. data/lib/selenium/webdriver/devtools/cast.rb +70 -0
  40. data/lib/selenium/webdriver/devtools/console.rb +57 -0
  41. data/lib/selenium/webdriver/devtools/css.rb +165 -0
  42. data/lib/selenium/webdriver/devtools/database.rb +64 -0
  43. data/lib/selenium/webdriver/devtools/debugger.rb +229 -0
  44. data/lib/selenium/webdriver/devtools/device_orientation.rb +53 -0
  45. data/lib/selenium/webdriver/devtools/dom.rb +320 -0
  46. data/lib/selenium/webdriver/devtools/domdebugger.rb +93 -0
  47. data/lib/selenium/webdriver/devtools/domsnapshot.rb +65 -0
  48. data/lib/selenium/webdriver/devtools/domstorage.rb +79 -0
  49. data/lib/selenium/webdriver/devtools/emulation.rb +180 -0
  50. data/lib/selenium/webdriver/devtools/fetch.rb +97 -0
  51. data/lib/selenium/webdriver/devtools/headless_experimental.rb +61 -0
  52. data/lib/selenium/webdriver/devtools/heap_profiler.rb +107 -0
  53. data/lib/selenium/webdriver/devtools/indexed_db.rb +100 -0
  54. data/lib/selenium/webdriver/devtools/input.rb +140 -0
  55. data/lib/selenium/webdriver/devtools/inspector.rb +55 -0
  56. data/lib/selenium/webdriver/devtools/io.rb +59 -0
  57. data/lib/selenium/webdriver/devtools/layer_tree.rb +95 -0
  58. data/lib/selenium/webdriver/devtools/log.rb +66 -0
  59. data/lib/selenium/webdriver/devtools/media.rb +57 -0
  60. data/lib/selenium/webdriver/devtools/memory.rb +86 -0
  61. data/lib/selenium/webdriver/devtools/network.rb +228 -0
  62. data/lib/selenium/webdriver/devtools/overlay.rb +157 -0
  63. data/lib/selenium/webdriver/devtools/page.rb +374 -0
  64. data/lib/selenium/webdriver/devtools/performance.rb +63 -0
  65. data/lib/selenium/webdriver/devtools/profiler.rb +111 -0
  66. data/lib/selenium/webdriver/devtools/runtime.rb +193 -0
  67. data/lib/selenium/webdriver/devtools/schema.rb +46 -0
  68. data/lib/selenium/webdriver/devtools/security.rb +71 -0
  69. data/lib/selenium/webdriver/devtools/service_worker.rb +116 -0
  70. data/lib/selenium/webdriver/devtools/storage.rb +95 -0
  71. data/lib/selenium/webdriver/devtools/system_info.rb +50 -0
  72. data/lib/selenium/webdriver/devtools/target.rb +141 -0
  73. data/lib/selenium/webdriver/devtools/tethering.rb +55 -0
  74. data/lib/selenium/webdriver/devtools/tracing.rb +76 -0
  75. data/lib/selenium/webdriver/devtools/web_audio.rb +70 -0
  76. data/lib/selenium/webdriver/devtools/web_authn.rb +94 -0
  77. data/lib/selenium/webdriver/edge.rb +29 -9
  78. data/lib/selenium/webdriver/{firefox/util.rb → edge_chrome/bridge.rb} +11 -20
  79. data/lib/selenium/webdriver/{common/w3c_options.rb → edge_chrome/driver.rb} +14 -17
  80. data/lib/selenium/webdriver/edge_chrome/options.rb +36 -0
  81. data/lib/selenium/webdriver/edge_chrome/profile.rb +33 -0
  82. data/lib/selenium/webdriver/edge_chrome/service.rb +36 -0
  83. data/lib/selenium/webdriver/{common/w3c_manager.rb → edge_html/driver.rb} +11 -17
  84. data/lib/selenium/webdriver/{edge → edge_html}/options.rb +26 -22
  85. data/lib/selenium/webdriver/{edge → edge_html}/service.rb +2 -6
  86. data/lib/selenium/webdriver/firefox.rb +18 -15
  87. data/lib/selenium/webdriver/firefox/bridge.rb +1 -1
  88. data/lib/selenium/webdriver/firefox/driver.rb +2 -30
  89. data/lib/selenium/webdriver/firefox/extension.rb +8 -0
  90. data/lib/selenium/webdriver/firefox/options.rb +47 -52
  91. data/lib/selenium/webdriver/firefox/profile.rb +7 -78
  92. data/lib/selenium/webdriver/firefox/service.rb +0 -4
  93. data/lib/selenium/webdriver/ie.rb +8 -7
  94. data/lib/selenium/webdriver/ie/driver.rb +0 -32
  95. data/lib/selenium/webdriver/ie/options.rb +10 -33
  96. data/lib/selenium/webdriver/ie/service.rb +5 -9
  97. data/lib/selenium/webdriver/remote.rb +16 -10
  98. data/lib/selenium/webdriver/remote/bridge.rb +34 -42
  99. data/lib/selenium/webdriver/remote/capabilities.rb +22 -6
  100. data/lib/selenium/webdriver/remote/driver.rb +6 -12
  101. data/lib/selenium/webdriver/remote/http/default.rb +9 -4
  102. data/lib/selenium/webdriver/remote/http/persistent.rb +5 -6
  103. data/lib/selenium/webdriver/safari.rb +9 -8
  104. data/lib/selenium/webdriver/safari/bridge.rb +4 -4
  105. data/lib/selenium/webdriver/safari/driver.rb +3 -29
  106. data/lib/selenium/webdriver/safari/options.rb +18 -19
  107. data/lib/selenium/webdriver/safari/service.rb +0 -4
  108. data/lib/selenium/webdriver/support.rb +1 -0
  109. data/lib/selenium/webdriver/support/cdp_client_generator.rb +77 -0
  110. data/lib/selenium/webdriver/support/color.rb +2 -2
  111. data/lib/selenium/webdriver/support/event_firing_bridge.rb +1 -1
  112. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  113. data/lib/selenium/webdriver/version.rb +1 -1
  114. data/selenium-webdriver.gemspec +5 -4
  115. metadata +81 -42
  116. data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
  117. data/lib/selenium/webdriver/common/keyboard.rb +0 -70
  118. data/lib/selenium/webdriver/common/mouse.rb +0 -89
  119. data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
  120. data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
  121. data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
  122. data/lib/selenium/webdriver/edge/bridge.rb +0 -76
  123. data/lib/selenium/webdriver/edge/driver.rb +0 -70
  124. data/lib/selenium/webdriver/firefox/binary.rb +0 -110
  125. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
  126. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  127. data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
  128. data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
  129. data/lib/selenium/webdriver/firefox/marionette/bridge.rb +0 -49
  130. data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
  131. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  132. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  133. data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
  134. data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
  135. data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
  136. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
  137. data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -19,23 +19,26 @@
19
19
 
20
20
  require 'net/http'
21
21
 
22
- require 'selenium/webdriver/chrome/bridge'
23
- require 'selenium/webdriver/chrome/driver'
24
- require 'selenium/webdriver/chrome/profile'
25
- require 'selenium/webdriver/chrome/options'
26
-
27
22
  module Selenium
28
23
  module WebDriver
29
24
  module Chrome
25
+ autoload :Bridge, 'selenium/webdriver/chrome/bridge'
26
+ autoload :Driver, 'selenium/webdriver/chrome/driver'
27
+ autoload :Profile, 'selenium/webdriver/chrome/profile'
28
+ autoload :Options, 'selenium/webdriver/chrome/options'
29
+ autoload :Service, 'selenium/webdriver/chrome/service'
30
+
30
31
  def self.driver_path=(path)
31
32
  WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path=',
32
- 'Selenium::WebDriver::Chrome::Service#driver_path='
33
+ 'Selenium::WebDriver::Chrome::Service#driver_path=',
34
+ id: :driver_path
33
35
  Selenium::WebDriver::Chrome::Service.driver_path = path
34
36
  end
35
37
 
36
38
  def self.driver_path
37
39
  WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path',
38
- 'Selenium::WebDriver::Chrome::Service#driver_path'
40
+ 'Selenium::WebDriver::Chrome::Service#driver_path',
41
+ id: :driver_path
39
42
  Selenium::WebDriver::Chrome::Service.driver_path
40
43
  end
41
44
 
@@ -50,5 +53,3 @@ module Selenium
50
53
  end # Chrome
51
54
  end # WebDriver
52
55
  end # Selenium
53
-
54
- require 'selenium/webdriver/chrome/service'
@@ -20,12 +20,14 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Chrome
23
- module Bridge
23
+ class Bridge < WebDriver::Remote::Bridge
24
24
 
25
25
  COMMANDS = {
26
- get_network_conditions: [:get, '/session/:session_id/chromium/network_conditions'],
27
- set_network_conditions: [:post, '/session/:session_id/chromium/network_conditions'],
28
- send_command: [:post, '/session/:session_id/goog/cdp/execute']
26
+ get_network_conditions: [:get, 'session/:session_id/chromium/network_conditions'],
27
+ set_network_conditions: [:post, 'session/:session_id/chromium/network_conditions'],
28
+ send_command: [:post, 'session/:session_id/goog/cdp/execute'],
29
+ get_available_log_types: [:get, 'session/:session_id/se/log/types'],
30
+ get_log: [:post, 'session/:session_id/se/log']
29
31
  }.freeze
30
32
 
31
33
  def commands(command)
@@ -44,6 +46,20 @@ module Selenium
44
46
  execute :set_network_conditions, {}, {network_conditions: conditions}
45
47
  end
46
48
 
49
+ def available_log_types
50
+ types = execute :get_available_log_types
51
+ Array(types).map(&:to_sym)
52
+ end
53
+
54
+ def log(type)
55
+ data = execute :get_log, {}, {type: type.to_s}
56
+
57
+ Array(data).map do |l|
58
+ LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
59
+ rescue KeyError
60
+ next
61
+ end
62
+ end
47
63
  end # Bridge
48
64
  end # Chrome
49
65
  end # WebDriver
@@ -32,68 +32,19 @@ module Selenium
32
32
  include DriverExtensions::HasLocation
33
33
  include DriverExtensions::TakesScreenshot
34
34
  include DriverExtensions::DownloadsFiles
35
-
36
- def initialize(opts = {})
37
- opts[:desired_capabilities] = create_capabilities(opts)
38
-
39
- opts[:url] ||= service_url(opts)
40
-
41
- listener = opts.delete(:listener)
42
- desired_capabilities = opts.delete(:desired_capabilities)
43
-
44
- @bridge = Remote::Bridge.new(opts)
45
- @bridge.extend Bridge
46
- @bridge.create_session(desired_capabilities)
47
-
48
- super(@bridge, listener: listener)
49
- end
35
+ include DriverExtensions::HasDevTools
50
36
 
51
37
  def browser
52
38
  :chrome
53
39
  end
54
40
 
55
- def quit
56
- super
57
- ensure
58
- @service&.stop
41
+ def bridge_class
42
+ Bridge
59
43
  end
60
44
 
61
45
  def execute_cdp(cmd, **params)
62
46
  @bridge.send_command(cmd: cmd, params: params)
63
47
  end
64
-
65
- private
66
-
67
- def create_capabilities(opts)
68
- caps = opts.delete(:desired_capabilities) { Remote::Capabilities.chrome }
69
- options = opts.delete(:options) { Options.new }
70
-
71
- profile = opts.delete(:profile)
72
- if profile
73
- profile = profile.as_json
74
-
75
- options.add_argument("--user-data-dir=#{profile[:directory]}") if options.args.none?(&/user-data-dir/.method(:match?))
76
-
77
- if profile[:extensions]
78
- WebDriver.logger.deprecate 'Using Selenium::WebDriver::Chrome::Profile#extensions',
79
- 'Selenium::WebDriver::Chrome::Options#add_extension'
80
- profile[:extensions].each do |extension|
81
- options.add_encoded_extension(extension)
82
- end
83
- end
84
- end
85
-
86
- detach = opts.delete(:detach)
87
- options.add_option(:detach, true) if detach
88
-
89
- options = options.as_json
90
- caps.merge!(options) unless options[Options::KEY].empty?
91
-
92
- caps[:proxy] = opts.delete(:proxy) if opts.key?(:proxy)
93
- caps[:proxy] ||= opts.delete('proxy') if opts.key?('proxy')
94
-
95
- caps
96
- end
97
48
  end # Driver
98
49
  end # Chrome
99
50
  end # WebDriver
@@ -20,19 +20,44 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Chrome
23
- class Options
24
- attr_reader :args, :prefs, :options, :emulation, :extensions, :encoded_extensions
25
- attr_accessor :binary
23
+ class Options < WebDriver::Options
24
+ attr_accessor :profile
26
25
 
27
26
  KEY = 'goog:chromeOptions'
27
+ BROWSER = 'chrome'
28
+
29
+ # see: http://chromedriver.chromium.org/capabilities
30
+ CAPABILITIES = {args: 'args',
31
+ binary: 'binary',
32
+ extensions: 'extensions',
33
+ local_state: 'localState',
34
+ prefs: 'prefs',
35
+ detach: 'detach',
36
+ debugger_address: 'debuggerAddress',
37
+ exclude_switches: 'excludeSwitches',
38
+ minidump_path: 'minidumpPath',
39
+ emulation: 'mobileEmulation',
40
+ perf_logging_prefs: 'perfLoggingPrefs',
41
+ window_types: 'windowTypes'}.freeze
42
+
43
+ CAPABILITIES.each_key do |key|
44
+ define_method key do
45
+ @options[key]
46
+ end
47
+
48
+ define_method "#{key}=" do |value|
49
+ @options[key] = value
50
+ end
51
+ end
28
52
 
29
- #
30
53
  # Create a new Options instance.
31
54
  #
32
55
  # @example
33
56
  # options = Selenium::WebDriver::Chrome::Options.new(args: ['start-maximized', 'user-data-dir=/tmp/temp_profile'])
34
57
  # driver = Selenium::WebDriver.for(:chrome, options: options)
35
58
  #
59
+ # @param [Profile] :profile An instance of a Chrome::Profile Class
60
+ # @param [Array] :encoded_extensions List of extensions that do not need to be Base64 encoded
36
61
  # @param [Hash] opts the pre-defined options to create the Chrome::Options with
37
62
  # @option opts [Array<String>] :args List of command-line arguments to use when starting Chrome
38
63
  # @option opts [String] :binary Path to the Chrome executable to use
@@ -40,16 +65,21 @@ module Selenium
40
65
  # @option opts [Array<String>] :extensions A list of paths to (.crx) Chrome extensions to install on startup
41
66
  # @option opts [Hash] :options A hash for raw options
42
67
  # @option opts [Hash] :emulation A hash for raw emulation options
68
+ # @option opts [Hash] :local_state A hash for the Local State file in the user data folder
69
+ # @option opts [Boolean] :detach whether browser is closed when the driver is sent the quit command
70
+ # @option opts [String] :debugger_address address of a Chrome debugger server to connect to
71
+ # @option opts [Array<String>] :exclude_switches command line switches to exclude
72
+ # @option opts [String] :minidump_path Directory to store Chrome minidumps (linux only)
73
+ # @option opts [Hash] :perf_logging_prefs A hash for performance logging preferences
74
+ # @option opts [Array<String>] :window_types A list of window types to appear in the list of window handles
43
75
  #
44
76
 
45
- def initialize(**opts)
46
- @args = Set.new(opts.delete(:args) || [])
47
- @binary = opts.delete(:binary) || Chrome.path
48
- @prefs = opts.delete(:prefs) || {}
49
- @extensions = opts.delete(:extensions) || []
50
- @options = opts.delete(:options) || {}
51
- @emulation = opts.delete(:emulation) || {}
52
- @encoded_extensions = []
77
+ def initialize(profile: nil, encoded_extensions: nil, **opts)
78
+ super(**opts)
79
+
80
+ @profile = profile
81
+ @options[:encoded_extensions] = encoded_extensions if encoded_extensions
82
+ @options[:extensions]&.each(&method(:validate_extension))
53
83
  end
54
84
 
55
85
  #
@@ -63,10 +93,9 @@ module Selenium
63
93
  #
64
94
 
65
95
  def add_extension(path)
66
- raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.file?(path)
67
- raise Error::WebDriverError, "file was not an extension #{path.inspect}" unless File.extname(path) == '.crx'
68
-
69
- @extensions << path
96
+ validate_extension(path)
97
+ @options[:extensions] ||= []
98
+ @options[:extensions] << path
70
99
  end
71
100
 
72
101
  #
@@ -80,8 +109,10 @@ module Selenium
80
109
  #
81
110
 
82
111
  def add_encoded_extension(encoded)
83
- @encoded_extensions << encoded
112
+ @options[:encoded_extensions] ||= []
113
+ @options[:encoded_extensions] << encoded
84
114
  end
115
+ alias_method :encoded_extension=, :add_encoded_extension
85
116
 
86
117
  #
87
118
  # Add a command-line argument to use when starting Chrome.
@@ -94,22 +125,8 @@ module Selenium
94
125
  #
95
126
 
96
127
  def add_argument(arg)
97
- @args << arg
98
- end
99
-
100
- #
101
- # Add a new option not yet handled by bindings.
102
- #
103
- # @example Leave Chrome open when chromedriver is killed
104
- # options = Selenium::WebDriver::Chrome::Options.new
105
- # options.add_option(:detach, true)
106
- #
107
- # @param [String, Symbol] name Name of the option
108
- # @param [Boolean, String, Integer] value Value of the option
109
- #
110
-
111
- def add_option(name, value)
112
- @options[name] = value
128
+ @options[:args] ||= []
129
+ @options[:args] << arg
113
130
  end
114
131
 
115
132
  #
@@ -124,7 +141,8 @@ module Selenium
124
141
  #
125
142
 
126
143
  def add_preference(name, value)
127
- prefs[name] = value
144
+ @options[:prefs] ||= {}
145
+ @options[:prefs][name] = value
128
146
  end
129
147
 
130
148
  #
@@ -140,7 +158,9 @@ module Selenium
140
158
  end
141
159
 
142
160
  #
143
- # Add an emulation device name
161
+ # Add emulation device information
162
+ #
163
+ # see: http://chromedriver.chromium.org/mobile-emulation
144
164
  #
145
165
  # @example Start Chrome in mobile emulation mode by device name
146
166
  # options = Selenium::WebDriver::Chrome::Options.new
@@ -150,35 +170,55 @@ module Selenium
150
170
  # options = Selenium::WebDriver::Chrome::Options.new
151
171
  # options.add_emulation(device_metrics: {width: 400, height: 800, pixelRatio: 1, touch: true})
152
172
  #
153
- # @param [String] device_name Name of the device or a hash containing width, height, pixelRatio, touch
154
- # @param [Hash] device_metrics Hash containing width, height, pixelRatio, touch
155
- # @param [String] user_agent Full user agent
173
+ # @param [Hash] opts the pre-defined options for adding mobile emulation values
174
+ # @option opts [String] :device_name A valid device name from the Chrome DevTools Emulation panel
175
+ # @option opts [Hash] :device_metrics Hash containing width, height, pixelRatio, touch
176
+ # @option opts [String] :user_agent Full user agent
156
177
  #
157
178
 
158
- def add_emulation(device_name: nil, device_metrics: nil, user_agent: nil)
159
- @emulation[:deviceName] = device_name if device_name
160
- @emulation[:deviceMetrics] = device_metrics if device_metrics
161
- @emulation[:userAgent] = user_agent if user_agent
179
+ def add_emulation(**opts)
180
+ @options[:emulation] = opts
162
181
  end
163
182
 
164
- #
165
- # @api private
166
- #
183
+ private
167
184
 
168
- def as_json(*)
169
- extensions = @extensions.map do |crx_path|
170
- File.open(crx_path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
171
- end
172
- extensions.concat(@encoded_extensions)
185
+ def process_browser_options(browser_options)
186
+ options = browser_options[KEY]
187
+ options['binary'] ||= binary_path if binary_path
188
+ (options['args'] || []) << "--user-data-dir=#{@profile[:directory]}" if @profile
189
+ merge_extensions(options)
190
+ end
191
+
192
+ def merge_extensions(browser_options)
193
+ extensions = browser_options['extensions'] || []
194
+ encoded_extensions = browser_options.delete(:encoded_extensions) || []
195
+
196
+ browser_options['extensions'] = extensions.map(&method(:encode_extension)) + encoded_extensions
197
+ browser_options.delete('extensions') if browser_options['extensions'].empty?
198
+ end
173
199
 
174
- opts = @options
175
- opts[:binary] = @binary if @binary
176
- opts[:args] = @args.to_a if @args.any?
177
- opts[:extensions] = extensions if extensions.any?
178
- opts[:mobileEmulation] = @emulation unless @emulation.empty?
179
- opts[:prefs] = @prefs unless @prefs.empty?
200
+ def binary_path
201
+ Chrome.path
202
+ end
203
+
204
+ def encode_extension(path)
205
+ File.open(path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
206
+ end
207
+
208
+ def validate_extension(path)
209
+ raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.file?(path)
210
+ raise Error::WebDriverError, "file was not an extension #{path.inspect}" unless File.extname(path) == '.crx'
211
+ end
180
212
 
181
- {KEY => opts}
213
+ def generate_as_json(value, camelize_keys: true)
214
+ if value.is_a?(Hash)
215
+ value.each_with_object({}) do |(key, val), hash|
216
+ key = convert_json_key(key, camelize: camelize_keys)
217
+ hash[key] = generate_as_json(val, camelize_keys: key != 'prefs')
218
+ end
219
+ else
220
+ super
221
+ end
182
222
  end
183
223
  end # Options
184
224
  end # Chrome
@@ -77,8 +77,8 @@ module Selenium
77
77
 
78
78
  extensions.concat(@encoded_extensions)
79
79
 
80
- opts = {directory: directory || layout_on_disk}
81
- opts[:extensions] = extensions if extensions.any?
80
+ opts = {'directory' => directory || layout_on_disk}
81
+ opts['extensions'] = extensions if extensions.any?
82
82
  opts
83
83
  end
84
84
 
@@ -20,10 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Chrome
23
- #
24
- # @api private
25
- #
26
-
27
23
  class Service < WebDriver::Service
28
24
  DEFAULT_PORT = 9515
29
25
  EXECUTABLE = 'chromedriver'
@@ -23,6 +23,7 @@ require 'selenium/webdriver/common/proxy'
23
23
  require 'selenium/webdriver/common/log_entry'
24
24
  require 'selenium/webdriver/common/file_reaper'
25
25
  require 'selenium/webdriver/common/service'
26
+ require 'selenium/webdriver/common/service_manager'
26
27
  require 'selenium/webdriver/common/socket_lock'
27
28
  require 'selenium/webdriver/common/socket_poller'
28
29
  require 'selenium/webdriver/common/port_prober'
@@ -62,7 +63,9 @@ require 'selenium/webdriver/common/driver_extensions/has_permissions'
62
63
  require 'selenium/webdriver/common/driver_extensions/has_debugger'
63
64
  require 'selenium/webdriver/common/driver_extensions/uploads_files'
64
65
  require 'selenium/webdriver/common/driver_extensions/has_addons'
66
+ require 'selenium/webdriver/common/driver_extensions/has_devtools'
65
67
  require 'selenium/webdriver/common/keys'
66
68
  require 'selenium/webdriver/common/profile_helper'
69
+ require 'selenium/webdriver/common/options'
67
70
  require 'selenium/webdriver/common/driver'
68
71
  require 'selenium/webdriver/common/element'
@@ -43,17 +43,21 @@ module Selenium
43
43
  def for(browser, opts = {})
44
44
  case browser
45
45
  when :chrome
46
- Chrome::Driver.new(opts)
46
+ Chrome::Driver.new(**opts)
47
47
  when :internet_explorer, :ie
48
- IE::Driver.new(opts)
48
+ IE::Driver.new(**opts)
49
49
  when :safari
50
- Safari::Driver.new(opts)
50
+ Safari::Driver.new(**opts)
51
51
  when :firefox, :ff
52
- Firefox::Driver.new(opts)
52
+ Firefox::Driver.new(**opts)
53
53
  when :edge
54
- Edge::Driver.new(opts)
54
+ Edge::Driver.new(**opts)
55
+ when :edge_chrome
56
+ EdgeChrome::Driver.new(**opts)
57
+ when :edge_html
58
+ EdgeHtml::Driver.new(**opts)
55
59
  when :remote
56
- Remote::Driver.new(opts)
60
+ Remote::Driver.new(**opts)
57
61
  else
58
62
  raise ArgumentError, "unknown driver: #{browser.inspect}"
59
63
  end
@@ -67,9 +71,10 @@ module Selenium
67
71
  # @api private
68
72
  #
69
73
 
70
- def initialize(bridge, listener: nil)
71
- @bridge = bridge
72
- @bridge = Support::EventFiringBridge.new(bridge, listener) if listener
74
+ def initialize(bridge: nil, listener: nil, **opts)
75
+ @service = nil
76
+ bridge ||= create_bridge(**opts)
77
+ @bridge = listener ? Support::EventFiringBridge.new(bridge, listener) : bridge
73
78
  end
74
79
 
75
80
  def inspect
@@ -164,6 +169,8 @@ module Selenium
164
169
 
165
170
  def quit
166
171
  bridge.quit
172
+ ensure
173
+ @service&.stop
167
174
  end
168
175
 
169
176
  #
@@ -269,7 +276,7 @@ module Selenium
269
276
  end
270
277
 
271
278
  def browser
272
- bridge.browser
279
+ bridge&.browser
273
280
  end
274
281
 
275
282
  def capabilities
@@ -287,18 +294,70 @@ module Selenium
287
294
 
288
295
  attr_reader :bridge
289
296
 
297
+ def create_bridge(**opts)
298
+ opts[:url] ||= service_url(opts)
299
+ caps = opts.delete(:capabilities)
300
+ # Note: This is deprecated
301
+ cap_array = caps.is_a?(Hash) ? [caps] : Array(caps)
302
+
303
+ desired_capabilities = opts.delete(:desired_capabilities)
304
+ if desired_capabilities
305
+ WebDriver.logger.deprecate(':desired_capabilities as a parameter for driver initialization',
306
+ ':capabilities with an Array value of capabilities/options if necessary',
307
+ id: :desired_capabilities)
308
+ desired_capabilities = Remote::Capabilities.new(desired_capabilities) if desired_capabilities.is_a?(Hash)
309
+ cap_array << desired_capabilities
310
+ end
311
+
312
+ options = opts.delete(:options)
313
+ if options
314
+ WebDriver.logger.deprecate(':options as a parameter for driver initialization',
315
+ ':capabilities with an Array of value capabilities/options if necessary',
316
+ id: :browser_options)
317
+ cap_array << options
318
+ end
319
+
320
+ capabilities = generate_capabilities(cap_array)
321
+
322
+ bridge_opts = {http_client: opts.delete(:http_client), url: opts.delete(:url)}
323
+ raise ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
324
+
325
+ bridge = (respond_to?(:bridge_class) ? bridge_class : Remote::Bridge).new(**bridge_opts)
326
+
327
+ bridge.create_session(capabilities)
328
+ bridge
329
+ end
330
+
331
+ def generate_capabilities(cap_array)
332
+ cap_array.map { |cap|
333
+ if cap.is_a? Symbol
334
+ cap = Remote::Capabilities.send(cap)
335
+ elsif cap.is_a? Hash
336
+ WebDriver.logger.deprecate("passing a Hash value to :capabilities",
337
+ 'Capabilities instance initialized with the Hash, or build values with Options class',
338
+ id: :capabilities_hash)
339
+ cap = Remote::Capabilities.new(cap)
340
+ elsif !cap.respond_to? :as_json
341
+ msg = ":capabilities parameter only accepts objects responding to #as_json which #{cap.class} does not"
342
+ raise ArgumentError, msg
343
+ end
344
+ cap&.as_json
345
+ }.inject(:merge) || Remote::Capabilities.send(browser || :new)
346
+ end
347
+
290
348
  def service_url(opts)
291
- @service = opts.delete(:service)
349
+ service_config = opts.delete(:service)
292
350
  %i[driver_opts driver_path port].each do |key|
293
351
  next unless opts.key? key
294
352
 
295
- WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service')
353
+ WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service',
354
+ id: "service_#{key}".to_sym)
296
355
  end
297
- @service ||= Service.send(browser,
298
- args: opts.delete(:driver_opts),
299
- path: opts.delete(:driver_path),
300
- port: opts.delete(:port))
301
- @service.start
356
+ service_config ||= Service.send(browser,
357
+ args: opts.delete(:driver_opts),
358
+ path: opts.delete(:driver_path),
359
+ port: opts.delete(:port))
360
+ @service = service_config.launch
302
361
  @service.uri
303
362
  end
304
363
  end # Driver