selenium-webdriver 4.9.0 → 4.10.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +28 -0
  3. data/Gemfile +2 -0
  4. data/README.md +1 -1
  5. data/bin/linux/selenium-manager +0 -0
  6. data/bin/macos/selenium-manager +0 -0
  7. data/bin/windows/selenium-manager.exe +0 -0
  8. data/lib/selenium/server.rb +1 -1
  9. data/lib/selenium/webdriver/atoms/findElements.js +3 -4
  10. data/lib/selenium/webdriver/common/action_builder.rb +0 -8
  11. data/lib/selenium/webdriver/common/child_process.rb +9 -9
  12. data/lib/selenium/webdriver/common/driver_finder.rb +11 -7
  13. data/lib/selenium/webdriver/common/error.rb +28 -3
  14. data/lib/selenium/webdriver/common/logger.rb +87 -30
  15. data/lib/selenium/webdriver/common/options.rb +1 -1
  16. data/lib/selenium/webdriver/common/port_prober.rb +1 -1
  17. data/lib/selenium/webdriver/common/proxy.rb +1 -1
  18. data/lib/selenium/webdriver/common/selenium_manager.rb +42 -25
  19. data/lib/selenium/webdriver/common/service.rb +10 -2
  20. data/lib/selenium/webdriver/common/service_manager.rb +4 -2
  21. data/lib/selenium/webdriver/common/socket_lock.rb +1 -1
  22. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  23. data/lib/selenium/webdriver/common/websocket_connection.rb +2 -2
  24. data/lib/selenium/webdriver/devtools.rb +1 -1
  25. data/lib/selenium/webdriver/edge/options.rb +14 -0
  26. data/lib/selenium/webdriver/firefox/profile.rb +1 -1
  27. data/lib/selenium/webdriver/remote/bridge.rb +6 -5
  28. data/lib/selenium/webdriver/remote/http/common.rb +3 -3
  29. data/lib/selenium/webdriver/remote/http/curb.rb +1 -1
  30. data/lib/selenium/webdriver/remote/http/default.rb +1 -1
  31. data/lib/selenium/webdriver/safari/service.rb +10 -0
  32. data/lib/selenium/webdriver/support/color.rb +8 -8
  33. data/lib/selenium/webdriver/version.rb +1 -1
  34. data/lib/selenium/webdriver.rb +2 -1
  35. data/selenium-webdriver.gemspec +2 -2
  36. metadata +13 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1ae360596be57c29db02808a7dae16c36062801e7639e83b366a54cdba5e3b2
4
- data.tar.gz: f31169f7cfec055abb7a2f3595eee8e453acf316651a829af5f254a0f824cf9e
3
+ metadata.gz: 57f5a526cd47137c459f2eb349f03fbebc28b428a4d1eba1aed962cb8b32f0d4
4
+ data.tar.gz: 0e246eec8fc308f54bb651245cebaf3610a5abf4f6027728a6e96210b72a397e
5
5
  SHA512:
