selenium-webdriver 4.2.1 → 4.15.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +252 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +1 -1
  5. data/NOTICE +1 -1
  6. data/README.md +2 -2
  7. data/bin/linux/selenium-manager +0 -0
  8. data/bin/macos/selenium-manager +0 -0
  9. data/bin/windows/selenium-manager.exe +0 -0
  10. data/lib/selenium/server.rb +22 -30
  11. data/lib/selenium/webdriver/atoms/findElements.js +3 -4
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
  14. data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
  15. data/lib/selenium/webdriver/atoms.rb +5 -3
  16. data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
  17. data/lib/selenium/webdriver/{common/driver_extensions/has_network_connection.rb → bidi/browsing_context_info.rb} +10 -12
  18. data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
  19. data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
  20. data/lib/selenium/webdriver/{common/driver_extensions/has_location.rb → bidi/log/filter_by.rb} +14 -11
  21. data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
  22. data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
  23. data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
  24. data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
  25. data/lib/selenium/webdriver/bidi/session.rb +13 -0
  26. data/lib/selenium/webdriver/bidi.rb +3 -2
  27. data/lib/selenium/webdriver/chrome/driver.rb +9 -29
  28. data/lib/selenium/webdriver/chrome/features.rb +8 -71
  29. data/lib/selenium/webdriver/chrome/options.rb +3 -223
  30. data/lib/selenium/webdriver/chrome/profile.rb +3 -83
  31. data/lib/selenium/webdriver/chrome/service.rb +4 -19
  32. data/lib/selenium/webdriver/chrome.rb +0 -16
  33. data/lib/selenium/webdriver/chromium/driver.rb +60 -0
  34. data/lib/selenium/webdriver/chromium/features.rb +99 -0
  35. data/lib/selenium/webdriver/chromium/options.rb +243 -0
  36. data/lib/selenium/webdriver/chromium/profile.rb +113 -0
  37. data/lib/selenium/webdriver/chromium.rb +29 -0
  38. data/lib/selenium/webdriver/common/action_builder.rb +11 -58
  39. data/lib/selenium/webdriver/common/child_process.rb +124 -0
  40. data/lib/selenium/webdriver/common/driver.rb +29 -79
  41. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
  42. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
  43. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
  44. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
  45. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
  46. data/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +0 -2
  47. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +0 -1
  48. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
  49. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +0 -2
  50. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
  51. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
  52. data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
  53. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
  54. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +0 -1
  55. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
  56. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -69
  57. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
  58. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +0 -2
  59. data/lib/selenium/webdriver/common/driver_finder.rb +45 -0
  60. data/lib/selenium/webdriver/common/element.rb +7 -7
  61. data/lib/selenium/webdriver/common/error.rb +27 -4
  62. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
  63. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +19 -36
  64. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +2 -2
  65. data/lib/selenium/webdriver/common/interactions/scroll.rb +7 -5
  66. data/lib/selenium/webdriver/common/local_driver.rb +46 -0
  67. data/lib/selenium/webdriver/common/logger.rb +90 -25
  68. data/lib/selenium/webdriver/common/manager.rb +0 -27
  69. data/lib/selenium/webdriver/common/options.rb +17 -17
  70. data/lib/selenium/webdriver/common/platform.rb +4 -50
  71. data/lib/selenium/webdriver/common/port_prober.rb +1 -1
  72. data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
  73. data/lib/selenium/webdriver/common/proxy.rb +2 -2
  74. data/lib/selenium/webdriver/common/selenium_manager.rb +134 -0
  75. data/lib/selenium/webdriver/common/service.rb +21 -30
  76. data/lib/selenium/webdriver/common/service_manager.rb +6 -12
  77. data/lib/selenium/webdriver/common/shadow_root.rb +1 -2
  78. data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
  79. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  80. data/lib/selenium/webdriver/common/takes_screenshot.rb +2 -3
  81. data/lib/selenium/webdriver/common/target_locator.rb +2 -3
  82. data/lib/selenium/webdriver/common/timeouts.rb +2 -2
  83. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
  84. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
  85. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  86. data/lib/selenium/webdriver/common/websocket_connection.rb +22 -7
  87. data/lib/selenium/webdriver/common/window.rb +6 -6
  88. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  89. data/lib/selenium/webdriver/common.rb +8 -3
  90. data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
  91. data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
  92. data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
  93. data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
  94. data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
  95. data/lib/selenium/webdriver/devtools/request.rb +0 -2
  96. data/lib/selenium/webdriver/devtools/response.rb +0 -2
  97. data/lib/selenium/webdriver/devtools.rb +12 -2
  98. data/lib/selenium/webdriver/edge/driver.rb +9 -3
  99. data/lib/selenium/webdriver/edge/features.rb +7 -4
  100. data/lib/selenium/webdriver/edge/options.rb +17 -5
  101. data/lib/selenium/webdriver/edge/profile.rb +2 -2
  102. data/lib/selenium/webdriver/edge/service.rb +8 -7
  103. data/lib/selenium/webdriver/edge.rb +0 -2
  104. data/lib/selenium/webdriver/firefox/driver.rb +8 -2
  105. data/lib/selenium/webdriver/firefox/features.rb +10 -3
  106. data/lib/selenium/webdriver/firefox/options.rb +4 -15
  107. data/lib/selenium/webdriver/firefox/profile.rb +7 -7
  108. data/lib/selenium/webdriver/firefox/service.rb +0 -18
  109. data/lib/selenium/webdriver/firefox.rb +0 -14
  110. data/lib/selenium/webdriver/ie/driver.rb +7 -1
  111. data/lib/selenium/webdriver/{common/driver_extensions/has_remote_status.rb → ie/features.rb} +10 -7
  112. data/lib/selenium/webdriver/ie/options.rb +4 -3
  113. data/lib/selenium/webdriver/ie/service.rb +0 -22
  114. data/lib/selenium/webdriver/ie.rb +4 -17
  115. data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +10 -4
  116. data/lib/selenium/webdriver/remote/bridge.rb +48 -39
  117. data/lib/selenium/webdriver/remote/capabilities.rb +3 -53
  118. data/lib/selenium/webdriver/remote/driver.rb +35 -14
  119. data/lib/selenium/webdriver/remote/features.rb +75 -0
  120. data/lib/selenium/webdriver/remote/http/common.rb +3 -3
  121. data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
  122. data/lib/selenium/webdriver/remote/http/default.rb +2 -2
  123. data/lib/selenium/webdriver/remote/response.rb +0 -1
  124. data/lib/selenium/webdriver/remote/server_error.rb +1 -1
  125. data/lib/selenium/webdriver/remote.rb +1 -1
  126. data/lib/selenium/webdriver/safari/driver.rb +7 -1
  127. data/lib/selenium/webdriver/safari/features.rb +5 -3
  128. data/lib/selenium/webdriver/safari/options.rb +5 -1
  129. data/lib/selenium/webdriver/safari/service.rb +10 -4
  130. data/lib/selenium/webdriver/safari.rb +1 -15
  131. data/lib/selenium/webdriver/support/color.rb +17 -17
  132. data/lib/selenium/webdriver/support/guards/guard.rb +6 -5
  133. data/lib/selenium/webdriver/support/guards/guard_condition.rb +0 -2
  134. data/lib/selenium/webdriver/support/guards.rb +1 -1
  135. data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
  136. data/lib/selenium/webdriver/support/select.rb +3 -1
  137. data/lib/selenium/webdriver/version.rb +1 -1
  138. data/lib/selenium/webdriver.rb +5 -4
  139. data/selenium-webdriver.gemspec +10 -11
  140. metadata +51 -90
  141. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  142. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
  143. data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
