selenium-webdriver 3.142.7 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGES +611 -5
- data/Gemfile +5 -1
- data/LICENSE +1 -1
- data/NOTICE +2 -0
- data/README.md +4 -5
- 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 +94 -79
- data/lib/selenium/webdriver/atoms/findElements.js +121 -0
- data/lib/selenium/webdriver/atoms/getAttribute.js +100 -7
- data/lib/selenium/webdriver/atoms/isDisplayed.js +76 -78
- data/lib/selenium/webdriver/atoms/mutationListener.js +55 -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 +51 -0
- data/lib/selenium/webdriver/{common/keyboard.rb → bidi.rb} +21 -35
- data/lib/selenium/webdriver/chrome/driver.rb +9 -86
- data/lib/selenium/webdriver/chrome/features.rb +44 -0
- data/lib/selenium/webdriver/chrome/options.rb +9 -158
- data/lib/selenium/webdriver/chrome/profile.rb +3 -80
- data/lib/selenium/webdriver/chrome/service.rb +6 -33
- data/lib/selenium/webdriver/chrome.rb +5 -18
- data/lib/selenium/webdriver/chromium/driver.rb +61 -0
- data/lib/selenium/webdriver/{chrome/bridge.rb → chromium/features.rb} +51 -16
- 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 +128 -238
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +94 -43
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +42 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +49 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +87 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +9 -9
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +86 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +36 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +42 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +41 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +36 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -9
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +143 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_logs.rb} +4 -4
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +16 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +69 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -13
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +75 -0
- data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
- data/lib/selenium/webdriver/common/driver_finder.rb +47 -0
- data/lib/selenium/webdriver/common/element.rb +89 -29
- data/lib/selenium/webdriver/common/error.rb +53 -194
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -22
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +10 -6
- data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
- data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
- data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +71 -82
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
- data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
- data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
- data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
- data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
- data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
- data/lib/selenium/webdriver/common/{w3c_manager.rb → interactions/wheel_input.rb} +14 -17
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/local_driver.rb +55 -0
- data/lib/selenium/webdriver/common/log_entry.rb +2 -2
- data/lib/selenium/webdriver/common/logger.rb +119 -19
- data/lib/selenium/webdriver/common/manager.rb +11 -38
- data/lib/selenium/webdriver/common/options.rb +169 -23
- data/lib/selenium/webdriver/common/platform.rb +14 -6
- data/lib/selenium/webdriver/common/port_prober.rb +4 -6
- data/lib/selenium/webdriver/common/profile_helper.rb +11 -9
- data/lib/selenium/webdriver/common/proxy.rb +8 -5
- data/lib/selenium/webdriver/common/search_context.rb +7 -9
- data/lib/selenium/webdriver/common/selenium_manager.rb +125 -0
- data/lib/selenium/webdriver/common/service.rb +26 -137
- data/lib/selenium/webdriver/common/service_manager.rb +144 -0
- data/lib/selenium/webdriver/common/shadow_root.rb +86 -0
- data/lib/selenium/webdriver/common/socket_lock.rb +4 -4
- data/lib/selenium/webdriver/common/socket_poller.rb +4 -4
- data/lib/selenium/webdriver/common/takes_screenshot.rb +65 -0
- data/lib/selenium/webdriver/common/target_locator.rb +31 -4
- data/lib/selenium/webdriver/common/timeouts.rb +31 -4
- 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/wait.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
- data/lib/selenium/webdriver/common/window.rb +6 -10
- data/lib/selenium/webdriver/common/zipper.rb +4 -10
- data/lib/selenium/webdriver/common.rb +42 -18
- data/lib/selenium/webdriver/devtools/console_event.rb +36 -0
- data/lib/selenium/webdriver/devtools/exception_event.rb +34 -0
- data/lib/selenium/webdriver/devtools/mutation_event.rb +35 -0
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +57 -0
- data/lib/selenium/webdriver/devtools/request.rb +65 -0
- data/lib/selenium/webdriver/devtools/response.rb +64 -0
- data/lib/selenium/webdriver/devtools.rb +96 -0
- data/lib/selenium/webdriver/edge/driver.rb +11 -27
- data/lib/selenium/webdriver/edge/features.rb +44 -0
- data/lib/selenium/webdriver/edge/options.rb +18 -43
- data/lib/selenium/webdriver/edge/profile.rb +33 -0
- data/lib/selenium/webdriver/edge/service.rb +7 -27
- data/lib/selenium/webdriver/edge.rb +11 -14
- data/lib/selenium/webdriver/firefox/driver.rb +38 -19
- data/lib/selenium/webdriver/firefox/extension.rb +8 -0
- data/lib/selenium/webdriver/firefox/features.rb +66 -0
- data/lib/selenium/webdriver/firefox/options.rb +77 -50
- data/lib/selenium/webdriver/firefox/profile.rb +17 -71
- data/lib/selenium/webdriver/firefox/service.rb +3 -13
- data/lib/selenium/webdriver/firefox/util.rb +1 -1
- data/lib/selenium/webdriver/firefox.rb +17 -28
- data/lib/selenium/webdriver/ie/driver.rb +5 -45
- data/lib/selenium/webdriver/ie/options.rb +15 -46
- data/lib/selenium/webdriver/ie/service.rb +11 -19
- data/lib/selenium/webdriver/ie.rb +3 -16
- data/lib/selenium/webdriver/remote/bridge/commands.rb +170 -0
- data/lib/selenium/webdriver/remote/bridge.rb +592 -87
- data/lib/selenium/webdriver/remote/capabilities.rb +182 -124
- data/lib/selenium/webdriver/remote/driver.rb +30 -15
- data/lib/selenium/webdriver/remote/http/common.rb +3 -8
- data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
- data/lib/selenium/webdriver/remote/http/default.rb +23 -31
- data/lib/selenium/webdriver/remote/response.rb +17 -49
- data/lib/selenium/webdriver/remote.rb +14 -12
- data/lib/selenium/webdriver/safari/driver.rb +7 -29
- data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -5
- data/lib/selenium/webdriver/safari/options.rb +12 -27
- data/lib/selenium/webdriver/safari/service.rb +13 -11
- data/lib/selenium/webdriver/safari.rb +14 -20
- data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
- data/lib/selenium/webdriver/support/color.rb +24 -24
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
- data/lib/selenium/webdriver/support/guards/guard.rb +87 -0
- data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +21 -20
- data/lib/selenium/webdriver/support/guards.rb +95 -0
- data/lib/selenium/webdriver/support/relative_locator.rb +50 -0
- data/lib/selenium/webdriver/support/select.rb +6 -4
- data/lib/selenium/webdriver/support.rb +1 -0
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +18 -17
- data/selenium-webdriver.gemspec +36 -18
- metadata +159 -89
- data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
- data/lib/selenium/webdriver/common/mouse.rb +0 -89
- data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
- data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
- data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
- data/lib/selenium/webdriver/edge/bridge.rb +0 -76
- data/lib/selenium/webdriver/firefox/binary.rb +0 -187
- data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
- data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
- data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
- data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/remote/http/persistent.rb +0 -60
- data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
- data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
- data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
- data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
- data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -0,0 +1,125 @@
|
|
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
|
+
class << self
|
31
|
+
attr_writer :bin_path
|
32
|
+
|
33
|
+
def bin_path
|
34
|
+
@bin_path ||= '../../../../../bin'
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [Options] options browser options.
|
38
|
+
# @return [String] the path to the correct driver.
|
39
|
+
def driver_path(options)
|
40
|
+
message = 'applicable driver not found; attempting to install with Selenium Manager (Beta)'
|
41
|
+
WebDriver.logger.debug(message, id: :selenium_manager)
|
42
|
+
|
43
|
+
command = generate_command(binary, options)
|
44
|
+
|
45
|
+
location = run(*command)
|
46
|
+
WebDriver.logger.debug("Driver found at #{location}", id: :selenium_manager)
|
47
|
+
Platform.assert_executable location
|
48
|
+
|
49
|
+
location
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def generate_command(binary, options)
|
55
|
+
command = [binary, '--browser', options.browser_name, '--output', 'json']
|
56
|
+
if options.browser_version
|
57
|
+
command << '--browser-version'
|
58
|
+
command << options.browser_version
|
59
|
+
end
|
60
|
+
if options.respond_to?(:binary) && !options.binary.nil?
|
61
|
+
command << '--browser-path'
|
62
|
+
command << options.binary.gsub('\\', '\\\\\\')
|
63
|
+
end
|
64
|
+
if options.proxy
|
65
|
+
command << '--proxy'
|
66
|
+
(command << options.proxy.ssl) || options.proxy.http
|
67
|
+
end
|
68
|
+
command << '--debug' if WebDriver.logger.debug?
|
69
|
+
command
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [String] the path to the correct selenium manager
|
73
|
+
def binary
|
74
|
+
@binary ||= begin
|
75
|
+
path = File.expand_path(bin_path, __FILE__)
|
76
|
+
path << if Platform.windows?
|
77
|
+
'/windows/selenium-manager.exe'
|
78
|
+
elsif Platform.mac?
|
79
|
+
'/macos/selenium-manager'
|
80
|
+
elsif Platform.linux?
|
81
|
+
'/linux/selenium-manager'
|
82
|
+
end
|
83
|
+
location = File.expand_path(path, __FILE__)
|
84
|
+
|
85
|
+
begin
|
86
|
+
Platform.assert_file(location)
|
87
|
+
Platform.assert_executable(location)
|
88
|
+
rescue TypeError
|
89
|
+
raise Error::WebDriverError,
|
90
|
+
"Unable to locate or obtain Selenium Manager binary; #{location} is not a valid file object"
|
91
|
+
rescue Error::WebDriverError => e
|
92
|
+
raise Error::WebDriverError, "Selenium Manager binary located, but #{e.message}"
|
93
|
+
end
|
94
|
+
|
95
|
+
WebDriver.logger.debug("Selenium Manager binary found at #{location}", id: :selenium_manager)
|
96
|
+
location
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def run(*command)
|
101
|
+
WebDriver.logger.debug("Executing Process #{command}", id: :selenium_manager)
|
102
|
+
|
103
|
+
begin
|
104
|
+
stdout, stderr, status = Open3.capture3(*command)
|
105
|
+
json_output = stdout.empty? ? nil : JSON.parse(stdout)
|
106
|
+
result = json_output&.dig('result', 'message')
|
107
|
+
rescue StandardError => e
|
108
|
+
raise Error::WebDriverError, "Unsuccessful command executed: #{command}; #{e.message}"
|
109
|
+
end
|
110
|
+
|
111
|
+
(json_output&.fetch('logs') || []).each do |log|
|
112
|
+
level = log['level'].casecmp('info').zero? ? 'debug' : log['level'].downcase
|
113
|
+
WebDriver.logger.send(level, log['message'], id: :selenium_manager)
|
114
|
+
end
|
115
|
+
|
116
|
+
if status.exitstatus.positive?
|
117
|
+
raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
|
118
|
+
end
|
119
|
+
|
120
|
+
result
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end # SeleniumManager
|
124
|
+
end # WebDriver
|
125
|
+
end # Selenium
|
@@ -21,51 +21,31 @@ module Selenium
|
|
21
21
|
module WebDriver
|
22
22
|
#
|
23
23
|
# Base class implementing default behavior of service object,
|
24
|
-
# responsible for
|
24
|
+
# responsible for storing a service manager configuration.
|
25
25
|
#
|
26
26
|
|
27
27
|
class Service
|
28
|
-
START_TIMEOUT = 20
|
29
|
-
SOCKET_LOCK_TIMEOUT = 45
|
30
|
-
STOP_TIMEOUT = 20
|
31
|
-
|
32
|
-
@default_port = nil
|
33
|
-
@driver_path = nil
|
34
|
-
@executable = nil
|
35
|
-
@missing_text = nil
|
36
|
-
|
37
28
|
class << self
|
38
|
-
attr_reader :
|
29
|
+
attr_reader :driver_path
|
39
30
|
|
40
31
|
def chrome(**opts)
|
41
32
|
Chrome::Service.new(**opts)
|
42
33
|
end
|
43
34
|
|
44
35
|
def firefox(**opts)
|
45
|
-
binary_path = Firefox::Binary.path
|
46
|
-
args = opts.delete(:args)
|
47
|
-
case args
|
48
|
-
when Hash
|
49
|
-
args[:binary] ||= binary_path
|
50
|
-
opts[:args] = args
|
51
|
-
when Array
|
52
|
-
opts[:args] = ["--binary=#{binary_path}"]
|
53
|
-
opts[:args] += args
|
54
|
-
else
|
55
|
-
opts[:args] = ["--binary=#{binary_path}"]
|
56
|
-
end
|
57
|
-
|
58
36
|
Firefox::Service.new(**opts)
|
59
37
|
end
|
60
38
|
|
61
39
|
def ie(**opts)
|
62
40
|
IE::Service.new(**opts)
|
63
41
|
end
|
64
|
-
|
42
|
+
alias internet_explorer ie
|
65
43
|
|
66
44
|
def edge(**opts)
|
67
45
|
Edge::Service.new(**opts)
|
68
46
|
end
|
47
|
+
alias microsoftedge edge
|
48
|
+
alias msedge edge
|
69
49
|
|
70
50
|
def safari(**opts)
|
71
51
|
Safari::Service.new(**opts)
|
@@ -77,8 +57,8 @@ module Selenium
|
|
77
57
|
end
|
78
58
|
end
|
79
59
|
|
80
|
-
attr_accessor :host
|
81
|
-
|
60
|
+
attr_accessor :host, :executable_path, :port, :log, :args
|
61
|
+
alias extra_args args
|
82
62
|
|
83
63
|
#
|
84
64
|
# End users should use a class method for the desired driver, rather than using this directly.
|
@@ -86,134 +66,43 @@ module Selenium
|
|
86
66
|
# @api private
|
87
67
|
#
|
88
68
|
|
89
|
-
def initialize(path: nil, port: nil, args: nil)
|
90
|
-
|
91
|
-
port ||= self.class.default_port
|
69
|
+
def initialize(path: nil, port: nil, log: nil, args: nil)
|
70
|
+
port ||= self.class::DEFAULT_PORT
|
92
71
|
args ||= []
|
93
72
|
|
94
|
-
@executable_path =
|
73
|
+
@executable_path = path
|
95
74
|
@host = Platform.localhost
|
96
75
|
@port = Integer(port)
|
76
|
+
@log = case log
|
77
|
+
when :stdout
|
78
|
+
$stdout
|
79
|
+
when :stderr
|
80
|
+
$stderr
|
81
|
+
else
|
82
|
+
log
|
83
|
+
end
|
97
84
|
|
98
|
-
@
|
85
|
+
@args = args.is_a?(Hash) ? extract_service_args(args) : args
|
99
86
|
|
100
87
|
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
101
88
|
end
|
102
89
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
|
107
|
-
|
108
|
-
socket_lock.locked do
|
109
|
-
find_free_port
|
110
|
-
start_process
|
111
|
-
connect_until_stable
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def stop
|
116
|
-
return unless self.class.shutdown_supported
|
117
|
-
|
118
|
-
stop_server
|
119
|
-
@process.poll_for_exit STOP_TIMEOUT
|
120
|
-
rescue ChildProcess::TimeoutError
|
121
|
-
nil # noop
|
122
|
-
ensure
|
123
|
-
stop_process
|
124
|
-
end
|
125
|
-
|
126
|
-
def uri
|
127
|
-
@uri ||= URI.parse("http://#{@host}:#{@port}")
|
128
|
-
end
|
129
|
-
|
130
|
-
private
|
131
|
-
|
132
|
-
def binary_path(path = nil)
|
133
|
-
path = path.call if path.is_a?(Proc)
|
134
|
-
path ||= Platform.find_binary(self.class.executable)
|
135
|
-
|
136
|
-
raise Error::WebDriverError, self.class.missing_text unless path
|
137
|
-
|
138
|
-
Platform.assert_executable path
|
139
|
-
path
|
140
|
-
end
|
141
|
-
|
142
|
-
def build_process(*command)
|
143
|
-
WebDriver.logger.debug("Executing Process #{command}")
|
144
|
-
@process = ChildProcess.build(*command)
|
145
|
-
if WebDriver.logger.debug?
|
146
|
-
@process.io.stdout = @process.io.stderr = WebDriver.logger.io
|
147
|
-
elsif Platform.jruby?
|
148
|
-
# Apparently we need to read the output of drivers on JRuby.
|
149
|
-
@process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
|
150
|
-
end
|
151
|
-
|
152
|
-
@process
|
153
|
-
end
|
154
|
-
|
155
|
-
def connect_to_server
|
156
|
-
Net::HTTP.start(@host, @port) do |http|
|
157
|
-
http.open_timeout = STOP_TIMEOUT / 2
|
158
|
-
http.read_timeout = STOP_TIMEOUT / 2
|
159
|
-
|
160
|
-
yield http
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def find_free_port
|
165
|
-
@port = PortProber.above(@port)
|
166
|
-
end
|
167
|
-
|
168
|
-
def start_process
|
169
|
-
@process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
|
170
|
-
# Note: this is a bug only in Windows 7
|
171
|
-
@process.leader = true unless Platform.windows?
|
172
|
-
@process.start
|
173
|
-
end
|
174
|
-
|
175
|
-
def stop_process
|
176
|
-
return if process_exited?
|
177
|
-
|
178
|
-
@process.stop STOP_TIMEOUT
|
179
|
-
@process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
|
180
|
-
end
|
181
|
-
|
182
|
-
def stop_server
|
183
|
-
return if process_exited?
|
184
|
-
|
185
|
-
connect_to_server { |http| http.get('/shutdown') }
|
186
|
-
end
|
187
|
-
|
188
|
-
def process_running?
|
189
|
-
defined?(@process) && @process&.alive?
|
90
|
+
def launch
|
91
|
+
ServiceManager.new(self).tap(&:start)
|
190
92
|
end
|
191
93
|
|
192
|
-
def
|
193
|
-
|
194
|
-
end
|
195
|
-
|
196
|
-
def connect_until_stable
|
197
|
-
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
|
198
|
-
return if socket_poller.connected?
|
199
|
-
|
200
|
-
raise Error::WebDriverError, cannot_connect_error_text
|
201
|
-
end
|
202
|
-
|
203
|
-
def cannot_connect_error_text
|
204
|
-
"unable to connect to #{self.class.executable} #{@host}:#{@port}"
|
205
|
-
end
|
206
|
-
|
207
|
-
def socket_lock
|
208
|
-
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
|
94
|
+
def shutdown_supported
|
95
|
+
self.class::SHUTDOWN_SUPPORTED
|
209
96
|
end
|
210
97
|
|
211
98
|
protected
|
212
99
|
|
213
100
|
def extract_service_args(driver_opts)
|
101
|
+
WebDriver.logger.deprecate('initializing Service class with :args using Hash',
|
102
|
+
':args parameter with an Array of String values',
|
103
|
+
id: :driver_opts)
|
214
104
|
driver_opts.key?(:args) ? driver_opts.delete(:args) : []
|
215
105
|
end
|
216
|
-
|
217
106
|
end # Service
|
218
107
|
end # WebDriver
|
219
108
|
end # Selenium
|
@@ -0,0 +1,144 @@
|
|
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
|
+
#
|
23
|
+
# Base class implementing default behavior of service_manager object,
|
24
|
+
# responsible for starting and stopping driver implementations.
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
#
|
28
|
+
class ServiceManager
|
29
|
+
START_TIMEOUT = 20
|
30
|
+
SOCKET_LOCK_TIMEOUT = 45
|
31
|
+
STOP_TIMEOUT = 20
|
32
|
+
|
33
|
+
#
|
34
|
+
# End users should use a class method for the desired driver, rather than using this directly.
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
|
39
|
+
def initialize(config)
|
40
|
+
@executable_path = config.executable_path
|
41
|
+
@host = Platform.localhost
|
42
|
+
@port = config.port
|
43
|
+
@extra_args = config.args
|
44
|
+
@io = config.log
|
45
|
+
@shutdown_supported = config.shutdown_supported
|
46
|
+
|
47
|
+
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def start
|
51
|
+
raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
|
52
|
+
|
53
|
+
Platform.exit_hook { stop } # make sure we don't leave the server running
|
54
|
+
|
55
|
+
socket_lock.locked do
|
56
|
+
find_free_port
|
57
|
+
start_process
|
58
|
+
connect_until_stable
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop
|
63
|
+
return unless @shutdown_supported
|
64
|
+
return if process_exited?
|
65
|
+
|
66
|
+
stop_server
|
67
|
+
@process.poll_for_exit STOP_TIMEOUT
|
68
|
+
rescue ChildProcess::TimeoutError, Errno::ECONNREFUSED
|
69
|
+
nil # noop
|
70
|
+
ensure
|
71
|
+
stop_process
|
72
|
+
end
|
73
|
+
|
74
|
+
def uri
|
75
|
+
@uri ||= URI.parse("http://#{@host}:#{@port}")
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def build_process(*command)
|
81
|
+
WebDriver.logger.debug("Executing Process #{command}", id: :driver_service)
|
82
|
+
@process = ChildProcess.build(*command)
|
83
|
+
@io ||= WebDriver.logger.io if WebDriver.logger.debug?
|
84
|
+
@process.io = @io if @io
|
85
|
+
|
86
|
+
@process
|
87
|
+
end
|
88
|
+
|
89
|
+
def connect_to_server
|
90
|
+
Net::HTTP.start(@host, @port) do |http|
|
91
|
+
http.open_timeout = STOP_TIMEOUT / 2
|
92
|
+
http.read_timeout = STOP_TIMEOUT / 2
|
93
|
+
|
94
|
+
yield http
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_free_port
|
99
|
+
@port = PortProber.above(@port)
|
100
|
+
end
|
101
|
+
|
102
|
+
def start_process
|
103
|
+
@process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
|
104
|
+
@process.start
|
105
|
+
end
|
106
|
+
|
107
|
+
def stop_process
|
108
|
+
return if process_exited?
|
109
|
+
|
110
|
+
@process.stop STOP_TIMEOUT
|
111
|
+
end
|
112
|
+
|
113
|
+
def stop_server
|
114
|
+
connect_to_server do |http|
|
115
|
+
headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
|
116
|
+
http.get('/shutdown', headers)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def process_running?
|
121
|
+
defined?(@process) && @process&.alive?
|
122
|
+
end
|
123
|
+
|
124
|
+
def process_exited?
|
125
|
+
@process.nil? || @process.exited?
|
126
|
+
end
|
127
|
+
|
128
|
+
def connect_until_stable
|
129
|
+
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
|
130
|
+
return if socket_poller.connected?
|
131
|
+
|
132
|
+
raise Error::WebDriverError, cannot_connect_error_text
|
133
|
+
end
|
134
|
+
|
135
|
+
def cannot_connect_error_text
|
136
|
+
"unable to connect to #{@executable_path} #{@host}:#{@port}"
|
137
|
+
end
|
138
|
+
|
139
|
+
def socket_lock
|
140
|
+
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
|
141
|
+
end
|
142
|
+
end # Service
|
143
|
+
end # WebDriver
|
144
|
+
end # Selenium
|
@@ -0,0 +1,86 @@
|
|
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
|
+
class ShadowRoot
|
23
|
+
ROOT_KEY = 'shadow-6066-11e4-a52e-4f735466cecf'
|
24
|
+
|
25
|
+
include SearchContext
|
26
|
+
|
27
|
+
#
|
28
|
+
# Creates a new shadow root
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
|
33
|
+
def initialize(bridge, id)
|
34
|
+
@bridge = bridge
|
35
|
+
@id = id
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
format '#<%<class>s:0x%<hash>x id=%<id>s>', class: self.class, hash: hash * 2, id: @id.inspect
|
40
|
+
end
|
41
|
+
|
42
|
+
def ==(other)
|
43
|
+
other.is_a?(self.class) && ref == other.ref
|
44
|
+
end
|
45
|
+
alias eql? ==
|
46
|
+
|
47
|
+
def hash
|
48
|
+
[@id, @bridge].hash
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
# @see SearchContext
|
54
|
+
#
|
55
|
+
|
56
|
+
def ref
|
57
|
+
[:shadow_root, @id]
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Convert to a ShadowRoot JSON Object for transmission over the wire.
|
62
|
+
# @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#basic-terms-and-concepts
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
#
|
66
|
+
|
67
|
+
def to_json(*)
|
68
|
+
JSON.generate as_json
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# For Rails 3 - http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
#
|
76
|
+
|
77
|
+
def as_json(*)
|
78
|
+
{ROOT_KEY => @id}
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
attr_reader :bridge
|
84
|
+
end # ShadowRoot
|
85
|
+
end # WebDriver
|
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,11 +67,10 @@ 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
|
-
rescue SocketError, Errno::EADDRINUSE, Errno::EBADF =>
|
73
|
-
WebDriver.logger.debug("#{self}: #{
|
72
|
+
rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
|
73
|
+
WebDriver.logger.debug("#{self}: #{e.message}", id: :driver_service)
|
74
74
|
false
|
75
75
|
end
|
76
76
|
|
@@ -66,8 +66,8 @@ module Selenium
|
|
66
66
|
}.freeze
|
67
67
|
|
68
68
|
if Platform.jruby?
|
69
|
-
# we use a plain TCPSocket here since JRuby has issues
|
70
|
-
# see
|
69
|
+
# we use a plain TCPSocket here since JRuby has issues closing socket
|
70
|
+
# see https://github.com/jruby/jruby/issues/5709
|
71
71
|
def listening?
|
72
72
|
TCPSocket.new(@host, @port).close
|
73
73
|
true
|
@@ -93,13 +93,13 @@ module Selenium
|
|
93
93
|
true
|
94
94
|
rescue *NOT_CONNECTED_ERRORS
|
95
95
|
sock&.close
|
96
|
-
WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}")
|
96
|
+
WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}", id: :driver_service)
|
97
97
|
false
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
101
|
def socket_writable?(sock)
|
102
|
-
|
102
|
+
sock.wait_writable(CONNECT_TIMEOUT)
|
103
103
|
end
|
104
104
|
|
105
105
|
def conn_completed?(sock)
|
@@ -0,0 +1,65 @@
|
|
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
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
module TakesScreenshot
|
26
|
+
#
|
27
|
+
# Save a PNG screenshot of the viewport to the given path
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
#
|
31
|
+
|
32
|
+
def save_screenshot(png_path, full_page: false)
|
33
|
+
extension = File.extname(png_path).downcase
|
34
|
+
if extension != '.png'
|
35
|
+
WebDriver.logger.warn 'name used for saved screenshot does not match file type. ' \
|
36
|
+
'It should end with .png extension',
|
37
|
+
id: :screenshot
|
38
|
+
end
|
39
|
+
File.open(png_path, 'wb') { |f| f << screenshot_as(:png, full_page: full_page) }
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Return a PNG screenshot in the given format as a string
|
44
|
+
#
|
45
|
+
# @param [:base64, :png] format
|
46
|
+
# @param [Boolean] full_page allows taking full page screenshots if supported
|
47
|
+
# @return String screenshot
|
48
|
+
#
|
49
|
+
# @api public
|
50
|
+
|
51
|
+
def screenshot_as(format, full_page: false)
|
52
|
+
case format
|
53
|
+
when :base64
|
54
|
+
full_page ? full_screenshot : screenshot
|
55
|
+
when :png
|
56
|
+
screenshot_as(:base64, full_page: full_page).unpack1('m')
|
57
|
+
else
|
58
|
+
raise Error::UnsupportedOperationError, "unsupported format: #{format.inspect}"
|
59
|
+
end
|
60
|
+
rescue NameError
|
61
|
+
raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
|
62
|
+
end
|
63
|
+
end # TakesScreenshot
|
64
|
+
end # WebDriver
|
65
|
+
end # Selenium
|