6
- metadata.gz: 675dad14a433939c81dabbb0276b1c5a97d95f0b1451ca48aa0e908c2cc0658f1d90e3970be4e3f62e65ff5eaef5cf391b5d646f50413ca46ea2f24de821fb5a
7
- data.tar.gz: dd9d26b855e7b1f01f667d113181f6e7b35367ee74459b693a3c5872cc92a9e58f9d2d849de3aaff9738e3e77cb3f3117617219b4f76e2fda2f66bc75279ee8d
6
+ metadata.gz: 679674b291a9b3da8d41a917ed782d14743150b524767a915e464e39751971ce9420e7a21127dcf10ca070f3bd7bff14a61690c028b013daab8d638550f38a61
7
+ data.tar.gz: a39c8fdecf8a60ad8736aa3392bba7f540c692bdba451546463b8db4e9b967d81d1d30ca665aedb99901e6f173deb3af29fcdfcd7d7c087783b8e24358ae8814
data/CHANGES CHANGED
@@ -1,3 +1,31 @@
1
+ 4.10.0 (2023-06-07)
2
+ =========================
3
+ Ruby:
4
+ * Implement proxy support for Selenium Manager
5
+ * Prevent setting driver log level in Safari
6
+ * Change all Selenium Manager logging to :debug (#12145)
7
+ * Error messages include links to documentation
8
+ * Add custom error class for driver location and improve error logic
9
+
10
+ BiDi:
11
+ * Released selenium-devtools 0.114.0 (supports CDP v85, v112, v113, v114)
12
+
13
+ Edge:
14
+ * Add support for webview2
15
+
16
+ 4.9.1 (2023-05-08)
17
+ =========================
18
+ Ruby:
19
+ * Allow users to specify driver process output in Service class (#11964)
20
+ * Updated minimum required Ruby version to 3.0
21
+ * Selenium Logger defaults to :info and all debugging is now logged as :debug (#11967)
22
+ * Every logging entry can be ignored based on ID, not just warnings
23
+ * Logging entries can be filtered to allow or ignore specific IDs
24
+
25
+ BiDi:
26
+ * Fix bug with loading devtools (#11931) (thanks Boris Petrov!)
27
+ * Released selenium-devtools 0.113.0 (supports CDP v85, v111, v112, v113)
28
+
1
29
  4.9.0 (2023-04-21)
2
30
  =========================
3
31
  Ruby:
data/Gemfile CHANGED
@@ -4,3 +4,5 @@ source 'https://rubygems.org'
4
4
  Dir["#{__dir__}/*.gemspec"].each do |spec|
5
5
  gemspec name: File.basename(spec, '.gemspec')
6
6
  end
7
+
8
+ gem 'debug', '~> 1.7', require: false, platforms: %i[mri mingw x64_mingw]
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # selenium-webdriver
2
2
 
3
- This gem provides Ruby bindings for Selenium and supports MRI >= 2.6
3
+ This gem provides Ruby bindings for Selenium and supports MRI >= 3.0.
4
4
 
5
5
  ## Install
6
6
 
Binary file
Binary file
Binary file
@@ -254,7 +254,7 @@ module Selenium
254
254
  args = ['-jar', @jar, @role, '--port', @port.to_s]
255
255
  server_command = ['java'] + properties + args + @additional_args
256
256
  cp = WebDriver::ChildProcess.build(*server_command)
257
- WebDriver.logger.debug("Executing Process #{server_command}")
257
+ WebDriver.logger.debug("Executing Process #{server_command}", id: :server)
258
258
 
259
259
  if @log.is_a?(String)
260
260
  cp.io = @log
@@ -113,10 +113,9 @@ function nd(a,b,c,d){a=a.nodeValue.replace(/[\u200b\u200e\u200f]/g,"");a=a.repla
113
113
  function pd(a,b,c,d,e){if(3==a.nodeType&&c)nd(a,b,d,e);else if(S(a))if(S(a,"CONTENT")||S(a,"SLOT")){for(var f=a;f.parentNode;)f=f.parentNode;f instanceof ShadowRoot?(a=S(a,"CONTENT")?a.getDistributedNodes():a.assignedNodes(),l(a,function(g){pd(g,b,c,d,e)})):jd(a,b)}else if(S(a,"SHADOW")){for(f=a;f.parentNode;)f=f.parentNode;if(f instanceof ShadowRoot&&(a=f))for(a=a.olderShadowRoot;a;)l(a.childNodes,function(g){pd(g,b,c,d,e)}),a=a.olderShadowRoot}else jd(a,b)}
114
114
  function jd(a,b){a.shadowRoot&&l(a.shadowRoot.childNodes,function(c){pd(c,b,!0,null,null)});ld(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)||pd(c,d,e,f,g)})};var qd={C:function(a,b){return!(!a.querySelectorAll||!a.querySelector)&&!/^\d.*/.test(b)},o:function(a,b){var c=eb(b),d="string"===typeof a?c.a.getElementById(a):a;return d?Uc(d,"id")==a&&b!=d&&hb(b,d)?d:ua(mb(c,"*"),function(e){return Uc(e,"id")==a&&b!=e&&hb(b,e)}):null},j:function(a,b){if(!a)return[];if(qd.C(b,a))try{return b.querySelectorAll("#"+qd.T(a))}catch(c){return[]}b=mb(eb(b),"*",null,b);return pa(b,function(c){return Uc(c,"id")==a})},T:function(a){return a.replace(/([\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-\/\[\]\(\)])/g,
115
115
  "\\$1")}};var Y={},rd={};Y.N=function(a,b,c){try{var d=Nc.j("a",b)}catch(e){d=mb(eb(b),"A",null,b)}return ua(d,function(e){e=id(e);e=e.replace(/^[\s]+|[\s]+$/g,"");return c&&-1!=e.indexOf(a)||e==a})};Y.K=function(a,b,c){try{var d=Nc.j("a",b)}catch(e){d=mb(eb(b),"A",null,b)}return pa(d,function(e){e=id(e);e=e.replace(/^[\s]+|[\s]+$/g,"");return c&&-1!=e.indexOf(a)||e==a})};Y.o=function(a,b){return Y.N(a,b,!1)};Y.j=function(a,b){return Y.K(a,b,!1)};rd.o=function(a,b){return Y.N(a,b,!0)};
116
- rd.j=function(a,b){return Y.K(a,b,!0)};var Z={F:function(a,b){return function(c){var d=Z.u(a);d=W(d);c=W(c);return b.call(null,d,c)}},R:function(a){return Z.F(a,function(b,c){return c.b+c.height<b.b})},S:function(a){return Z.F(a,function(b,c){return b.b+b.height<c.b})},V:function(a){return Z.F(a,function(b,c){return c.a+c.width<b.a})},aa:function(a){return Z.F(a,function(b,c){return b.a+b.width<c.a})},W:function(a,b){var c;b?c=b:"number"==typeof a.distance&&(c=a.distance);c||(c=100);return function(d){var e=Z.u(a);if(e===d)return!1;e=
117
- W(e);d=W(d);var f=Math.abs(e.a+e.width-d.a),g=Math.abs(e.b+e.height-d.b);g=Math.abs(e.b-(d.b+d.height))<=c||g<=c;return(Math.abs(e.a-(d.a+d.width))<=c||f<=c)&&g?!0:Math.sqrt(Math.pow(Math.abs(e.a+e.width/2-(d.a+d.width/2)),2)+Math.pow(Math.abs(e.b+e.height/2-(d.b+d.height/2)),2))<=c}},u:function(a){if(ha(a)&&1==a.nodeType)return a;if(ea(a))return Z.u(a.call(null));if(ha(a)){var b;a:{if(b=sd(a)){var c=td[b];if(c&&ea(c.o)){b=c.o(a[b],Bc.document);break a}}throw new P(61,"Unsupported locator strategy: "+
118
- b);}if(!b)throw new P(7,"No element has been found by "+JSON.stringify(a));return b}throw new P(61,"Selector is of wrong type: "+JSON.stringify(a));}};Z.P={left:Z.V,right:Z.aa,above:Z.R,below:Z.S,near:Z.W};Z.O={left:Z.u,right:Z.u,above:Z.u,below:Z.u,near:Z.u};
119
- Z.U=function(a,b){var c=[];l(a,function(e){e&&ta(b,function(f){var g=f.kind,h=Z.P[g];if(!h)throw new P(61,"Cannot find filter suitable for "+g);return h.apply(null,f.args)(e)},null)&&c.push(e)},null);a=b[b.length-1];var d=Z.O[a?a.kind:"unknown"];return d?(a=d.apply(null,a.args))?Z.ba(a,c):c:c};
116
+ rd.j=function(a,b){return Y.K(a,b,!0)};var Z={F:function(a,b){return function(c){var d=Z.u(a);d=W(d);c=W(c);return b.call(null,d,c)}},R:function(a){return Z.F(a,function(b,c){return c.b+c.height<b.b})},S:function(a){return Z.F(a,function(b,c){return b.b+b.height<c.b})},V:function(a){return Z.F(a,function(b,c){return c.a+c.width<b.a})},aa:function(a){return Z.F(a,function(b,c){return b.a+b.width<c.a})},W:function(a,b){var c;b?c=b:"number"==typeof a.distance&&(c=a.distance);c||(c=50);return function(d){var e=Z.u(a);if(e===d)return!1;e=W(e);
117
+ d=W(d);e=new U(e.a-c,e.b-c,e.width+2*c,e.height+2*c);return e.a<=d.a+d.width&&d.a<=e.a+e.width&&e.b<=d.b+d.height&&d.b<=e.b+e.height}},u:function(a){if(ha(a)&&1==a.nodeType)return a;if(ea(a))return Z.u(a.call(null));if(ha(a)){var b;a:{if(b=sd(a)){var c=td[b];if(c&&ea(c.o)){b=c.o(a[b],Bc.document);break a}}throw new P(61,"Unsupported locator strategy: "+b);}if(!b)throw new P(7,"No element has been found by "+JSON.stringify(a));return b}throw new P(61,"Selector is of wrong type: "+JSON.stringify(a));
118
+ }};Z.P={left:Z.V,right:Z.aa,above:Z.R,below:Z.S,near:Z.W};Z.O={left:Z.u,right:Z.u,above:Z.u,below:Z.u,near:Z.u};Z.U=function(a,b){var c=[];l(a,function(e){e&&ta(b,function(f){var g=f.kind,h=Z.P[g];if(!h)throw new P(61,"Cannot find filter suitable for "+g);return h.apply(null,f.args)(e)},null)&&c.push(e)},null);a=b[b.length-1];var d=Z.O[a?a.kind:"unknown"];return d?(a=d.apply(null,a.args))?Z.ba(a,c):c:c};
120
119
  Z.ba=function(a,b){function c(f){f=W(f);return Math.sqrt(Math.pow(d-(f.a+Math.max(1,f.width)/2),2)+Math.pow(e-(f.b+Math.max(1,f.height)/2),2))}a=W(a);var d=a.a+Math.max(1,a.width)/2,e=a.b+Math.max(1,a.height)/2;xa(b,function(f,g){return c(f)-c(g)});return b};Z.o=function(a,b){a=Z.j(a,b);return 0==a.length?null:a[0]};
121
120
  Z.j=function(a,b){if(!a.hasOwnProperty("root")||!a.hasOwnProperty("filters"))throw new P(61,"Locator not suitable for relative locators: "+JSON.stringify(a));var c=a.filters,d=da(c);if("array"!=d&&("object"!=d||"number"!=typeof c.length))throw new P(61,"Targets should be an array: "+JSON.stringify(a));var e;S(a.root)?e=[a.root]:e=ud(a.root,b);return 0==e.length?[]:Z.U(e,a.filters)};var vd={o:function(a,b){if(""===a)throw new P(32,'Unable to locate an element with the tagName ""');return b.getElementsByTagName(a)[0]||null},j:function(a,b){if(""===a)throw new P(32,'Unable to locate an element with the tagName ""');return b.getElementsByTagName(a)}};var td={className:Dc,"class name":Dc,css:Nc,"css selector":Nc,relative:Z,id:qd,linkText:Y,"link text":Y,name:{o:function(a,b){b=mb(eb(b),"*",null,b);return ua(b,function(c){return Uc(c,"name")==a})},j:function(a,b){b=mb(eb(b),"*",null,b);return pa(b,function(c){return Uc(c,"name")==a})}},partialLinkText:rd,"partial link text":rd,tagName:vd,"tag name":vd,xpath:T};function sd(a){for(var b in a)if(a.hasOwnProperty(b))return b;return null}
122
121
  function ud(a,b){var c=sd(a);if(c){var d=td[c];if(d&&ea(d.j))return d.j(a[c],b||Bc.document)}throw new P(61,"Unsupported locator strategy: "+c);};ca("_",ud);; return this._.apply(null,arguments);}).apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);}