@@ -20,7 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Error
23
-
24
23
  #
25
24
  # Returns exception from its string representation.
26
25
  # @param [String, nil] error
@@ -35,13 +34,20 @@ module Selenium
35
34
  WebDriverError
36
35
  end
37
36
 
37
+ SUPPORT_MSG = 'For documentation on this error, please visit:'
38
+ ERROR_URL = 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors'
39
+
38
40
  class WebDriverError < StandardError; end
39
41
 
40
42
  #
41
43
  # An element could not be located on the page using the given search parameters.
42
44
  #
43
45
 
44
- 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
45
51
 
46
52
  #
47
53
  # A command to switch to a frame could not be satisfied because the frame could not be found.
@@ -59,7 +65,11 @@ module Selenium
59
65
  # A command failed because the referenced element is no longer attached to the DOM.
60
66
  #
61
67
 
62
- 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
63
73
 
64
74
  #
65
75
  # A command failed because the referenced shadow root is no longer attached to the DOM.
@@ -133,7 +143,11 @@ module Selenium
133
143
  # Argument was an invalid selector.
134
144
  #
135
145
 
136
- 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
137
151
 
138
152
  #
139
153
  # A new session could not be created.
@@ -214,6 +228,15 @@ module Selenium
214
228
 
215
229
  class UnsupportedOperationError < WebDriverError; end
