selenium-webdriver 4.0.0.alpha2 → 4.0.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 +338 -4
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/NOTICE +2 -0
- data/README.md +4 -5
- data/lib/selenium/server.rb +21 -29
- 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 -56
- data/lib/selenium/webdriver/chrome/features.rb +106 -0
- data/lib/selenium/webdriver/chrome/options.rb +127 -52
- data/lib/selenium/webdriver/chrome/profile.rb +8 -5
- data/lib/selenium/webdriver/chrome/service.rb +4 -6
- data/lib/selenium/webdriver/chrome.rb +10 -9
- data/lib/selenium/webdriver/common/driver.rb +110 -19
- 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_cdp.rb +38 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +45 -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_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 +79 -16
- data/lib/selenium/webdriver/common/error.rb +12 -0
- data/lib/selenium/webdriver/common/interactions/interaction.rb +4 -1
- 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 +14 -14
- data/lib/selenium/webdriver/common/options.rb +186 -0
- 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 +10 -2
- 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 +23 -113
- 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/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.rb +17 -2
- 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/{chrome/bridge.rb → devtools/pinned_script.rb} +26 -17
- 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 +5 -31
- data/lib/selenium/webdriver/edge/features.rb +44 -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 +9 -24
- data/lib/selenium/webdriver/edge.rb +11 -13
- data/lib/selenium/webdriver/firefox/driver.rb +20 -30
- data/lib/selenium/webdriver/firefox/extension.rb +8 -0
- data/lib/selenium/webdriver/firefox/{bridge.rb → features.rb} +23 -4
- data/lib/selenium/webdriver/firefox/options.rb +70 -49
- data/lib/selenium/webdriver/firefox/profile.rb +16 -77
- data/lib/selenium/webdriver/firefox/service.rb +1 -5
- data/lib/selenium/webdriver/firefox.rb +22 -16
- data/lib/selenium/webdriver/ie/driver.rb +1 -34
- data/lib/selenium/webdriver/ie/options.rb +13 -44
- data/lib/selenium/webdriver/ie/service.rb +9 -11
- data/lib/selenium/webdriver/ie.rb +8 -7
- data/lib/selenium/webdriver/remote/bridge.rb +112 -86
- data/lib/selenium/webdriver/remote/capabilities.rb +120 -62
- data/lib/selenium/webdriver/remote/commands.rb +7 -0
- data/lib/selenium/webdriver/remote/driver.rb +15 -12
- data/lib/selenium/webdriver/remote/http/common.rb +0 -5
- data/lib/selenium/webdriver/remote/http/default.rb +17 -11
- data/lib/selenium/webdriver/remote/http/persistent.rb +11 -6
- data/lib/selenium/webdriver/remote.rb +15 -9
- data/lib/selenium/webdriver/safari/driver.rb +3 -34
- data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +6 -6
- data/lib/selenium/webdriver/safari/options.rb +10 -29
- data/lib/selenium/webdriver/safari/service.rb +0 -4
- data/lib/selenium/webdriver/safari.rb +16 -8
- 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/support/guards/guard_condition.rb +52 -0
- 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 +2 -2
- data/lib/selenium/webdriver/support.rb +1 -0
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +10 -8
- data/selenium-webdriver.gemspec +29 -13
- metadata +125 -51
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
- data/lib/selenium/webdriver/firefox/binary.rb +0 -110
- data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
|
@@ -23,6 +23,7 @@ module Selenium
|
|
|
23
23
|
ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf'
|
|
24
24
|
|
|
25
25
|
include SearchContext
|
|
26
|
+
include TakesScreenshot
|
|
26
27
|
|
|
27
28
|
#
|
|
28
29
|
# Creates a new Element
|
|
@@ -91,13 +92,18 @@ module Selenium
|
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
95
|
+
# This method attempts to provide the most likely desired current value for the attribute
|
|
96
|
+
# of the element, even when that desired value is actually a JavaScript property.
|
|
97
|
+
# It is implemented with a custom JavaScript atom. To obtain the exact value of the attribute or property,
|
|
98
|
+
# use #dom_attribute or #property methods respectively.
|
|
99
|
+
#
|
|
100
|
+
# More exactly, this method will return the value of the property with the given name,
|
|
101
|
+
# if it exists. If it does not, then the value of the attribute with the given name is returned.
|
|
102
|
+
# If neither exists, null is returned.
|
|
103
|
+
#
|
|
104
|
+
# The "style" attribute is converted as best can be to a text representation with a trailing semi-colon.
|
|
105
|
+
#
|
|
106
|
+
# The following are deemed to be "boolean" attributes, and will return either "true" or "false":
|
|
101
107
|
#
|
|
102
108
|
# async, autofocus, autoplay, checked, compact, complete, controls, declare, defaultchecked,
|
|
103
109
|
# defaultselected, defer, disabled, draggable, ended, formnovalidate, hidden, indeterminate,
|
|
@@ -105,29 +111,73 @@ module Selenium
|
|
|
105
111
|
# nowrap, open, paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
|
|
106
112
|
# selected, spellcheck, truespeed, willvalidate
|
|
107
113
|
#
|
|
108
|
-
# Finally, the following commonly mis-capitalized attribute/property names are evaluated as
|
|
109
|
-
# expected:
|
|
114
|
+
# Finally, the following commonly mis-capitalized attribute/property names are evaluated as expected:
|
|
110
115
|
#
|
|
111
|
-
# class,
|
|
116
|
+
# When the value of "class" is requested, the "className" property is returned.
|
|
117
|
+
# When the value of "readonly" is requested, the "readOnly" property is returned.
|
|
112
118
|
#
|
|
113
119
|
# @param [String] name attribute name
|
|
114
120
|
# @return [String, nil] attribute value
|
|
115
121
|
#
|
|
122
|
+
# @see #dom_attribute
|
|
123
|
+
# @see #property
|
|
124
|
+
#
|
|
116
125
|
|
|
117
126
|
def attribute(name)
|
|
118
127
|
bridge.element_attribute self, name
|
|
119
128
|
end
|
|
120
129
|
|
|
121
130
|
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
131
|
+
# Gets the value of a declared HTML attribute of this element.
|
|
132
|
+
#
|
|
133
|
+
# As opposed to the #attribute method, this method
|
|
134
|
+
# only returns attributes declared in the element's HTML markup.
|
|
135
|
+
#
|
|
136
|
+
# If the attribute is not set, nil is returned.
|
|
137
|
+
#
|
|
138
|
+
# @param [String] name attribute name
|
|
139
|
+
# @return [String, nil] attribute value
|
|
140
|
+
#
|
|
141
|
+
# @see #attribute
|
|
142
|
+
# @see #property
|
|
143
|
+
#
|
|
144
|
+
|
|
145
|
+
def dom_attribute(name)
|
|
146
|
+
bridge.element_dom_attribute @id, name
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#
|
|
150
|
+
# Gets the value of a JavaScript property of this element
|
|
151
|
+
# This will return the current value,
|
|
152
|
+
# even if this has been modified after the page has been loaded.
|
|
153
|
+
# If the value is not set, nil is returned.
|
|
124
154
|
#
|
|
125
155
|
# @param [String] name property name
|
|
126
156
|
# @return [String, nil] property value
|
|
127
157
|
#
|
|
128
158
|
|
|
129
159
|
def property(name)
|
|
130
|
-
bridge.element_property
|
|
160
|
+
bridge.element_property @id, name
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
#
|
|
164
|
+
# Gets the computed WAI-ARIA role of element
|
|
165
|
+
#
|
|
166
|
+
# @return [String]
|
|
167
|
+
#
|
|
168
|
+
|
|
169
|
+
def aria_role
|
|
170
|
+
bridge.element_aria_role @id
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
#
|
|
174
|
+
# Gets the computed WAI-ARIA label of element.
|
|
175
|
+
#
|
|
176
|
+
# @return [String]
|
|
177
|
+
#
|
|
178
|
+
|
|
179
|
+
def accessible_name
|
|
180
|
+
bridge.element_aria_label @id
|
|
131
181
|
end
|
|
132
182
|
|
|
133
183
|
#
|
|
@@ -267,6 +317,16 @@ module Selenium
|
|
|
267
317
|
bridge.element_size @id
|
|
268
318
|
end
|
|
269
319
|
|
|
320
|
+
#
|
|
321
|
+
# Returns the shadow root of an element.
|
|
322
|
+
#
|
|
323
|
+
# @return [WebDriver::ShadowRoot]
|
|
324
|
+
#
|
|
325
|
+
|
|
326
|
+
def shadow_root
|
|
327
|
+
bridge.shadow_root @id
|
|
328
|
+
end
|
|
329
|
+
|
|
270
330
|
#-------------------------------- sugar --------------------------------
|
|
271
331
|
|
|
272
332
|
#
|
|
@@ -286,14 +346,13 @@ module Selenium
|
|
|
286
346
|
#
|
|
287
347
|
alias_method :[], :attribute
|
|
288
348
|
|
|
289
|
-
#
|
|
290
|
-
# for SearchContext and execute_script
|
|
291
349
|
#
|
|
292
350
|
# @api private
|
|
351
|
+
# @see SearchContext
|
|
293
352
|
#
|
|
294
353
|
|
|
295
354
|
def ref
|
|
296
|
-
@id
|
|
355
|
+
[:element, @id]
|
|
297
356
|
end
|
|
298
357
|
|
|
299
358
|
#
|
|
@@ -327,6 +386,10 @@ module Selenium
|
|
|
327
386
|
|
|
328
387
|
tn == 'option' || (tn == 'input' && %w[radio checkbox].include?(type))
|
|
329
388
|
end
|
|
389
|
+
|
|
390
|
+
def screenshot
|
|
391
|
+
bridge.element_screenshot(@id)
|
|
392
|
+
end
|
|
330
393
|
end # Element
|
|
331
394
|
end # WebDriver
|
|
332
395
|
end # Selenium
|
|
@@ -61,6 +61,12 @@ module Selenium
|
|
|
61
61
|
|
|
62
62
|
class StaleElementReferenceError < WebDriverError; end
|
|
63
63
|
|
|
64
|
+
#
|
|
65
|
+
# A command failed because the referenced shadow root is no longer attached to the DOM.
|
|
66
|
+
#
|
|
67
|
+
|
|
68
|
+
class DetachedShadowRootError < WebDriverError; end
|
|
69
|
+
|
|
64
70
|
#
|
|
65
71
|
# The target element is in an invalid state, rendering it impossible to interact with, for
|
|
66
72
|
# example if you click a disabled element.
|
|
@@ -93,6 +99,12 @@ module Selenium
|
|
|
93
99
|
|
|
94
100
|
class NoSuchWindowError < WebDriverError; end
|
|
95
101
|
|
|
102
|
+
#
|
|
103
|
+
# The element does not have a shadow root.
|
|
104
|
+
#
|
|
105
|
+
|
|
106
|
+
class NoSuchShadowRootError < WebDriverError; end
|
|
107
|
+
|
|
96
108
|
#
|
|
97
109
|
# An illegal attempt was made to set a cookie under a different domain than the current page.
|
|
98
110
|
#
|
|
@@ -26,7 +26,10 @@ module Selenium
|
|
|
26
26
|
attr_reader :source
|
|
27
27
|
|
|
28
28
|
def initialize(source)
|
|
29
|
-
|
|
29
|
+
unless Interactions::SOURCE_TYPES.include? source.type
|
|
30
|
+
raise TypeError,
|
|
31
|
+
"#{source.type} is not a valid input type"
|
|
32
|
+
end
|
|
30
33
|
|
|
31
34
|
@source = source
|
|
32
35
|
end
|
|
@@ -30,14 +30,14 @@ module Selenium
|
|
|
30
30
|
|
|
31
31
|
def as_json(*)
|
|
32
32
|
{
|
|
33
|
-
'level' => level,
|
|
34
33
|
'timestamp' => timestamp,
|
|
34
|
+
'level' => level,
|
|
35
35
|
'message' => message
|
|
36
36
|
}
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def to_s
|
|
40
|
-
"#{
|
|
40
|
+
"#{time} #{level}: #{message}"
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def time
|
|
@@ -40,13 +40,17 @@ module Selenium
|
|
|
40
40
|
:close,
|
|
41
41
|
:debug, :debug?,
|
|
42
42
|
:info, :info?,
|
|
43
|
-
:warn
|
|
43
|
+
:warn?,
|
|
44
44
|
:error, :error?,
|
|
45
45
|
:fatal, :fatal?,
|
|
46
46
|
:level, :level=
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
#
|
|
49
|
+
# @param [String] progname Allow child projects to use Selenium's Logger pattern
|
|
50
|
+
#
|
|
51
|
+
def initialize(progname = 'Selenium')
|
|
52
|
+
@logger = create_logger(progname)
|
|
53
|
+
@ignored = []
|
|
50
54
|
end
|
|
51
55
|
|
|
52
56
|
#
|
|
@@ -73,28 +77,63 @@ module Selenium
|
|
|
73
77
|
@logger.instance_variable_get(:@logdev).dev
|
|
74
78
|
end
|
|
75
79
|
|
|
80
|
+
#
|
|
81
|
+
# Will not log the provided ID.
|
|
82
|
+
#
|
|
83
|
+
# @param [Array, Symbol] id
|
|
84
|
+
#
|
|
85
|
+
def ignore(id)
|
|
86
|
+
Array(id).each { |ignore| @ignored << ignore }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
#
|
|
90
|
+
# Overrides default #warn to skip ignored messages by provided id
|
|
91
|
+
#
|
|
92
|
+
# @param [String] message
|
|
93
|
+
# @param [Symbol, Array<Sybmol>] id
|
|
94
|
+
# @yield see #deprecate
|
|
95
|
+
#
|
|
96
|
+
def warn(message, id: [])
|
|
97
|
+
id = Array(id)
|
|
98
|
+
return if (@ignored & id).any?
|
|
99
|
+
|
|
100
|
+
msg = id.empty? ? message : "[#{id.map(&:inspect).join(', ')}] #{message} "
|
|
101
|
+
msg += " #{yield}" if block_given?
|
|
102
|
+
|
|
103
|
+
@logger.warn { msg }
|
|
104
|
+
end
|
|
105
|
+
|
|
76
106
|
#
|
|
77
107
|
# Marks code as deprecated with/without replacement.
|
|
78
108
|
#
|
|
79
109
|
# @param [String] old
|
|
80
110
|
# @param [String, nil] new
|
|
111
|
+
# @param [Symbol, Array<Symbol>] id
|
|
112
|
+
# @param [String] reference
|
|
113
|
+
# @yield appends additional message to end of provided template
|
|
81
114
|
#
|
|
82
|
-
def deprecate(old, new = nil)
|
|
83
|
-
|
|
115
|
+
def deprecate(old, new = nil, id: [], reference: '', &block)
|
|
116
|
+
id = Array(id)
|
|
117
|
+
return if @ignored.include?(:deprecations) || (@ignored & id).any?
|
|
118
|
+
|
|
119
|
+
ids = id.empty? ? '' : "[#{id.map(&:inspect).join(', ')}] "
|
|
120
|
+
|
|
121
|
+
message = +"[DEPRECATION] #{ids}#{old} is deprecated"
|
|
84
122
|
message << if new
|
|
85
123
|
". Use #{new} instead."
|
|
86
124
|
else
|
|
87
|
-
' and will be removed in
|
|
125
|
+
' and will be removed in a future release.'
|
|
88
126
|
end
|
|
127
|
+
message << " See explanation for this deprecation: #{reference}." unless reference.empty?
|
|
89
128
|
|
|
90
|
-
warn message
|
|
129
|
+
warn message, &block
|
|
91
130
|
end
|
|
92
131
|
|
|
93
132
|
private
|
|
94
133
|
|
|
95
|
-
def create_logger(
|
|
96
|
-
logger = ::Logger.new(
|
|
97
|
-
logger.progname =
|
|
134
|
+
def create_logger(name)
|
|
135
|
+
logger = ::Logger.new($stdout)
|
|
136
|
+
logger.progname = name
|
|
98
137
|
logger.level = default_level
|
|
99
138
|
logger.formatter = proc do |severity, time, progname, msg|
|
|
100
139
|
"#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
|
|
@@ -104,11 +143,7 @@ module Selenium
|
|
|
104
143
|
end
|
|
105
144
|
|
|
106
145
|
def default_level
|
|
107
|
-
|
|
108
|
-
:debug
|
|
109
|
-
else
|
|
110
|
-
:warn
|
|
111
|
-
end
|
|
146
|
+
$DEBUG || ENV.key?('DEBUG') ? :debug : :warn
|
|
112
147
|
end
|
|
113
148
|
end # Logger
|
|
114
149
|
end # WebDriver
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -0,0 +1,186 @@
|
|
|
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 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
|
|
63
|
+
end
|
|
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
|
|
121
|
+
|
|
122
|
+
process_browser_options(browser_options)
|
|
123
|
+
generate_as_json(w3c_options.merge(browser_options))
|
|
124
|
+
end
|
|
125
|
+
|
|
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
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
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)
|
|
168
|
+
end
|
|
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
|
|
183
|
+
|
|
184
|
+
# Options
|
|
185
|
+
end # WebDriver
|
|
186
|
+
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,7 +71,7 @@ module Selenium
|
|
|
63
71
|
|
|
64
72
|
module ClassMethods
|
|
65
73
|
def from_json(json)
|
|
66
|
-
data =
|
|
74
|
+
data = decoded(json)
|
|
67
75
|
|
|
68
76
|
# can't use Tempfile here since it doesn't support File::BINARY mode on 1.8
|
|
69
77
|
# can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
|
|
@@ -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,
|