@@ -250,14 +250,6 @@ module Selenium
250
250
  @devices << device
251
251
  device
252
252
  end
253
-
254
- def deprecate_method(device = nil, duration = nil, number = nil, method: :pause)
255
- return unless device || number || duration
256
-
257
- WebDriver.logger.deprecate "ActionBuilder##{method} with ordered parameters",
258
- ':device, :duration, :number keywords',
259
- id: method
260
- end
261
253
  end # ActionBuilder
262
254
  end # WebDriver
263
255
  end # Selenium
@@ -53,9 +53,9 @@ module Selenium
53
53
  options = {%i[out err] => io}
54
54
  options[:pgroup] = true unless Platform.windows? # NOTE: this is a bug only in Windows 7
55
55
 
56
- WebDriver.logger.debug("Starting process: #{@command} with #{options}")
56
+ WebDriver.logger.debug("Starting process: #{@command} with #{options}", id: :process)
57
57
  @pid = Process.spawn(*@command, options)
58
- WebDriver.logger.debug(" -> pid: #{@pid}")
58
+ WebDriver.logger.debug(" -> pid: #{@pid}", id: :process)
59
59
 
60
60
  Process.detach(@pid) if detach
61
61
  end
@@ -64,16 +64,16 @@ module Selenium
64
64
  return unless @pid
65
65
  return if exited?
66
66
 
67
- WebDriver.logger.debug("Sending TERM to process: #{@pid}")
67
+ WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
68
68
  terminate(@pid)
69
69
  poll_for_exit(timeout)
70
70
 
71
- WebDriver.logger.debug(" -> stopped #{@pid}")
71
+ WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
72
72
  rescue TimeoutError, Errno::EINVAL
73
- WebDriver.logger.debug(" -> sending KILL to process: #{@pid}")
73
+ WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
74
74
  kill(@pid)
75
75
  wait
76
- WebDriver.logger.debug(" -> killed #{@pid}")
76
+ WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
77
77
  end
78
78
 
79
79
  def alive?
@@ -83,18 +83,18 @@ module Selenium
83
83
  def exited?
84
84
  return unless @pid
85
85
 
86
- WebDriver.logger.debug("Checking if #{@pid} is exited:")
86
+ WebDriver.logger.debug("Checking if #{@pid} is exited:", id: :process)
87
87
  _, @status = Process.waitpid2(@pid, Process::WNOHANG | Process::WUNTRACED) if @status.nil?
88
88
  return if @status.nil?
89
89
 
90
90
  exit_code = @status.exitstatus || @status.termsig
91
- WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}")
91
+ WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}", id: :process)
92
92
 
93
93
  !!exit_code
94
94
  end
95
95
 
96
96
  def poll_for_exit(timeout)
97
- WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}")
97
+ WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}", id: :process)
98
98
 
99
99
  end_time = Time.now + timeout
100
100
  sleep POLL_INTERVAL until exited? || Time.now > end_time
@@ -26,16 +26,20 @@ module Selenium
26
26
  path ||= Platform.find_binary(klass::EXECUTABLE)
27
27
 
