selenium-webdriver 3.142.7 → 4.10.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 +611 -5
- data/Gemfile +5 -1
- data/LICENSE +1 -1
- data/NOTICE +2 -0
- data/README.md +4 -5
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +94 -79
- data/lib/selenium/webdriver/atoms/findElements.js +121 -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/atoms.rb +2 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
- data/lib/selenium/webdriver/bidi/browsing_context_info.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/filter_by.rb +40 -0
- data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
- data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
- data/lib/selenium/webdriver/bidi/session.rb +51 -0
- data/lib/selenium/webdriver/{common/keyboard.rb → bidi.rb} +21 -35
- data/lib/selenium/webdriver/chrome/driver.rb +9 -86
- data/lib/selenium/webdriver/chrome/features.rb +44 -0
- data/lib/selenium/webdriver/chrome/options.rb +9 -158
- data/lib/selenium/webdriver/chrome/profile.rb +3 -80
- data/lib/selenium/webdriver/chrome/service.rb +6 -33
- data/lib/selenium/webdriver/chrome.rb +5 -18
- data/lib/selenium/webdriver/chromium/driver.rb +61 -0
- data/lib/selenium/webdriver/{chrome/bridge.rb → chromium/features.rb} +51 -16
- data/lib/selenium/webdriver/chromium/options.rb +261 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium/service.rb +42 -0
- data/lib/selenium/webdriver/chromium.rb +32 -0
- data/lib/selenium/webdriver/common/action_builder.rb +128 -238
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +94 -43
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +42 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +49 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +87 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +9 -9
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +86 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +36 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +42 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +41 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +36 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -9
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +143 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_logs.rb} +4 -4
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +16 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +69 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -13
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +75 -0
- data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
- data/lib/selenium/webdriver/common/driver_finder.rb +47 -0
- data/lib/selenium/webdriver/common/element.rb +89 -29
- data/lib/selenium/webdriver/common/error.rb +53 -194
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -22
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +10 -6
- data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
- data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
- data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +71 -82
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
- data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
- data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
- data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
- data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
- data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
- data/lib/selenium/webdriver/common/{w3c_manager.rb → interactions/wheel_input.rb} +14 -17
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/local_driver.rb +55 -0
- data/lib/selenium/webdriver/common/log_entry.rb +2 -2
- data/lib/selenium/webdriver/common/logger.rb +119 -19
- data/lib/selenium/webdriver/common/manager.rb +11 -38
- data/lib/selenium/webdriver/common/options.rb +169 -23
- data/lib/selenium/webdriver/common/platform.rb +14 -6
- 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 +8 -5
- data/lib/selenium/webdriver/common/search_context.rb +7 -9
- data/lib/selenium/webdriver/common/selenium_manager.rb +125 -0
- data/lib/selenium/webdriver/common/service.rb +26 -137
- data/lib/selenium/webdriver/common/service_manager.rb +144 -0
- data/lib/selenium/webdriver/common/shadow_root.rb +86 -0
- data/lib/selenium/webdriver/common/socket_lock.rb +4 -4
- data/lib/selenium/webdriver/common/socket_poller.rb +4 -4
- data/lib/selenium/webdriver/common/takes_screenshot.rb +65 -0
- data/lib/selenium/webdriver/common/target_locator.rb +31 -4
- data/lib/selenium/webdriver/common/timeouts.rb +31 -4
- data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
- data/lib/selenium/webdriver/common/wait.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
- data/lib/selenium/webdriver/common/window.rb +6 -10
- data/lib/selenium/webdriver/common/zipper.rb +4 -10
- data/lib/selenium/webdriver/common.rb +42 -18
- data/lib/selenium/webdriver/devtools/console_event.rb +36 -0
- data/lib/selenium/webdriver/devtools/exception_event.rb +34 -0
- data/lib/selenium/webdriver/devtools/mutation_event.rb +35 -0
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +57 -0
- data/lib/selenium/webdriver/devtools/request.rb +65 -0
- data/lib/selenium/webdriver/devtools/response.rb +64 -0
- data/lib/selenium/webdriver/devtools.rb +96 -0
- data/lib/selenium/webdriver/edge/driver.rb +11 -27
- data/lib/selenium/webdriver/edge/features.rb +44 -0
- data/lib/selenium/webdriver/edge/options.rb +18 -43
- data/lib/selenium/webdriver/edge/profile.rb +33 -0
- data/lib/selenium/webdriver/edge/service.rb +7 -27
- data/lib/selenium/webdriver/edge.rb +11 -14
- data/lib/selenium/webdriver/firefox/driver.rb +38 -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 +77 -50
- data/lib/selenium/webdriver/firefox/profile.rb +17 -71
- data/lib/selenium/webdriver/firefox/service.rb +3 -13
- data/lib/selenium/webdriver/firefox/util.rb +1 -1
- data/lib/selenium/webdriver/firefox.rb +17 -28
- data/lib/selenium/webdriver/ie/driver.rb +5 -45
- data/lib/selenium/webdriver/ie/options.rb +15 -46
- data/lib/selenium/webdriver/ie/service.rb +11 -19
- data/lib/selenium/webdriver/ie.rb +3 -16
- data/lib/selenium/webdriver/remote/bridge/commands.rb +170 -0
- data/lib/selenium/webdriver/remote/bridge.rb +592 -87
- data/lib/selenium/webdriver/remote/capabilities.rb +182 -124
- data/lib/selenium/webdriver/remote/driver.rb +30 -15
- data/lib/selenium/webdriver/remote/http/common.rb +3 -8
- data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
- data/lib/selenium/webdriver/remote/http/default.rb +23 -31
- data/lib/selenium/webdriver/remote/response.rb +17 -49
- data/lib/selenium/webdriver/remote.rb +14 -12
- data/lib/selenium/webdriver/safari/driver.rb +7 -29
- data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -5
- data/lib/selenium/webdriver/safari/options.rb +12 -27
- data/lib/selenium/webdriver/safari/service.rb +13 -11
- data/lib/selenium/webdriver/safari.rb +14 -20
- data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
- data/lib/selenium/webdriver/support/color.rb +24 -24
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
- data/lib/selenium/webdriver/support/guards/guard.rb +87 -0
- data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +21 -20
- data/lib/selenium/webdriver/support/guards.rb +95 -0
- data/lib/selenium/webdriver/support/relative_locator.rb +50 -0
- data/lib/selenium/webdriver/support/select.rb +6 -4
- data/lib/selenium/webdriver/support.rb +1 -0
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +18 -17
- data/selenium-webdriver.gemspec +36 -18
- metadata +159 -89
- 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/mouse.rb +0 -89
- data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
- data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
- data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
- data/lib/selenium/webdriver/edge/bridge.rb +0 -76
- data/lib/selenium/webdriver/firefox/binary.rb +0 -187
- data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
- data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
- data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
- data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/remote/http/persistent.rb +0 -60
- data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
- data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
- data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
- data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
- data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -21,126 +21,579 @@ module Selenium
|
|
21
21
|
module WebDriver
|
22
22
|
module Remote
|
23
23
|
class Bridge
|
24
|
+
autoload :COMMANDS, 'selenium/webdriver/remote/bridge/commands'
|
24
25
|
include Atoms
|
25
|
-
include BridgeHelper
|
26
26
|
|
27
27
|
PORT = 4444
|
28
|
-
COMMANDS = {
|
29
|
-
new_session: [:post, 'session']
|
30
|
-
}.freeze
|
31
28
|
|
32
|
-
attr_accessor :
|
33
|
-
attr_reader :capabilities
|
29
|
+
attr_accessor :http, :file_detector
|
30
|
+
attr_reader :capabilities
|
34
31
|
|
35
32
|
#
|
36
|
-
#
|
33
|
+
# Initializes the bridge with the given server URL
|
34
|
+
# @param [String, URI] url url for the remote server
|
35
|
+
# @param [Object] http_client an HTTP client instance that implements the same protocol as Http::Default
|
36
|
+
# @api private
|
37
37
|
#
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
|
39
|
+
def initialize(url:, http_client: nil)
|
40
|
+
uri = url.is_a?(URI) ? url : URI.parse(url)
|
41
|
+
uri.path += '/' unless uri.path.end_with?('/')
|
42
|
+
|
43
|
+
@http = http_client || Http::Default.new
|
44
|
+
@http.server_url = uri
|
45
|
+
@file_detector = nil
|
46
|
+
end
|
47
|
+
|
41
48
|
#
|
42
|
-
#
|
49
|
+
# Creates session.
|
43
50
|
#
|
44
|
-
def self.handshake(**opts)
|
45
|
-
desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.new }
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
|
50
|
-
end
|
52
|
+
def create_session(capabilities)
|
53
|
+
response = execute(:new_session, {}, prepare_capabilities_payload(capabilities))
|
51
54
|
|
52
|
-
|
55
|
+
@session_id = response['sessionId']
|
56
|
+
capabilities = response['capabilities']
|
57
|
+
|
58
|
+
raise Error::WebDriverError, 'no sessionId in returned payload' unless @session_id
|
59
|
+
|
60
|
+
@capabilities = Capabilities.json_create(capabilities)
|
61
|
+
|
62
|
+
case @capabilities[:browser_name]
|
63
|
+
when 'chrome'
|
64
|
+
extend(WebDriver::Chrome::Features)
|
65
|
+
when 'firefox'
|
66
|
+
extend(WebDriver::Firefox::Features)
|
67
|
+
when 'msedge'
|
68
|
+
extend(WebDriver::Edge::Features)
|
69
|
+
when 'Safari', 'Safari Technology Preview'
|
70
|
+
extend(WebDriver::Safari::Features)
|
53
71
|
end
|
72
|
+
end
|
54
73
|
|
55
|
-
|
56
|
-
|
74
|
+
#
|
75
|
+
# Returns the current session ID.
|
76
|
+
#
|
57
77
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
78
|
+
def session_id
|
79
|
+
@session_id || raise(Error::WebDriverError, 'no current session exists')
|
80
|
+
end
|
81
|
+
|
82
|
+
def browser
|
83
|
+
@browser ||= begin
|
84
|
+
name = @capabilities.browser_name
|
85
|
+
name ? name.tr(' ', '_').downcase.to_sym : 'unknown'
|
65
86
|
end
|
66
87
|
end
|
67
88
|
|
89
|
+
def status
|
90
|
+
execute :status
|
91
|
+
end
|
92
|
+
|
93
|
+
def get(url)
|
94
|
+
execute :get, {}, {url: url}
|
95
|
+
end
|
96
|
+
|
68
97
|
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
98
|
+
# timeouts
|
99
|
+
#
|
100
|
+
|
101
|
+
def timeouts
|
102
|
+
execute :get_timeouts, {}
|
103
|
+
end
|
104
|
+
|
105
|
+
def timeouts=(timeouts)
|
106
|
+
execute :set_timeout, {}, timeouts
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# alerts
|
75
111
|
#
|
76
112
|
|
77
|
-
def
|
78
|
-
|
113
|
+
def accept_alert
|
114
|
+
execute :accept_alert
|
115
|
+
end
|
79
116
|
|
80
|
-
|
81
|
-
|
82
|
-
|
117
|
+
def dismiss_alert
|
118
|
+
execute :dismiss_alert
|
119
|
+
end
|
83
120
|
|
84
|
-
|
85
|
-
|
86
|
-
|
121
|
+
def alert=(keys)
|
122
|
+
execute :send_alert_text, {}, {value: keys.chars, text: keys}
|
123
|
+
end
|
87
124
|
|
88
|
-
|
89
|
-
|
125
|
+
def alert_text
|
126
|
+
execute :get_alert_text
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# navigation
|
131
|
+
#
|
90
132
|
|
91
|
-
|
133
|
+
def go_back
|
134
|
+
execute :back
|
135
|
+
end
|
92
136
|
|
93
|
-
|
94
|
-
|
137
|
+
def go_forward
|
138
|
+
execute :forward
|
139
|
+
end
|
140
|
+
|
141
|
+
def url
|
142
|
+
execute :get_current_url
|
143
|
+
end
|
144
|
+
|
145
|
+
def title
|
146
|
+
execute :get_title
|
147
|
+
end
|
148
|
+
|
149
|
+
def page_source
|
150
|
+
execute :get_page_source
|
95
151
|
end
|
96
152
|
|
97
153
|
#
|
98
|
-
#
|
154
|
+
# Create a new top-level browsing context
|
155
|
+
# https://w3c.github.io/webdriver/#new-window
|
156
|
+
# @param type [String] Supports two values: 'tab' and 'window'.
|
157
|
+
# Use 'tab' if you'd like the new window to share an OS-level window
|
158
|
+
# with the current browsing context.
|
159
|
+
# Use 'window' otherwise
|
160
|
+
# @return [Hash] Containing 'handle' with the value of the window handle
|
161
|
+
# and 'type' with the value of the created window type
|
99
162
|
#
|
163
|
+
def new_window(type)
|
164
|
+
execute :new_window, {}, {type: type}
|
165
|
+
end
|
100
166
|
|
101
|
-
def
|
102
|
-
|
167
|
+
def switch_to_window(name)
|
168
|
+
execute :switch_to_window, {}, {handle: name}
|
169
|
+
end
|
103
170
|
|
104
|
-
|
105
|
-
|
106
|
-
|
171
|
+
def switch_to_frame(id)
|
172
|
+
id = find_element_by('id', id) if id.is_a? String
|
173
|
+
execute :switch_to_frame, {}, {id: id}
|
174
|
+
end
|
107
175
|
|
108
|
-
|
109
|
-
|
176
|
+
def switch_to_parent_frame
|
177
|
+
execute :switch_to_parent_frame
|
178
|
+
end
|
110
179
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
180
|
+
def switch_to_default_content
|
181
|
+
switch_to_frame nil
|
182
|
+
end
|
183
|
+
|
184
|
+
QUIT_ERRORS = [IOError].freeze
|
185
|
+
|
186
|
+
def quit
|
187
|
+
execute :delete_session
|
188
|
+
http.close
|
189
|
+
rescue *QUIT_ERRORS
|
190
|
+
nil
|
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
|
254
|
+
|
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
|
119
273
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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, devices: [], duration: 250)
|
373
|
+
ActionBuilder.new self, async: async, devices: devices, duration: duration
|
374
|
+
end
|
375
|
+
alias actions action
|
376
|
+
|
377
|
+
def send_actions(data)
|
378
|
+
execute :actions, {}, {actions: data}
|
379
|
+
end
|
380
|
+
|
381
|
+
def release_actions
|
382
|
+
execute :release_actions
|
383
|
+
end
|
384
|
+
|
385
|
+
def print_page(options = {})
|
386
|
+
execute :print_page, {}, {options: options}
|
387
|
+
end
|
388
|
+
|
389
|
+
def click_element(element)
|
390
|
+
execute :element_click, id: element
|
391
|
+
end
|
392
|
+
|
393
|
+
def send_keys_to_element(element, keys)
|
394
|
+
# TODO: rework file detectors before Selenium 4.0
|
395
|
+
if @file_detector
|
396
|
+
local_files = keys.first&.split("\n")&.map { |key| @file_detector.call(Array(key)) }&.compact
|
397
|
+
if local_files&.any?
|
398
|
+
keys = local_files.map { |local_file| upload(local_file) }
|
399
|
+
keys = Array(keys.join("\n"))
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
# Keep .split(//) for backward compatibility for now
|
404
|
+
text = keys.join
|
405
|
+
execute :element_send_keys, {id: element}, {value: text.chars, text: text}
|
406
|
+
end
|
407
|
+
|
408
|
+
def upload(local_file)
|
409
|
+
unless File.file?(local_file)
|
410
|
+
WebDriver.logger.debug("File detector only works with files. #{local_file.inspect} isn`t a file!",
|
411
|
+
id: :file_detector)
|
412
|
+
raise Error::WebDriverError, "You are trying to work with something that isn't a file."
|
143
413
|
end
|
414
|
+
|
415
|
+
execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
|
416
|
+
end
|
417
|
+
|
418
|
+
def clear_element(element)
|
419
|
+
execute :element_clear, id: element
|
420
|
+
end
|
421
|
+
|
422
|
+
def submit_element(element)
|
423
|
+
script = "/* submitForm */ var form = arguments[0];\n" \
|
424
|
+
"while (form.nodeName != \"FORM\" && form.parentNode) {\n " \
|
425
|
+
"form = form.parentNode;\n" \
|
426
|
+
"}\n" \
|
427
|
+
"if (!form) { throw Error('Unable to find containing form element'); }\n" \
|
428
|
+
"if (!form.ownerDocument) { throw Error('Unable to find owning document'); }\n" \
|
429
|
+
"var e = form.ownerDocument.createEvent('Event');\n" \
|
430
|
+
"e.initEvent('submit', true, true);\n" \
|
431
|
+
"if (form.dispatchEvent(e)) { HTMLFormElement.prototype.submit.call(form) }\n"
|
432
|
+
|
433
|
+
execute_script(script, Element::ELEMENT_KEY => element)
|
434
|
+
rescue Error::JavascriptError
|
435
|
+
raise Error::UnsupportedOperationError, 'To submit an element, it must be nested inside a form element'
|
436
|
+
end
|
437
|
+
|
438
|
+
#
|
439
|
+
# element properties
|
440
|
+
#
|
441
|
+
|
442
|
+
def element_tag_name(element)
|
443
|
+
execute :get_element_tag_name, id: element
|
444
|
+
end
|
445
|
+
|
446
|
+
def element_attribute(element, name)
|
447
|
+
WebDriver.logger.debug "Using script for :getAttribute of #{name}", id: :script
|
448
|
+
execute_atom :getAttribute, element, name
|
449
|
+
end
|
450
|
+
|
451
|
+
def element_dom_attribute(element, name)
|
452
|
+
execute :get_element_attribute, id: element, name: name
|
453
|
+
end
|
454
|
+
|
455
|
+
def element_property(element, name)
|
456
|
+
execute :get_element_property, id: element, name: name
|
457
|
+
end
|
458
|
+
|
459
|
+
def element_aria_role(element)
|
460
|
+
execute :get_element_aria_role, id: element
|
461
|
+
end
|
462
|
+
|
463
|
+
def element_aria_label(element)
|
464
|
+
execute :get_element_aria_label, id: element
|
465
|
+
end
|
466
|
+
|
467
|
+
def element_value(element)
|
468
|
+
element_property element, 'value'
|
469
|
+
end
|
470
|
+
|
471
|
+
def element_text(element)
|
472
|
+
execute :get_element_text, id: element
|
473
|
+
end
|
474
|
+
|
475
|
+
def element_location(element)
|
476
|
+
data = execute :get_element_rect, id: element
|
477
|
+
|
478
|
+
Point.new data['x'], data['y']
|
479
|
+
end
|
480
|
+
|
481
|
+
def element_rect(element)
|
482
|
+
data = execute :get_element_rect, id: element
|
483
|
+
|
484
|
+
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
485
|
+
end
|
486
|
+
|
487
|
+
def element_location_once_scrolled_into_view(element)
|
488
|
+
send_keys_to_element(element, [''])
|
489
|
+
element_location(element)
|
490
|
+
end
|
491
|
+
|
492
|
+
def element_size(element)
|
493
|
+
data = execute :get_element_rect, id: element
|
494
|
+
|
495
|
+
Dimension.new data['width'], data['height']
|
496
|
+
end
|
497
|
+
|
498
|
+
def element_enabled?(element)
|
499
|
+
execute :is_element_enabled, id: element
|
500
|
+
end
|
501
|
+
|
502
|
+
def element_selected?(element)
|
503
|
+
execute :is_element_selected, id: element
|
504
|
+
end
|
505
|
+
|
506
|
+
def element_displayed?(element)
|
507
|
+
WebDriver.logger.debug 'Using script for :isDisplayed', id: :script
|
508
|
+
execute_atom :isDisplayed, element
|
509
|
+
end
|
510
|
+
|
511
|
+
def element_value_of_css_property(element, prop)
|
512
|
+
execute :get_element_css_value, id: element, property_name: prop
|
513
|
+
end
|
514
|
+
|
515
|
+
#
|
516
|
+
# finding elements
|
517
|
+
#
|
518
|
+
|
519
|
+
def active_element
|
520
|
+
Element.new self, element_id_from(execute(:get_active_element))
|
521
|
+
end
|
522
|
+
|
523
|
+
alias switch_to_active_element active_element
|
524
|
+
|
525
|
+
def find_element_by(how, what, parent_ref = [])
|
526
|
+
how, what = convert_locator(how, what)
|
527
|
+
|
528
|
+
return execute_atom(:findElements, Support::RelativeLocator.new(what).as_json).first if how == 'relative'
|
529
|
+
|
530
|
+
parent_type, parent_id = parent_ref
|
531
|
+
id = case parent_type
|
532
|
+
when :element
|
533
|
+
execute :find_child_element, {id: parent_id}, {using: how, value: what.to_s}
|
534
|
+
when :shadow_root
|
535
|
+
execute :find_shadow_child_element, {id: parent_id}, {using: how, value: what.to_s}
|
536
|
+
else
|
537
|
+
execute :find_element, {}, {using: how, value: what.to_s}
|
538
|
+
end
|
539
|
+
|
540
|
+
Element.new self, element_id_from(id)
|
541
|
+
end
|
542
|
+
|
543
|
+
def find_elements_by(how, what, parent_ref = [])
|
544
|
+
how, what = convert_locator(how, what)
|
545
|
+
|
546
|
+
return execute_atom :findElements, Support::RelativeLocator.new(what).as_json if how == 'relative'
|
547
|
+
|
548
|
+
parent_type, parent_id = parent_ref
|
549
|
+
ids = case parent_type
|
550
|
+
when :element
|
551
|
+
execute :find_child_elements, {id: parent_id}, {using: how, value: what.to_s}
|
552
|
+
when :shadow_root
|
553
|
+
execute :find_shadow_child_elements, {id: parent_id}, {using: how, value: what.to_s}
|
554
|
+
else
|
555
|
+
execute :find_elements, {}, {using: how, value: what.to_s}
|
556
|
+
end
|
557
|
+
|
558
|
+
ids.map { |id| Element.new self, element_id_from(id) }
|
559
|
+
end
|
560
|
+
|
561
|
+
def shadow_root(element)
|
562
|
+
id = execute :get_element_shadow_root, id: element
|
563
|
+
ShadowRoot.new self, shadow_root_id_from(id)
|
564
|
+
end
|
565
|
+
|
566
|
+
#
|
567
|
+
# virtual-authenticator
|
568
|
+
#
|
569
|
+
|
570
|
+
def add_virtual_authenticator(options)
|
571
|
+
authenticator_id = execute :add_virtual_authenticator, {}, options.as_json
|
572
|
+
VirtualAuthenticator.new(self, authenticator_id, options)
|
573
|
+
end
|
574
|
+
|
575
|
+
def remove_virtual_authenticator(id)
|
576
|
+
execute :remove_virtual_authenticator, {authenticatorId: id}
|
577
|
+
end
|
578
|
+
|
579
|
+
def add_credential(credential, id)
|
580
|
+
execute :add_credential, {authenticatorId: id}, credential
|
581
|
+
end
|
582
|
+
|
583
|
+
def credentials(authenticator_id)
|
584
|
+
execute :get_credentials, {authenticatorId: authenticator_id}
|
585
|
+
end
|
586
|
+
|
587
|
+
def remove_credential(credential_id, authenticator_id)
|
588
|
+
execute :remove_credential, {credentialId: credential_id, authenticatorId: authenticator_id}
|
589
|
+
end
|
590
|
+
|
591
|
+
def remove_all_credentials(authenticator_id)
|
592
|
+
execute :remove_all_credentials, {authenticatorId: authenticator_id}
|
593
|
+
end
|
594
|
+
|
595
|
+
def user_verified(verified, authenticator_id)
|
596
|
+
execute :set_user_verified, {authenticatorId: authenticator_id}, {isUserVerified: verified}
|
144
597
|
end
|
145
598
|
|
146
599
|
private
|
@@ -163,8 +616,8 @@ module Selenium
|
|
163
616
|
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
|
164
617
|
end
|
165
618
|
|
166
|
-
WebDriver.logger.
|
167
|
-
http.call(verb, path, command_hash)
|
619
|
+
WebDriver.logger.debug("-> #{verb.to_s.upcase} #{path}", id: :command)
|
620
|
+
http.call(verb, path, command_hash)['value']
|
168
621
|
end
|
169
622
|
|
170
623
|
def escaper
|
@@ -172,23 +625,75 @@ module Selenium
|
|
172
625
|
end
|
173
626
|
|
174
627
|
def commands(command)
|
175
|
-
raise NotImplementedError unless command == :new_session
|
176
|
-
|
177
628
|
COMMANDS[command]
|
178
629
|
end
|
179
630
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
631
|
+
def unwrap_script_result(arg)
|
632
|
+
case arg
|
633
|
+
when Array
|
634
|
+
arg.map { |e| unwrap_script_result(e) }
|
635
|
+
when Hash
|
636
|
+
element_id = element_id_from(arg)
|
637
|
+
return Element.new(self, element_id) if element_id
|
638
|
+
|
639
|
+
shadow_root_id = shadow_root_id_from(arg)
|
640
|
+
return ShadowRoot.new self, shadow_root_id if shadow_root_id
|
183
641
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
}
|
189
|
-
}
|
642
|
+
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
|
643
|
+
else
|
644
|
+
arg
|
645
|
+
end
|
190
646
|
end
|
191
647
|
|
648
|
+
def element_id_from(id)
|
649
|
+
id['ELEMENT'] || id[Element::ELEMENT_KEY]
|
650
|
+
end
|
651
|
+
|
652
|
+
def shadow_root_id_from(id)
|
653
|
+
id[ShadowRoot::ROOT_KEY]
|
654
|
+
end
|
655
|
+
|
656
|
+
def prepare_capabilities_payload(capabilities)
|
657
|
+
capabilities = {alwaysMatch: capabilities} if !capabilities['alwaysMatch'] && !capabilities['firstMatch']
|
658
|
+
{capabilities: capabilities}
|
659
|
+
end
|
660
|
+
|
661
|
+
def convert_locator(how, what)
|
662
|
+
how = SearchContext::FINDERS[how.to_sym] || how
|
663
|
+
|
664
|
+
case how
|
665
|
+
when 'class name'
|
666
|
+
how = 'css selector'
|
667
|
+
what = ".#{escape_css(what.to_s)}"
|
668
|
+
when 'id'
|
669
|
+
how = 'css selector'
|
670
|
+
what = "##{escape_css(what.to_s)}"
|
671
|
+
when 'name'
|
672
|
+
how = 'css selector'
|
673
|
+
what = "*[name='#{escape_css(what.to_s)}']"
|
674
|
+
end
|
675
|
+
|
676
|
+
if what.is_a?(Hash)
|
677
|
+
what = what.each_with_object({}) do |(h, w), hash|
|
678
|
+
h, w = convert_locator(h.to_s, w)
|
679
|
+
hash[h] = w
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
[how, what]
|
684
|
+
end
|
685
|
+
|
686
|
+
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
|
687
|
+
UNICODE_CODE_POINT = 30
|
688
|
+
|
689
|
+
# Escapes invalid characters in CSS selector.
|
690
|
+
# @see https://mathiasbynens.be/notes/css-escapes
|
691
|
+
def escape_css(string)
|
692
|
+
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
693
|
+
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
|
694
|
+
|
695
|
+
string
|
696
|
+
end
|
192
697
|
end # Bridge
|
193
698
|
end # Remote
|
194
699
|
end # WebDriver
|