selenium-webdriver 3.142.7 → 4.3.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 +432 -5
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/NOTICE +2 -0
- data/README.md +4 -5
- data/lib/selenium/server.rb +75 -64
- data/lib/selenium/webdriver/atoms/findElements.js +122 -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/bidi/session.rb +38 -0
- data/lib/selenium/webdriver/bidi.rb +55 -0
- data/lib/selenium/webdriver/chrome/driver.rb +26 -83
- data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +55 -12
- data/lib/selenium/webdriver/chrome/options.rb +138 -67
- data/lib/selenium/webdriver/chrome/profile.rb +6 -3
- data/lib/selenium/webdriver/chrome/service.rb +8 -15
- data/lib/selenium/webdriver/chrome.rb +5 -18
- data/lib/selenium/webdriver/common/action_builder.rb +171 -236
- data/lib/selenium/webdriver/common/driver.rb +76 -29
- data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +43 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +51 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +89 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +10 -8
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +87 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +38 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +44 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +43 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +38 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -8
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +144 -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 +17 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +136 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -11
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +77 -0
- data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
- data/lib/selenium/webdriver/common/element.rb +83 -23
- data/lib/selenium/webdriver/common/error.rb +32 -196
- 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 +68 -78
- 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 +57 -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/log_entry.rb +2 -2
- data/lib/selenium/webdriver/common/logger.rb +50 -15
- data/lib/selenium/webdriver/common/manager.rb +11 -38
- data/lib/selenium/webdriver/common/options.rb +147 -23
- data/lib/selenium/webdriver/common/platform.rb +10 -5
- 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 +6 -3
- data/lib/selenium/webdriver/common/search_context.rb +7 -9
- data/lib/selenium/webdriver/common/service.rb +17 -125
- data/lib/selenium/webdriver/common/service_manager.rb +150 -0
- data/lib/selenium/webdriver/common/shadow_root.rb +87 -0
- data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
- data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
- data/lib/selenium/webdriver/common/takes_screenshot.rb +66 -0
- data/lib/selenium/webdriver/common/target_locator.rb +32 -4
- data/lib/selenium/webdriver/common/timeouts.rb +31 -4
- data/lib/selenium/webdriver/common/wait.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +149 -0
- data/lib/selenium/webdriver/common/window.rb +0 -4
- data/lib/selenium/webdriver/common/zipper.rb +3 -9
- data/lib/selenium/webdriver/common.rb +35 -18
- data/lib/selenium/webdriver/devtools/console_event.rb +38 -0
- data/lib/selenium/webdriver/devtools/exception_event.rb +36 -0
- data/lib/selenium/webdriver/devtools/mutation_event.rb +37 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +59 -0
- data/lib/selenium/webdriver/devtools/request.rb +67 -0
- data/lib/selenium/webdriver/devtools/response.rb +66 -0
- data/lib/selenium/webdriver/devtools.rb +86 -0
- data/lib/selenium/webdriver/edge/driver.rb +7 -29
- data/lib/selenium/webdriver/edge/features.rb +45 -0
- data/lib/selenium/webdriver/edge/options.rb +11 -48
- data/lib/selenium/webdriver/edge/profile.rb +33 -0
- data/lib/selenium/webdriver/edge/service.rb +10 -26
- data/lib/selenium/webdriver/edge.rb +11 -14
- data/lib/selenium/webdriver/firefox/driver.rb +32 -19
- data/lib/selenium/webdriver/firefox/extension.rb +8 -0
- data/lib/selenium/webdriver/firefox/features.rb +63 -0
- data/lib/selenium/webdriver/firefox/options.rb +73 -50
- data/lib/selenium/webdriver/firefox/profile.rb +16 -70
- data/lib/selenium/webdriver/firefox/service.rb +5 -9
- data/lib/selenium/webdriver/firefox/util.rb +1 -1
- data/lib/selenium/webdriver/firefox.rb +17 -28
- data/lib/selenium/webdriver/ie/driver.rb +1 -47
- data/lib/selenium/webdriver/ie/options.rb +15 -46
- data/lib/selenium/webdriver/ie/service.rb +13 -15
- data/lib/selenium/webdriver/ie.rb +3 -16
- data/lib/selenium/webdriver/remote/bridge.rb +563 -86
- data/lib/selenium/webdriver/remote/capabilities.rb +159 -123
- data/lib/selenium/webdriver/remote/commands.rb +158 -0
- data/lib/selenium/webdriver/remote/driver.rb +22 -13
- data/lib/selenium/webdriver/remote/http/common.rb +0 -5
- data/lib/selenium/webdriver/remote/http/default.rb +22 -31
- data/lib/selenium/webdriver/remote/response.rb +18 -49
- data/lib/selenium/webdriver/remote.rb +15 -12
- data/lib/selenium/webdriver/safari/driver.rb +3 -31
- data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -3
- data/lib/selenium/webdriver/safari/options.rb +10 -29
- data/lib/selenium/webdriver/safari/service.rb +4 -8
- data/lib/selenium/webdriver/safari.rb +13 -19
- data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
- data/lib/selenium/webdriver/support/cdp/domain.rb.erb +63 -0
- data/lib/selenium/webdriver/support/cdp_client_generator.rb +108 -0
- data/lib/selenium/webdriver/support/color.rb +9 -9
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
- data/lib/selenium/webdriver/support/guards/guard.rb +89 -0
- data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +22 -19
- data/lib/selenium/webdriver/support/guards.rb +95 -0
- data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
- data/lib/selenium/webdriver/support/select.rb +3 -3
- data/lib/selenium/webdriver/support.rb +1 -0
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +14 -13
- data/selenium-webdriver.gemspec +32 -13
- metadata +176 -69
- 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/keyboard.rb +0 -70
- 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
|
@@ -21,40 +21,18 @@ 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
|
|
|
@@ -66,6 +44,7 @@ module Selenium
|
|
|
66
44
|
def edge(**opts)
|
|
67
45
|
Edge::Service.new(**opts)
|
|
68
46
|
end
|
|
47
|
+
alias_method :microsoftedge, :edge
|
|
69
48
|
|
|
70
49
|
def safari(**opts)
|
|
71
50
|
Safari::Service.new(**opts)
|
|
@@ -78,7 +57,7 @@ module Selenium
|
|
|
78
57
|
end
|
|
79
58
|
|
|
80
59
|
attr_accessor :host
|
|
81
|
-
attr_reader :executable_path
|
|
60
|
+
attr_reader :executable_path, :port, :extra_args
|
|
82
61
|
|
|
83
62
|
#
|
|
84
63
|
# End users should use a class method for the desired driver, rather than using this directly.
|
|
@@ -88,7 +67,7 @@ module Selenium
|
|
|
88
67
|
|
|
89
68
|
def initialize(path: nil, port: nil, args: nil)
|
|
90
69
|
path ||= self.class.driver_path
|
|
91
|
-
port ||= self.class
|
|
70
|
+
port ||= self.class::DEFAULT_PORT
|
|
92
71
|
args ||= []
|
|
93
72
|
|
|
94
73
|
@executable_path = binary_path(path)
|
|
@@ -100,120 +79,33 @@ module Selenium
|
|
|
100
79
|
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
|
101
80
|
end
|
|
102
81
|
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
socket_lock.locked do
|
|
109
|
-
find_free_port
|
|
110
|
-
start_process
|
|
111
|
-
connect_until_stable
|
|
112
|
-
end
|
|
82
|
+
def launch
|
|
83
|
+
sm = ServiceManager.new(self)
|
|
84
|
+
sm.start
|
|
85
|
+
sm
|
|
113
86
|
end
|
|
114
87
|
|
|
115
|
-
def
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
stop_server
|
|
119
|
-
@process.poll_for_exit STOP_TIMEOUT
|
|
120
|
-
rescue ChildProcess::TimeoutError
|
|
121
|
-
nil # noop
|
|
122
|
-
ensure
|
|
123
|
-
stop_process
|
|
88
|
+
def shutdown_supported
|
|
89
|
+
self.class::SHUTDOWN_SUPPORTED
|
|
124
90
|
end
|
|
125
91
|
|
|
126
|
-
|
|
127
|
-
|
|
92
|
+
protected
|
|
93
|
+
|
|
94
|
+
def extract_service_args(driver_opts)
|
|
95
|
+
driver_opts.key?(:args) ? driver_opts.delete(:args) : []
|
|
128
96
|
end
|
|
129
97
|
|
|
130
98
|
private
|
|
131
99
|
|
|
132
100
|
def binary_path(path = nil)
|
|
133
101
|
path = path.call if path.is_a?(Proc)
|
|
134
|
-
path ||= Platform.find_binary(self.class
|
|
102
|
+
path ||= Platform.find_binary(self.class::EXECUTABLE)
|
|
135
103
|
|
|
136
|
-
raise Error::WebDriverError, self.class
|
|
104
|
+
raise Error::WebDriverError, self.class::MISSING_TEXT unless path
|
|
137
105
|
|
|
138
106
|
Platform.assert_executable path
|
|
139
107
|
path
|
|
140
108
|
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?
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def process_exited?
|
|
193
|
-
@process.nil? || @process.exited?
|
|
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)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
protected
|
|
212
|
-
|
|
213
|
-
def extract_service_args(driver_opts)
|
|
214
|
-
driver_opts.key?(:args) ? driver_opts.delete(:args) : []
|
|
215
|
-
end
|
|
216
|
-
|
|
217
109
|
end # Service
|
|
218
110
|
end # WebDriver
|
|
219
111
|
end # Selenium
|
|
@@ -0,0 +1,150 @@
|
|
|
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.extra_args
|
|
44
|
+
@shutdown_supported = config.shutdown_supported
|
|
45
|
+
|
|
46
|
+
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def start
|
|
50
|
+
raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
|
|
51
|
+
|
|
52
|
+
Platform.exit_hook { stop } # make sure we don't leave the server running
|
|
53
|
+
|
|
54
|
+
socket_lock.locked do
|
|
55
|
+
find_free_port
|
|
56
|
+
start_process
|
|
57
|
+
connect_until_stable
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def stop
|
|
62
|
+
return unless @shutdown_supported
|
|
63
|
+
return if process_exited?
|
|
64
|
+
|
|
65
|
+
stop_server
|
|
66
|
+
@process.poll_for_exit STOP_TIMEOUT
|
|
67
|
+
rescue ChildProcess::TimeoutError
|
|
68
|
+
nil # noop
|
|
69
|
+
ensure
|
|
70
|
+
stop_process
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def uri
|
|
74
|
+
@uri ||= URI.parse("http://#{@host}:#{@port}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def build_process(*command)
|
|
80
|
+
WebDriver.logger.debug("Executing Process #{command}")
|
|
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
|
|
88
|
+
|
|
89
|
+
@process
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def connect_to_server
|
|
93
|
+
Net::HTTP.start(@host, @port) do |http|
|
|
94
|
+
http.open_timeout = STOP_TIMEOUT / 2
|
|
95
|
+
http.read_timeout = STOP_TIMEOUT / 2
|
|
96
|
+
|
|
97
|
+
yield http
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def find_free_port
|
|
102
|
+
@port = PortProber.above(@port)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def start_process
|
|
106
|
+
@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
|
+
@process.start
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def stop_process
|
|
113
|
+
return if process_exited?
|
|
114
|
+
|
|
115
|
+
@process.stop STOP_TIMEOUT
|
|
116
|
+
@process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def stop_server
|
|
120
|
+
connect_to_server do |http|
|
|
121
|
+
headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
|
|
122
|
+
http.get('/shutdown', headers)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def process_running?
|
|
127
|
+
defined?(@process) && @process&.alive?
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def process_exited?
|
|
131
|
+
@process.nil? || @process.exited?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def connect_until_stable
|
|
135
|
+
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
|
|
136
|
+
return if socket_poller.connected?
|
|
137
|
+
|
|
138
|
+
raise Error::WebDriverError, cannot_connect_error_text
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def cannot_connect_error_text
|
|
142
|
+
"unable to connect to #{@executable_path} #{@host}:#{@port}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def socket_lock
|
|
146
|
+
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
|
|
147
|
+
end
|
|
148
|
+
end # Service
|
|
149
|
+
end # WebDriver
|
|
150
|
+
end # Selenium
|
|
@@ -0,0 +1,87 @@
|
|
|
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_method :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
|
+
|
|
85
|
+
end # ShadowRoot
|
|
86
|
+
end # WebDriver
|
|
87
|
+
end # Selenium
|
|
@@ -69,8 +69,8 @@ module Selenium
|
|
|
69
69
|
ChildProcess.close_on_exec @server
|
|
70
70
|
|
|
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}")
|
|
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
|
|
@@ -99,7 +99,7 @@ module Selenium
|
|
|
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,66 @@
|
|
|
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
|
+
|
|
64
|
+
end # TakesScreenshot
|
|
65
|
+
end # WebDriver
|
|
66
|
+
end # Selenium
|
|
@@ -44,6 +44,34 @@ module Selenium
|
|
|
44
44
|
@bridge.switch_to_parent_frame
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
#
|
|
48
|
+
# Switch to a new top-level browsing context
|
|
49
|
+
#
|
|
50
|
+
# @param type either :tab or :window
|
|
51
|
+
#
|
|
52
|
+
|
|
53
|
+
def new_window(type = :window)
|
|
54
|
+
unless %i[window tab].include?(type)
|
|
55
|
+
raise ArgumentError, "Valid types are :tab and :window, received: #{type.inspect}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
handle = @bridge.new_window(type)['handle']
|
|
59
|
+
|
|
60
|
+
if block_given?
|
|
61
|
+
execute_and_close = proc do
|
|
62
|
+
yield(self)
|
|
63
|
+
begin
|
|
64
|
+
@bridge.close
|
|
65
|
+
rescue Error::NoSuchWindowError
|
|
66
|
+
# window already closed
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
window(handle, &execute_and_close)
|
|
70
|
+
else
|
|
71
|
+
window(handle)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
47
75
|
#
|
|
48
76
|
# switch to the given window handle
|
|
49
77
|
#
|
|
@@ -57,10 +85,10 @@ module Selenium
|
|
|
57
85
|
def window(id)
|
|
58
86
|
if block_given?
|
|
59
87
|
original = begin
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
@bridge.window_handle
|
|
89
|
+
rescue Error::NoSuchWindowError
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
64
92
|
|
|
65
93
|
unless @bridge.window_handles.include? id
|
|
66
94
|
raise Error::NoSuchWindowError, "The specified identifier '#{id}' is not found in the window handle list"
|
|
@@ -24,22 +24,49 @@ module Selenium
|
|
|
24
24
|
@bridge = bridge
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
#
|
|
28
|
+
# Gets the amount of time the driver should wait when searching for elements.
|
|
29
|
+
#
|
|
30
|
+
|
|
31
|
+
def implicit_wait
|
|
32
|
+
Float(@bridge.timeouts['implicit']) / 1000
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
#
|
|
28
36
|
# Set the amount of time the driver should wait when searching for elements.
|
|
29
37
|
#
|
|
30
38
|
|
|
31
39
|
def implicit_wait=(seconds)
|
|
32
|
-
@bridge.
|
|
40
|
+
@bridge.timeouts = {'implicit' => Integer(seconds * 1000)}
|
|
33
41
|
end
|
|
34
42
|
|
|
43
|
+
#
|
|
44
|
+
# Gets the amount of time to wait for an asynchronous script to finish
|
|
45
|
+
# execution before throwing an error.
|
|
46
|
+
#
|
|
47
|
+
|
|
48
|
+
def script
|
|
49
|
+
Float(@bridge.timeouts['script']) / 1000
|
|
50
|
+
end
|
|
51
|
+
alias_method :script_timeout, :script
|
|
52
|
+
|
|
35
53
|
#
|
|
36
54
|
# Sets the amount of time to wait for an asynchronous script to finish
|
|
37
55
|
# execution before throwing an error. If the timeout is negative, then the
|
|
38
56
|
# script will be allowed to run indefinitely.
|
|
39
57
|
#
|
|
40
58
|
|
|
41
|
-
def
|
|
42
|
-
@bridge.
|
|
59
|
+
def script=(seconds)
|
|
60
|
+
@bridge.timeouts = {'script' => Integer(seconds * 1000)}
|
|
61
|
+
end
|
|
62
|
+
alias_method :script_timeout=, :script=
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# Gets the amount of time to wait for a page load to complete before throwing an error.
|
|
66
|
+
#
|
|
67
|
+
|
|
68
|
+
def page_load
|
|
69
|
+
Float(@bridge.timeouts['pageLoad']) / 1000
|
|
43
70
|
end
|
|
44
71
|
|
|
45
72
|
#
|
|
@@ -48,7 +75,7 @@ module Selenium
|
|
|
48
75
|
#
|
|
49
76
|
|
|
50
77
|
def page_load=(seconds)
|
|
51
|
-
@bridge.
|
|
78
|
+
@bridge.timeouts = {'pageLoad' => Integer(seconds * 1000)}
|
|
52
79
|
end
|
|
53
80
|
end # Timeouts
|
|
54
81
|
end # WebDriver
|