28
28
  path ||= begin
29
- SeleniumManager.driver_path(options)
29
+ SeleniumManager.driver_path(options) unless options.is_a?(Remote::Capabilities)
30
30
  rescue StandardError => e
31
- WebDriver.logger.warn("Unable obtain driver using Selenium Manager\n #{e.message}")
32
- nil
31
+ raise Error::NoSuchDriverError, "Unable to obtain #{klass::EXECUTABLE} using Selenium Manager; #{e.message}"
32
+ end
33
+
34
+ begin
35
+ Platform.assert_file(path)
36
+ Platform.assert_executable(path)
37
+ rescue TypeError
38
+ raise Error::NoSuchDriverError, "Unable to locate or obtain #{klass::EXECUTABLE}"
39
+ rescue Error::WebDriverError => e
40
+ raise Error::NoSuchDriverError, "#{klass::EXECUTABLE} located, but: #{e.message}"
33
41
  end
34
- msg = "Unable to locate the #{klass::EXECUTABLE} executable; for more information on how to install drivers, " \
35
- 'see https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/'
36
- raise Error::WebDriverError, msg unless path
37
42
 
38
- Platform.assert_executable path
39
43
  path
40
44
  end
41
45
  end
@@ -34,13 +34,20 @@ module Selenium
34
34
  WebDriverError
35
35
  end
36
36
 
37
+ SUPPORT_MSG = 'For documentation on this error, please visit:'
38
+ ERROR_URL = 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors'
39
+
37
40
  class WebDriverError < StandardError; end
38
41
 
39
42
  #
40
43
  # An element could not be located on the page using the given search parameters.
41
44
  #
42
45
 
43
- class NoSuchElementError < WebDriverError; end
46
+ class NoSuchElementError < WebDriverError
47
+ def initialize(msg = '')
48
+ super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}#no-such-element-exception")
49
+ end
50
+ end
44
51
 
45
52
  #
46
53
  # A command to switch to a frame could not be satisfied because the frame could not be found.
@@ -58,7 +65,11 @@ module Selenium
58
65
  # A command failed because the referenced element is no longer attached to the DOM.
59
66
  #
60
67
 
61
- class StaleElementReferenceError < WebDriverError; end
68
+ class StaleElementReferenceError < WebDriverError
69
+ def initialize(msg = '')
70
+ super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}#stale-element-reference-exception")
71
+ end
72
+ end
62
73
 
63
74
  #
64
75
  # A command failed because the referenced shadow root is no longer attached to the DOM.
@@ -132,7 +143,11 @@ module Selenium
132
143
  # Argument was an invalid selector.
133
144
  #
134
145
 
135
- class InvalidSelectorError < WebDriverError; end
146
+ class InvalidSelectorError < WebDriverError
147
+ def initialize(msg = '')
148
+ super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}#invalid-selector-exception")
149
+ end
150
+ end
136
151
 
137
152
  #
138
153
  # A new session could not be created.
@@ -212,6 +227,16 @@ module Selenium
212
227
  #
213
228
 
214
229
  class UnsupportedOperationError < WebDriverError; end
230
+
231
+ #
232
+ # Indicates that driver was not specified and could not be located.
233
+ #
234
+
235
+ class NoSuchDriverError < WebDriverError
236
+ def initialize(msg = '')
237
+ super("#{msg}; #{SUPPORT_MSG} #{ERROR_URL}/driver_location")
238
+ end
239
+ end
215
240
  end # Error
216
241
  end # WebDriver
217
242
  end # Selenium
@@ -38,22 +38,33 @@ module Selenium
38
38
 
39
39
  def_delegators :@logger,
40
40
  :close,
41
- :debug, :debug?,
42
- :info, :info?,
41
+ :debug?,
42
+ :info?,
43
43
  :warn?,
44
- :error, :error?,
44
+ :error?,
45
45
  :fatal, :fatal?,
46
- :level, :level=
46
+ :level
47
47
 
48
48
  #
49
49
  # @param [String] progname Allow child projects to use Selenium's Logger pattern
50
50
  #
51
- def initialize(progname = 'Selenium', ignored: nil)
52
- @logger = create_logger(progname)
51
+ def initialize(progname = 'Selenium', default_level: nil, ignored: nil, allowed: nil)
52
+ default_level ||= $DEBUG || ENV.key?('DEBUG') ? :debug : :warn
53
+
54
+ @logger = create_logger(progname, level: default_level)
53
55
  @ignored = Array(ignored)
56
+ @allowed = Array(allowed)
54
57
  @first_warning = false
55
58
  end
56
59
 
60
+ def level=(level)
61
+ if level == :info && @logger.level == :info
62
+ info(':info is now the default log level, to see additional logging, set log level to :debug')
63
+ end
64
+
65
+ @logger.level = level
66
+ end
67
+
57
68
  #
58
69
  # Changes logger output to a new IO.
59
70
  #
@@ -81,34 +92,64 @@ module Selenium
81
92
  #
82
93
  # Will not log the provided ID.
83
94
  #
84
- # @param [Array, Symbol] id
95
+ # @param [Array, Symbol] ids
85
96
  #
86
- def ignore(id)
87
- Array(id).each { |ignore| @ignored << ignore }
97
+ def ignore(*ids)
98
+ @ignored += Array(ids).flatten
88
99
  end
89
100
 
90
101
  #
91
- # Overrides default #warn to skip ignored messages by provided id
102
+ # Will only log the provided ID.
103
+ #
104
+ # @param [Array, Symbol] ids
105
+ #
106
+ def allow(ids)
107
+ @allowed += Array(ids).flatten
108
+ end
109
+
110
+ #
111
+ # Used to supply information of interest for debugging a problem
112
+ # Overrides default #debug to skip ignored messages by provided id
92
113
  #
93
114
  # @param [String] message
94
115
  # @param [Symbol, Array<Sybmol>] id
95
116
  # @yield see #deprecate
96
117
  #
97
- def warn(message, id: [])
98
- unless @first_warning
99
- @first_warning = true
100
- warn("Details on how to use and modify Selenium logger:\n", id: [:logger_info]) do
101
- "https://selenium.dev/documentation/webdriver/troubleshooting/logging#ruby\n"
102
- end
103
- end
118
+ def debug(message, id: [], &block)
119
+ discard_or_log(:debug, message, id, &block)
120
+ end
104
121
 
