selenium-webdriver 2.53.4 → 3.0.0.beta1
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.
- data/CHANGES +24 -18
- data/README.md +2 -3
- data/lib/selenium/server.rb +64 -68
- data/lib/selenium/webdriver.rb +5 -9
- data/lib/selenium/webdriver/chrome.rb +18 -3
- data/lib/selenium/webdriver/chrome/bridge.rb +13 -16
- data/lib/selenium/webdriver/chrome/profile.rb +7 -9
- data/lib/selenium/webdriver/chrome/service.rb +8 -84
- data/lib/selenium/webdriver/common.rb +1 -2
- data/lib/selenium/webdriver/common/action_builder.rb +28 -38
- data/lib/selenium/webdriver/common/alert.rb +7 -10
- data/lib/selenium/webdriver/common/bridge_helper.rb +10 -15
- data/lib/selenium/webdriver/common/driver.rb +19 -28
- data/lib/selenium/webdriver/common/driver_extensions/has_input_devices.rb +0 -3
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +4 -6
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +4 -5
- data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +0 -3
- data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +3 -6
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +2 -5
- data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +2 -5
- data/lib/selenium/webdriver/common/element.rb +27 -29
- data/lib/selenium/webdriver/common/error.rb +17 -20
- data/lib/selenium/webdriver/common/file_reaper.rb +3 -9
- data/lib/selenium/webdriver/common/html5/local_storage.rb +6 -8
- data/lib/selenium/webdriver/common/html5/session_storage.rb +6 -8
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +6 -15
- data/lib/selenium/webdriver/common/keyboard.rb +7 -12
- data/lib/selenium/webdriver/common/keys.rb +67 -69
- data/lib/selenium/webdriver/common/log_entry.rb +3 -4
- data/lib/selenium/webdriver/common/logs.rb +2 -4
- data/lib/selenium/webdriver/common/mouse.rb +9 -12
- data/lib/selenium/webdriver/common/navigation.rb +2 -4
- data/lib/selenium/webdriver/common/options.rb +16 -19
- data/lib/selenium/webdriver/common/platform.rb +61 -90
- data/lib/selenium/webdriver/common/port_prober.rb +1 -2
- data/lib/selenium/webdriver/common/profile_helper.rb +5 -8
- data/lib/selenium/webdriver/common/proxy.rb +58 -70
- data/lib/selenium/webdriver/common/search_context.rb +15 -19
- data/lib/selenium/webdriver/common/service.rb +127 -0
- data/lib/selenium/webdriver/common/socket_lock.rb +5 -11
- data/lib/selenium/webdriver/common/socket_poller.rb +4 -9
- data/lib/selenium/webdriver/common/target_locator.rb +11 -13
- data/lib/selenium/webdriver/common/timeouts.rb +4 -6
- data/lib/selenium/webdriver/common/touch_action_builder.rb +2 -4
- data/lib/selenium/webdriver/common/touch_screen.rb +15 -18
- data/lib/selenium/webdriver/common/w3c_error.rb +3 -6
- data/lib/selenium/webdriver/common/wait.rb +6 -11
- data/lib/selenium/webdriver/common/window.rb +12 -15
- data/lib/selenium/webdriver/common/zipper.rb +6 -8
- data/lib/selenium/webdriver/edge.rb +18 -3
- data/lib/selenium/webdriver/edge/bridge.rb +11 -16
- data/lib/selenium/webdriver/edge/legacy_support.rb +38 -39
- data/lib/selenium/webdriver/edge/service.rb +8 -82
- data/lib/selenium/webdriver/firefox.rb +25 -6
- data/lib/selenium/webdriver/firefox/binary.rb +37 -53
- data/lib/selenium/webdriver/firefox/bridge.rb +3 -6
- data/lib/selenium/webdriver/firefox/extension.rb +4 -6
- data/lib/selenium/webdriver/firefox/extension/prefs.json +1 -10
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +8 -11
- data/lib/selenium/webdriver/firefox/profile.rb +40 -42
- data/lib/selenium/webdriver/firefox/profiles_ini.rb +8 -15
- data/lib/selenium/webdriver/firefox/service.rb +23 -79
- data/lib/selenium/webdriver/firefox/util.rb +0 -2
- data/lib/selenium/webdriver/firefox/w3c_bridge.rb +2 -4
- data/lib/selenium/webdriver/ie.rb +16 -7
- data/lib/selenium/webdriver/ie/bridge.rb +16 -23
- data/lib/selenium/webdriver/{iphone.rb → ie/service.rb} +26 -4
- data/lib/selenium/webdriver/phantomjs.rb +8 -3
- data/lib/selenium/webdriver/phantomjs/bridge.rb +9 -11
- data/lib/selenium/webdriver/phantomjs/service.rb +17 -81
- data/lib/selenium/webdriver/remote.rb +0 -2
- data/lib/selenium/webdriver/remote/bridge.rb +193 -191
- data/lib/selenium/webdriver/remote/capabilities.rb +60 -90
- data/lib/selenium/webdriver/remote/commands.rb +197 -192
- data/lib/selenium/webdriver/remote/http/common.rb +15 -13
- data/lib/selenium/webdriver/remote/http/curb.rb +5 -9
- data/lib/selenium/webdriver/remote/http/default.rb +32 -37
- data/lib/selenium/webdriver/remote/http/persistent.rb +4 -6
- data/lib/selenium/webdriver/remote/response.rb +13 -21
- data/lib/selenium/webdriver/remote/server_error.rb +1 -3
- data/lib/selenium/webdriver/remote/w3c_bridge.rb +200 -195
- data/lib/selenium/webdriver/remote/w3c_capabilities.rb +38 -46
- data/lib/selenium/webdriver/remote/w3c_commands.rb +116 -113
- data/lib/selenium/webdriver/safari.rb +23 -7
- data/lib/selenium/{client/javascript_frameworks/jquery.rb → webdriver/safari/apple_bridge.rb} +28 -9
- data/lib/selenium/webdriver/safari/browser.rb +0 -2
- data/lib/selenium/webdriver/safari/{bridge.rb → legacy_bridge.rb} +12 -9
- data/lib/selenium/webdriver/safari/options.rb +3 -4
- data/lib/selenium/webdriver/safari/resources/client.js +56 -7255
- data/lib/selenium/webdriver/safari/server.rb +18 -24
- data/lib/selenium/{client/javascript_frameworks/prototype.rb → webdriver/safari/service.rb} +27 -9
- data/lib/selenium/webdriver/support.rb +1 -0
- data/lib/selenium/webdriver/support/abstract_event_listener.rb +17 -2
- data/lib/selenium/webdriver/support/block_event_listener.rb +1 -3
- data/lib/selenium/webdriver/support/color.rb +55 -38
- data/lib/selenium/webdriver/{android.rb → support/escaper.rb} +19 -4
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +36 -38
- data/lib/selenium/webdriver/support/select.rb +33 -84
- data/selenium-webdriver.gemspec +23 -23
- metadata +19 -30
- data/lib/selenium-client.rb +0 -21
- data/lib/selenium/client.rb +0 -57
- data/lib/selenium/client/base.rb +0 -151
- data/lib/selenium/client/driver.rb +0 -29
- data/lib/selenium/client/errors.rb +0 -28
- data/lib/selenium/client/extensions.rb +0 -132
- data/lib/selenium/client/idiomatic.rb +0 -507
- data/lib/selenium/client/javascript_expression_builder.rb +0 -135
- data/lib/selenium/client/legacy_driver.rb +0 -1722
- data/lib/selenium/client/protocol.rb +0 -123
- data/lib/selenium/client/selenium_helper.rb +0 -49
- data/lib/selenium/rake/server_task.rb +0 -176
- data/lib/selenium/webdriver/android/bridge.rb +0 -68
- data/lib/selenium/webdriver/common/core_ext/base64.rb +0 -28
- data/lib/selenium/webdriver/common/core_ext/dir.rb +0 -61
- data/lib/selenium/webdriver/common/html5/location.rb +0 -19
- data/lib/selenium/webdriver/ie/server.rb +0 -133
- data/lib/selenium/webdriver/iphone/bridge.rb +0 -64
|
@@ -38,7 +38,7 @@ module Selenium
|
|
|
38
38
|
port
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
IGNORED_ERRORS = [Errno::EADDRNOTAVAIL]
|
|
41
|
+
IGNORED_ERRORS = [Errno::EADDRNOTAVAIL].freeze
|
|
42
42
|
IGNORED_ERRORS << Errno::EBADF if Platform.cygwin?
|
|
43
43
|
|
|
44
44
|
def self.free?(port)
|
|
@@ -55,7 +55,6 @@ module Selenium
|
|
|
55
55
|
rescue SocketError, Errno::EADDRINUSE
|
|
56
56
|
false
|
|
57
57
|
end
|
|
58
|
-
|
|
59
58
|
end # PortProber
|
|
60
59
|
end # WebDriver
|
|
61
60
|
end # Selenium
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
|
-
|
|
23
22
|
#
|
|
24
23
|
# @api private
|
|
25
24
|
#
|
|
@@ -28,27 +27,26 @@ module Selenium
|
|
|
28
27
|
#
|
|
29
28
|
|
|
30
29
|
module ProfileHelper
|
|
31
|
-
|
|
32
30
|
def self.included(base)
|
|
33
31
|
base.extend ClassMethods
|
|
34
32
|
end
|
|
35
33
|
|
|
36
|
-
def as_json(
|
|
34
|
+
def as_json(*)
|
|
37
35
|
{'zip' => Zipper.zip(layout_on_disk)}
|
|
38
36
|
end
|
|
39
37
|
|
|
40
|
-
def to_json(*
|
|
38
|
+
def to_json(*)
|
|
41
39
|
JSON.generate as_json
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
private
|
|
45
43
|
|
|
46
44
|
def create_tmp_copy(directory)
|
|
47
|
-
tmp_directory = Dir.mktmpdir(
|
|
45
|
+
tmp_directory = Dir.mktmpdir('webdriver-rb-profilecopy')
|
|
48
46
|
|
|
49
47
|
# TODO: must be a better way..
|
|
50
48
|
FileUtils.rm_rf tmp_directory
|
|
51
|
-
FileUtils.mkdir_p File.dirname(tmp_directory), :
|
|
49
|
+
FileUtils.mkdir_p File.dirname(tmp_directory), mode: 0700
|
|
52
50
|
FileUtils.cp_r directory, tmp_directory
|
|
53
51
|
|
|
54
52
|
tmp_directory
|
|
@@ -72,7 +70,7 @@ module Selenium
|
|
|
72
70
|
tmp_dir = Dir.mktmpdir
|
|
73
71
|
begin
|
|
74
72
|
zip_path = File.join(tmp_dir, "webdriver-profile-duplicate-#{json.hash}.zip")
|
|
75
|
-
File.open(zip_path,
|
|
73
|
+
File.open(zip_path, 'wb') { |zip_file| zip_file << Base64.decode64(data) }
|
|
76
74
|
|
|
77
75
|
new Zipper.unzip(zip_path)
|
|
78
76
|
ensure
|
|
@@ -80,7 +78,6 @@ module Selenium
|
|
|
80
78
|
end
|
|
81
79
|
end
|
|
82
80
|
end # ClassMethods
|
|
83
|
-
|
|
84
81
|
end # ProfileHelper
|
|
85
82
|
end # WebDriver
|
|
86
83
|
end # Selenium
|
|
@@ -21,45 +21,56 @@ module Selenium
|
|
|
21
21
|
module WebDriver
|
|
22
22
|
class Proxy
|
|
23
23
|
TYPES = {
|
|
24
|
-
:
|
|
25
|
-
:
|
|
26
|
-
:
|
|
27
|
-
:
|
|
28
|
-
:
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
24
|
+
direct: 'DIRECT', # Direct connection, no proxy (default on Windows).
|
|
25
|
+
manual: 'MANUAL', # Manual proxy settings (e.g., for httpProxy).
|
|
26
|
+
pac: 'PAC', # Proxy autoconfiguration from URL.
|
|
27
|
+
auto_detect: 'AUTODETECT', # Proxy autodetection (presumably with WPAD).
|
|
28
|
+
system: 'SYSTEM' # Use system settings (default on Linux).
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
ALLOWED = {type: 'proxyType',
|
|
32
|
+
ftp: 'ftpProxy',
|
|
33
|
+
http: 'httpProxy',
|
|
34
|
+
no_proxy: 'noProxy',
|
|
35
|
+
pac: 'proxyAutoconfigUrl',
|
|
36
|
+
ssl: 'sslProxy',
|
|
37
|
+
auto_detect: 'autodetect',
|
|
38
|
+
socks: 'socksProxy',
|
|
39
|
+
socks_username: 'socksUsername',
|
|
40
|
+
socks_password: 'socksPassword'}.freeze
|
|
41
|
+
|
|
42
|
+
ALLOWED.keys.each { |t| attr_reader t }
|
|
43
|
+
|
|
44
|
+
def self.json_create(data)
|
|
45
|
+
data['proxyType'] = data['proxyType'].downcase.to_sym
|
|
46
|
+
return if data['proxyType'] == :unspecified
|
|
47
|
+
|
|
48
|
+
proxy = new
|
|
49
|
+
|
|
50
|
+
ALLOWED.each do |k, v|
|
|
51
|
+
proxy.send("#{k}=", data[v]) if data.key?(v)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
proxy
|
|
55
|
+
end
|
|
41
56
|
|
|
42
57
|
def initialize(opts = {})
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.auto_detect = opts.delete(:auto_detect) if opts.has_key? :auto_detect
|
|
52
|
-
self.socks = opts.delete(:socks) if opts.has_key? :socks
|
|
53
|
-
self.socks_username = opts.delete(:socks_username) if opts.has_key? :socks_username
|
|
54
|
-
self.socks_password = opts.delete(:socks_password) if opts.has_key? :socks_password
|
|
55
|
-
|
|
56
|
-
unless opts.empty?
|
|
57
|
-
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
|
|
58
|
+
not_allowed = []
|
|
59
|
+
|
|
60
|
+
opts.each do |k, v|
|
|
61
|
+
if ALLOWED.key?(k)
|
|
62
|
+
send("#{k}=", v)
|
|
63
|
+
else
|
|
64
|
+
not_allowed << k
|
|
65
|
+
end
|
|
58
66
|
end
|
|
67
|
+
|
|
68
|
+
return if not_allowed.empty?
|
|
69
|
+
raise ArgumentError, "unknown option#{'s' if not_allowed.size != 1}: #{not_allowed.inspect}"
|
|
59
70
|
end
|
|
60
71
|
|
|
61
72
|
def ==(other)
|
|
62
|
-
other.
|
|
73
|
+
other.is_a?(self.class) && as_json == other.as_json
|
|
63
74
|
end
|
|
64
75
|
alias_method :eql?, :==
|
|
65
76
|
|
|
@@ -109,7 +120,7 @@ module Selenium
|
|
|
109
120
|
end
|
|
110
121
|
|
|
111
122
|
def type=(type)
|
|
112
|
-
unless TYPES.
|
|
123
|
+
unless TYPES.key? type
|
|
113
124
|
raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
|
|
114
125
|
end
|
|
115
126
|
|
|
@@ -120,49 +131,26 @@ module Selenium
|
|
|
120
131
|
@type = type
|
|
121
132
|
end
|
|
122
133
|
|
|
123
|
-
def as_json(
|
|
134
|
+
def as_json(*)
|
|
124
135
|
json_result = {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
json_result["socksPassword"] = socks_password if socks_password
|
|
136
|
+
'proxyType' => TYPES[type],
|
|
137
|
+
'ftpProxy' => ftp,
|
|
138
|
+
'httpProxy' => http,
|
|
139
|
+
'noProxy' => no_proxy,
|
|
140
|
+
'proxyAutoconfigUrl' => pac,
|
|
141
|
+
'sslProxy' => ssl,
|
|
142
|
+
'autodetect' => auto_detect,
|
|
143
|
+
'socksProxy' => socks,
|
|
144
|
+
'socksUsername' => socks_username,
|
|
145
|
+
'socksPassword' => socks_password
|
|
146
|
+
}.delete_if { |_k, v| v.nil? }
|
|
137
147
|
|
|
138
148
|
json_result if json_result.length > 1
|
|
139
149
|
end
|
|
140
150
|
|
|
141
|
-
def to_json(*
|
|
151
|
+
def to_json(*)
|
|
142
152
|
JSON.generate as_json
|
|
143
153
|
end
|
|
144
|
-
|
|
145
|
-
class << self
|
|
146
|
-
def json_create(data)
|
|
147
|
-
return if data['proxyType'] == 'UNSPECIFIED'
|
|
148
|
-
|
|
149
|
-
proxy = new
|
|
150
|
-
|
|
151
|
-
proxy.type = data['proxyType'].downcase.to_sym if data.has_key? 'proxyType'
|
|
152
|
-
proxy.ftp = data['ftpProxy'] if data.has_key? 'ftpProxy'
|
|
153
|
-
proxy.http = data['httpProxy'] if data.has_key? 'httpProxy'
|
|
154
|
-
proxy.no_proxy = data['noProxy'] if data.has_key? 'noProxy'
|
|
155
|
-
proxy.pac = data['proxyAutoconfigUrl'] if data.has_key? 'proxyAutoconfigUrl'
|
|
156
|
-
proxy.ssl = data['sslProxy'] if data.has_key? 'sslProxy'
|
|
157
|
-
proxy.auto_detect = data['autodetect'] if data.has_key? 'autodetect'
|
|
158
|
-
proxy.socks = data['socksProxy'] if data.has_key? 'socksProxy'
|
|
159
|
-
proxy.socks_username = data['socksUsername'] if data.has_key? 'socksUsername'
|
|
160
|
-
proxy.socks_password = data['socksPassword'] if data.has_key? 'socksPassword'
|
|
161
|
-
|
|
162
|
-
proxy
|
|
163
|
-
end
|
|
164
|
-
end # class << self
|
|
165
|
-
|
|
166
154
|
end # Proxy
|
|
167
155
|
end # WebDriver
|
|
168
156
|
end # Selenium
|
|
@@ -20,20 +20,19 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module SearchContext
|
|
23
|
-
|
|
24
23
|
# @api private
|
|
25
24
|
FINDERS = {
|
|
26
|
-
:
|
|
27
|
-
:
|
|
28
|
-
:
|
|
29
|
-
:
|
|
30
|
-
:
|
|
31
|
-
:
|
|
32
|
-
:
|
|
33
|
-
:
|
|
34
|
-
:
|
|
35
|
-
:
|
|
36
|
-
}
|
|
25
|
+
class: 'class name',
|
|
26
|
+
class_name: 'class name',
|
|
27
|
+
css: 'css selector',
|
|
28
|
+
id: 'id',
|
|
29
|
+
link: 'link text',
|
|
30
|
+
link_text: 'link text',
|
|
31
|
+
name: 'name',
|
|
32
|
+
partial_link_text: 'partial link text',
|
|
33
|
+
tag_name: 'tag name',
|
|
34
|
+
xpath: 'xpath'
|
|
35
|
+
}.freeze
|
|
37
36
|
|
|
38
37
|
#
|
|
39
38
|
# Find the first element matching the given arguments.
|
|
@@ -54,9 +53,8 @@ module Selenium
|
|
|
54
53
|
def find_element(*args)
|
|
55
54
|
how, what = extract_args(args)
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
end
|
|
56
|
+
by = FINDERS[how.to_sym]
|
|
57
|
+
raise ArgumentError, "cannot find element by #{how.inspect}" unless by
|
|
60
58
|
|
|
61
59
|
bridge.find_element_by by, what.to_s, ref
|
|
62
60
|
rescue Selenium::WebDriver::Error::TimeOutError
|
|
@@ -77,9 +75,8 @@ module Selenium
|
|
|
77
75
|
def find_elements(*args)
|
|
78
76
|
how, what = extract_args(args)
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
end
|
|
78
|
+
by = FINDERS[how.to_sym]
|
|
79
|
+
raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
|
|
83
80
|
|
|
84
81
|
bridge.find_elements_by by, what.to_s, ref
|
|
85
82
|
rescue Selenium::WebDriver::Error::TimeOutError
|
|
@@ -111,7 +108,6 @@ module Selenium
|
|
|
111
108
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
|
|
112
109
|
end
|
|
113
110
|
end
|
|
114
|
-
|
|
115
111
|
end # SearchContext
|
|
116
112
|
end # WebDriver
|
|
117
113
|
end # Selenium
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
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 object,
|
|
24
|
+
# responsible for starting and stopping driver implementations.
|
|
25
|
+
#
|
|
26
|
+
# Subclasses must implement the following private methods:
|
|
27
|
+
# * #start_process
|
|
28
|
+
# * #stop_server
|
|
29
|
+
# * #cannot_connect_error_text
|
|
30
|
+
#
|
|
31
|
+
# @api private
|
|
32
|
+
#
|
|
33
|
+
|
|
34
|
+
class Service
|
|
35
|
+
START_TIMEOUT = 20
|
|
36
|
+
SOCKET_LOCK_TIMEOUT = 45
|
|
37
|
+
STOP_TIMEOUT = 5
|
|
38
|
+
|
|
39
|
+
attr_accessor :host
|
|
40
|
+
|
|
41
|
+
def initialize(executable_path, port, *extra_args)
|
|
42
|
+
@executable_path = executable_path
|
|
43
|
+
@host = Platform.localhost
|
|
44
|
+
@port = Integer(port)
|
|
45
|
+
@extra_args = extra_args
|
|
46
|
+
|
|
47
|
+
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def start
|
|
51
|
+
if process_running?
|
|
52
|
+
raise "already started: #{uri.inspect} #{@executable_path.inspect}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Platform.exit_hook { stop } # make sure we don't leave the server running
|
|
56
|
+
|
|
57
|
+
socket_lock.locked do
|
|
58
|
+
find_free_port
|
|
59
|
+
start_process
|
|
60
|
+
connect_until_stable
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def stop
|
|
65
|
+
return if process_exited?
|
|
66
|
+
stop_server
|
|
67
|
+
ensure
|
|
68
|
+
stop_process
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def uri
|
|
72
|
+
@uri ||= URI.parse("http://#{@host}:#{@port}")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def connect_to_server
|
|
78
|
+
Net::HTTP.start(@host, @port) do |http|
|
|
79
|
+
http.open_timeout = STOP_TIMEOUT / 2
|
|
80
|
+
http.read_timeout = STOP_TIMEOUT / 2
|
|
81
|
+
|
|
82
|
+
yield http
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def find_free_port
|
|
87
|
+
@port = PortProber.above(@port)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def start_process
|
|
91
|
+
raise NotImplementedError, 'subclass responsibility'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def stop_server
|
|
95
|
+
raise NotImplementedError, 'subclass responsibility'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def stop_process
|
|
99
|
+
@process.poll_for_exit STOP_TIMEOUT
|
|
100
|
+
rescue ChildProcess::TimeoutError
|
|
101
|
+
@process.stop STOP_TIMEOUT
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def process_running?
|
|
105
|
+
@process && @process.alive?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def process_exited?
|
|
109
|
+
@process.nil? || @process.exited?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def connect_until_stable
|
|
113
|
+
socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
|
|
114
|
+
return if socket_poller.connected?
|
|
115
|
+
raise Error::WebDriverError, cannot_connect_error_text
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def cannot_connect_error_text
|
|
119
|
+
raise NotImplementedError, 'subclass responsibility'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def socket_lock
|
|
123
|
+
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
|
|
124
|
+
end
|
|
125
|
+
end # Service
|
|
126
|
+
end # WebDriver
|
|
127
|
+
end # Selenium
|
|
@@ -19,13 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
|
-
|
|
23
22
|
#
|
|
24
23
|
# @api private
|
|
25
24
|
#
|
|
26
25
|
|
|
27
26
|
class SocketLock
|
|
28
|
-
|
|
29
27
|
def initialize(port, timeout)
|
|
30
28
|
@port = port
|
|
31
29
|
@timeout = timeout
|
|
@@ -36,7 +34,7 @@ module Selenium
|
|
|
36
34
|
# execution block if the lock could be successfully obtained.
|
|
37
35
|
#
|
|
38
36
|
|
|
39
|
-
def locked
|
|
37
|
+
def locked
|
|
40
38
|
lock
|
|
41
39
|
|
|
42
40
|
begin
|
|
@@ -51,13 +49,10 @@ module Selenium
|
|
|
51
49
|
def lock
|
|
52
50
|
max_time = Time.now + @timeout
|
|
53
51
|
|
|
54
|
-
until can_lock? || Time.now >= max_time
|
|
55
|
-
sleep 0.1
|
|
56
|
-
end
|
|
52
|
+
sleep 0.1 until can_lock? || Time.now >= max_time
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
54
|
+
return if did_lock?
|
|
55
|
+
raise Error::WebDriverError, "unable to bind to locking port #{@port} within #{@timeout} seconds"
|
|
61
56
|
end
|
|
62
57
|
|
|
63
58
|
def release
|
|
@@ -75,9 +70,8 @@ module Selenium
|
|
|
75
70
|
end
|
|
76
71
|
|
|
77
72
|
def did_lock?
|
|
78
|
-
|
|
73
|
+
!@server.nil?
|
|
79
74
|
end
|
|
80
|
-
|
|
81
75
|
end # SocketLock
|
|
82
76
|
end # WebDriver
|
|
83
77
|
end # Selenium
|