selenium-webdriver 4.0.0.alpha5 → 4.0.0.alpha6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +38 -0
  3. data/LICENSE +1 -1
  4. data/lib/selenium/webdriver/atoms/findElements.js +1 -1
  5. data/lib/selenium/webdriver/chrome/bridge.rb +4 -6
  6. data/lib/selenium/webdriver/chrome/driver.rb +4 -0
  7. data/lib/selenium/webdriver/chrome/options.rb +24 -19
  8. data/lib/selenium/webdriver/common.rb +1 -0
  9. data/lib/selenium/webdriver/common/driver.rb +55 -23
  10. data/lib/selenium/webdriver/common/logger.rb +1 -1
  11. data/lib/selenium/webdriver/common/manager.rb +5 -0
  12. data/lib/selenium/webdriver/common/options.rb +32 -9
  13. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  14. data/lib/selenium/webdriver/common/service.rb +12 -106
  15. data/lib/selenium/webdriver/common/service_manager.rb +151 -0
  16. data/lib/selenium/webdriver/devtools.rb +118 -0
  17. data/lib/selenium/webdriver/devtools/accessibility.rb +62 -0
  18. data/lib/selenium/webdriver/devtools/animation.rb +98 -0
  19. data/lib/selenium/webdriver/devtools/application_cache.rb +64 -0
  20. data/lib/selenium/webdriver/devtools/audits.rb +61 -0
  21. data/lib/selenium/webdriver/devtools/background_service.rb +67 -0
  22. data/lib/selenium/webdriver/devtools/browser.rb +123 -0
  23. data/lib/selenium/webdriver/devtools/cache_storage.rb +73 -0
  24. data/lib/selenium/webdriver/devtools/cast.rb +70 -0
  25. data/lib/selenium/webdriver/devtools/console.rb +57 -0
  26. data/lib/selenium/webdriver/devtools/css.rb +165 -0
  27. data/lib/selenium/webdriver/devtools/database.rb +64 -0
  28. data/lib/selenium/webdriver/devtools/debugger.rb +229 -0
  29. data/lib/selenium/webdriver/devtools/device_orientation.rb +53 -0
  30. data/lib/selenium/webdriver/devtools/dom.rb +320 -0
  31. data/lib/selenium/webdriver/devtools/domdebugger.rb +93 -0
  32. data/lib/selenium/webdriver/devtools/domsnapshot.rb +65 -0
  33. data/lib/selenium/webdriver/devtools/domstorage.rb +79 -0
  34. data/lib/selenium/webdriver/devtools/emulation.rb +180 -0
  35. data/lib/selenium/webdriver/devtools/fetch.rb +97 -0
  36. data/lib/selenium/webdriver/devtools/headless_experimental.rb +61 -0
  37. data/lib/selenium/webdriver/devtools/heap_profiler.rb +107 -0
  38. data/lib/selenium/webdriver/devtools/indexed_db.rb +100 -0
  39. data/lib/selenium/webdriver/devtools/input.rb +140 -0
  40. data/lib/selenium/webdriver/devtools/inspector.rb +55 -0
  41. data/lib/selenium/webdriver/devtools/io.rb +59 -0
  42. data/lib/selenium/webdriver/devtools/layer_tree.rb +95 -0
  43. data/lib/selenium/webdriver/devtools/log.rb +66 -0
  44. data/lib/selenium/webdriver/devtools/media.rb +57 -0
  45. data/lib/selenium/webdriver/devtools/memory.rb +86 -0
  46. data/lib/selenium/webdriver/devtools/network.rb +228 -0
  47. data/lib/selenium/webdriver/devtools/overlay.rb +157 -0
  48. data/lib/selenium/webdriver/devtools/page.rb +374 -0
  49. data/lib/selenium/webdriver/devtools/performance.rb +63 -0
  50. data/lib/selenium/webdriver/devtools/profiler.rb +111 -0
  51. data/lib/selenium/webdriver/devtools/runtime.rb +193 -0
  52. data/lib/selenium/webdriver/devtools/schema.rb +46 -0
  53. data/lib/selenium/webdriver/devtools/security.rb +71 -0
  54. data/lib/selenium/webdriver/devtools/service_worker.rb +116 -0
  55. data/lib/selenium/webdriver/devtools/storage.rb +95 -0
  56. data/lib/selenium/webdriver/devtools/system_info.rb +50 -0
  57. data/lib/selenium/webdriver/devtools/target.rb +141 -0
  58. data/lib/selenium/webdriver/devtools/tethering.rb +55 -0
  59. data/lib/selenium/webdriver/devtools/tracing.rb +76 -0
  60. data/lib/selenium/webdriver/devtools/web_audio.rb +70 -0
  61. data/lib/selenium/webdriver/devtools/web_authn.rb +94 -0
  62. data/lib/selenium/webdriver/edge_chrome/bridge.rb +9 -2
  63. data/lib/selenium/webdriver/edge_chrome/driver.rb +4 -0
  64. data/lib/selenium/webdriver/edge_chrome/options.rb +2 -0
  65. data/lib/selenium/webdriver/edge_html/options.rb +2 -9
  66. data/lib/selenium/webdriver/firefox/bridge.rb +1 -1
  67. data/lib/selenium/webdriver/firefox/driver.rb +4 -0
  68. data/lib/selenium/webdriver/firefox/options.rb +5 -10
  69. data/lib/selenium/webdriver/ie/options.rb +7 -10
  70. data/lib/selenium/webdriver/remote/bridge.rb +3 -13
  71. data/lib/selenium/webdriver/remote/capabilities.rb +11 -6
  72. data/lib/selenium/webdriver/safari/bridge.rb +1 -1
  73. data/lib/selenium/webdriver/safari/driver.rb +4 -0
  74. data/lib/selenium/webdriver/safari/options.rb +1 -8
  75. data/lib/selenium/webdriver/support/cdp_client_generator.rb +77 -0
  76. data/lib/selenium/webdriver/support/color.rb +2 -2
  77. data/lib/selenium/webdriver/version.rb +1 -1
  78. data/selenium-webdriver.gemspec +2 -2
  79. metadata +53 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d04cb682531f39f2806727c1267c2bad759729c9c3a11f134ce3b3b03577bfd2