105
- id = Array(id)
106
- return if (@ignored & id).any?
122
+ #
123
+ # Used to supply information of general interest
124
+ #
125
+ # @param [String] message
126
+ # @param [Symbol, Array<Sybmol>] id
127
+ # @yield see #deprecate
128
+ #
129
+ def info(message, id: [], &block)
130
+ discard_or_log(:info, message, id, &block)
131
+ end
107
132
 
108
- msg = id.empty? ? message : "[#{id.map(&:inspect).join(', ')}] #{message} "
109
- msg += " #{yield}" if block_given?
133
+ #
134
+ # Used to supply information that suggests an error occurred
135
+ #
136
+ # @param [String] message
137
+ # @param [Symbol, Array<Sybmol>] id
138
+ # @yield see #deprecate
139
+ #
140
+ def error(message, id: [], &block)
141
+ discard_or_log(:error, message, id, &block)
142
+ end
110
143
 
111
- @logger.warn { msg }
144
+ #
145
+ # Used to supply information that suggests action be taken by user
146
+ #
147
+ # @param [String] message
148
+ # @param [Symbol, Array<Sybmol>] id
149
+ # @yield see #deprecate
150
+ #
151
+ def warn(message, id: [], &block)
152
+ discard_or_log(:warn, message, id, &block)
112
153
  end
113
154
 
114
155
  #
@@ -122,11 +163,11 @@ module Selenium
122
163
  #
123
164
  def deprecate(old, new = nil, id: [], reference: '', &block)
124
165
  id = Array(id)
125
- return if @ignored.include?(:deprecations) || (@ignored & id).any?
166
+ return if @ignored.include?(:deprecations)
126
167
 
127
- ids = id.empty? ? '' : "[#{id.map(&:inspect).join(', ')}] "
168
+ id << :deprecations if @allowed.include?(:deprecations)
128
169
 
129
- message = +"[DEPRECATION] #{ids}#{old} is deprecated"
170
+ message = +"[DEPRECATION] #{old} is deprecated"
130
171
  message << if new
131
172
  ". Use #{new} instead."
132
173
  else
@@ -134,15 +175,15 @@ module Selenium
134
175
  end
135
176
  message << " See explanation for this deprecation: #{reference}." unless reference.empty?
136
177
 
137
- warn message, &block
178
+ discard_or_log(:warn, message, id, &block)
138
179
  end
139
180
 
140
181
  private
141
182
 
142
- def create_logger(name)
183
+ def create_logger(name, level:)
143
184
  logger = ::Logger.new($stdout)
144
185
  logger.progname = name
145
- logger.level = default_level
186
+ logger.level = level
146
187
  logger.formatter = proc do |severity, time, progname, msg|
147
188
  "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
148
189
  end
@@ -150,8 +191,24 @@ module Selenium
150
191
  logger
151
192
  end
152
193
 
153
- def default_level
154
- $DEBUG || ENV.key?('DEBUG') ? :debug : :warn
194
+ def discard_or_log(level, message, id)
195
+ id = Array(id)
196
+ return if (@ignored & id).any?
197
+ return if @allowed.any? && (@allowed & id).none?
198
+
199
+ return if ::Logger::Severity.const_get(level.upcase) < @logger.level
200
+
201
+ unless @first_warning
202
+ @first_warning = true
203
+ info("Details on how to use and modify Selenium logger:\n", id: [:logger_info]) do
204
+ "https://selenium.dev/documentation/webdriver/troubleshooting/logging\n"
205
+ end
206
+ end
207
+
208
+ msg = id.empty? ? message : "[#{id.map(&:inspect).join(', ')}] #{message} "
209
+ msg += " #{yield}" if block_given?
210
+
211
+ @logger.send(level) { msg }
155
212
  end
156
213
  end # Logger
157
214
  end # WebDriver
@@ -128,7 +128,7 @@ module Selenium
128
128
 
129
129
  unless options.empty?
130
130
  msg = 'These options are not w3c compliant and will result in failures in a future release'
131
- WebDriver.logger.warn("#{msg}: #{options}")
131
+ WebDriver.logger.warn("#{msg}: #{options}", id: :w3c_options)
132
132
  browser_options.merge!(options)
133
133
  end
134
134
 
@@ -34,7 +34,7 @@ module Selenium
34
34
  Platform.interfaces.each do |host|
35
35
  TCPServer.new(host, port).close
36
36
  rescue *IGNORED_ERRORS => e
37
- WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
37
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})", id: :driver_service)
38
38
  # ignored - some machines appear unable to bind to some of their interfaces
39
39
  end
40
40
 
@@ -152,7 +152,7 @@ module Selenium
152
152
  'socksUsername' => socks_username,
153
153
  'socksPassword' => socks_password,
154
154
  'socksVersion' => socks_version
155
- }.delete_if { |_k, v| v.nil? }
155
+ }.compact
156
156
 
157
157
  json_result if json_result.length > 1
158
158
  end
@@ -27,19 +27,31 @@ module Selenium
27
27
  # @api private
28
28
  #
29
29
  class SeleniumManager
30
- BIN_PATH = '../../../../../bin'
31
-
32
30
  class << self
31
+ attr_writer :bin_path
32
+
33
+ def bin_path
34
+ @bin_path ||= '../../../../../bin'
35
+ end
36
+
33
37
  # @param [Options] options browser options.
34
38
  # @return [String] the path to the correct driver.
35
39
  def driver_path(options)
36
- message = 'applicable driver not found; attempting to install with Selenium Manager'
37
- WebDriver.logger.warn(message)
40
+ message = 'applicable driver not found; attempting to install with Selenium Manager (Beta)'
41
+ WebDriver.logger.debug(message, id: :selenium_manager)
38
42
 
39
- unless options.is_a?(Options)
40
- raise ArgumentError, "SeleniumManager requires a WebDriver::Options instance, not #{options.inspect}"
41
- end
43
+ command = generate_command(binary, options)
44
+
45
+ location = run(*command)
46
+ WebDriver.logger.debug("Driver found at #{location}", id: :selenium_manager)
47
+ Platform.assert_executable location
48
+
49
+ location
50
+ end
51
+
52
+ private
42
53
 
54
+ def generate_command(binary, options)
43
55
  command = [binary, '--browser', options.browser_name, '--output', 'json']
44
56
  if options.browser_version
45
57
  command << '--browser-version'
