selenium-webdriver 3.142.7 → 4.0.3
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 +350 -5
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/NOTICE +2 -0
- data/README.md +4 -5
- data/lib/selenium/server.rb +69 -63
- 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/chrome/driver.rb +26 -83
- data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +50 -12
- data/lib/selenium/webdriver/chrome/options.rb +129 -58
- data/lib/selenium/webdriver/chrome/profile.rb +6 -3
- data/lib/selenium/webdriver/chrome/service.rb +8 -15
- data/lib/selenium/webdriver/chrome.rb +10 -9
- data/lib/selenium/webdriver/common/action_builder.rb +97 -249
- data/lib/selenium/webdriver/common/driver.rb +112 -23
- 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_casting.rb +77 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_cdp.rb} +10 -8
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +45 -0
- data/lib/selenium/webdriver/{firefox/util.rb → common/driver_extensions/has_devtools.rb} +16 -19
- 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_logs.rb +30 -0
- 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/has_remote_status.rb +1 -0
- data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
- data/lib/selenium/webdriver/common/element.rb +82 -22
- data/lib/selenium/webdriver/common/error.rb +32 -196
- data/lib/selenium/webdriver/common/interactions/interaction.rb +4 -1
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -5
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +13 -13
- 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 +15 -15
- data/lib/selenium/webdriver/common/options.rb +154 -23
- data/lib/selenium/webdriver/common/platform.rb +6 -1
- 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 -3
- data/lib/selenium/webdriver/common/service.rb +17 -125
- data/lib/selenium/webdriver/common/service_manager.rb +151 -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 +2 -2
- 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/window.rb +0 -4
- data/lib/selenium/webdriver/common/zipper.rb +1 -9
- data/lib/selenium/webdriver/common.rb +23 -17
- 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 +182 -0
- data/lib/selenium/webdriver/edge/driver.rb +7 -29
- data/lib/selenium/webdriver/edge/features.rb +44 -0
- data/lib/selenium/webdriver/edge/options.rb +11 -48
- data/lib/selenium/webdriver/{common/w3c_manager.rb → edge/profile.rb} +7 -19
- data/lib/selenium/webdriver/edge/service.rb +10 -26
- data/lib/selenium/webdriver/edge.rb +11 -14
- data/lib/selenium/webdriver/firefox/driver.rb +31 -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 +70 -49
- data/lib/selenium/webdriver/firefox/profile.rb +21 -71
- data/lib/selenium/webdriver/firefox/service.rb +5 -9
- data/lib/selenium/webdriver/firefox.rb +22 -20
- data/lib/selenium/webdriver/ie/driver.rb +1 -47
- data/lib/selenium/webdriver/ie/options.rb +13 -44
- data/lib/selenium/webdriver/ie/service.rb +13 -15
- data/lib/selenium/webdriver/ie.rb +8 -7
- data/lib/selenium/webdriver/remote/bridge.rb +558 -86
- data/lib/selenium/webdriver/remote/capabilities.rb +159 -123
- data/lib/selenium/webdriver/remote/commands.rb +163 -0
- data/lib/selenium/webdriver/remote/driver.rb +22 -12
- data/lib/selenium/webdriver/remote/http/common.rb +0 -5
- data/lib/selenium/webdriver/remote/http/default.rb +17 -20
- data/lib/selenium/webdriver/remote/http/persistent.rb +11 -6
- data/lib/selenium/webdriver/remote/response.rb +16 -47
- data/lib/selenium/webdriver/remote.rb +15 -13
- 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 +17 -9
- 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 +2 -2
- 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 +12 -12
- data/selenium-webdriver.gemspec +28 -12
- metadata +128 -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/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
|
@@ -36,6 +36,7 @@ module Selenium
|
|
|
36
36
|
# @option opts [String] :value A value
|
|
37
37
|
# @option opts [String] :path ('/') A path
|
|
38
38
|
# @option opts [String] :secure (false) A boolean
|
|
39
|
+
# @option opts [String] :same_site (Strict or Lax) currently supported only in chrome 80+ versions
|
|
39
40
|
# @option opts [Time,DateTime,Numeric,nil] :expires (nil) Expiry date, either as a Time, DateTime, or seconds since epoch.
|
|
40
41
|
#
|
|
41
42
|
# @raise [ArgumentError] if :name or :value is not specified
|
|
@@ -45,9 +46,15 @@ module Selenium
|
|
|
45
46
|
raise ArgumentError, 'name is required' unless opts[:name]
|
|
46
47
|
raise ArgumentError, 'value is required' unless opts[:value]
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
# NOTE: This is required because of https://bugs.chromium.org/p/chromedriver/issues/detail?id=3732
|
|
49
50
|
opts[:secure] ||= false
|
|
50
51
|
|
|
52
|
+
same_site = opts.delete(:same_site)
|
|
53
|
+
opts[:sameSite] = same_site if same_site
|
|
54
|
+
|
|
55
|
+
http_only = opts.delete(:http_only)
|
|
56
|
+
opts[:httpOnly] = http_only if http_only
|
|
57
|
+
|
|
51
58
|
obj = opts.delete(:expires)
|
|
52
59
|
opts[:expiry] = seconds_from(obj).to_i if obj
|
|
53
60
|
|
|
@@ -62,7 +69,7 @@ module Selenium
|
|
|
62
69
|
#
|
|
63
70
|
|
|
64
71
|
def cookie_named(name)
|
|
65
|
-
|
|
72
|
+
convert_cookie(@bridge.cookie(name))
|
|
66
73
|
end
|
|
67
74
|
|
|
68
75
|
#
|
|
@@ -97,24 +104,19 @@ module Selenium
|
|
|
97
104
|
@timeouts ||= Timeouts.new(@bridge)
|
|
98
105
|
end
|
|
99
106
|
|
|
100
|
-
#
|
|
101
|
-
# @api beta This API may be changed or removed in a future release.
|
|
102
|
-
#
|
|
103
|
-
|
|
104
107
|
def logs
|
|
108
|
+
WebDriver.logger.deprecate('Manager#logs', 'Chrome::Driver#logs')
|
|
105
109
|
@logs ||= Logs.new(@bridge)
|
|
106
110
|
end
|
|
107
111
|
|
|
108
112
|
#
|
|
109
|
-
# Create a new top-level browsing context
|
|
110
|
-
# https://w3c.github.io/webdriver/#new-window
|
|
111
113
|
# @param type [Symbol] Supports two values: :tab and :window.
|
|
112
|
-
# Use :tab if you'd like the new window to share an OS-level window
|
|
113
|
-
# with the current browsing context.
|
|
114
|
-
# Use :window otherwise
|
|
115
114
|
# @return [String] The value of the window handle
|
|
116
115
|
#
|
|
117
116
|
def new_window(type = :tab)
|
|
117
|
+
WebDriver.logger.deprecate('Manager#new_window', 'TargetLocator#new_window', id: :new_window) do
|
|
118
|
+
'e.g., `driver.switch_to.new_window(:tab)`'
|
|
119
|
+
end
|
|
118
120
|
case type
|
|
119
121
|
when :tab, :window
|
|
120
122
|
result = @bridge.new_window(type)
|
|
@@ -129,10 +131,6 @@ module Selenium
|
|
|
129
131
|
end
|
|
130
132
|
end
|
|
131
133
|
|
|
132
|
-
#
|
|
133
|
-
# @api beta This API may be changed or removed in a future release.
|
|
134
|
-
#
|
|
135
|
-
|
|
136
134
|
def window
|
|
137
135
|
@window ||= Window.new(@bridge)
|
|
138
136
|
end
|
|
@@ -169,6 +167,8 @@ module Selenium
|
|
|
169
167
|
path: cookie['path'],
|
|
170
168
|
domain: cookie['domain'] && strip_port(cookie['domain']),
|
|
171
169
|
expires: cookie['expiry'] && datetime_at(cookie['expiry']),
|
|
170
|
+
same_site: cookie['sameSite'],
|
|
171
|
+
http_only: cookie['httpOnly'],
|
|
172
172
|
secure: cookie['secure']
|
|
173
173
|
}
|
|
174
174
|
end
|
|
@@ -19,35 +19,166 @@
|
|
|
19
19
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
class Options
|
|
23
|
+
W3C_OPTIONS = %i[browser_name browser_version platform_name accept_insecure_certs page_load_strategy proxy
|
|
24
|
+
set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability
|
|
25
|
+
web_socket_url].freeze
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
attr_reader :driver_path
|
|
29
|
+
|
|
30
|
+
def chrome(**opts)
|
|
31
|
+
Chrome::Options.new(**opts)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def firefox(**opts)
|
|
35
|
+
Firefox::Options.new(**opts)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def ie(**opts)
|
|
39
|
+
IE::Options.new(**opts)
|
|
40
|
+
end
|
|
41
|
+
alias_method :internet_explorer, :ie
|
|
42
|
+
|
|
43
|
+
def edge(**opts)
|
|
44
|
+
Edge::Options.new(**opts)
|
|
45
|
+
end
|
|
46
|
+
alias_method :microsoftedge, :edge
|
|
47
|
+
|
|
48
|
+
def safari(**opts)
|
|
49
|
+
Safari::Options.new(**opts)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def set_capabilities
|
|
53
|
+
(W3C_OPTIONS + self::CAPABILITIES.keys).each do |key|
|
|
54
|
+
next if method_defined? key
|
|
55
|
+
|
|
56
|
+
define_method key do
|
|
57
|
+
@options[key]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
define_method "#{key}=" do |value|
|
|
61
|
+
@options[key] = value
|
|
62
|
+
end
|
|
37
63
|
end
|
|
38
64
|
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
attr_accessor :options
|
|
68
|
+
|
|
69
|
+
def initialize(options: nil, **opts)
|
|
70
|
+
self.class.set_capabilities
|
|
71
|
+
|
|
72
|
+
@options = if options
|
|
73
|
+
WebDriver.logger.deprecate(":options as keyword for initializing #{self.class}",
|
|
74
|
+
"custom values directly in #new constructor",
|
|
75
|
+
id: :options_options)
|
|
76
|
+
opts.merge(options)
|
|
77
|
+
else
|
|
78
|
+
opts
|
|
79
|
+
end
|
|
80
|
+
@options[:browser_name] = self.class::BROWSER
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
#
|
|
84
|
+
# Add a new option not yet handled by bindings.
|
|
85
|
+
#
|
|
86
|
+
# @example Leave Chrome open when chromedriver is killed
|
|
87
|
+
# options = Selenium::WebDriver::Chrome::Options.new
|
|
88
|
+
# options.add_option(:detach, true)
|
|
89
|
+
#
|
|
90
|
+
# @param [String, Symbol] name Name of the option
|
|
91
|
+
# @param [Boolean, String, Integer] value Value of the option
|
|
92
|
+
#
|
|
93
|
+
|
|
94
|
+
def add_option(name, value = nil)
|
|
95
|
+
@options[name.keys.first] = name.values.first if value.nil? && name.is_a?(Hash)
|
|
96
|
+
@options[name] = value
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def ==(other)
|
|
100
|
+
return false unless other.is_a? self.class
|
|
101
|
+
|
|
102
|
+
as_json == other.as_json
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
alias_method :eql?, :==
|
|
106
|
+
|
|
107
|
+
#
|
|
108
|
+
# @api private
|
|
109
|
+
#
|
|
110
|
+
|
|
111
|
+
def as_json(*)
|
|
112
|
+
options = @options.dup
|
|
113
|
+
|
|
114
|
+
w3c_options = process_w3c_options(options)
|
|
115
|
+
|
|
116
|
+
self.class::CAPABILITIES.each do |capability_alias, capability_name|
|
|
117
|
+
capability_value = options.delete(capability_alias)
|
|
118
|
+
options[capability_name] = capability_value if !capability_value.nil? && !options.key?(capability_name)
|
|
119
|
+
end
|
|
120
|
+
browser_options = defined?(self.class::KEY) ? {self.class::KEY => options} : options
|
|
39
121
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
122
|
+
process_browser_options(browser_options)
|
|
123
|
+
generate_as_json(w3c_options.merge(browser_options))
|
|
124
|
+
end
|
|
43
125
|
|
|
44
|
-
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def w3c?(key)
|
|
129
|
+
W3C_OPTIONS.include?(key) || key.to_s.include?(':')
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def process_w3c_options(options)
|
|
133
|
+
w3c_options = options.select { |key, _val| w3c?(key) }
|
|
134
|
+
w3c_options[:unhandled_prompt_behavior] &&= w3c_options[:unhandled_prompt_behavior]&.to_s&.tr('_', ' ')
|
|
135
|
+
options.delete_if { |key, _val| w3c?(key) }
|
|
136
|
+
w3c_options
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def process_browser_options(_browser_options)
|
|
140
|
+
nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def camelize?(_key)
|
|
144
|
+
true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def generate_as_json(value, camelize_keys: true)
|
|
148
|
+
if value.is_a?(Hash)
|
|
149
|
+
process_json_hash(value, camelize_keys)
|
|
150
|
+
elsif value.respond_to?(:as_json)
|
|
151
|
+
value.as_json
|
|
152
|
+
elsif value.is_a?(Array)
|
|
153
|
+
value.map { |val| generate_as_json(val, camelize_keys: camelize_keys) }
|
|
154
|
+
elsif value.is_a?(Symbol)
|
|
155
|
+
value.to_s
|
|
156
|
+
else
|
|
157
|
+
value
|
|
45
158
|
end
|
|
159
|
+
end
|
|
46
160
|
|
|
47
|
-
|
|
48
|
-
|
|
161
|
+
def process_json_hash(value, camelize_keys)
|
|
162
|
+
value.each_with_object({}) do |(key, val), hash|
|
|
163
|
+
next if val.respond_to?(:empty?) && val.empty?
|
|
164
|
+
|
|
165
|
+
camelize = camelize_keys ? camelize?(key) : false
|
|
166
|
+
key = convert_json_key(key, camelize: camelize)
|
|
167
|
+
hash[key] = generate_as_json(val, camelize_keys: camelize)
|
|
49
168
|
end
|
|
50
|
-
end
|
|
51
|
-
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def convert_json_key(key, camelize: true)
|
|
172
|
+
key = key.to_s if key.is_a?(Symbol)
|
|
173
|
+
key = camel_case(key) if camelize
|
|
174
|
+
return key if key.is_a?(String)
|
|
175
|
+
|
|
176
|
+
raise TypeError, "expected String or Symbol, got #{key.inspect}:#{key.class}"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def camel_case(str)
|
|
180
|
+
str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
|
|
181
|
+
end
|
|
182
|
+
end # Options
|
|
52
183
|
end # WebDriver
|
|
53
184
|
end # Selenium
|
|
@@ -57,6 +57,8 @@ module Selenium
|
|
|
57
57
|
:jenkins
|
|
58
58
|
elsif ENV['APPVEYOR']
|
|
59
59
|
:appveyor
|
|
60
|
+
elsif ENV['GITHUB_ACTIONS']
|
|
61
|
+
:github
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
@@ -95,7 +97,10 @@ module Selenium
|
|
|
95
97
|
def wsl?
|
|
96
98
|
return false unless linux?
|
|
97
99
|
|
|
98
|
-
File.read('/proc/version').include?('
|
|
100
|
+
File.read('/proc/version').downcase.include?('microsoft')
|
|
101
|
+
rescue Errno::EACCES
|
|
102
|
+
# the file cannot be accessed on Linux on DeX
|
|
103
|
+
false
|
|
99
104
|
end
|
|
100
105
|
|
|
101
106
|
def cygwin?
|
|
@@ -32,12 +32,10 @@ module Selenium
|
|
|
32
32
|
|
|
33
33
|
def self.free?(port)
|
|
34
34
|
Platform.interfaces.each do |host|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# ignored - some machines appear unable to bind to some of their interfaces
|
|
40
|
-
end
|
|
35
|
+
TCPServer.new(host, port).close
|
|
36
|
+
rescue *IGNORED_ERRORS => e
|
|
37
|
+
WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
|
|
38
|
+
# ignored - some machines appear unable to bind to some of their interfaces
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
true
|
|
@@ -31,8 +31,16 @@ module Selenium
|
|
|
31
31
|
base.extend ClassMethods
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
def self.decoded(json)
|
|
35
|
+
JSON.parse(json).fetch('zip')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def encoded
|
|
39
|
+
Zipper.zip(layout_on_disk)
|
|
40
|
+
end
|
|
41
|
+
|
|
34
42
|
def as_json(*)
|
|
35
|
-
{"zip" =>
|
|
43
|
+
{"zip" => encoded}
|
|
36
44
|
end
|
|
37
45
|
|
|
38
46
|
def to_json(*)
|
|
@@ -63,18 +71,12 @@ module Selenium
|
|
|
63
71
|
|
|
64
72
|
module ClassMethods
|
|
65
73
|
def from_json(json)
|
|
66
|
-
data =
|
|
74
|
+
data = decoded(json)
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
# can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
|
|
70
|
-
tmp_dir = Dir.mktmpdir
|
|
71
|
-
begin
|
|
72
|
-
zip_path = File.join(tmp_dir, "webdriver-profile-duplicate-#{json.hash}.zip")
|
|
76
|
+
Tempfile.create do |zip_path|
|
|
73
77
|
File.open(zip_path, 'wb') { |zip_file| zip_file << Base64.decode64(data) }
|
|
74
78
|
|
|
75
79
|
new Zipper.unzip(zip_path)
|
|
76
|
-
ensure
|
|
77
|
-
FileUtils.rm_rf tmp_dir
|
|
78
80
|
end
|
|
79
81
|
end
|
|
80
82
|
end # ClassMethods
|
|
@@ -127,7 +127,10 @@ module Selenium
|
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def type=(type)
|
|
130
|
-
|
|
130
|
+
unless TYPES.key? type
|
|
131
|
+
raise ArgumentError,
|
|
132
|
+
"invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
|
|
133
|
+
end
|
|
131
134
|
|
|
132
135
|
if defined?(@type) && type != @type
|
|
133
136
|
raise ArgumentError, "incompatible proxy type #{type.inspect} (already set to #{@type.inspect})"
|
|
@@ -138,10 +141,10 @@ module Selenium
|
|
|
138
141
|
|
|
139
142
|
def as_json(*)
|
|
140
143
|
json_result = {
|
|
141
|
-
'proxyType' => TYPES[type],
|
|
144
|
+
'proxyType' => TYPES[type].downcase,
|
|
142
145
|
'ftpProxy' => ftp,
|
|
143
146
|
'httpProxy' => http,
|
|
144
|
-
'noProxy' => no_proxy,
|
|
147
|
+
'noProxy' => no_proxy.is_a?(String) ? no_proxy.split(', ') : no_proxy,
|
|
145
148
|
'proxyAutoconfigUrl' => pac,
|
|
146
149
|
'sslProxy' => ssl,
|
|
147
150
|
'autodetect' => auto_detect,
|
|
@@ -30,6 +30,7 @@ module Selenium
|
|
|
30
30
|
link_text: 'link text',
|
|
31
31
|
name: 'name',
|
|
32
32
|
partial_link_text: 'partial link text',
|
|
33
|
+
relative: 'relative',
|
|
33
34
|
tag_name: 'tag name',
|
|
34
35
|
xpath: 'xpath'
|
|
35
36
|
}.freeze
|
|
@@ -59,7 +60,7 @@ module Selenium
|
|
|
59
60
|
by = FINDERS[how.to_sym]
|
|
60
61
|
raise ArgumentError, "cannot find element by #{how.inspect}" unless by
|
|
61
62
|
|
|
62
|
-
bridge.find_element_by by, what
|
|
63
|
+
bridge.find_element_by by, what, ref
|
|
63
64
|
rescue Selenium::WebDriver::Error::TimeoutError
|
|
64
65
|
# Implicit Wait times out in Edge
|
|
65
66
|
raise Selenium::WebDriver::Error::NoSuchElementError
|
|
@@ -77,7 +78,7 @@ module Selenium
|
|
|
77
78
|
by = FINDERS[how.to_sym]
|
|
78
79
|
raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
|
|
79
80
|
|
|
80
|
-
bridge.find_elements_by by, what
|
|
81
|
+
bridge.find_elements_by by, what, ref
|
|
81
82
|
rescue Selenium::WebDriver::Error::TimeoutError
|
|
82
83
|
# Implicit Wait times out in Edge
|
|
83
84
|
[]
|
|
@@ -92,7 +93,10 @@ module Selenium
|
|
|
92
93
|
when 1
|
|
93
94
|
arg = args.first
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
unless arg.respond_to?(:shift)
|
|
97
|
+
raise ArgumentError,
|
|
98
|
+
"expected #{arg.inspect}:#{arg.class} to respond to #shift"
|
|
99
|
+
end
|
|
96
100
|
|
|
97
101
|
# this will be a single-entry hash, so use #shift over #first or #[]
|
|
98
102
|
arr = arg.dup.shift
|
|
@@ -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
|