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
|
@@ -22,125 +22,546 @@ module Selenium
|
|
|
22
22
|
module Remote
|
|
23
23
|
class Bridge
|
|
24
24
|
include Atoms
|
|
25
|
-
include BridgeHelper
|
|
26
25
|
|
|
27
26
|
PORT = 4444
|
|
28
|
-
COMMANDS = {
|
|
29
|
-
new_session: [:post, 'session']
|
|
30
|
-
}.freeze
|
|
31
27
|
|
|
32
|
-
attr_accessor :
|
|
33
|
-
attr_reader :capabilities
|
|
28
|
+
attr_accessor :http, :file_detector
|
|
29
|
+
attr_reader :capabilities
|
|
34
30
|
|
|
35
31
|
#
|
|
36
|
-
#
|
|
32
|
+
# Initializes the bridge with the given server URL
|
|
33
|
+
# @param [String, URI] :url url for the remote server
|
|
34
|
+
# @param [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
|
|
35
|
+
# @api private
|
|
37
36
|
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
|
|
38
|
+
def initialize(url:, http_client: nil)
|
|
39
|
+
uri = url.is_a?(URI) ? url : URI.parse(url)
|
|
40
|
+
uri.path += '/' unless uri.path.end_with?('/')
|
|
41
|
+
|
|
42
|
+
@http = http_client || Http::Default.new
|
|
43
|
+
@http.server_url = uri
|
|
44
|
+
@file_detector = nil
|
|
45
|
+
end
|
|
46
|
+
|
|
41
47
|
#
|
|
42
|
-
#
|
|
48
|
+
# Creates session.
|
|
43
49
|
#
|
|
44
|
-
def self.handshake(**opts)
|
|
45
|
-
desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.new }
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
def create_session(capabilities)
|
|
52
|
+
response = execute(:new_session, {}, prepare_capabilities_payload(capabilities))
|
|
53
|
+
|
|
54
|
+
@session_id = response['sessionId']
|
|
55
|
+
capabilities = response['capabilities']
|
|
56
|
+
|
|
57
|
+
raise Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
@capabilities = Capabilities.json_create(capabilities)
|
|
60
|
+
|
|
61
|
+
case @capabilities[:browser_name]
|
|
62
|
+
when 'chrome'
|
|
63
|
+
extend(WebDriver::Chrome::Features)
|
|
64
|
+
when 'firefox'
|
|
65
|
+
extend(WebDriver::Firefox::Features)
|
|
66
|
+
when 'msedge'
|
|
67
|
+
extend(WebDriver::Edge::Features)
|
|
68
|
+
when 'Safari', 'Safari Technology Preview'
|
|
69
|
+
extend(WebDriver::Safari::Features)
|
|
53
70
|
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Returns the current session ID.
|
|
75
|
+
#
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
def session_id
|
|
78
|
+
@session_id || raise(Error::WebDriverError, 'no current session exists')
|
|
79
|
+
end
|
|
57
80
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Remote::W3C::Bridge.new(capabilities, bridge.session_id, **opts)
|
|
63
|
-
else
|
|
64
|
-
raise WebDriverError, 'cannot understand dialect'
|
|
81
|
+
def browser
|
|
82
|
+
@browser ||= begin
|
|
83
|
+
name = @capabilities.browser_name
|
|
84
|
+
name ? name.tr(' ', '_').downcase.to_sym : 'unknown'
|
|
65
85
|
end
|
|
66
86
|
end
|
|
67
87
|
|
|
88
|
+
def status
|
|
89
|
+
execute :status
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def get(url)
|
|
93
|
+
execute :get, {}, {url: url}
|
|
94
|
+
end
|
|
95
|
+
|
|
68
96
|
#
|
|
69
|
-
#
|
|
70
|
-
# @param [Hash] opts options for the driver
|
|
71
|
-
# @option opts [String] :url url for the remote server
|
|
72
|
-
# @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
|
|
73
|
-
# @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
|
|
74
|
-
# @api private
|
|
97
|
+
# timeouts
|
|
75
98
|
#
|
|
76
99
|
|
|
77
|
-
def
|
|
78
|
-
|
|
100
|
+
def timeouts
|
|
101
|
+
execute :get_timeouts, {}
|
|
102
|
+
end
|
|
79
103
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
104
|
+
def timeouts=(timeouts)
|
|
105
|
+
execute :set_timeout, {}, timeouts
|
|
106
|
+
end
|
|
83
107
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
108
|
+
#
|
|
109
|
+
# alerts
|
|
110
|
+
#
|
|
87
111
|
|
|
88
|
-
|
|
89
|
-
|
|
112
|
+
def accept_alert
|
|
113
|
+
execute :accept_alert
|
|
114
|
+
end
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
def dismiss_alert
|
|
117
|
+
execute :dismiss_alert
|
|
118
|
+
end
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
def alert=(keys)
|
|
121
|
+
execute :send_alert_text, {}, {value: keys.split(//), text: keys}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def alert_text
|
|
125
|
+
execute :get_alert_text
|
|
95
126
|
end
|
|
96
127
|
|
|
97
128
|
#
|
|
98
|
-
#
|
|
129
|
+
# navigation
|
|
99
130
|
#
|
|
100
131
|
|
|
101
|
-
def
|
|
102
|
-
|
|
132
|
+
def go_back
|
|
133
|
+
execute :back
|
|
134
|
+
end
|
|
103
135
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
136
|
+
def go_forward
|
|
137
|
+
execute :forward
|
|
138
|
+
end
|
|
107
139
|
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
def url
|
|
141
|
+
execute :get_current_url
|
|
142
|
+
end
|
|
110
143
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
144
|
+
def title
|
|
145
|
+
execute :get_title
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def page_source
|
|
149
|
+
execute_script('var source = document.documentElement.outerHTML;' \
|
|
150
|
+
'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
|
|
151
|
+
'return source;')
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
#
|
|
155
|
+
# Create a new top-level browsing context
|
|
156
|
+
# https://w3c.github.io/webdriver/#new-window
|
|
157
|
+
# @param type [String] Supports two values: 'tab' and 'window'.
|
|
158
|
+
# Use 'tab' if you'd like the new window to share an OS-level window
|
|
159
|
+
# with the current browsing context.
|
|
160
|
+
# Use 'window' otherwise
|
|
161
|
+
# @return [Hash] Containing 'handle' with the value of the window handle
|
|
162
|
+
# and 'type' with the value of the created window type
|
|
163
|
+
#
|
|
164
|
+
def new_window(type)
|
|
165
|
+
execute :new_window, {}, {type: type}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def switch_to_window(name)
|
|
169
|
+
execute :switch_to_window, {}, {handle: name}
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def switch_to_frame(id)
|
|
173
|
+
id = find_element_by('id', id) if id.is_a? String
|
|
174
|
+
execute :switch_to_frame, {}, {id: id}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def switch_to_parent_frame
|
|
178
|
+
execute :switch_to_parent_frame
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def switch_to_default_content
|
|
182
|
+
switch_to_frame nil
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
QUIT_ERRORS = [IOError].freeze
|
|
186
|
+
|
|
187
|
+
def quit
|
|
188
|
+
execute :delete_session
|
|
189
|
+
http.close
|
|
190
|
+
rescue *QUIT_ERRORS
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def close
|
|
194
|
+
execute :close_window
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def refresh
|
|
198
|
+
execute :refresh
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
#
|
|
202
|
+
# window handling
|
|
203
|
+
#
|
|
204
|
+
|
|
205
|
+
def window_handles
|
|
206
|
+
execute :get_window_handles
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def window_handle
|
|
210
|
+
execute :get_window_handle
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def resize_window(width, height, handle = :current)
|
|
214
|
+
raise Error::WebDriverError, 'Switch to desired window before changing its size' unless handle == :current
|
|
215
|
+
|
|
216
|
+
set_window_rect(width: width, height: height)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def window_size(handle = :current)
|
|
220
|
+
unless handle == :current
|
|
221
|
+
raise Error::UnsupportedOperationError,
|
|
222
|
+
'Switch to desired window before getting its size'
|
|
116
223
|
end
|
|
117
224
|
|
|
118
|
-
|
|
225
|
+
data = execute :get_window_rect
|
|
226
|
+
Dimension.new data['width'], data['height']
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def minimize_window
|
|
230
|
+
execute :minimize_window
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def maximize_window(handle = :current)
|
|
234
|
+
unless handle == :current
|
|
235
|
+
raise Error::UnsupportedOperationError,
|
|
236
|
+
'Switch to desired window before changing its size'
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
execute :maximize_window
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def full_screen_window
|
|
243
|
+
execute :fullscreen_window
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def reposition_window(x, y)
|
|
247
|
+
set_window_rect(x: x, y: y)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def window_position
|
|
251
|
+
data = execute :get_window_rect
|
|
252
|
+
Point.new data['x'], data['y']
|
|
253
|
+
end
|
|
119
254
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
255
|
+
def set_window_rect(x: nil, y: nil, width: nil, height: nil)
|
|
256
|
+
params = {x: x, y: y, width: width, height: height}
|
|
257
|
+
params.update(params) { |_k, v| Integer(v) unless v.nil? }
|
|
258
|
+
execute :set_window_rect, {}, params
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def window_rect
|
|
262
|
+
data = execute :get_window_rect
|
|
263
|
+
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def screenshot
|
|
267
|
+
execute :take_screenshot
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def element_screenshot(element)
|
|
271
|
+
execute :take_element_screenshot, id: element
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
#
|
|
275
|
+
# HTML 5
|
|
276
|
+
#
|
|
277
|
+
|
|
278
|
+
def local_storage_item(key, value = nil)
|
|
279
|
+
if value
|
|
280
|
+
execute_script("localStorage.setItem('#{key}', '#{value}')")
|
|
124
281
|
else
|
|
125
|
-
|
|
126
|
-
@dialect = :w3c
|
|
127
|
-
W3C::Capabilities.json_create(value)
|
|
282
|
+
execute_script("return localStorage.getItem('#{key}')")
|
|
128
283
|
end
|
|
129
284
|
end
|
|
130
285
|
|
|
286
|
+
def remove_local_storage_item(key)
|
|
287
|
+
execute_script("localStorage.removeItem('#{key}')")
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def local_storage_keys
|
|
291
|
+
execute_script('return Object.keys(localStorage)')
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def clear_local_storage
|
|
295
|
+
execute_script('localStorage.clear()')
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def local_storage_size
|
|
299
|
+
execute_script('return localStorage.length')
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def session_storage_item(key, value = nil)
|
|
303
|
+
if value
|
|
304
|
+
execute_script("sessionStorage.setItem('#{key}', '#{value}')")
|
|
305
|
+
else
|
|
306
|
+
execute_script("return sessionStorage.getItem('#{key}')")
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def remove_session_storage_item(key)
|
|
311
|
+
execute_script("sessionStorage.removeItem('#{key}')")
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def session_storage_keys
|
|
315
|
+
execute_script('return Object.keys(sessionStorage)')
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def clear_session_storage
|
|
319
|
+
execute_script('sessionStorage.clear()')
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def session_storage_size
|
|
323
|
+
execute_script('return sessionStorage.length')
|
|
324
|
+
end
|
|
325
|
+
|
|
131
326
|
#
|
|
132
|
-
#
|
|
327
|
+
# javascript execution
|
|
133
328
|
#
|
|
134
329
|
|
|
135
|
-
def
|
|
136
|
-
|
|
330
|
+
def execute_script(script, *args)
|
|
331
|
+
result = execute :execute_script, {}, {script: script, args: args}
|
|
332
|
+
unwrap_script_result result
|
|
137
333
|
end
|
|
138
334
|
|
|
139
|
-
def
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
335
|
+
def execute_async_script(script, *args)
|
|
336
|
+
result = execute :execute_async_script, {}, {script: script, args: args}
|
|
337
|
+
unwrap_script_result result
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
#
|
|
341
|
+
# cookies
|
|
342
|
+
#
|
|
343
|
+
|
|
344
|
+
def manage
|
|
345
|
+
@manage ||= WebDriver::Manager.new(self)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def add_cookie(cookie)
|
|
349
|
+
execute :add_cookie, {}, {cookie: cookie}
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def delete_cookie(name)
|
|
353
|
+
execute :delete_cookie, name: name
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def cookie(name)
|
|
357
|
+
execute :get_cookie, name: name
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def cookies
|
|
361
|
+
execute :get_all_cookies
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def delete_all_cookies
|
|
365
|
+
execute :delete_all_cookies
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
#
|
|
369
|
+
# actions
|
|
370
|
+
#
|
|
371
|
+
|
|
372
|
+
def action(async = false)
|
|
373
|
+
ActionBuilder.new self,
|
|
374
|
+
Interactions.pointer(:mouse, name: 'mouse'),
|
|
375
|
+
Interactions.key('keyboard'),
|
|
376
|
+
async
|
|
377
|
+
end
|
|
378
|
+
alias_method :actions, :action
|
|
379
|
+
|
|
380
|
+
def mouse
|
|
381
|
+
raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def keyboard
|
|
385
|
+
raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def send_actions(data)
|
|
389
|
+
execute :actions, {}, {actions: data}
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def release_actions
|
|
393
|
+
execute :release_actions
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def print_page(options = {})
|
|
397
|
+
execute :print_page, {}, {options: options}
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def click_element(element)
|
|
401
|
+
execute :element_click, id: element
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def send_keys_to_element(element, keys)
|
|
405
|
+
# TODO: rework file detectors before Selenium 4.0
|
|
406
|
+
if @file_detector
|
|
407
|
+
local_files = keys.first&.split("\n")&.map { |key| @file_detector.call(Array(key)) }&.compact
|
|
408
|
+
if local_files.any?
|
|
409
|
+
keys = local_files.map { |local_file| upload(local_file) }
|
|
410
|
+
keys = Array(keys.join("\n"))
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Keep .split(//) for backward compatibility for now
|
|
415
|
+
text = keys.join('')
|
|
416
|
+
execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def upload(local_file)
|
|
420
|
+
unless File.file?(local_file)
|
|
421
|
+
WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!")
|
|
422
|
+
raise Error::WebDriverError, "You are trying to work with something that isn't a file."
|
|
143
423
|
end
|
|
424
|
+
|
|
425
|
+
execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def clear_element(element)
|
|
429
|
+
execute :element_clear, id: element
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def submit_element(element)
|
|
433
|
+
form = find_element_by('xpath', "./ancestor-or-self::form", [:element, element])
|
|
434
|
+
execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
|
|
435
|
+
"e.initEvent('submit', true, true);" \
|
|
436
|
+
'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
#
|
|
440
|
+
# element properties
|
|
441
|
+
#
|
|
442
|
+
|
|
443
|
+
def element_tag_name(element)
|
|
444
|
+
execute :get_element_tag_name, id: element
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def element_attribute(element, name)
|
|
448
|
+
WebDriver.logger.info "Using script for :getAttribute of #{name}"
|
|
449
|
+
execute_atom :getAttribute, element, name
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def element_dom_attribute(element, name)
|
|
453
|
+
execute :get_element_attribute, id: element, name: name
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def element_property(element, name)
|
|
457
|
+
execute :get_element_property, id: element, name: name
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def element_aria_role(element)
|
|
461
|
+
execute :get_element_aria_role, id: element
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def element_aria_label(element)
|
|
465
|
+
execute :get_element_aria_label, id: element
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def element_value(element)
|
|
469
|
+
element_property element, 'value'
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def element_text(element)
|
|
473
|
+
execute :get_element_text, id: element
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def element_location(element)
|
|
477
|
+
data = execute :get_element_rect, id: element
|
|
478
|
+
|
|
479
|
+
Point.new data['x'], data['y']
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def element_rect(element)
|
|
483
|
+
data = execute :get_element_rect, id: element
|
|
484
|
+
|
|
485
|
+
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def element_location_once_scrolled_into_view(element)
|
|
489
|
+
send_keys_to_element(element, [''])
|
|
490
|
+
element_location(element)
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
def element_size(element)
|
|
494
|
+
data = execute :get_element_rect, id: element
|
|
495
|
+
|
|
496
|
+
Dimension.new data['width'], data['height']
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def element_enabled?(element)
|
|
500
|
+
execute :is_element_enabled, id: element
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def element_selected?(element)
|
|
504
|
+
execute :is_element_selected, id: element
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def element_displayed?(element)
|
|
508
|
+
WebDriver.logger.info 'Using script for :isDisplayed'
|
|
509
|
+
execute_atom :isDisplayed, element
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def element_value_of_css_property(element, prop)
|
|
513
|
+
execute :get_element_css_value, id: element, property_name: prop
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
#
|
|
517
|
+
# finding elements
|
|
518
|
+
#
|
|
519
|
+
|
|
520
|
+
def active_element
|
|
521
|
+
Element.new self, element_id_from(execute(:get_active_element))
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
alias_method :switch_to_active_element, :active_element
|
|
525
|
+
|
|
526
|
+
def find_element_by(how, what, parent_ref = [])
|
|
527
|
+
how, what = convert_locator(how, what)
|
|
528
|
+
|
|
529
|
+
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
|
|
530
|
+
|
|
531
|
+
parent_type, parent_id = parent_ref
|
|
532
|
+
id = case parent_type
|
|
533
|
+
when :element
|
|
534
|
+
execute :find_child_element, {id: parent_id}, {using: how, value: what.to_s}
|
|
535
|
+
when :shadow_root
|
|
536
|
+
execute :find_shadow_child_element, {id: parent_id}, {using: how, value: what.to_s}
|
|
537
|
+
else
|
|
538
|
+
execute :find_element, {}, {using: how, value: what.to_s}
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
Element.new self, element_id_from(id)
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def find_elements_by(how, what, parent_ref = [])
|
|
545
|
+
how, what = convert_locator(how, what)
|
|
546
|
+
|
|
547
|
+
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
|
|
548
|
+
|
|
549
|
+
parent_type, parent_id = parent_ref
|
|
550
|
+
ids = case parent_type
|
|
551
|
+
when :element
|
|
552
|
+
execute :find_child_elements, {id: parent_id}, {using: how, value: what.to_s}
|
|
553
|
+
when :shadow_root
|
|
554
|
+
execute :find_shadow_child_elements, {id: parent_id}, {using: how, value: what.to_s}
|
|
555
|
+
else
|
|
556
|
+
execute :find_elements, {}, {using: how, value: what.to_s}
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
ids.map { |id| Element.new self, element_id_from(id) }
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def shadow_root(element)
|
|
563
|
+
id = execute :get_element_shadow_root, id: element
|
|
564
|
+
ShadowRoot.new self, shadow_root_id_from(id)
|
|
144
565
|
end
|
|
145
566
|
|
|
146
567
|
private
|
|
@@ -164,7 +585,7 @@ module Selenium
|
|
|
164
585
|
end
|
|
165
586
|
|
|
166
587
|
WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
|
|
167
|
-
http.call(verb, path, command_hash)
|
|
588
|
+
http.call(verb, path, command_hash)['value']
|
|
168
589
|
end
|
|
169
590
|
|
|
170
591
|
def escaper
|
|
@@ -172,23 +593,74 @@ module Selenium
|
|
|
172
593
|
end
|
|
173
594
|
|
|
174
595
|
def commands(command)
|
|
175
|
-
raise NotImplementedError unless command == :new_session
|
|
176
|
-
|
|
177
596
|
COMMANDS[command]
|
|
178
597
|
end
|
|
179
598
|
|
|
180
|
-
def
|
|
181
|
-
|
|
182
|
-
|
|
599
|
+
def unwrap_script_result(arg)
|
|
600
|
+
case arg
|
|
601
|
+
when Array
|
|
602
|
+
arg.map { |e| unwrap_script_result(e) }
|
|
603
|
+
when Hash
|
|
604
|
+
element_id = element_id_from(arg)
|
|
605
|
+
return Element.new(self, element_id) if element_id
|
|
183
606
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
}
|
|
607
|
+
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
|
|
608
|
+
else
|
|
609
|
+
arg
|
|
610
|
+
end
|
|
190
611
|
end
|
|
191
612
|
|
|
613
|
+
def element_id_from(id)
|
|
614
|
+
id['ELEMENT'] || id[Element::ELEMENT_KEY]
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def shadow_root_id_from(id)
|
|
618
|
+
id[ShadowRoot::ROOT_KEY]
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def prepare_capabilities_payload(capabilities)
|
|
622
|
+
capabilities = {alwaysMatch: capabilities} if !capabilities['alwaysMatch'] && !capabilities['firstMatch']
|
|
623
|
+
{capabilities: capabilities}
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
def convert_locator(how, what)
|
|
627
|
+
how = SearchContext::FINDERS[how.to_sym] || how
|
|
628
|
+
|
|
629
|
+
case how
|
|
630
|
+
when 'class name'
|
|
631
|
+
how = 'css selector'
|
|
632
|
+
what = ".#{escape_css(what.to_s)}"
|
|
633
|
+
when 'id'
|
|
634
|
+
how = 'css selector'
|
|
635
|
+
what = "##{escape_css(what.to_s)}"
|
|
636
|
+
when 'name'
|
|
637
|
+
how = 'css selector'
|
|
638
|
+
what = "*[name='#{escape_css(what.to_s)}']"
|
|
639
|
+
when 'tag name'
|
|
640
|
+
how = 'css selector'
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
if what.is_a?(Hash)
|
|
644
|
+
what = what.each_with_object({}) do |(h, w), hash|
|
|
645
|
+
h, w = convert_locator(h.to_s, w)
|
|
646
|
+
hash[h] = w
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
[how, what]
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/.freeze
|
|
654
|
+
UNICODE_CODE_POINT = 30
|
|
655
|
+
|
|
656
|
+
# Escapes invalid characters in CSS selector.
|
|
657
|
+
# @see https://mathiasbynens.be/notes/css-escapes
|
|
658
|
+
def escape_css(string)
|
|
659
|
+
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
|
660
|
+
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
|
|
661
|
+
|
|
662
|
+
string
|
|
663
|
+
end
|
|
192
664
|
end # Bridge
|
|
193
665
|
end # Remote
|
|
194
666
|
end # WebDriver
|