@@ -49,21 +61,18 @@ module Selenium
49
61
  command << '--browser-path'
50
62
  command << options.binary.gsub('\\', '\\\\\\')
51
63
  end
64
+ if options.proxy
65
+ command << '--proxy'
66
+ (command << options.proxy.ssl) || options.proxy.http
67
+ end
52
68
  command << '--debug' if WebDriver.logger.debug?
53
-
54
- location = run(*command)
55
- WebDriver.logger.debug("Driver found at #{location}")
56
- Platform.assert_executable location
57
-
58
- location
69
+ command
59
70
  end
60
71
 
61
- private
62
-
63
72
  # @return [String] the path to the correct selenium manager
64
73
  def binary
65
74
  @binary ||= begin
66
- path = File.expand_path(BIN_PATH, __FILE__)
75
+ path = File.expand_path(bin_path, __FILE__)
67
76
  path << if Platform.windows?
68
77
  '/windows/selenium-manager.exe'
69
78
  elsif Platform.mac?
@@ -72,32 +81,40 @@ module Selenium
72
81
  '/linux/selenium-manager'
73
82
  end
74
83
  location = File.expand_path(path, __FILE__)
75
- unless location.is_a?(String) && File.exist?(location) && File.executable?(location)
76
- raise Error::WebDriverError, 'Unable to obtain Selenium Manager'
84
+
85
+ begin
86
+ Platform.assert_file(location)
87
+ Platform.assert_executable(location)
88
+ rescue TypeError
89
+ raise Error::WebDriverError,
90
+ "Unable to locate or obtain Selenium Manager binary; #{location} is not a valid file object"
91
+ rescue Error::WebDriverError => e
92
+ raise Error::WebDriverError, "Selenium Manager binary located, but #{e.message}"
77
93
  end
78
94
 
79
- WebDriver.logger.debug("Selenium Manager found at #{location}")
95
+ WebDriver.logger.debug("Selenium Manager binary found at #{location}", id: :selenium_manager)
80
96
  location
81
97
  end
82
98
  end
83
99
 
84
100
  def run(*command)
85
- WebDriver.logger.debug("Executing Process #{command}")
101
+ WebDriver.logger.debug("Executing Process #{command}", id: :selenium_manager)
86
102
 
87
103
  begin
88
104
  stdout, stderr, status = Open3.capture3(*command)
89
105
  json_output = stdout.empty? ? nil : JSON.parse(stdout)
90
106
  result = json_output&.dig('result', 'message')
91
107
  rescue StandardError => e
92
- raise Error::WebDriverError, "Unsuccessful command executed: #{command}", e.message
108
+ raise Error::WebDriverError, "Unsuccessful command executed: #{command}; #{e.message}"
93
109
  end
94
110
 
95
- if status.exitstatus.positive?
96
- raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
111
+ (json_output&.fetch('logs') || []).each do |log|
112
+ level = log['level'].casecmp('info').zero? ? 'debug' : log['level'].downcase
113
+ WebDriver.logger.send(level, log['message'], id: :selenium_manager)
97
114
  end
98
115
 
99
- json_output['logs'].each do |log|
100
- WebDriver.logger.send(log['level'].downcase, log['message'])
116
+ if status.exitstatus.positive?
117
+ raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
101
118
  end
102
119
 
103
120
  result
@@ -57,7 +57,7 @@ module Selenium
57
57
  end
58
58
  end
59
59
 
60
- attr_accessor :host, :executable_path, :port, :args
60
+ attr_accessor :host, :executable_path, :port, :log, :args
61
61
  alias extra_args args
62
62
 
63
63
  #
@@ -66,13 +66,21 @@ module Selenium
66
66
  # @api private
67
67
  #
68
68
 
69
- def initialize(path: nil, port: nil, args: nil)
69
+ def initialize(path: nil, port: nil, log: nil, args: nil)
70
70
  port ||= self.class::DEFAULT_PORT
71
71
  args ||= []
72
72
 
73
73
  @executable_path = path
74
74
  @host = Platform.localhost
75
75
  @port = Integer(port)
76
+ @log = case log
77
+ when :stdout
78
+ $stdout
79
+ when :stderr
80
+ $stderr
81
+ else
82
+ log
83
+ end
76
84
 
77
85
  @args = args.is_a?(Hash) ? extract_service_args(args) : args
78
86
 
@@ -41,6 +41,7 @@ module Selenium
41
41
  @host = Platform.localhost
42
42
  @port = config.port
43
43
  @extra_args = config.args
44
+ @io = config.log
44
45
  @shutdown_supported = config.shutdown_supported
45
46
 
46
47
  raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
@@ -77,9 +78,10 @@ module Selenium
77
78
  private
78
79
 
79
80
  def build_process(*command)
80
- WebDriver.logger.debug("Executing Process #{command}")
81
+ WebDriver.logger.debug("Executing Process #{command}", id: :driver_service)
81
82
  @process = ChildProcess.build(*command)
82
- @process.io = WebDriver.logger.io if WebDriver.logger.debug?
83
+ @io ||= WebDriver.logger.io if WebDriver.logger.debug?
84
+ @process.io = @io if @io
83
85
 
84
86
  @process
85
87
  end
@@ -70,7 +70,7 @@ module Selenium
70
70
  @server.close_on_exec = true
71
71
  true
72
72
  rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
73
- WebDriver.logger.debug("#{self}: #{e.message}")
73
+ WebDriver.logger.debug("#{self}: #{e.message}", id: :driver_service)
74
74
  false
75
75
  end
76
76
 
@@ -93,7 +93,7 @@ module Selenium
93
93
  true
94
94
  rescue *NOT_CONNECTED_ERRORS
95
95
  sock&.close
96
- WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}")
96
+ WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}", id: :driver_service)
97
97
  false
98
98
  end
99
99
  end
@@ -55,7 +55,7 @@ module Selenium
55
55
  def send_cmd(**payload)
56
56
  id = next_id
57
57
  data = payload.merge(id: id)
58
- WebDriver.logger.debug "WebSocket -> #{data}"[...MAX_LOG_MESSAGE_SIZE]
58
+ WebDriver.logger.debug "WebSocket -> #{data}"[...MAX_LOG_MESSAGE_SIZE], id: :bidi
59
59
  data = JSON.generate(data)
