selenium-webdriver 4.5.0 → 4.9.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 +117 -1
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- 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 +6 -8
- data/lib/selenium/webdriver/atoms/findElements.js +0 -0
- 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 +2 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
- data/lib/selenium/webdriver/bidi/browsing_context_info.rb +35 -0
- 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/bidi/log/filter_by.rb +40 -0
- 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 -30
- data/lib/selenium/webdriver/chrome/features.rb +5 -72
- data/lib/selenium/webdriver/chrome/options.rb +3 -237
- data/lib/selenium/webdriver/chrome/profile.rb +3 -83
- data/lib/selenium/webdriver/chrome/service.rb +3 -23
- data/lib/selenium/webdriver/chromium/driver.rb +61 -0
- data/lib/selenium/webdriver/chromium/features.rb +103 -0
- data/lib/selenium/webdriver/chromium/options.rb +261 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium/service.rb +42 -0
- data/lib/selenium/webdriver/chromium.rb +32 -0
- data/lib/selenium/webdriver/common/action_builder.rb +11 -48
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +21 -26
- 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_launching.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +1 -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 +0 -2
- 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 +43 -0
- data/lib/selenium/webdriver/common/element.rb +6 -6
- data/lib/selenium/webdriver/common/error.rb +0 -2
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +3 -3
- 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 +55 -0
- data/lib/selenium/webdriver/common/logger.rb +10 -2
- data/lib/selenium/webdriver/common/options.rb +30 -8
- data/lib/selenium/webdriver/common/platform.rb +4 -0
- data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
- data/lib/selenium/webdriver/common/proxy.rb +1 -1
- data/lib/selenium/webdriver/common/selenium_manager.rb +108 -0
- data/lib/selenium/webdriver/common/service.rb +11 -22
- data/lib/selenium/webdriver/common/service_manager.rb +3 -11
- data/lib/selenium/webdriver/common/shadow_root.rb +1 -2
- data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
- 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 +8 -6
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +0 -1
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +16 -16
- data/lib/selenium/webdriver/common/websocket_connection.rb +10 -2
- data/lib/selenium/webdriver/common.rb +4 -0
- 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 +4 -7
- 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 +11 -2
- data/lib/selenium/webdriver/edge/driver.rb +9 -3
- data/lib/selenium/webdriver/edge/features.rb +3 -4
- data/lib/selenium/webdriver/edge/options.rb +3 -5
- data/lib/selenium/webdriver/edge/profile.rb +2 -2
- data/lib/selenium/webdriver/edge/service.rb +2 -6
- data/lib/selenium/webdriver/firefox/driver.rb +8 -2
- data/lib/selenium/webdriver/firefox/features.rb +0 -1
- data/lib/selenium/webdriver/firefox/options.rb +4 -1
- data/lib/selenium/webdriver/firefox/profile.rb +6 -6
- data/lib/selenium/webdriver/firefox/service.rb +0 -6
- data/lib/selenium/webdriver/ie/driver.rb +7 -1
- data/lib/selenium/webdriver/ie/service.rb +1 -7
- data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +0 -2
- data/lib/selenium/webdriver/remote/bridge.rb +9 -15
- data/lib/selenium/webdriver/remote/capabilities.rb +34 -12
- data/lib/selenium/webdriver/remote/driver.rb +19 -13
- data/lib/selenium/webdriver/remote/http/curb.rb +0 -2
- data/lib/selenium/webdriver/remote/http/default.rb +1 -0
- data/lib/selenium/webdriver/remote/response.rb +0 -1
- data/lib/selenium/webdriver/remote.rb +0 -1
- data/lib/selenium/webdriver/safari/driver.rb +7 -1
- data/lib/selenium/webdriver/safari/features.rb +0 -2
- data/lib/selenium/webdriver/safari/options.rb +5 -1
- data/lib/selenium/webdriver/safari/service.rb +0 -4
- data/lib/selenium/webdriver/safari.rb +1 -1
- data/lib/selenium/webdriver/support/color.rb +15 -15
- data/lib/selenium/webdriver/support/guards/guard.rb +0 -2
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +0 -2
- data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
- data/lib/selenium/webdriver/support/select.rb +2 -5
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +3 -4
- data/selenium-webdriver.gemspec +8 -9
- metadata +33 -74
- data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
- data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
|
@@ -43,7 +43,7 @@ module Selenium
|
|
|
43
43
|
def ==(other)
|
|
44
44
|
other.is_a?(self.class) && ref == other.ref
|
|
45
45
|
end
|
|
46
|
-
|
|
46
|
+
alias eql? ==
|
|
47
47
|
|
|
48
48
|
def hash
|
|
49
49
|
[@id, @bridge].hash
|
|
@@ -207,7 +207,7 @@ module Selenium
|
|
|
207
207
|
def send_keys(*args)
|
|
208
208
|
bridge.send_keys_to_element @id, Keys.encode(args)
|
|
209
209
|
end
|
|
210
|
-
|
|
210
|
+
alias send_key send_keys
|
|
211
211
|
|
|
212
212
|
#
|
|
213
213
|
# If this element is a text entry element, this will clear the value. Has no effect on other
|
|
@@ -275,7 +275,7 @@ module Selenium
|
|
|
275
275
|
def css_value(prop)
|
|
276
276
|
bridge.element_value_of_css_property @id, prop
|
|
277
277
|
end
|
|
278
|
-
|
|
278
|
+
alias style css_value
|
|
279
279
|
|
|
280
280
|
#
|
|
281
281
|
# Get the location of this element.
|
|
@@ -333,18 +333,18 @@ module Selenium
|
|
|
333
333
|
# element.first(id: 'foo')
|
|
334
334
|
#
|
|
335
335
|
|
|
336
|
-
|
|
336
|
+
alias first find_element
|
|
337
337
|
|
|
338
338
|
#
|
|
339
339
|
# element.all(class: 'bar')
|
|
340
340
|
#
|
|
341
341
|
|
|
342
|
-
|
|
342
|
+
alias all find_elements
|
|
343
343
|
|
|
344
344
|
#
|
|
345
345
|
# element['class'] or element[:class] #=> "someclass"
|
|
346
346
|
#
|
|
347
|
-
|
|
347
|
+
alias [] attribute
|
|
348
348
|
|
|
349
349
|
#
|
|
350
350
|
# @api private
|
|
@@ -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
|
|
@@ -213,7 +212,6 @@ module Selenium
|
|
|
213
212
|
#
|
|
214
213
|
|
|
215
214
|
class UnsupportedOperationError < WebDriverError; end
|
|
216
|
-
|
|
217
215
|
end # Error
|
|
218
216
|
end # WebDriver
|
|
219
217
|
end # Selenium
|
|
@@ -95,9 +95,9 @@ module Selenium
|
|
|
95
95
|
# @return [ActionBuilder] A self reference.
|
|
96
96
|
#
|
|
97
97
|
|
|
98
|
-
def move_to(element, right_by = nil, down_by = nil,
|
|
99
|
-
pointer = pointer_input(device)
|
|
100
|
-
pointer.create_pointer_move(duration: duration,
|
|
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
101
|
x: right_by || 0,
|
|
102
102
|
y: down_by || 0,
|
|
103
103
|
origin: element,
|
|
@@ -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,55 @@
|
|
|
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(capabilities, 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, capabilities, service)
|
|
28
|
+
url = service_url(service)
|
|
29
|
+
|
|
30
|
+
[caps, url]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def process_options(options, capabilities, service)
|
|
34
|
+
default_options = Options.send(browser)
|
|
35
|
+
|
|
36
|
+
if options && capabilities
|
|
37
|
+
msg = "Don't use both :options and :capabilities when initializing #{self.class}, prefer :options"
|
|
38
|
+
raise ArgumentError, msg
|
|
39
|
+
elsif options && !options.is_a?(default_options.class)
|
|
40
|
+
raise ArgumentError, ":options must be an instance of #{default_options.class}"
|
|
41
|
+
elsif capabilities
|
|
42
|
+
WebDriver.logger.deprecate("The :capabilities parameter for #{self.class}",
|
|
43
|
+
":options argument with an instance of #{self.class}",
|
|
44
|
+
id: :capabilities)
|
|
45
|
+
service.executable_path ||= WebDriver::DriverFinder.path(capabilities, service.class)
|
|
46
|
+
generate_capabilities(capabilities)
|
|
47
|
+
else
|
|
48
|
+
options ||= default_options
|
|
49
|
+
service.executable_path ||= WebDriver::DriverFinder.path(options, service.class)
|
|
50
|
+
options.as_json
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -48,9 +48,10 @@ module Selenium
|
|
|
48
48
|
#
|
|
49
49
|
# @param [String] progname Allow child projects to use Selenium's Logger pattern
|
|
50
50
|
#
|
|
51
|
-
def initialize(progname = 'Selenium')
|
|
51
|
+
def initialize(progname = 'Selenium', ignored: nil)
|
|
52
52
|
@logger = create_logger(progname)
|
|
53
|
-
@ignored =
|
|
53
|
+
@ignored = Array(ignored)
|
|
54
|
+
@first_warning = false
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
#
|
|
@@ -94,6 +95,13 @@ module Selenium
|
|
|
94
95
|
# @yield see #deprecate
|
|
95
96
|
#
|
|
96
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
|
|
104
|
+
|
|
97
105
|
id = Array(id)
|
|
98
106
|
return if (@ignored & id).any?
|
|
99
107
|
|
|
@@ -38,12 +38,12 @@ module Selenium
|
|
|
38
38
|
def ie(**opts)
|
|
39
39
|
IE::Options.new(**opts)
|
|
40
40
|
end
|
|
41
|
-
|
|
41
|
+
alias internet_explorer ie
|
|
42
42
|
|
|
43
43
|
def edge(**opts)
|
|
44
44
|
Edge::Options.new(**opts)
|
|
45
45
|
end
|
|
46
|
-
|
|
46
|
+
alias microsoftedge edge
|
|
47
47
|
|
|
48
48
|
def safari(**opts)
|
|
49
49
|
Safari::Options.new(**opts)
|
|
@@ -85,7 +85,13 @@ module Selenium
|
|
|
85
85
|
#
|
|
86
86
|
|
|
87
87
|
def add_option(name, value = nil)
|
|
88
|
-
|
|
88
|
+
name, value = name.first if value.nil? && name.is_a?(Hash)
|
|
89
|
+
|
|
90
|
+
unless name.to_s.include?(':')
|
|
91
|
+
WebDriver.logger.deprecate('Options#add_option for w3c or browser specific capabilities',
|
|
92
|
+
'applicable attribute accessors or pass into constructor',
|
|
93
|
+
id: :add_option)
|
|
94
|
+
end
|
|
89
95
|
@options[name] = value
|
|
90
96
|
end
|
|
91
97
|
|
|
@@ -95,7 +101,7 @@ module Selenium
|
|
|
95
101
|
as_json == other.as_json
|
|
96
102
|
end
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
alias eql? ==
|
|
99
105
|
|
|
100
106
|
#
|
|
101
107
|
# @api private
|
|
@@ -106,11 +112,27 @@ module Selenium
|
|
|
106
112
|
|
|
107
113
|
w3c_options = process_w3c_options(options)
|
|
108
114
|
|
|
109
|
-
self.class::CAPABILITIES.
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
browser_options = self.class::CAPABILITIES.each_with_object({}) do |(capability_alias, capability_name), hash|
|
|
116
|
+
from_name = options.delete(capability_name)
|
|
117
|
+
from_alias = options.delete(capability_alias)
|
|
118
|
+
capability_value = if !from_name.nil? && capability_alias != capability_name
|
|
119
|
+
WebDriver.logger.deprecate("#{capability_name} as option",
|
|
120
|
+
capability_alias.to_s, id: :option_symbols)
|
|
121
|
+
from_name
|
|
122
|
+
elsif !from_alias.nil?
|
|
123
|
+
from_alias
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
hash[capability_name] = capability_value unless capability_value.nil?
|
|
112
127
|
end
|
|
113
|
-
|
|
128
|
+
|
|
129
|
+
unless options.empty?
|
|
130
|
+
msg = 'These options are not w3c compliant and will result in failures in a future release'
|
|
131
|
+
WebDriver.logger.warn("#{msg}: #{options}")
|
|
132
|
+
browser_options.merge!(options)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
browser_options = {self.class::KEY => browser_options} if defined?(self.class::KEY)
|
|
114
136
|
|
|
115
137
|
process_browser_options(browser_options)
|
|
116
138
|
generate_as_json(w3c_options.merge(browser_options))
|
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
require 'open3'
|
|
21
|
+
|
|
22
|
+
module Selenium
|
|
23
|
+
module WebDriver
|
|
24
|
+
#
|
|
25
|
+
# Wrapper for getting information from the Selenium Manager binaries.
|
|
26
|
+
# This implementation is still in beta, and may change.
|
|
27
|
+
# @api private
|
|
28
|
+
#
|
|
29
|
+
class SeleniumManager
|
|
30
|
+
BIN_PATH = '../../../../../bin'
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
# @param [Options] options browser options.
|
|
34
|
+
# @return [String] the path to the correct driver.
|
|
35
|
+
def driver_path(options)
|
|
36
|
+
message = 'applicable driver not found; attempting to install with Selenium Manager'
|
|
37
|
+
WebDriver.logger.warn(message)
|
|
38
|
+
|
|
39
|
+
unless options.is_a?(Options)
|
|
40
|
+
raise ArgumentError, "SeleniumManager requires a WebDriver::Options instance, not #{options.inspect}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
command = [binary, '--browser', options.browser_name, '--output', 'json']
|
|
44
|
+
if options.browser_version
|
|
45
|
+
command << '--browser-version'
|
|
46
|
+
command << options.browser_version
|
|
47
|
+
end
|
|
48
|
+
if options.respond_to?(:binary) && !options.binary.nil?
|
|
49
|
+
command << '--browser-path'
|
|
50
|
+
command << options.binary.gsub('\\', '\\\\\\')
|
|
51
|
+
end
|
|
52
|
+
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
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# @return [String] the path to the correct selenium manager
|
|
64
|
+
def binary
|
|
65
|
+
@binary ||= begin
|
|
66
|
+
path = File.expand_path(BIN_PATH, __FILE__)
|
|
67
|
+
path << if Platform.windows?
|
|
68
|
+
'/windows/selenium-manager.exe'
|
|
69
|
+
elsif Platform.mac?
|
|
70
|
+
'/macos/selenium-manager'
|
|
71
|
+
elsif Platform.linux?
|
|
72
|
+
'/linux/selenium-manager'
|
|
73
|
+
end
|
|
74
|
+
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'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
WebDriver.logger.debug("Selenium Manager found at #{location}")
|
|
80
|
+
location
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def run(*command)
|
|
85
|
+
WebDriver.logger.debug("Executing Process #{command}")
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
stdout, stderr, status = Open3.capture3(*command)
|
|
89
|
+
json_output = stdout.empty? ? nil : JSON.parse(stdout)
|
|
90
|
+
result = json_output&.dig('result', 'message')
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
raise Error::WebDriverError, "Unsuccessful command executed: #{command}", e.message
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if status.exitstatus.positive?
|
|
96
|
+
raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
json_output['logs'].each do |log|
|
|
100
|
+
WebDriver.logger.send(log['level'].downcase, log['message'])
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
result
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end # SeleniumManager
|
|
107
|
+
end # WebDriver
|
|
108
|
+
end # Selenium
|
|
@@ -39,12 +39,13 @@ module Selenium
|
|
|
39
39
|
def ie(**opts)
|
|
40
40
|
IE::Service.new(**opts)
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
alias internet_explorer ie
|
|
43
43
|
|
|
44
44
|
def edge(**opts)
|
|
45
45
|
Edge::Service.new(**opts)
|
|
46
46
|
end
|
|
47
|
-
|
|
47
|
+
alias microsoftedge edge
|
|
48
|
+
alias msedge edge
|
|
48
49
|
|
|
49
50
|
def safari(**opts)
|
|
50
51
|
Safari::Service.new(**opts)
|
|
@@ -56,8 +57,8 @@ module Selenium
|
|
|
56
57
|
end
|
|
57
58
|
end
|
|
58
59
|
|
|
59
|
-
attr_accessor :host
|
|
60
|
-
|
|
60
|
+
attr_accessor :host, :executable_path, :port, :args
|
|
61
|
+
alias extra_args args
|
|
61
62
|
|
|
62
63
|
#
|
|
63
64
|
# End users should use a class method for the desired driver, rather than using this directly.
|
|
@@ -66,23 +67,20 @@ module Selenium
|
|
|
66
67
|
#
|
|
67
68
|
|
|
68
69
|
def initialize(path: nil, port: nil, args: nil)
|
|
69
|
-
path ||= self.class.driver_path
|
|
70
70
|
port ||= self.class::DEFAULT_PORT
|
|
71
71
|
args ||= []
|
|
72
72
|
|
|
73
|
-
@executable_path =
|
|
73
|
+
@executable_path = path
|
|
74
74
|
@host = Platform.localhost
|
|
75
75
|
@port = Integer(port)
|
|
76
76
|
|
|
77
|
-
@
|
|
77
|
+
@args = args.is_a?(Hash) ? extract_service_args(args) : args
|
|
78
78
|
|
|
79
79
|
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def launch
|
|
83
|
-
|
|
84
|
-
sm.start
|
|
85
|
-
sm
|
|
83
|
+
ServiceManager.new(self).tap(&:start)
|
|
86
84
|
end
|
|
87
85
|
|
|
88
86
|
def shutdown_supported
|
|
@@ -92,20 +90,11 @@ module Selenium
|
|
|
92
90
|
protected
|
|
93
91
|
|
|
94
92
|
def extract_service_args(driver_opts)
|
|
93
|
+
WebDriver.logger.deprecate('initializing Service class with :args using Hash',
|
|
94
|
+
':args parameter with an Array of String values',
|
|
95
|
+
id: :driver_opts)
|
|
95
96
|
driver_opts.key?(:args) ? driver_opts.delete(:args) : []
|
|
96
97
|
end
|
|
97
|
-
|
|
98
|
-
private
|
|
99
|
-
|
|
100
|
-
def binary_path(path = nil)
|
|
101
|
-
path = path.call if path.is_a?(Proc)
|
|
102
|
-
path ||= Platform.find_binary(self.class::EXECUTABLE)
|
|
103
|
-
|
|
104
|
-
raise Error::WebDriverError, self.class::MISSING_TEXT unless path
|
|
105
|
-
|
|
106
|
-
Platform.assert_executable path
|
|
107
|
-
path
|
|
108
|
-
end
|
|
109
98
|
end # Service
|
|
110
99
|
end # WebDriver
|
|
111
100
|
end # Selenium
|
|
@@ -40,7 +40,7 @@ module Selenium
|
|
|
40
40
|
@executable_path = config.executable_path
|
|
41
41
|
@host = Platform.localhost
|
|
42
42
|
@port = config.port
|
|
43
|
-
@extra_args = config.
|
|
43
|
+
@extra_args = config.args
|
|
44
44
|
@shutdown_supported = config.shutdown_supported
|
|
45
45
|
|
|
46
46
|
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
|
@@ -64,7 +64,7 @@ module Selenium
|
|
|
64
64
|
|
|
65
65
|
stop_server
|
|
66
66
|
@process.poll_for_exit STOP_TIMEOUT
|
|
67
|
-
rescue ChildProcess::TimeoutError
|
|
67
|
+
rescue ChildProcess::TimeoutError, Errno::ECONNREFUSED
|
|
68
68
|
nil # noop
|
|
69
69
|
ensure
|
|
70
70
|
stop_process
|
|
@@ -79,12 +79,7 @@ module Selenium
|
|
|
79
79
|
def build_process(*command)
|
|
80
80
|
WebDriver.logger.debug("Executing Process #{command}")
|
|
81
81
|
@process = ChildProcess.build(*command)
|
|
82
|
-
if WebDriver.logger.debug?
|
|
83
|
-
@process.io.stdout = @process.io.stderr = WebDriver.logger.io
|
|
84
|
-
elsif Platform.jruby?
|
|
85
|
-
# Apparently we need to read the output of drivers on JRuby.
|
|
86
|
-
@process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
|
|
87
|
-
end
|
|
82
|
+
@process.io = WebDriver.logger.io if WebDriver.logger.debug?
|
|
88
83
|
|
|
89
84
|
@process
|
|
90
85
|
end
|
|
@@ -104,8 +99,6 @@ module Selenium
|
|
|
104
99
|
|
|
105
100
|
def start_process
|
|
106
101
|
@process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
|
|
107
|
-
# NOTE: this is a bug only in Windows 7
|
|
108
|
-
@process.leader = true unless Platform.windows?
|
|
109
102
|
@process.start
|
|
110
103
|
end
|
|
111
104
|
|
|
@@ -113,7 +106,6 @@ module Selenium
|
|
|
113
106
|
return if process_exited?
|
|
114
107
|
|
|
115
108
|
@process.stop STOP_TIMEOUT
|
|
116
|
-
@process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
|
|
117
109
|
end
|
|
118
110
|
|
|
119
111
|
def stop_server
|
|
@@ -42,7 +42,7 @@ module Selenium
|
|
|
42
42
|
def ==(other)
|
|
43
43
|
other.is_a?(self.class) && ref == other.ref
|
|
44
44
|
end
|
|
45
|
-
|
|
45
|
+
alias eql? ==
|
|
46
46
|
|
|
47
47
|
def hash
|
|
48
48
|
[@id, @bridge].hash
|
|
@@ -81,7 +81,6 @@ module Selenium
|
|
|
81
81
|
private
|
|
82
82
|
|
|
83
83
|
attr_reader :bridge
|
|
84
|
-
|
|
85
84
|
end # ShadowRoot
|
|
86
85
|
end # WebDriver
|
|
87
86
|
end # Selenium
|
|
@@ -26,6 +26,7 @@ module Selenium
|
|
|
26
26
|
class SocketLock
|
|
27
27
|
def initialize(port, timeout)
|
|
28
28
|
@port = port
|
|
29
|
+
@server = nil
|
|
29
30
|
@timeout = timeout
|
|
30
31
|
end
|
|
31
32
|
|
|
@@ -66,8 +67,7 @@ module Selenium
|
|
|
66
67
|
|
|
67
68
|
def can_lock?
|
|
68
69
|
@server = TCPServer.new(Platform.localhost, @port)
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
@server.close_on_exec = true
|
|
71
71
|
true
|
|
72
72
|
rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
|
|
73
73
|
WebDriver.logger.debug("#{self}: #{e.message}")
|
|
@@ -32,8 +32,8 @@ module Selenium
|
|
|
32
32
|
def save_screenshot(png_path, full_page: false)
|
|
33
33
|
extension = File.extname(png_path).downcase
|
|
34
34
|
if extension != '.png'
|
|
35
|
-
WebDriver.logger.warn
|
|
36
|
-
|
|
35
|
+
WebDriver.logger.warn 'name used for saved screenshot does not match file type. ' \
|
|
36
|
+
'It should end with .png extension',
|
|
37
37
|
id: :screenshot
|
|
38
38
|
end
|
|
39
39
|
File.open(png_path, 'wb') { |f| f << screenshot_as(:png, full_page: full_page) }
|
|
@@ -60,7 +60,6 @@ module Selenium
|
|
|
60
60
|
rescue NameError
|
|
61
61
|
raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
|
|
62
62
|
end
|
|
63
|
-
|
|
64
63
|
end # TakesScreenshot
|
|
65
64
|
end # WebDriver
|
|
66
65
|
end # Selenium
|
|
@@ -51,9 +51,8 @@ module Selenium
|
|
|
51
51
|
#
|
|
52
52
|
|
|
53
53
|
def new_window(type = :window)
|
|
54
|
-
unless %i[window
|
|
55
|
-
|
|
56
|
-
end
|
|
54
|
+
raise ArgumentError, "Valid types are :tab and :window, received: #{type.inspect}" unless %i[window
|
|
55
|
+
tab].include?(type)
|
|
57
56
|
|
|
58
57
|
handle = @bridge.new_window(type)['handle']
|
|
59
58
|
|
|
@@ -48,7 +48,7 @@ module Selenium
|
|
|
48
48
|
def script
|
|
49
49
|
Float(@bridge.timeouts['script']) / 1000
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
alias script_timeout script
|
|
52
52
|
|
|
53
53
|
#
|
|
54
54
|
# Sets the amount of time to wait for an asynchronous script to finish
|
|
@@ -59,7 +59,7 @@ module Selenium
|
|
|
59
59
|
def script=(seconds)
|
|
60
60
|
@bridge.timeouts = {'script' => Integer(seconds * 1000)}
|
|
61
61
|
end
|
|
62
|
-
|
|
62
|
+
alias script_timeout= script=
|
|
63
63
|
|
|
64
64
|
#
|
|
65
65
|
# Gets the amount of time to wait for a page load to complete before throwing an error.
|
|
@@ -44,8 +44,8 @@ module Selenium
|
|
|
44
44
|
|
|
45
45
|
def from_json(opts)
|
|
46
46
|
user_handle = opts['userHandle'] ? decode(opts['userHandle']) : nil
|
|
47
|
-
new(id: decode(opts[
|
|
48
|
-
resident_credential: opts[
|
|
47
|
+
new(id: decode(opts['credentialId']),
|
|
48
|
+
resident_credential: opts['isResidentCredential'],
|
|
49
49
|
rp_id: opts['rpId'],
|
|
50
50
|
private_key: opts['privateKey'],
|
|
51
51
|
sign_count: opts['signCount'],
|
|
@@ -54,15 +54,17 @@ module Selenium
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
attr_reader :id, :resident_credential, :rp_id, :user_handle, :private_key, :sign_count
|
|
57
|
-
|
|
57
|
+
alias resident_credential? resident_credential
|
|
58
58
|
|
|
59
|
-
def initialize(id:, resident_credential:, rp_id:, private_key:,
|
|
59
|
+
def initialize(id:, resident_credential:, rp_id:, private_key:, **opts)
|
|
60
60
|
@id = id
|
|
61
61
|
@resident_credential = resident_credential
|
|
62
62
|
@rp_id = rp_id
|
|
63
|
-
@user_handle = user_handle
|
|
63
|
+
@user_handle = opts.delete(:user_handle) { nil }
|
|
64
64
|
@private_key = private_key
|
|
65
|
-
@sign_count = sign_count
|
|
65
|
+
@sign_count = opts.delete(:sign_count) { 0 }
|
|
66
|
+
|
|
67
|
+
raise ArgumentError, "Invalid arguments: #{opts.keys}" unless opts.empty?
|
|
66
68
|
end
|
|
67
69
|
|
|
68
70
|
#
|