216
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
217
240
  end # Error
218
241
  end # WebDriver
219
242
  end # Selenium
@@ -26,8 +26,8 @@ module Selenium
26
26
  def key?(key)
27
27
  keys.include? key
28
28
  end
29
- alias_method :member?, :key?
30
- alias_method :has_key?, :key?
29
+ alias member? key?
30
+ alias has_key? key?
31
31
 
32
32
  def fetch(key)
33
33
  return self[key] if key? key
@@ -68,9 +68,8 @@ module Selenium
68
68
  end
69
69
 
70
70
  #
71
- # Moves the pointer to the middle of the given element.
72
- # its location is calculated using getBoundingClientRect.
73
- # Then the pointer is moved to optional offset coordinates from the element.
71
+ # Moves the pointer to the in-view center point of the given element.
72
+ # Then the pointer is moved to optional offset coordinates.
74
73
  #
75
74
  # The element is not scrolled into view.
76
75
  # MoveTargetOutOfBoundsError will be raised if element with offset is outside the viewport
@@ -88,32 +87,19 @@ module Selenium
88
87
  # driver.action.move_to(el, 100, 100).perform
89
88
  #
90
89
  # @param [Selenium::WebDriver::Element] element to move to.
91
- # @param [Integer] right_by Optional offset from the top-left corner. A negative value means
92
- # coordinates to the left of the element.
93
- # @param [Integer] down_by Optional offset from the top-left corner. A negative value means
94
- # coordinates above the element.
90
+ # @param [Integer] right_by Optional offset from the in-view center of the
91
+ # element. A negative value means coordinates to the left of the center.
92
+ # @param [Integer] down_by Optional offset from the in-view center of the
93
+ # element. A negative value means coordinates to the top of the center.
95
94
  # @param [Symbol || String] device optional name of the PointerInput device to move.
96
95
  # @return [ActionBuilder] A self reference.
97
96
  #
98
97
 