60
60
  out_frame = WebSocket::Frame::Outgoing::Client.new(version: ws.version, data: data, type: 'text')
61
61
  socket.write(out_frame.to_s)
@@ -112,7 +112,7 @@ module Selenium
112
112
 
113
113
  message = JSON.parse(message)
114
114
  messages[message['id']] = message
115
- WebDriver.logger.debug "WebSocket <- #{message}"[...MAX_LOG_MESSAGE_SIZE]
115
+ WebDriver.logger.debug "WebSocket <- #{message}"[...MAX_LOG_MESSAGE_SIZE], id: :bidi
116
116
 
117
117
  message
118
118
  end
@@ -60,7 +60,7 @@ module Selenium
60
60
  "#{namespace}::#{Object.const_get(methods_to_classes)[method]}"
61
61
  else
62
62
  # selenium-devtools 0.112 and older
63
- "#{namespace}::#{method.capitalize}}"
63
+ "#{namespace}::#{method.capitalize}"
64
64
  end
65
65
 
66
66
  return unless Object.const_defined?(desired_class)
@@ -26,6 +26,20 @@ module Selenium
26
26
  KEY = 'ms:edgeOptions'
27
27
  BROWSER = 'MicrosoftEdge'
28
28
 
29
+ #
30
+ # Changes the browser name enable webview2
31
+ # see: https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/webdriver
32
+ # Automation of WebView2 apps with Microsoft Edge WebDriver
33
+ #
34
+ # @example Enable webview2
35
+ # options = Selenium::WebDriver::Edge::Options.new
36
+ # options.webview2!
37
+ #
38
+
39
+ def webview2!
40
+ @options[:browser_name] = 'webview2'
41
+ end
42
+
29
43
  private
30
44
 
31
45
  def enable_logging(browser_options)
@@ -160,7 +160,7 @@ module Selenium
160
160
  destination = File.join(directory, 'extensions')
161
161
 
162
162
  @extensions.each do |name, extension|
163
- WebDriver.logger.debug({extenstion: name}.inspect)
163
+ WebDriver.logger.debug({extension: name}.inspect, id: :firefox_profile)
164
164
  extension.write_to(destination)
165
165
  end
166
166
  end
@@ -407,7 +407,8 @@ module Selenium
407
407
 
408
408
  def upload(local_file)
409
409
  unless File.file?(local_file)
410
- WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!")
410
+ WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!",
411
+ id: :file_detector)
411
412
  raise Error::WebDriverError, "You are trying to work with something that isn't a file."
412
413
  end
413
414
 
@@ -443,7 +444,7 @@ module Selenium
443
444
  end
444
445
 
445
446
  def element_attribute(element, name)
446
- WebDriver.logger.info "Using script for :getAttribute of #{name}"
447
+ WebDriver.logger.debug "Using script for :getAttribute of #{name}", id: :script
447
448
  execute_atom :getAttribute, element, name
448
449
  end
449
450
 
@@ -503,7 +504,7 @@ module Selenium
503
504
  end
504
505
 
505
506
  def element_displayed?(element)
506
- WebDriver.logger.info 'Using script for :isDisplayed'
507
+ WebDriver.logger.debug 'Using script for :isDisplayed', id: :script
507
508
  execute_atom :isDisplayed, element
508
509
  end
509
510
 
@@ -615,7 +616,7 @@ module Selenium
615
616
  raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
616
617
  end
617
618
 
618
- WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
619
+ WebDriver.logger.debug("-> #{verb.to_s.upcase} #{path}", id: :command)
619
620
  http.call(verb, path, command_hash)['value']
620
621
  end
621
622
 
@@ -682,7 +683,7 @@ module Selenium
682
683
  [how, what]
683
684
  end
684
685
 
685
- ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/.freeze
686
+ ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
686
687
  UNICODE_CODE_POINT = 30
687
688
 
688
689
  # Escapes invalid characters in CSS selector.
@@ -49,8 +49,8 @@ module Selenium
49
49
  payload = JSON.generate(command_hash)
50
50
  headers['Content-Length'] = payload.bytesize.to_s if %i[post put].include?(verb)
51
51
 
52
- WebDriver.logger.info(" >>> #{url} | #{payload}")
53
- WebDriver.logger.debug(" > #{headers.inspect}")
52
+ WebDriver.logger.debug(" >>> #{url} | #{payload}", id: :command)
53
+ WebDriver.logger.debug(" > #{headers.inspect}", id: :header)
54
54
  elsif verb == :post
55
55
  payload = '{}'
56
56
  headers['Content-Length'] = '2'
@@ -75,7 +75,7 @@ module Selenium
75
75
  code = code.to_i
76
76
  body = body.to_s.strip
77
77
  content_type = content_type.to_s
78
- WebDriver.logger.info("<- #{body}")
78
+ WebDriver.logger.debug("<- #{body}", id: :command)
79
79
 
80
80
  if content_type.include? CONTENT_TYPE
81
81
  raise Error::WebDriverError, "empty body: #{content_type.inspect} (#{code})\n#{body}" if body.empty?
@@ -83,7 +83,7 @@ module Selenium
83
83
  c.max_redirects = MAX_REDIRECTS
84
84
  c.follow_location = true
85
85
  c.timeout = @timeout if @timeout
86
- c.verbose = WebDriver.logger.info?
86
+ c.verbose = WebDriver.logger.debug?
87
87
 
88
88
  c
89
89
  end
@@ -100,7 +100,7 @@ module Selenium
100
100
 
101
101
  request(:get, URI.parse(response['Location']), DEFAULT_HEADERS.dup, nil, redirects + 1)
102
102
  else
103
- WebDriver.logger.debug(" <<< #{response.instance_variable_get(:@header).inspect}")
103
+ WebDriver.logger.debug(" <<< #{response.instance_variable_get(:@header).inspect}", id: :header)
104
104
  create_response response.code, response.body, response.content_type
105
105
  end
106
106
  end
@@ -24,6 +24,16 @@ module Selenium
24
24
  DEFAULT_PORT = 7050
25
25
  EXECUTABLE = 'safaridriver'
26
26
  SHUTDOWN_SUPPORTED = false
27
+
28
+ def initialize(path: nil, port: nil, log: nil, args: nil)
29
+ raise Error::WebDriverError, 'Safari Service does not support setting log output' if log
30
+
31
+ super
32
+ end
33
+
34
+ def log=(*)
35
+ raise Error::WebDriverError, 'Safari Service does not support setting log output'
36
+ end
27
37
  end # Service