4
- data.tar.gz: 1a08d5c92d03e56fb5374dae94b568f1531e91c722b04a971f1594ca6ef982f3
3
+ metadata.gz: e4dbf1d975d636f48a1f346ffdb79c19d612287f1f4b625e150ff586b290be49
4
+ data.tar.gz: 1835a0d9d692b3d158a596fc081b132984e458e372c7ab882b17a10bdfc48a81
5
5
  SHA512:
6
- metadata.gz: c13f2b89e2002814d96bf6a82cf384c7000757d29ba9a1038b01fe2b7df4842b145d33531f7f7ae8bf015c6b82b43e8a0205cb068f344af7092e4a9d07f1e6c3
7
- data.tar.gz: ad5cfc2f64a1a1b3e27ed6df91b06638778de23f82246e12e9e5003f162829b88fabbb8db5764d7330f35b315f1ffe2de22342e431a3fd9404cf524f2e623ed1
6
+ metadata.gz: d33d076a090456d98b7361f9d890ad97ca5771ef832f609a0145dcbc19a149602ff4cfceb71d882307954017ce971aa0f4197cd612add189770f0c9b4267aacf
7
+ data.tar.gz: c84c80cf18da2e301122a350df46db0fb35455e1c88738ffff7bb5b784c622a1bb2b63c68fffe885c65fd445133427ac1632562b02473f5fa76c5a02d8649db1
data/CHANGES CHANGED
@@ -1,3 +1,41 @@
1
+ 4.0.0.alpha6 (2020-05-28)
2
+ =========================
3
+
4
+ Chrome:
5
+ * Added DevTools classes and methods generated from the CDP specification.
6
+ It currently supports commands and events and provides Rubyish API:
7
+ driver.devtools.page.navigate(url: 'http://google.com')
8
+ driver.devtools.console.clear_messages
9
+ driver.devtools.page.enable
10
+ driver.devtools.page.on(:load_event_fired) do |params|
11
+ puts("Page loaded in #{params['timestamp']}")
12
+ end
13
+ Check https://chromedevtools.github.io/devtools-protocol/ for more information
14
+ about possible commands and events. Please note that this API is considered
15
+ to be experimental and may change any time before stable 4.0 release.
16
+ * Fixed an issue when passing :prompt_for_download (or similar snake_cased
17
+ symbols) in Options#prefs would be ignored by Chrome because Selenium
18
+ would internally convert it to camelCased format (:promptForDownload).
19
+ Now :prefs are left intact.
20
+
21
+ Edge:
22
+ * Fixed an issue when Driver#execute_cdp would not work for Chromium-based
23
+ Edge browser.
24
+
25
+ Ruby:
26
+ * Deprecated passing :desired_capabilities and :options to driver initialization
27
+ in favor of :capabilities. They now can be combined in an array to make
28
+ custom capabilities requirements:
29
+ caps = Selenium::WebDriver::Remote::Capabilities.chrome
30
+ opts = Selenium::WebDriver::Chrome::Options.new
31
+ Selenium::WebDriver.for(:remote, capabilities: :chrome)
32
+ Selenium::WebDriver.for(:remote, capabilities: caps)
33
+ Selenium::WebDriver.for(:remote, capabilities: opts)
34
+ Selenium::WebDriver.for(:remote, capabilities: [caps, opts])
35
+ * Deprecated passing Hash to :capabilities in favor of Remote::Capabilities object.
36
+ * Updated minimum require Ruby version to 2.5.
37
+ * Improved keyword arguments handling to avoid warnings in Ruby 2.7.
38
+
1
39
  4.0.0.alpha5 (2020-03-18)
