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.
- checksums.yaml +4 -4
- data/CHANGES +252 -0
- data/Gemfile +2 -0
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/README.md +2 -2
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +22 -30
- data/lib/selenium/webdriver/atoms/findElements.js +3 -4
- data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
- data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
- data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
- data/lib/selenium/webdriver/atoms.rb +5 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
- data/lib/selenium/webdriver/{common/driver_extensions/has_network_connection.rb → bidi/browsing_context_info.rb} +10 -12
- data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
- data/lib/selenium/webdriver/{common/driver_extensions/has_location.rb → bidi/log/filter_by.rb} +14 -11
- data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
- data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
- data/lib/selenium/webdriver/bidi/session.rb +13 -0
- data/lib/selenium/webdriver/bidi.rb +3 -2
- data/lib/selenium/webdriver/chrome/driver.rb +9 -29
- data/lib/selenium/webdriver/chrome/features.rb +8 -71
- data/lib/selenium/webdriver/chrome/options.rb +3 -223
- data/lib/selenium/webdriver/chrome/profile.rb +3 -83
- data/lib/selenium/webdriver/chrome/service.rb +4 -19
- data/lib/selenium/webdriver/chrome.rb +0 -16
- data/lib/selenium/webdriver/chromium/driver.rb +60 -0
- data/lib/selenium/webdriver/chromium/features.rb +99 -0
- data/lib/selenium/webdriver/chromium/options.rb +243 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium.rb +29 -0
- data/lib/selenium/webdriver/common/action_builder.rb +11 -58
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +29 -79
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +0 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +0 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -69
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +0 -2
- data/lib/selenium/webdriver/common/driver_finder.rb +45 -0
- data/lib/selenium/webdriver/common/element.rb +7 -7
- data/lib/selenium/webdriver/common/error.rb +27 -4
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +19 -36
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/scroll.rb +7 -5
- data/lib/selenium/webdriver/common/local_driver.rb +46 -0
- data/lib/selenium/webdriver/common/logger.rb +90 -25
- data/lib/selenium/webdriver/common/manager.rb +0 -27
- data/lib/selenium/webdriver/common/options.rb +17 -17
- data/lib/selenium/webdriver/common/platform.rb +4 -50
- data/lib/selenium/webdriver/common/port_prober.rb +1 -1
- data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
- data/lib/selenium/webdriver/common/proxy.rb +2 -2
- data/lib/selenium/webdriver/common/selenium_manager.rb +134 -0
- data/lib/selenium/webdriver/common/service.rb +21 -30
- data/lib/selenium/webdriver/common/service_manager.rb +6 -12
- data/lib/selenium/webdriver/common/shadow_root.rb +1 -2
- data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
- data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
- data/lib/selenium/webdriver/common/takes_screenshot.rb +2 -3
- data/lib/selenium/webdriver/common/target_locator.rb +2 -3
- data/lib/selenium/webdriver/common/timeouts.rb +2 -2
- data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
- data/lib/selenium/webdriver/common/websocket_connection.rb +22 -7
- data/lib/selenium/webdriver/common/window.rb +6 -6
- data/lib/selenium/webdriver/common/zipper.rb +1 -1
- data/lib/selenium/webdriver/common.rb +8 -3
- data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
- data/lib/selenium/webdriver/devtools/request.rb +0 -2
- data/lib/selenium/webdriver/devtools/response.rb +0 -2
- data/lib/selenium/webdriver/devtools.rb +12 -2
- data/lib/selenium/webdriver/edge/driver.rb +9 -3
- data/lib/selenium/webdriver/edge/features.rb +7 -4
- data/lib/selenium/webdriver/edge/options.rb +17 -5
- data/lib/selenium/webdriver/edge/profile.rb +2 -2
- data/lib/selenium/webdriver/edge/service.rb +8 -7
- data/lib/selenium/webdriver/edge.rb +0 -2
- data/lib/selenium/webdriver/firefox/driver.rb +8 -2
- data/lib/selenium/webdriver/firefox/features.rb +10 -3
- data/lib/selenium/webdriver/firefox/options.rb +4 -15
- data/lib/selenium/webdriver/firefox/profile.rb +7 -7
- data/lib/selenium/webdriver/firefox/service.rb +0 -18
- data/lib/selenium/webdriver/firefox.rb +0 -14
- data/lib/selenium/webdriver/ie/driver.rb +7 -1
- data/lib/selenium/webdriver/{common/driver_extensions/has_remote_status.rb → ie/features.rb} +10 -7
- data/lib/selenium/webdriver/ie/options.rb +4 -3
- data/lib/selenium/webdriver/ie/service.rb +0 -22
- data/lib/selenium/webdriver/ie.rb +4 -17
- data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +10 -4
- data/lib/selenium/webdriver/remote/bridge.rb +48 -39
- data/lib/selenium/webdriver/remote/capabilities.rb +3 -53
- data/lib/selenium/webdriver/remote/driver.rb +35 -14
- data/lib/selenium/webdriver/remote/features.rb +75 -0
- data/lib/selenium/webdriver/remote/http/common.rb +3 -3
- data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
- data/lib/selenium/webdriver/remote/http/default.rb +2 -2
- data/lib/selenium/webdriver/remote/response.rb +0 -1
- data/lib/selenium/webdriver/remote/server_error.rb +1 -1
- data/lib/selenium/webdriver/remote.rb +1 -1
- data/lib/selenium/webdriver/safari/driver.rb +7 -1
- data/lib/selenium/webdriver/safari/features.rb +5 -3
- data/lib/selenium/webdriver/safari/options.rb +5 -1
- data/lib/selenium/webdriver/safari/service.rb +10 -4
- data/lib/selenium/webdriver/safari.rb +1 -15
- data/lib/selenium/webdriver/support/color.rb +17 -17
- data/lib/selenium/webdriver/support/guards/guard.rb +6 -5
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +0 -2
- data/lib/selenium/webdriver/support/guards.rb +1 -1
- data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
- data/lib/selenium/webdriver/support/select.rb +3 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +5 -4
- data/selenium-webdriver.gemspec +10 -11
- metadata +51 -90
- data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
- data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
- 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
|
|
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
|
|
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
|
|
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
|
|
@@ -68,9 +68,8 @@ module Selenium
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
#
|
|
71
|
-
# Moves the pointer to the
|
|
72
|
-
#
|
|
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
|
|
92
|
-
# coordinates to the left of the
|
|
93
|
-
# @param [Integer] down_by Optional offset from the
|
|
94
|
-
# coordinates
|
|
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,
|
|
100
|
-
pointer = pointer_input(device)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
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,
|
|
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:,
|
|
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
|
|
42
|
-
:info
|
|
41
|
+
:debug?,
|
|
42
|
+
:info?,
|
|
43
43
|
:warn?,
|
|
44
|
-
:error
|
|
44
|
+
:error?,
|
|
45
45
|
:fatal, :fatal?,
|
|
46
|
-
: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
|
-
|
|
53
|
-
|
|
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]
|
|
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
|
-
|
|
86
|
-
|
|
104
|
+
# @param [Array, Symbol] ids
|
|
105
|
+
#
|
|
106
|
+
def allow(ids)
|
|
107
|
+
@allowed += Array(ids).flatten
|
|
87
108
|
end
|
|
88
109
|
|
|
89
110
|
#
|
|
90
|
-
#
|
|
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
|
|
97
|
-
|
|
98
|
-
|
|
118
|
+
def debug(message, id: [], &block)
|
|
119
|
+
discard_or_log(:debug, message, id, &block)
|
|
120
|
+
end
|
|
99
121
|
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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)
|
|
166
|
+
return if @ignored.include?(:deprecations)
|
|
118
167
|
|
|
119
|
-
|
|
168
|
+
id << :deprecations if @allowed.include?(:deprecations)
|
|
120
169
|
|
|
121
|
-
message = +"[DEPRECATION] #{
|
|
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 =
|
|
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
|
|
146
|
-
|
|
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
|
-
|
|
43
|
+
alias internet_explorer ie
|
|
42
44
|
|
|
43
45
|
def edge(**opts)
|
|
44
46
|
Edge::Options.new(**opts)
|
|
45
47
|
end
|
|
46
|
-
|
|
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(
|
|
71
|
+
def initialize(**opts)
|
|
70
72
|
self.class.set_capabilities
|
|
71
73
|
|
|
72
|
-
@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
|
-
|
|
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
|
-
|
|
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.
|
|
113
|
+
browser_options = self.class::CAPABILITIES.each_with_object({}) do |(capability_alias, capability_name), hash|
|
|
117
114
|
capability_value = options.delete(capability_alias)
|
|
118
|
-
|
|
115
|
+
hash[capability_name] = capability_value unless capability_value.nil?
|
|
119
116
|
end
|
|
120
|
-
|
|
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,
|
|
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
|
|