28
38
  end # Safari
29
39
  end # WebDriver
@@ -23,27 +23,27 @@ module Selenium
23
23
  class Color
24
24
  RGB_PATTERN = /^\s*rgb\(\s*(\d{1,3})\s*,
25
25
  \s*(\d{1,3})\s*,
26
- \s*(\d{1,3})\s*\)\s*$/x.freeze
26
+ \s*(\d{1,3})\s*\)\s*$/x
27
27
  RGB_PCT_PATTERN = /^\s*rgb\(\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,
28
28
  \s*(\d{1,3}|\d{1,2}\.\d+)%\s*,
29
- \s*(\d{1,3}|\d{1,2}\.\d+)%\s*\)\s*$/x.freeze
29
+ \s*(\d{1,3}|\d{1,2}\.\d+)%\s*\)\s*$/x
30
30
  RGBA_PATTERN = /^\s*rgba\(\s*(\d{1,3})\s*,
31
31
  \s*(\d{1,3})\s*,
32
32
  \s*(\d{1,3})\s*,
33
- \s*(0|1|0\.\d+)\s*\)\s*$/x.freeze
33
+ \s*(0|1|0\.\d+)\s*\)\s*$/x
34
34
  RGBA_PCT_PATTERN = /^\s*rgba\(\s*(\d{1,3}|\d{1,2}\.\d+)
35
35
  %\s*,\s*(\d{1,3}|\d{1,2}\.\d+)
36
36
  %\s*,\s*(\d{1,3}|\d{1,2}\.\d+)
37
- %\s*,\s*(0|1|0\.\d+)\s*\)\s*$/x.freeze
38
- HEX_PATTERN = /#(\h{2})(\h{2})(\h{2})/.freeze
39
- HEX3_PATTERN = /#(\h)(\h)(\h)/.freeze
37
+ %\s*,\s*(0|1|0\.\d+)\s*\)\s*$/x
38
+ HEX_PATTERN = /#(\h{2})(\h{2})(\h{2})/
39
+ HEX3_PATTERN = /#(\h)(\h)(\h)/
40
40
  HSL_PATTERN = /^\s*hsl\(\s*(\d{1,3})\s*,
41
41
  \s*(\d{1,3})%\s*,
42
- \s*(\d{1,3})%\s*\)\s*$/x.freeze
42
+ \s*(\d{1,3})%\s*\)\s*$/x
43
43
  HSLA_PATTERN = /^\s*hsla\(\s*(\d{1,3})\s*,
44
44
  \s*(\d{1,3})%\s*,
45
45
  \s*(\d{1,3})%\s*,
46
- \s*(0|1|0\.\d+)\s*\)\s*$/x.freeze
46
+ \s*(0|1|0\.\d+)\s*\)\s*$/x
47
47
 
48
48
  attr_reader :red, :green, :blue, :alpha
49
49
 
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- VERSION = '4.9.0'
22
+ VERSION = '4.10.0'
23
23
  end # WebDriver
24
24
  end # Selenium
@@ -95,7 +95,8 @@ module Selenium
95
95
  #
96
96
 
97
97
  def self.logger(**opts)
98
- @logger ||= WebDriver::Logger.new('Selenium', **opts)
98
+ level = $DEBUG || ENV.key?('DEBUG') ? :debug : :info
99
+ @logger ||= WebDriver::Logger.new('Selenium', default_level: level, **opts)
99
100
  end
100
101
  end # WebDriver
101
102
  end # Selenium
@@ -30,7 +30,7 @@ Gem::Specification.new do |s|
30
30
  }
31
31
 
32
32
  s.required_rubygems_version = Gem::Requirement.new('> 1.3.1') if s.respond_to? :required_rubygems_version=
33
- s.required_ruby_version = Gem::Requirement.new('>= 2.7')
33
+ s.required_ruby_version = Gem::Requirement.new('>= 3.0')
34
34
 
35
35
  s.files = [
36
36
  'CHANGES',
@@ -53,8 +53,8 @@ Gem::Specification.new do |s|
53
53
  s.add_runtime_dependency 'rubyzip', ['>= 1.2.2', '< 3.0']
54
54
  s.add_runtime_dependency 'websocket', ['~> 1.0']
55
55
 
56
- s.add_development_dependency 'pry', ['~> 0.14']
57
56
  s.add_development_dependency 'rack', ['~> 2.0']
57
+ s.add_development_dependency 'rake', ['~> 13.0']
58
58
  s.add_development_dependency 'rspec', ['~> 3.0']
59
59
  s.add_development_dependency 'rubocop', ['~> 1.42']
60
60
  s.add_development_dependency 'rubocop-performance', ['~> 1.15']
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: selenium-webdriver
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.0
4
+ version: 4.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Rodionov
8
8
  - Titus Fortner
9
9
  - Thomas Walpole
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-04-21 00:00:00.000000000 Z
13
+ date: 2023-06-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rexml
@@ -67,33 +67,33 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: rack
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.14'
75
+ version: '2.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.14'
82
+ version: '2.0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rack
84
+ name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '2.0'
89
+ version: '13.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '2.0'
96
+ version: '13.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -389,7 +389,7 @@ metadata:
389
389
  github_repo: ssh://github.com/SeleniumHQ/selenium
390
390
  source_code_uri: https://github.com/SeleniumHQ/selenium/tree/trunk/rb
391
391
  rubygems_mfa_required: 'true'
392
- post_install_message:
392
+ post_install_message:
393
393
  rdoc_options: []
394
394
  require_paths:
395
395
  - lib
@@ -397,15 +397,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
397
397
  requirements:
398
398
  - - ">="
399
399
  - !ruby/object:Gem::Version
400
- version: '2.7'
400
+ version: '3.0'
401
401
  required_rubygems_version: !ruby/object:Gem::Requirement
402
402
  requirements:
403
403
  - - ">"
404
404
  - !ruby/object:Gem::Version
405
405
  version: 1.3.1
406
406
  requirements: []
407
- rubygems_version: 3.1.6
408
- signing_key:
407
+ rubygems_version: 3.2.33
408
+ signing_key:
409
409
  specification_version: 4
410
410
  summary: Selenium is a browser automation tool for automated testing of webapps and
411
411
  more