99
- def move_to(element, right_by = nil, down_by = nil, device: nil, duration: default_move_duration, **opts)
100
- pointer = pointer_input(device)
101
- if right_by || down_by
102
- WebDriver.logger.warn("moving to an element with offset currently tries to use
103
- the top left corner of the element as the origin; in Selenium 4.3 it will use the in-view
104
- center point of the element as the origin.")
105
- size = element.size
106
- left_offset = (size[:width] / 2).to_i
107
- top_offset = (size[:height] / 2).to_i
108
- left = -left_offset + (right_by || 0)
109
- top = -top_offset + (down_by || 0)
110
- else
111
- left = 0
112
- top = 0
113
- end
114
- pointer.create_pointer_move(duration: duration,
115
- x: left,
116
- y: top,
98
+ def move_to(element, right_by = nil, down_by = nil, **opts)
99
+ pointer = pointer_input(opts.delete(:device))
100
+ pointer.create_pointer_move(duration: opts.delete(:duration) || default_move_duration,
101
+ x: right_by || 0,
102
+ y: down_by || 0,
117
103
  origin: element,
118
104
  **opts)
119
105
  tick(pointer)
@@ -192,9 +178,9 @@ center point of the element as the origin.")
192
178
  # @return [ActionBuilder] A self reference.
193
179
  #
194
180
 
195
- def click_and_hold(element = nil, device: nil)
181
+ def click_and_hold(element = nil, button: nil, device: nil)
196
182
  move_to(element, device: device) if element
197
- pointer_down(:left, device: device)
183
+ pointer_down(button || :left, device: device)
198
184
  self
199
185
  end
200
186
 
@@ -211,8 +197,8 @@ center point of the element as the origin.")
211
197
  # @return [ActionBuilder] A self reference.
212
198
  #
213
199
 
214
- def release(device: nil)
215
- pointer_up(:left, device: device)
200
+ def release(button: nil, device: nil)
201
+ pointer_up(button || :left, device: device)
216
202
  self
217
203
  end
218
204
 
@@ -238,10 +224,10 @@ center point of the element as the origin.")
238
224
  # @return [ActionBuilder] A self reference.
239
225
  #
240
226
 
241
- def click(element = nil, device: nil)
227
+ def click(element = nil, button: nil, device: nil)
242
228
  move_to(element, device: device) if element
243
- pointer_down(:left, device: device)
244
- pointer_up(:left, device: device)
229
+ pointer_down(button || :left, device: device)
230
+ pointer_up(button || :left, device: device)
245
231
  self
246
232
  end
247
233
 
@@ -296,10 +282,7 @@ center point of the element as the origin.")
296
282
  #
297
283
 
298
284
  def context_click(element = nil, device: nil)
299
- move_to(element, device: device) if element
300
- pointer_down(:right, device: device)
301
- pointer_up(:right, device: device)
302
- self
285
+ click(element, button: :right, device: device)
303
286
  end
304
287
 
305
288
  #
@@ -33,12 +33,12 @@ module Selenium
33
33
  POINTER = :pointer
34
34
  ORIGINS = [VIEWPORT, POINTER].freeze
35
35
 
36
- def initialize(source, duration, x, y, element: nil, origin: nil, **opts)
36
+ def initialize(source, duration, x, y, **opts)
37
37
  super(source)
38
38
  @duration = duration * 1000
39
39
  @x_offset = x
40
40
  @y_offset = y
41
- @origin = element || origin || :viewport
41
+ @origin = opts.delete(:element) || opts.delete(:origin) || :viewport
42
42
  @type = :pointerMove
43
43
  @opts = opts
44
44
  end
@@ -27,15 +27,17 @@ module Selenium
27
27
  #
28
28
 
29
29
  class Scroll < Interaction
30
- def initialize(source:, x: 0, y: 0, delta_x: 0, delta_y: 0, origin: :viewport, duration: 0.25)
30
+ def initialize(source:, origin: :viewport, duration: 0.25, **opts)
31
31
  super(source)
32
32
  @type = :scroll
33
33
  @duration = duration * 1000
34
34
  @origin = origin
35
- @x_offset = x
36
- @y_offset = y
37
- @delta_x = delta_x
38
- @delta_y = delta_y
35
+ @x_offset = opts.delete(:x) || 0
36
+ @y_offset = opts.delete(:y) || 0
37
+ @delta_x = opts.delete(:delta_x) || 0
38
+ @delta_y = opts.delete(:delta_y) || 0
39
+
40
+ raise ArgumentError, "Invalid arguments: #{opts.keys}" unless opts.empty?
39
41
  end
40
42
 
41
43
  def assert_source(source)
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed to the Software Freedom Conservancy (SFC) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The SFC licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+
20
+ module Selenium
21
+ module WebDriver
22
+ module LocalDriver
23
+ def initialize_local_driver(options, service, url)
24
+ raise ArgumentError, "Can't initialize #{self.class} with :url" if url
25
+
26
+ service ||= Service.send(browser)
27
+ caps = process_options(options, service)
28
+ url = service_url(service)
29
+
30
+ [caps, url]
31
+ end
32
+
33
+ def process_options(options, service)
34
+ default_options = Options.send(browser)
35
+ options ||= default_options
36
+
37
+ unless options.is_a?(default_options.class)
38
+ raise ArgumentError, ":options must be an instance of #{default_options.class}"
39
+ end
40
+
41
+ service.executable_path ||= WebDriver::DriverFinder.path(options, service.class)
42
+ options.as_json
43
+ end
44
+ end
45
+ end
46
+ end
@@ -38,19 +38,31 @@ 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')
52
- @logger = create_logger(progname)
53
- @ignored = []
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)
55
+ @ignored = Array(ignored)
56
+ @allowed = Array(allowed)
57
+ @first_warning = false
58
+ end
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
54
66
  end
55
67
 
56
68
  #
@@ -80,27 +92,64 @@ module Selenium
80
92
  #
81
93
  # Will not log the provided ID.
82
94
  #
83
- # @param [Array, Symbol] id
95
+ # @param [Array, Symbol] ids
96
+ #
97
+ def ignore(*ids)
98
+ @ignored += Array(ids).flatten
99
+ end
100
+
101
+ #
102
+ # Will only log the provided ID.
84
103
  #
85
- def ignore(id)
86
- Array(id).each { |ignore| @ignored << ignore }
104
+ # @param [Array, Symbol] ids
105
+ #
106
+ def allow(ids)
107
+ @allowed += Array(ids).flatten
87
108
  end
88
109
 
89
110
  #
90
- # Overrides default #warn to skip ignored messages by provided id
111
+ # Used to supply information of interest for debugging a problem
112
+ # Overrides default #debug to skip ignored messages by provided id
91
113
  #
92
114
  # @param [String] message
93
115
  # @param [Symbol, Array<Sybmol>] id
94
116
  # @yield see #deprecate
95
117
  #
96
- def warn(message, id: [])
97
- id = Array(id)
98
- return if (@ignored & id).any?
118
+ def debug(message, id: [], &block)
119
+ discard_or_log(:debug, message, id, &block)
120
+ end
99
121
 
100
- msg = id.empty? ? message : "[#{id.map(&:inspect).join(', ')}] #{message} "
101
- msg += " #{yield}" if block_given?
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
102
132
 
103
- @logger.warn { msg }
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
143
+
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)
104
153
  end