2
40
  =========================
3
41
 
data/LICENSE CHANGED
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright 2019 Software Freedom Conservancy (SFC)
190
+ Copyright 2020 Software Freedom Conservancy (SFC)
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ function jd(a){var b=a.shape.toLowerCase();a=a.coords.split(",");if("rect"==b&&4
110
110
  function kd(a){return a.replace(/^[^\S\xa0]+|[^\S\xa0]+$/g,"")}function ld(a){var b=[];$c?md(a,b):nd(a,b);a=pa(b,kd);return kd(a.join("\n")).replace(/\xa0/g," ")}
111
111
  function od(a,b,c){if(T(a,"BR"))b.push("");else{var d=T(a,"TD"),e=W(a,"display"),f=!d&&!(0<=na(pd,e)),g=k(a.previousElementSibling)?a.previousElementSibling:fb(a.previousSibling);g=g?W(g,"display"):"";var h=W(a,"float")||W(a,"cssFloat")||W(a,"styleFloat");!f||"run-in"==g&&"none"==h||/^[\s\xa0]*$/.test(b[b.length-1]||"")||b.push("");var n=hd(a),t=null,p=null;n&&(t=W(a,"white-space"),p=W(a,"text-transform"));r(a.childNodes,function(G){c(G,b,n,t,p)});a=b[b.length-1]||"";!d&&"table-cell"!=e||!a||wa(a)||
112
112
  (b[b.length-1]+=" ");f&&"run-in"!=e&&!/^[\s\xa0]*$/.test(a)&&b.push("")}}function nd(a,b){od(a,b,function(c,d,e,f,g){3==c.nodeType&&e?qd(c,d,f,g):T(c)&&nd(c,d)})}var pd="inline inline-block inline-table none table-cell table-column table-column-group".split(" ");
113
- function qd(a,b,c,d){a=a.nodeValue.replace(/[\u200b\u200e\u200f]/g,"");a=a.replace(/(\r\n|\r|\n)/g,"\n");if("normal"==c||"nowrap"==c)a=a.replace(/\n/g," ");a="pre"==c||"pre-wrap"==c?a.replace(/[ \f\t\v\u2028\u2029]/g,"\u00a0"):a.replace(/[ \f\t\v\u2028\u2029]+/g," ");"capitalize"==d?a=a.replace(/\b(\S)/g,function(e,f){return f.toUpperCase()}):"uppercase"==d?a=a.toUpperCase():"lowercase"==d&&(a=a.toLowerCase());c=b.pop()||"";wa(c)&&0==a.lastIndexOf(" ",0)&&(a=a.substr(1));b.push(c+a)}
113
+ function qd(a,b,c,d){a=a.nodeValue.replace(/[\u200b\u200e\u200f]/g,"");a=a.replace(/(\r\n|\r|\n)/g,"\n");if("normal"==c||"nowrap"==c)a=a.replace(/\n/g," ");a="pre"==c||"pre-wrap"==c?a.replace(/[ \f\t\v\u2028\u2029]/g,"\u00a0"):a.replace(/[ \f\t\v\u2028\u2029]+/g," ");"capitalize"==d?a=a.replace(/(^|\s|\b)(\S)/g,function(e,f,g){return f+g.toUpperCase()}):"uppercase"==d?a=a.toUpperCase():"lowercase"==d&&(a=a.toLowerCase());c=b.pop()||"";wa(c)&&0==a.lastIndexOf(" ",0)&&(a=a.substr(1));b.push(c+a)}
114
114
  function gd(a){if(Oc){if("relative"==W(a,"position"))return 1;a=W(a,"filter");return(a=a.match(/^alpha\(opacity=(\d*)\)/)||a.match(/^progid:DXImageTransform.Microsoft.Alpha\(Opacity=(\d*)\)/))?Number(a[1])/100:1}return rd(a)}function rd(a){var b=1,c=W(a,"opacity");c&&(b=Number(c));(a=ad(a))&&(b*=rd(a));return b}
115
115
  function sd(a,b,c,d,e){if(3==a.nodeType&&c)qd(a,b,d,e);else if(T(a))if(T(a,"CONTENT")||T(a,"SLOT")){for(var f=a;f.parentNode;)f=f.parentNode;f instanceof ShadowRoot?(a=T(a,"CONTENT")?a.getDistributedNodes():a.assignedNodes(),r(a,function(g){sd(g,b,c,d,e)})):md(a,b)}else if(T(a,"SHADOW")){for(f=a;f.parentNode;)f=f.parentNode;if(f instanceof ShadowRoot&&(a=f))for(a=a.olderShadowRoot;a;)r(a.childNodes,function(g){sd(g,b,c,d,e)}),a=a.olderShadowRoot}else md(a,b)}
116
116
  function md(a,b){a.shadowRoot&&r(a.shadowRoot.childNodes,function(c){sd(c,b,!0,null,null)});od(a,b,function(c,d,e,f,g){var h=null;1==c.nodeType?h=c:3==c.nodeType&&(h=c);null!=h&&(null!=h.assignedSlot||h.getDestinationInsertionPoints&&0<h.getDestinationInsertionPoints().length)||sd(c,d,e,f,g)})};var td={A:function(a,b){return!(!a.querySelectorAll||!a.querySelector)&&!/^\d.*/.test(b)},o:function(a,b){var c=db(b),d=l(a)?c.a.getElementById(a):a;return d?Wc(d,"id")==a&&b!=d&&gb(b,d)?d:ta(lb(c,"*"),function(e){return Wc(e,"id")==a&&b!=e&&gb(b,e)}):null},j:function(a,b){if(!a)return[];if(td.A(b,a))try{return b.querySelectorAll("#"+td.R(a))}catch(c){return[]}b=lb(db(b),"*",null,b);return oa(b,function(c){return Wc(c,"id")==a})},R:function(a){return a.replace(/([\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-\/\[\]\(\)])/g,
@@ -20,7 +20,7 @@
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
26
  get_network_conditions: [:get, 'session/:session_id/chromium/network_conditions'],
@@ -55,11 +55,9 @@ module Selenium
55
55
  data = execute :get_log, {}, {type: type.to_s}
56
56
 
57
57
  Array(data).map do |l|
58
- begin
59
- LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
60
- rescue KeyError
61
- next
62
- end
58
+ LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
59
+ rescue KeyError
60
+ next
63
61
  end
64
62
  end
65
63
  end # Bridge
@@ -38,6 +38,10 @@ module Selenium
38
38
  :chrome
39
39
  end
40
40
 
41
+ def bridge_class
42
+ Bridge
43
+ end
44
+
41
45
  def execute_cdp(cmd, **params)
42
46
  @bridge.send_command(cmd: cmd, params: params)
43
47
  end
@@ -24,6 +24,7 @@ module Selenium
24
24
  attr_accessor :profile
25
25
 
26
26
  KEY = 'goog:chromeOptions'
27
+ BROWSER = 'chrome'
27
28
 
28
29
  # see: http://chromedriver.chromium.org/capabilities
29
30
  CAPABILITIES = {args: 'args',
@@ -74,7 +75,7 @@ module Selenium
74
75
  #
75
76
 
76
77
  def initialize(profile: nil, encoded_extensions: nil, **opts)
77
- super(opts)
78
+ super(**opts)
78
79
 
79
80
  @profile = profile
80
81
  @options[:encoded_extensions] = encoded_extensions if encoded_extensions
@@ -179,30 +180,23 @@ module Selenium
179
180
  @options[:emulation] = opts
180
181
  end
181
182
 
182
- #
183
- # @api private
184
- #
185
-
186
- def as_json(*)
187
- options = super
188
-
189
- if @profile
190
- options['args'] ||= []
191
- options['args'] << "--user-data-dir=#{@profile[:directory]}"
192
- end
183
+ private
193
184
 
185
+ def process_browser_options(browser_options)
186
+ options = browser_options[KEY]
194
187
  options['binary'] ||= binary_path if binary_path
195
- extensions = options['extensions'] || []
196
- encoded_extensions = options.delete(:encoded_extensions) || []
188
+ (options['args'] || []) << "--user-data-dir=#{@profile[:directory]}" if @profile
189
+ merge_extensions(options)
190
+ end
197
191
 
198
- options['extensions'] = extensions.map(&method(:encode_extension)) + encoded_extensions
199
- options.delete('extensions') if options['extensions'].empty?
192
+ def merge_extensions(browser_options)
193
+ extensions = browser_options['extensions'] || []
194
+ encoded_extensions = browser_options.delete(:encoded_extensions) || []
200
195
 
201
- {KEY => generate_as_json(options)}
196
+ browser_options['extensions'] = extensions.map(&method(:encode_extension)) + encoded_extensions
197
+ browser_options.delete('extensions') if browser_options['extensions'].empty?
202
198
  end
203
199
 
204
- private
205
-
206
200
  def binary_path
207
201
  Chrome.path
208
202
  end
@@ -215,6 +209,17 @@ module Selenium
215
209
  raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.file?(path)
216
210
  raise Error::WebDriverError, "file was not an extension #{path.inspect}" unless File.extname(path) == '.crx'
217
211
  end
212
+
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
222
+ end
218
223
  end # Options
219
224
  end # Chrome
220
225
  end # WebDriver
@@ -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'
@@ -43,21 +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
55
  when :edge_chrome
56
- EdgeChrome::Driver.new(opts)
56
+ EdgeChrome::Driver.new(**opts)
57
57
  when :edge_html
58
- EdgeHtml::Driver.new(opts)
58
+ EdgeHtml::Driver.new(**opts)
59
59
  when :remote
60
- Remote::Driver.new(opts)
60
+ Remote::Driver.new(**opts)
61
61
  else
62
62
  raise ArgumentError, "unknown driver: #{browser.inspect}"
63
63
  end
@@ -73,7 +73,7 @@ module Selenium
73
73
 
74
74
  def initialize(bridge: nil, listener: nil, **opts)
75
75
  @service = nil
76
- bridge ||= create_bridge(opts)
76
+ bridge ||= create_bridge(**opts)
77
77
  @bridge = listener ? Support::EventFiringBridge.new(bridge, listener) : bridge
78
78
  end
79
79
 
@@ -296,36 +296,68 @@ module Selenium
296
296
 
297
297
  def create_bridge(**opts)
298
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
299
311
 
300
- desired_capabilities = opts.delete(:desired_capabilities) || Remote::Capabilities.send(browser || :new)
301
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
302
319
 
303
- bridge = Remote::Bridge.new(http_client: opts.delete(:http_client), url: opts.delete(:url))
304
- raise ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
320
+ capabilities = generate_capabilities(cap_array)
305
321
 
306
- namespacing = self.class.to_s.split('::')
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?
307
324
 
308
- if Object.const_defined?("#{namespacing[0..-2].join('::')}::Bridge") && !namespacing.include?('Remote')
309
- bridge.extend Object.const_get("#{namespacing[0, namespacing.length - 1].join('::')}::Bridge")
310
- end
325
+ bridge = (respond_to?(:bridge_class) ? bridge_class : Remote::Bridge).new(**bridge_opts)
311
326
 
312
- bridge.create_session(desired_capabilities, options)
327
+ bridge.create_session(capabilities)
313
328
  bridge
314
329
  end
315
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
+
316
348
  def service_url(opts)
317
- @service = opts.delete(:service)
349
+ service_config = opts.delete(:service)
318
350
  %i[driver_opts driver_path port].each do |key|
319
351
  next unless opts.key? key
320
352
 
321
353
  WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service',
322
354
  id: "service_#{key}".to_sym)
323
355
  end
324
- @service ||= Service.send(browser,
325
- args: opts.delete(:driver_opts),
326
- path: opts.delete(:driver_path),
327
- port: opts.delete(:port))
328
- @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
329
361
  @service.uri
330
362
  end
331
363
  end # Driver
@@ -40,7 +40,7 @@ module Selenium
40
40
  :close,
41
41
  :debug, :debug?,
42
42
  :info, :info?,
43
- :warn, :warn?,
43
+ :warn?,
44
44
  :error, :error?,
45
45
  :fatal, :fatal?,
46
46
  :level, :level=
@@ -36,6 +36,7 @@ module Selenium
36
36
  # @option opts [String] :value A value
37
37
  # @option opts [String] :path ('/') A path
38
38
  # @option opts [String] :secure (false) A boolean
39
+ # @option opts [String] :same_site (Strict or Lax) currently supported only in chrome 80+ versions
39
40
  # @option opts [Time,DateTime,Numeric,nil] :expires (nil) Expiry date, either as a Time, DateTime, or seconds since epoch.
40
41
  #
41
42
  # @raise [ArgumentError] if :name or :value is not specified
@@ -48,6 +49,9 @@ module Selenium
48
49
  opts[:path] ||= '/'
49
50
  opts[:secure] ||= false
50
51
 
52
+ same_site = opts.delete(:same_site)
53
+ opts[:sameSite] = same_site if same_site
54
+
51
55
  obj = opts.delete(:expires)
52
56
  opts[:expiry] = seconds_from(obj).to_i if obj
53
57
 
@@ -169,6 +173,7 @@ module Selenium
169
173
  path: cookie['path'],
170
174
  domain: cookie['domain'] && strip_port(cookie['domain']),
171
175
  expires: cookie['expiry'] && datetime_at(cookie['expiry']),
176
+ same_site: cookie['sameSite'],
172
177
  secure: cookie['secure']
173
178
  }
174
179
  end
@@ -20,6 +20,19 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  class Options
23
+ W3C_OPTIONS = %i[browser_name browser_version platform_name accept_insecure_certs page_load_strategy proxy
24
+ set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability].freeze
25
+
26
+ W3C_OPTIONS.each do |key|
27
+ define_method key do
28
+ @options[key]
29
+ end
30
+
31
+ define_method "#{key}=" do |value|
32
+ @options[key] = value
33
+ end
34
+ end
35
+
23
36
  attr_accessor :options
24
37
 
25
38
  def initialize(options: nil, **opts)
@@ -31,6 +44,7 @@ module Selenium
31
44
  else
32
45
  opts
33
46
  end
47
+ @options[:browser_name] = self.class::BROWSER
34
48
  end
35
49
 
36
50
  #
@@ -55,22 +69,30 @@ module Selenium
55
69
  def as_json(*)
56
70
  options = @options.dup
57
71
 
58
- opts = self.class::CAPABILITIES.each_with_object({}) do |(capability_alias, capability_name), hash|
72
+ w3c_options = options.select { |key, _val| W3C_OPTIONS.include?(key) }
73
+ options.delete_if { |key, _val| W3C_OPTIONS.include?(key) }
74
+
75
+ self.class::CAPABILITIES.each do |capability_alias, capability_name|
59
76
  capability_value = options.delete(capability_alias)
60
- hash[capability_name] = capability_value unless capability_value.nil?
77
+ options[capability_name] = capability_value unless capability_value.nil?
61
78
  end
62
- opts.merge(options)
79
+ browser_options = defined?(self.class::KEY) ? {self.class::KEY => options} : options
80
+
81
+ process_browser_options(browser_options) if private_methods(false).include?(:process_browser_options)
82
+ generate_as_json(w3c_options.merge(browser_options))
63
83
  end
64
84
 
65
85
  private
66
86
 
67
- def generate_as_json(value)
87
+ def generate_as_json(value, camelize_keys: true)
68
88
  if value.respond_to?(:as_json)
69
89
  value.as_json
70
90
  elsif value.is_a?(Hash)
71
- value.each_with_object({}) { |(key, val), hash| hash[convert_json_key(key)] = generate_as_json(val) }
91
+ value.each_with_object({}) do |(key, val), hash|
92
+ hash[convert_json_key(key, camelize: camelize_keys)] = generate_as_json(val, camelize_keys: camelize_keys)
93
+ end
72
94
  elsif value.is_a?(Array)
73
- value.map(&method(:generate_as_json))
95
+ value.map { |val| generate_as_json(val, camelize_keys: camelize_keys) }
74
96
  elsif value.is_a?(Symbol)
75
97
  value.to_s
76
98
  else
@@ -78,15 +100,16 @@ module Selenium
78
100
  end
79
101
  end
80
102
 
81
- def convert_json_key(key)
82
- key = camel_case(key) if key.is_a?(Symbol)
103
+ def convert_json_key(key, camelize: true)
104
+ key = key.to_s if key.is_a?(Symbol)
105
+ key = camel_case(key) if camelize
83
106
  return key if key.is_a?(String)
84
107
 
85
108
  raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
86
109
  end
87
110
 
88
111
  def camel_case(str)
89
- str.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
112
+ str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
90
113
  end
91
114
  end # Options
92
115
  end # WebDriver
@@ -32,12 +32,10 @@ module Selenium
32
32
 
33
33
  def self.free?(port)
34
34
  Platform.interfaces.each do |host|
35
- begin
36
- TCPServer.new(host, port).close
37
- rescue *IGNORED_ERRORS => e
38
- WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
39
- # ignored - some machines appear unable to bind to some of their interfaces
40
- end
35
+ TCPServer.new(host, port).close
36
+ rescue *IGNORED_ERRORS => e
37
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
38
+ # ignored - some machines appear unable to bind to some of their interfaces
41
39
  end
42
40
 
43
41
  true