105
154
 
106
155
  #
@@ -114,11 +163,11 @@ module Selenium
114
163
  #
115
164
  def deprecate(old, new = nil, id: [], reference: '', &block)
116
165
  id = Array(id)
117
- return if @ignored.include?(:deprecations) || (@ignored & id).any?
166
+ return if @ignored.include?(:deprecations)
118
167
 
119
- ids = id.empty? ? '' : "[#{id.map(&:inspect).join(', ')}] "
168
+ id << :deprecations if @allowed.include?(:deprecations)
120
169
 
121
- message = +"[DEPRECATION] #{ids}#{old} is deprecated"
170
+ message = +"[DEPRECATION] #{old} is deprecated"
122
171
  message << if new
123
172
  ". Use #{new} instead."
124
173
  else
@@ -126,15 +175,15 @@ module Selenium
126
175
  end
127
176
  message << " See explanation for this deprecation: #{reference}." unless reference.empty?
128
177
 
129
- warn message, &block
178
+ discard_or_log(:warn, message, id, &block)
130
179
  end
131
180
 
132
181
  private
133
182
 
134
- def create_logger(name)
183
+ def create_logger(name, level:)
135
184
  logger = ::Logger.new($stdout)
136
185
  logger.progname = name
137
- logger.level = default_level
186
+ logger.level = level
138
187
  logger.formatter = proc do |severity, time, progname, msg|
139
188
  "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
140
189
  end
@@ -142,8 +191,24 @@ module Selenium
142
191
  logger
143
192
  end
144
193
 
145
- def default_level
146
- $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 }
147
212
  end
148
213
  end # Logger
149
214
  end # WebDriver
@@ -104,33 +104,6 @@ module Selenium
104
104
  @timeouts ||= Timeouts.new(@bridge)
105
105
  end
106
106
 
107
- def logs
108
- WebDriver.logger.deprecate('Manager#logs', 'Chrome::Driver#logs')
109
- @logs ||= Logs.new(@bridge)
110
- end
111
-
112
- #
113
- # @param type [Symbol] Supports two values: :tab and :window.
114
- # @return [String] The value of the window handle
115
- #
116
- def new_window(type = :tab)
117
- WebDriver.logger.deprecate('Manager#new_window', 'TargetLocator#new_window', id: :new_window) do
118
- 'e.g., `driver.switch_to.new_window(:tab)`'
119
- end
120
- case type
121
- when :tab, :window
122
- result = @bridge.new_window(type)
123
- unless result.key?('handle')
124
- raise UnknownError, "the driver did not return a handle. " \
125
- "The returned result: #{result.inspect}"
126
- end
127
- result['handle']
128
- else
129
- raise ArgumentError, "invalid argument for type. Got: '#{type.inspect}'. " \
130
- "Try :tab or :window"
131
- end
132
- end
133
-
134
107
  def window
135
108
  @window ||= Window.new(@bridge)
136
109
  end
@@ -24,6 +24,8 @@ module Selenium
24
24
  set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability
25
25
  web_socket_url].freeze
26
26
 
27
+ GRID_OPTIONS = %i[enable_downloads].freeze
28
+
27
29
  class << self
28
30
  attr_reader :driver_path
29
31
 
@@ -38,12 +40,12 @@ module Selenium
38
40
  def ie(**opts)
39
41
  IE::Options.new(**opts)
40
42
  end
41
- alias_method :internet_explorer, :ie
43
+ alias internet_explorer ie
42
44
 
43
45
  def edge(**opts)
44
46
  Edge::Options.new(**opts)
45
47
  end
46
- alias_method :microsoftedge, :edge
48
+ alias microsoftedge edge
47
49
 
48
50
  def safari(**opts)
49
51
  Safari::Options.new(**opts)
@@ -66,17 +68,10 @@ module Selenium
66
68
 
67
69
  attr_accessor :options
68
70
 
69
- def initialize(options: nil, **opts)
71
+ def initialize(**opts)
70
72
  self.class.set_capabilities
71
73
 
72
- @options = if options
73
- WebDriver.logger.deprecate(":options as keyword for initializing #{self.class}",
74
- "custom values directly in #new constructor",
75
- id: :options_options)
76
- opts.merge(options)
77
- else
78
- opts
79
- end
74
+ @options = opts
80
75
  @options[:browser_name] = self.class::BROWSER
81
76
  end
82
77
 
@@ -92,7 +87,7 @@ module Selenium
92
87
  #
93
88
 
94
89
  def add_option(name, value = nil)
95
- @options[name.keys.first] = name.values.first if value.nil? && name.is_a?(Hash)
90
+ name, value = name.first if value.nil? && name.is_a?(Hash)
96
91
  @options[name] = value
97
92
  end
98
93
 
@@ -102,7 +97,7 @@ module Selenium
102
97
  as_json == other.as_json
103
98
  end
104
99
 
105
- alias_method :eql?, :==
100
+ alias eql? ==
106
101
 
107
102
  #
108
103
  # @api private
@@ -111,13 +106,18 @@ module Selenium
111
106
  def as_json(*)
112
107
  options = @options.dup
113
108
 
109
+ downloads = options.delete(:enable_downloads)
110
+ options['se:downloadsEnabled'] = downloads unless downloads.nil?
114
111
  w3c_options = process_w3c_options(options)
115
112
 
116
- self.class::CAPABILITIES.each do |capability_alias, capability_name|
113
+ browser_options = self.class::CAPABILITIES.each_with_object({}) do |(capability_alias, capability_name), hash|
117
114
  capability_value = options.delete(capability_alias)
118
- options[capability_name] = capability_value if !capability_value.nil? && !options.key?(capability_name)
115
+ hash[capability_name] = capability_value unless capability_value.nil?
119
116
  end
120
- browser_options = defined?(self.class::KEY) ? {self.class::KEY => options} : options
117
+
118
+ raise Error::WebDriverError, "These options are not w3c compliant: #{options}" unless options.empty?
119
+
120
+ browser_options = {self.class::KEY => browser_options} if defined?(self.class::KEY)
121
121
 
122
122
  process_browser_options(browser_options)
123
123
  generate_as_json(w3c_options.merge(browser_options))
@@ -130,7 +130,7 @@ module Selenium
130
130
  end
131
131
 
132
132
  def process_w3c_options(options)
133
- w3c_options = options.select { |key, _val| w3c?(key) }
133
+ w3c_options = options.select { |key, val| w3c?(key) && !val.nil? }
134
134
  w3c_options[:unhandled_prompt_behavior] &&= w3c_options[:unhandled_prompt_behavior]&.to_s&.tr('_', ' ')
135
135
  options.delete_if { |key, _val| w3c?(key) }
136
136
  w3c_options
@@ -62,22 +62,14 @@ module Selenium
62
62
  end
63
63
  end
64
64
 
65
- def bitsize
66
- @bitsize ||= if defined?(FFI::Platform::ADDRESS_SIZE)
67
- FFI::Platform::ADDRESS_SIZE
68
- elsif defined?(FFI)
69
- FFI.type_size(:pointer) == 4 ? 32 : 64
70
- elsif jruby?
71
- Integer(ENV_JAVA['sun.arch.data.model'])
72
- else
73
- 1.size == 4 ? 32 : 64
74
- end
75
- end
76
-
77
65
  def jruby?
78
66
  engine == :jruby
79
67
  end
80
68
 
69
+ def truffleruby?
70
+ engine == :truffleruby
71
+ end
72
+
81
73
  def ruby_version
82
74
  RUBY_VERSION
83
75
  end
@@ -105,7 +97,6 @@ module Selenium
105
97
 
106
98
  def cygwin?
107
99
  RUBY_PLATFORM.include?('cygwin')
108
- !Regexp.last_match.nil?
109
100
  end
110
101
 
111
102
  def null_device
@@ -155,43 +146,6 @@ module Selenium
155
146
  at_exit { yield if Process.pid == pid }
156
147
  end
157
148
 
158
- def find_binary(*binary_names)
159
- paths = ENV['PATH'].split(File::PATH_SEPARATOR)
160
-
161
- if windows?
162
- binary_names.map! { |n| "#{n}.exe" }
163
- binary_names.dup.each { |n| binary_names << n.gsub('exe', 'bat') }
164
- end
165
-
166
- binary_names.each do |binary_name|
167
- paths.each do |path|
168
- full_path = File.join(path, binary_name)
169
- full_path = unix_path(full_path) if windows?
170
- exe = Dir.glob(full_path).find { |f| File.executable?(f) }
171
- return exe if exe
172
- end
173
- end
174
-
175
- nil
176
- end
177
-
178
- def find_in_program_files(*binary_names)
179
- paths = [
180
- ENV.fetch('PROGRAMFILES', '\\Program Files'),
181
- ENV.fetch('ProgramFiles(x86)', '\\Program Files (x86)'),
182
- ENV.fetch('ProgramW6432', '\\Program Files')
183
- ]
184
-
185
- paths.each do |root|
186
- binary_names.each do |name|
187
- exe = File.join(root, name)
188
- return exe if File.executable?(exe)
189
- end
190
- end
191
-
192
- nil
193
- end
194
-
195
149
  def localhost
196
150
  info = Socket.getaddrinfo 'localhost', 80, Socket::AF_INET, Socket::SOCK_STREAM
197
151
 
@@ -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
 
@@ -40,7 +40,7 @@ module Selenium
40
40
  end
41
41
 
42
42
  def as_json(*)
43
- {"zip" => encoded}
43
+ {'zip' => encoded}
44
44
  end
45
45
 
46
46
  def to_json(*)