selenium-webdriver 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +35 -1
- data/Gemfile.lock +54 -0
- data/lib/selenium/webdriver.rb +8 -10
- data/lib/selenium/webdriver/chrome.rb +2 -1
- data/lib/selenium/webdriver/chrome/{bridge.rb → driver.rb} +47 -32
- data/lib/selenium/webdriver/chrome/options.rb +170 -0
- data/lib/selenium/webdriver/common/driver.rb +20 -39
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +5 -0
- data/lib/selenium/webdriver/common/element.rb +5 -1
- data/lib/selenium/webdriver/common/logger.rb +10 -0
- data/lib/selenium/webdriver/common/w3c_error.rb +2 -2
- data/lib/selenium/webdriver/edge.rb +2 -1
- data/lib/selenium/webdriver/edge/bridge.rb +2 -91
- data/lib/selenium/webdriver/edge/driver.rb +80 -0
- data/lib/selenium/webdriver/firefox.rb +6 -3
- data/lib/selenium/webdriver/firefox/driver.rb +50 -0
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/legacy/driver.rb +81 -0
- data/lib/selenium/webdriver/firefox/marionette/driver.rb +101 -0
- data/lib/selenium/webdriver/firefox/options.rb +139 -0
- data/lib/selenium/webdriver/ie.rb +1 -1
- data/lib/selenium/webdriver/ie/{bridge.rb → driver.rb} +13 -13
- data/lib/selenium/webdriver/phantomjs.rb +1 -1
- data/lib/selenium/webdriver/phantomjs/{bridge.rb → driver.rb} +10 -13
- data/lib/selenium/webdriver/remote.rb +11 -13
- data/lib/selenium/webdriver/remote/bridge.rb +87 -586
- data/lib/selenium/webdriver/remote/capabilities.rb +3 -1
- data/lib/selenium/webdriver/remote/driver.rb +44 -0
- data/lib/selenium/webdriver/remote/http/default.rb +1 -1
- data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
- data/lib/selenium/webdriver/remote/{commands.rb → oss/commands.rb} +9 -5
- data/lib/selenium/webdriver/remote/oss/driver.rb +44 -0
- data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
- data/lib/selenium/webdriver/remote/w3c/capabilities.rb +266 -0
- data/lib/selenium/webdriver/remote/{w3c_commands.rb → w3c/commands.rb} +9 -4
- data/lib/selenium/webdriver/remote/w3c/driver.rb +41 -0
- data/lib/selenium/webdriver/safari.rb +3 -6
- data/lib/selenium/webdriver/safari/{bridge.rb → driver.rb} +14 -6
- data/selenium-webdriver.gemspec +1 -3
- metadata +29 -45
- data/lib/selenium/webdriver/firefox/bridge.rb +0 -70
- data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -114
- data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -663
- data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -231
@@ -1,70 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
5
|
-
# distributed with this work for additional information
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
8
|
-
# "License"); you may not use this file except in compliance
|
9
|
-
# with the License. You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
14
|
-
# software distributed under the License is distributed on an
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
16
|
-
# KIND, either express or implied. See the License for the
|
17
|
-
# specific language governing permissions and limitations
|
18
|
-
# under the License.
|
19
|
-
|
20
|
-
module Selenium
|
21
|
-
module WebDriver
|
22
|
-
module Firefox
|
23
|
-
# @api private
|
24
|
-
class Bridge < Remote::Bridge
|
25
|
-
def initialize(opts = {})
|
26
|
-
opts[:desired_capabilities] ||= Remote::Capabilities.firefox
|
27
|
-
|
28
|
-
if opts.key? :proxy
|
29
|
-
WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
|
30
|
-
[DEPRECATION] `:proxy` is deprecated. Pass in as capability: `Remote::Capabilities.firefox(proxy: #{opts[:proxy]})`
|
31
|
-
DEPRECATE
|
32
|
-
opts[:desired_capabilities].proxy = opts.delete(:proxy)
|
33
|
-
end
|
34
|
-
|
35
|
-
unless opts.key?(:url)
|
36
|
-
port = opts.delete(:port) || DEFAULT_PORT
|
37
|
-
profile = opts.delete(:profile)
|
38
|
-
|
39
|
-
Binary.path = opts[:desired_capabilities][:firefox_binary] if opts[:desired_capabilities][:firefox_binary]
|
40
|
-
@launcher = Launcher.new Binary.new, port, profile
|
41
|
-
@launcher.launch
|
42
|
-
opts[:url] = @launcher.url
|
43
|
-
end
|
44
|
-
|
45
|
-
begin
|
46
|
-
super(opts)
|
47
|
-
rescue
|
48
|
-
@launcher.quit if @launcher
|
49
|
-
raise
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def browser
|
54
|
-
:firefox
|
55
|
-
end
|
56
|
-
|
57
|
-
def driver_extensions
|
58
|
-
[DriverExtensions::TakesScreenshot]
|
59
|
-
end
|
60
|
-
|
61
|
-
def quit
|
62
|
-
super
|
63
|
-
nil
|
64
|
-
ensure
|
65
|
-
@launcher.quit
|
66
|
-
end
|
67
|
-
end # Bridge
|
68
|
-
end # Firefox
|
69
|
-
end # WebDriver
|
70
|
-
end # Selenium
|
@@ -1,114 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
5
|
-
# distributed with this work for additional information
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
8
|
-
# "License"); you may not use this file except in compliance
|
9
|
-
# with the License. You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
14
|
-
# software distributed under the License is distributed on an
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
16
|
-
# KIND, either express or implied. See the License for the
|
17
|
-
# specific language governing permissions and limitations
|
18
|
-
# under the License.
|
19
|
-
|
20
|
-
module Selenium
|
21
|
-
module WebDriver
|
22
|
-
module Firefox
|
23
|
-
# @api private
|
24
|
-
class W3CBridge < Remote::W3CBridge
|
25
|
-
def initialize(opts = {})
|
26
|
-
opts[:desired_capabilities] = create_capabilities(opts)
|
27
|
-
|
28
|
-
unless opts.key?(:url)
|
29
|
-
driver_path = opts.delete(:driver_path) || Firefox.driver_path
|
30
|
-
port = opts.delete(:port) || Service::DEFAULT_PORT
|
31
|
-
|
32
|
-
opts[:driver_opts] ||= {}
|
33
|
-
if opts.key? :service_args
|
34
|
-
WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
|
35
|
-
[DEPRECATION] `:service_args` is deprecated. Pass switches using `driver_opts`
|
36
|
-
DEPRECATE
|
37
|
-
opts[:driver_opts][:args] = opts.delete(:service_args)
|
38
|
-
end
|
39
|
-
|
40
|
-
@service = Service.new(driver_path, port, opts.delete(:driver_opts))
|
41
|
-
@service.start
|
42
|
-
opts[:url] = @service.uri
|
43
|
-
end
|
44
|
-
|
45
|
-
super(opts)
|
46
|
-
end
|
47
|
-
|
48
|
-
def browser
|
49
|
-
:firefox
|
50
|
-
end
|
51
|
-
|
52
|
-
def driver_extensions
|
53
|
-
[DriverExtensions::TakesScreenshot,
|
54
|
-
DriverExtensions::HasWebStorage]
|
55
|
-
end
|
56
|
-
|
57
|
-
def quit
|
58
|
-
super
|
59
|
-
ensure
|
60
|
-
@service.stop if @service
|
61
|
-
end
|
62
|
-
|
63
|
-
# Support for geckodriver < 0.15
|
64
|
-
def resize_window(width, height, handle = :current)
|
65
|
-
super
|
66
|
-
rescue Error::UnknownCommandError
|
67
|
-
execute :set_window_size, {}, {width: width, height: height}
|
68
|
-
end
|
69
|
-
|
70
|
-
def window_size(handle = :current)
|
71
|
-
data = super
|
72
|
-
rescue Error::UnknownCommandError
|
73
|
-
data = execute :get_window_size
|
74
|
-
ensure
|
75
|
-
return Dimension.new data['width'], data['height']
|
76
|
-
end
|
77
|
-
|
78
|
-
def reposition_window(x, y)
|
79
|
-
super
|
80
|
-
rescue Error::UnknownCommandError
|
81
|
-
execute :set_window_position, {}, {x: x, y: y}
|
82
|
-
end
|
83
|
-
|
84
|
-
def window_position
|
85
|
-
data = super
|
86
|
-
rescue Error::UnknownCommandError
|
87
|
-
data = execute :get_window_position
|
88
|
-
ensure
|
89
|
-
return Point.new data['x'], data['y']
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
def create_capabilities(opts)
|
95
|
-
caps = Remote::W3CCapabilities.firefox
|
96
|
-
caps.merge!(opts.delete(:desired_capabilities)) if opts.key? :desired_capabilities
|
97
|
-
firefox_options = caps[:firefox_options] || {}
|
98
|
-
firefox_options = firefox_options_caps.merge(opts[:firefox_options]) if opts.key?(:firefox_options)
|
99
|
-
if opts.key?(:profile)
|
100
|
-
profile = opts.delete(:profile)
|
101
|
-
unless profile.is_a?(Profile)
|
102
|
-
profile = Profile.from_name(profile)
|
103
|
-
end
|
104
|
-
firefox_options[:profile] = profile.encoded
|
105
|
-
end
|
106
|
-
|
107
|
-
Binary.path = firefox_options[:binary] if firefox_options.key?(:binary)
|
108
|
-
caps[:firefox_options] = firefox_options unless firefox_options.empty?
|
109
|
-
caps
|
110
|
-
end
|
111
|
-
end # W3CBridge
|
112
|
-
end # Firefox
|
113
|
-
end # WebDriver
|
114
|
-
end # Selenium
|
@@ -1,663 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
5
|
-
# distributed with this work for additional information
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
8
|
-
# "License"); you may not use this file except in compliance
|
9
|
-
# with the License. You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
14
|
-
# software distributed under the License is distributed on an
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
16
|
-
# KIND, either express or implied. See the License for the
|
17
|
-
# specific language governing permissions and limitations
|
18
|
-
# under the License.
|
19
|
-
|
20
|
-
require 'json'
|
21
|
-
|
22
|
-
module Selenium
|
23
|
-
module WebDriver
|
24
|
-
module Remote
|
25
|
-
#
|
26
|
-
# Low level bridge to the remote server, through which the rest of the API works.
|
27
|
-
#
|
28
|
-
# @api private
|
29
|
-
#
|
30
|
-
|
31
|
-
class W3CBridge
|
32
|
-
include BridgeHelper
|
33
|
-
include Atoms
|
34
|
-
|
35
|
-
attr_accessor :context, :http, :file_detector
|
36
|
-
attr_reader :capabilities
|
37
|
-
|
38
|
-
#
|
39
|
-
# Initializes the bridge with the given server URL
|
40
|
-
# @param [Hash] opts options for the driver
|
41
|
-
# @option opts [String] :url url for the remote server
|
42
|
-
# @option opts [Integer] :port port number for the remote server
|
43
|
-
# @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
|
44
|
-
# @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
|
45
|
-
#
|
46
|
-
|
47
|
-
def initialize(opts = {})
|
48
|
-
|
49
|
-
opts = opts.dup
|
50
|
-
|
51
|
-
if opts.key?(:port)
|
52
|
-
WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
|
53
|
-
[DEPRECATION] `:port` is deprecated. Use full url desired.
|
54
|
-
DEPRECATE
|
55
|
-
end
|
56
|
-
port = opts.delete(:port) || 4444
|
57
|
-
|
58
|
-
http_client = opts.delete(:http_client) { Http::Default.new }
|
59
|
-
desired_capabilities = opts.delete(:desired_capabilities) { W3CCapabilities.firefox }
|
60
|
-
url = opts.delete(:url) { "http://#{Platform.localhost}:#{port}/wd/hub" }
|
61
|
-
|
62
|
-
desired_capabilities = W3CCapabilities.send(desired_capabilities) if desired_capabilities.is_a? Symbol
|
63
|
-
|
64
|
-
desired_capabilities[:marionette] = opts.delete(:marionette) unless opts[:marionette].nil?
|
65
|
-
|
66
|
-
unless opts.empty?
|
67
|
-
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
|
68
|
-
end
|
69
|
-
|
70
|
-
uri = url.is_a?(URI) ? url : URI.parse(url)
|
71
|
-
uri.path += '/' unless uri.path =~ %r{\/$}
|
72
|
-
|
73
|
-
http_client.server_url = uri
|
74
|
-
|
75
|
-
@http = http_client
|
76
|
-
@capabilities = create_session(desired_capabilities)
|
77
|
-
@file_detector = nil
|
78
|
-
end
|
79
|
-
|
80
|
-
def browser
|
81
|
-
@browser ||= (
|
82
|
-
name = @capabilities.browser_name
|
83
|
-
name ? name.tr(' ', '_').to_sym : 'unknown'
|
84
|
-
)
|
85
|
-
end
|
86
|
-
|
87
|
-
def driver_extensions
|
88
|
-
[DriverExtensions::UploadsFiles,
|
89
|
-
DriverExtensions::TakesScreenshot,
|
90
|
-
DriverExtensions::HasSessionId,
|
91
|
-
DriverExtensions::Rotatable,
|
92
|
-
DriverExtensions::HasRemoteStatus,
|
93
|
-
DriverExtensions::HasWebStorage]
|
94
|
-
end
|
95
|
-
|
96
|
-
def commands(command)
|
97
|
-
case command
|
98
|
-
when :status, :is_element_displayed
|
99
|
-
Bridge::COMMANDS[command]
|
100
|
-
else
|
101
|
-
COMMANDS[command]
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
#
|
106
|
-
# Returns the current session ID.
|
107
|
-
#
|
108
|
-
|
109
|
-
def session_id
|
110
|
-
@session_id || raise(Error::WebDriverError, 'no current session exists')
|
111
|
-
end
|
112
|
-
|
113
|
-
def create_session(desired_capabilities)
|
114
|
-
resp = execute :new_session, {}, {desiredCapabilities: desired_capabilities}
|
115
|
-
@session_id = resp['sessionId']
|
116
|
-
return W3CCapabilities.json_create(resp['capabilities'] || resp['value']) if @session_id
|
117
|
-
|
118
|
-
raise Error::WebDriverError, 'no sessionId in returned payload'
|
119
|
-
end
|
120
|
-
|
121
|
-
def status
|
122
|
-
execute :status
|
123
|
-
end
|
124
|
-
|
125
|
-
def get(url)
|
126
|
-
execute :get, {}, {url: url}
|
127
|
-
end
|
128
|
-
|
129
|
-
def implicit_wait_timeout=(milliseconds)
|
130
|
-
timeout('implicit', milliseconds)
|
131
|
-
end
|
132
|
-
|
133
|
-
def script_timeout=(milliseconds)
|
134
|
-
timeout('script', milliseconds)
|
135
|
-
end
|
136
|
-
|
137
|
-
def timeout(type, milliseconds)
|
138
|
-
type = 'pageLoad' if type == 'page load'
|
139
|
-
execute :set_timeout, {}, {type => milliseconds}
|
140
|
-
end
|
141
|
-
|
142
|
-
#
|
143
|
-
# alerts
|
144
|
-
#
|
145
|
-
|
146
|
-
def accept_alert
|
147
|
-
execute :accept_alert
|
148
|
-
end
|
149
|
-
|
150
|
-
def dismiss_alert
|
151
|
-
execute :dismiss_alert
|
152
|
-
end
|
153
|
-
|
154
|
-
def alert=(keys)
|
155
|
-
execute :send_alert_text, {}, {value: keys.split(//), text: keys}
|
156
|
-
end
|
157
|
-
|
158
|
-
def alert_text
|
159
|
-
execute :get_alert_text
|
160
|
-
end
|
161
|
-
|
162
|
-
#
|
163
|
-
# navigation
|
164
|
-
#
|
165
|
-
|
166
|
-
def go_back
|
167
|
-
execute :back
|
168
|
-
end
|
169
|
-
|
170
|
-
def go_forward
|
171
|
-
execute :forward
|
172
|
-
end
|
173
|
-
|
174
|
-
def url
|
175
|
-
execute :get_current_url
|
176
|
-
end
|
177
|
-
|
178
|
-
def title
|
179
|
-
execute :get_title
|
180
|
-
end
|
181
|
-
|
182
|
-
def page_source
|
183
|
-
execute_script('var source = document.documentElement.outerHTML;' \
|
184
|
-
'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
|
185
|
-
'return source;')
|
186
|
-
end
|
187
|
-
|
188
|
-
def switch_to_window(name)
|
189
|
-
execute :switch_to_window, {}, {handle: name}
|
190
|
-
end
|
191
|
-
|
192
|
-
def switch_to_frame(id)
|
193
|
-
id = find_element_by('id', id) if id.is_a? String
|
194
|
-
execute :switch_to_frame, {}, {id: id}
|
195
|
-
end
|
196
|
-
|
197
|
-
def switch_to_parent_frame
|
198
|
-
execute :switch_to_parent_frame
|
199
|
-
end
|
200
|
-
|
201
|
-
def switch_to_default_content
|
202
|
-
switch_to_frame nil
|
203
|
-
end
|
204
|
-
|
205
|
-
QUIT_ERRORS = [IOError].freeze
|
206
|
-
|
207
|
-
def quit
|
208
|
-
execute :delete_session
|
209
|
-
http.close
|
210
|
-
rescue *QUIT_ERRORS
|
211
|
-
end
|
212
|
-
|
213
|
-
def close
|
214
|
-
execute :close_window
|
215
|
-
end
|
216
|
-
|
217
|
-
def refresh
|
218
|
-
execute :refresh
|
219
|
-
end
|
220
|
-
|
221
|
-
#
|
222
|
-
# window handling
|
223
|
-
#
|
224
|
-
|
225
|
-
def window_handles
|
226
|
-
execute :get_window_handles
|
227
|
-
end
|
228
|
-
|
229
|
-
def window_handle
|
230
|
-
execute :get_window_handle
|
231
|
-
end
|
232
|
-
|
233
|
-
def resize_window(width, height, handle = :current)
|
234
|
-
unless handle == :current
|
235
|
-
raise Error::WebDriverError, 'Switch to desired window before changing its size'
|
236
|
-
end
|
237
|
-
execute :set_window_rect, {}, {width: width, height: height}
|
238
|
-
end
|
239
|
-
|
240
|
-
def window_size(handle = :current)
|
241
|
-
unless handle == :current
|
242
|
-
raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size'
|
243
|
-
end
|
244
|
-
data = execute :get_window_rect
|
245
|
-
|
246
|
-
Dimension.new data['width'], data['height']
|
247
|
-
end
|
248
|
-
|
249
|
-
def minimize_window
|
250
|
-
execute :minimize_window
|
251
|
-
end
|
252
|
-
|
253
|
-
def maximize_window(handle = :current)
|
254
|
-
unless handle == :current
|
255
|
-
raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size'
|
256
|
-
end
|
257
|
-
execute :maximize_window
|
258
|
-
end
|
259
|
-
|
260
|
-
def full_screen_window
|
261
|
-
execute :fullscreen_window
|
262
|
-
end
|
263
|
-
|
264
|
-
def reposition_window(x, y)
|
265
|
-
execute :set_window_rect, {}, {x: x, y: y}
|
266
|
-
end
|
267
|
-
|
268
|
-
def window_position
|
269
|
-
data = execute :get_window_rect
|
270
|
-
Point.new data['x'], data['y']
|
271
|
-
end
|
272
|
-
|
273
|
-
def set_window_rect(x: nil, y: nil, width: nil, height: nil)
|
274
|
-
execute :set_window_rect, {}, {x: x, y: y, width: width, height: height}
|
275
|
-
end
|
276
|
-
|
277
|
-
def window_rect
|
278
|
-
data = execute :get_window_rect
|
279
|
-
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
280
|
-
end
|
281
|
-
|
282
|
-
def screenshot
|
283
|
-
execute :take_screenshot
|
284
|
-
end
|
285
|
-
|
286
|
-
#
|
287
|
-
# HTML 5
|
288
|
-
#
|
289
|
-
|
290
|
-
def local_storage_item(key, value = nil)
|
291
|
-
if value
|
292
|
-
execute_script("localStorage.setItem('#{key}', '#{value}')")
|
293
|
-
else
|
294
|
-
execute_script("return localStorage.getItem('#{key}')")
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
def remove_local_storage_item(key)
|
299
|
-
execute_script("localStorage.removeItem('#{key}')")
|
300
|
-
end
|
301
|
-
|
302
|
-
def local_storage_keys
|
303
|
-
execute_script('return Object.keys(localStorage)')
|
304
|
-
end
|
305
|
-
|
306
|
-
def clear_local_storage
|
307
|
-
execute_script('localStorage.clear()')
|
308
|
-
end
|
309
|
-
|
310
|
-
def local_storage_size
|
311
|
-
execute_script('return localStorage.length')
|
312
|
-
end
|
313
|
-
|
314
|
-
def session_storage_item(key, value = nil)
|
315
|
-
if value
|
316
|
-
execute_script("sessionStorage.setItem('#{key}', '#{value}')")
|
317
|
-
else
|
318
|
-
execute_script("return sessionStorage.getItem('#{key}')")
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
def remove_session_storage_item(key)
|
323
|
-
execute_script("sessionStorage.removeItem('#{key}')")
|
324
|
-
end
|
325
|
-
|
326
|
-
def session_storage_keys
|
327
|
-
execute_script('return Object.keys(sessionStorage)')
|
328
|
-
end
|
329
|
-
|
330
|
-
def clear_session_storage
|
331
|
-
execute_script('sessionStorage.clear()')
|
332
|
-
end
|
333
|
-
|
334
|
-
def session_storage_size
|
335
|
-
execute_script('return sessionStorage.length')
|
336
|
-
end
|
337
|
-
|
338
|
-
def location
|
339
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
|
340
|
-
end
|
341
|
-
|
342
|
-
def set_location(_lat, _lon, _alt)
|
343
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
|
344
|
-
end
|
345
|
-
|
346
|
-
def network_connection
|
347
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
|
348
|
-
end
|
349
|
-
|
350
|
-
def network_connection=(_type)
|
351
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
|
352
|
-
end
|
353
|
-
|
354
|
-
#
|
355
|
-
# javascript execution
|
356
|
-
#
|
357
|
-
|
358
|
-
def execute_script(script, *args)
|
359
|
-
result = execute :execute_script, {}, {script: script, args: args}
|
360
|
-
unwrap_script_result result
|
361
|
-
end
|
362
|
-
|
363
|
-
def execute_async_script(script, *args)
|
364
|
-
result = execute :execute_async_script, {}, {script: script, args: args}
|
365
|
-
unwrap_script_result result
|
366
|
-
end
|
367
|
-
|
368
|
-
#
|
369
|
-
# cookies
|
370
|
-
#
|
371
|
-
|
372
|
-
def options
|
373
|
-
@options ||= WebDriver::W3COptions.new(self)
|
374
|
-
end
|
375
|
-
|
376
|
-
def add_cookie(cookie)
|
377
|
-
execute :add_cookie, {}, {cookie: cookie}
|
378
|
-
end
|
379
|
-
|
380
|
-
def delete_cookie(name)
|
381
|
-
execute :delete_cookie, name: name
|
382
|
-
end
|
383
|
-
|
384
|
-
def cookie(name)
|
385
|
-
execute :get_cookie, name: name
|
386
|
-
end
|
387
|
-
|
388
|
-
def cookies
|
389
|
-
execute :get_all_cookies
|
390
|
-
end
|
391
|
-
|
392
|
-
def delete_all_cookies
|
393
|
-
execute :delete_all_cookies
|
394
|
-
end
|
395
|
-
|
396
|
-
#
|
397
|
-
# actions
|
398
|
-
#
|
399
|
-
|
400
|
-
def action(async = false)
|
401
|
-
W3CActionBuilder.new self,
|
402
|
-
Interactions.pointer(:mouse, name: 'mouse'),
|
403
|
-
Interactions.key('keyboard'),
|
404
|
-
async
|
405
|
-
end
|
406
|
-
alias_method :actions, :action
|
407
|
-
|
408
|
-
def mouse
|
409
|
-
raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
|
410
|
-
end
|
411
|
-
|
412
|
-
def keyboard
|
413
|
-
raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
|
414
|
-
end
|
415
|
-
|
416
|
-
def send_actions(data)
|
417
|
-
execute :actions, {}, {actions: data}
|
418
|
-
end
|
419
|
-
|
420
|
-
def release_actions
|
421
|
-
execute :release_actions
|
422
|
-
end
|
423
|
-
|
424
|
-
def click_element(element)
|
425
|
-
execute :element_click, id: element
|
426
|
-
end
|
427
|
-
|
428
|
-
# TODO: - Implement file verification
|
429
|
-
def send_keys_to_element(element, keys)
|
430
|
-
# Keep .split(//) for backward compatibility for now
|
431
|
-
text = keys.join('')
|
432
|
-
execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
|
433
|
-
end
|
434
|
-
|
435
|
-
def clear_element(element)
|
436
|
-
execute :element_clear, id: element
|
437
|
-
end
|
438
|
-
|
439
|
-
def submit_element(element)
|
440
|
-
form = find_element_by('xpath', "./ancestor-or-self::form", element)
|
441
|
-
execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
|
442
|
-
"e.initEvent('submit', true, true);" \
|
443
|
-
'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
|
444
|
-
end
|
445
|
-
|
446
|
-
def drag_element(element, right_by, down_by)
|
447
|
-
execute :drag_element, {id: element}, {x: right_by, y: down_by}
|
448
|
-
end
|
449
|
-
|
450
|
-
def touch_single_tap(element)
|
451
|
-
execute :touch_single_tap, {}, {element: element}
|
452
|
-
end
|
453
|
-
|
454
|
-
def touch_double_tap(element)
|
455
|
-
execute :touch_double_tap, {}, {element: element}
|
456
|
-
end
|
457
|
-
|
458
|
-
def touch_long_press(element)
|
459
|
-
execute :touch_long_press, {}, {element: element}
|
460
|
-
end
|
461
|
-
|
462
|
-
def touch_down(x, y)
|
463
|
-
execute :touch_down, {}, {x: x, y: y}
|
464
|
-
end
|
465
|
-
|
466
|
-
def touch_up(x, y)
|
467
|
-
execute :touch_up, {}, {x: x, y: y}
|
468
|
-
end
|
469
|
-
|
470
|
-
def touch_move(x, y)
|
471
|
-
execute :touch_move, {}, {x: x, y: y}
|
472
|
-
end
|
473
|
-
|
474
|
-
def touch_scroll(element, x, y)
|
475
|
-
if element
|
476
|
-
execute :touch_scroll, {}, {element: element,
|
477
|
-
xoffset: x,
|
478
|
-
yoffset: y}
|
479
|
-
else
|
480
|
-
execute :touch_scroll, {}, {xoffset: x, yoffset: y}
|
481
|
-
end
|
482
|
-
end
|
483
|
-
|
484
|
-
def touch_flick(xspeed, yspeed)
|
485
|
-
execute :touch_flick, {}, {xspeed: xspeed, yspeed: yspeed}
|
486
|
-
end
|
487
|
-
|
488
|
-
def touch_element_flick(element, right_by, down_by, speed)
|
489
|
-
execute :touch_flick, {}, {element: element,
|
490
|
-
xoffset: right_by,
|
491
|
-
yoffset: down_by,
|
492
|
-
speed: speed}
|
493
|
-
end
|
494
|
-
|
495
|
-
def screen_orientation=(orientation)
|
496
|
-
execute :set_screen_orientation, {}, {orientation: orientation}
|
497
|
-
end
|
498
|
-
|
499
|
-
def screen_orientation
|
500
|
-
execute :get_screen_orientation
|
501
|
-
end
|
502
|
-
|
503
|
-
#
|
504
|
-
# element properties
|
505
|
-
#
|
506
|
-
|
507
|
-
def element_tag_name(element)
|
508
|
-
execute :get_element_tag_name, id: element
|
509
|
-
end
|
510
|
-
|
511
|
-
def element_attribute(element, name)
|
512
|
-
execute_atom :getAttribute, element, name
|
513
|
-
end
|
514
|
-
|
515
|
-
def element_property(element, name)
|
516
|
-
execute :get_element_property, id: element.ref, name: name
|
517
|
-
end
|
518
|
-
|
519
|
-
def element_value(element)
|
520
|
-
element_property element, 'value'
|
521
|
-
end
|
522
|
-
|
523
|
-
def element_text(element)
|
524
|
-
execute :get_element_text, id: element
|
525
|
-
end
|
526
|
-
|
527
|
-
def element_location(element)
|
528
|
-
data = execute :get_element_rect, id: element
|
529
|
-
|
530
|
-
Point.new data['x'], data['y']
|
531
|
-
end
|
532
|
-
|
533
|
-
def element_rect(element)
|
534
|
-
data = execute :get_element_rect, id: element
|
535
|
-
|
536
|
-
Rectangle.new data['x'], data['y'], data['width'], data['height']
|
537
|
-
end
|
538
|
-
|
539
|
-
def element_location_once_scrolled_into_view(element)
|
540
|
-
send_keys_to_element(element, [''])
|
541
|
-
element_location(element)
|
542
|
-
end
|
543
|
-
|
544
|
-
def element_size(element)
|
545
|
-
data = execute :get_element_rect, id: element
|
546
|
-
|
547
|
-
Dimension.new data['width'], data['height']
|
548
|
-
end
|
549
|
-
|
550
|
-
def element_enabled?(element)
|
551
|
-
execute :is_element_enabled, id: element
|
552
|
-
end
|
553
|
-
|
554
|
-
def element_selected?(element)
|
555
|
-
execute :is_element_selected, id: element
|
556
|
-
end
|
557
|
-
|
558
|
-
def element_displayed?(element)
|
559
|
-
execute :is_element_displayed, id: element
|
560
|
-
end
|
561
|
-
|
562
|
-
def element_value_of_css_property(element, prop)
|
563
|
-
execute :get_element_css_value, id: element, property_name: prop
|
564
|
-
end
|
565
|
-
|
566
|
-
#
|
567
|
-
# finding elements
|
568
|
-
#
|
569
|
-
|
570
|
-
def active_element
|
571
|
-
Element.new self, element_id_from(execute(:get_active_element))
|
572
|
-
end
|
573
|
-
|
574
|
-
alias_method :switch_to_active_element, :active_element
|
575
|
-
|
576
|
-
def find_element_by(how, what, parent = nil)
|
577
|
-
how, what = convert_locators(how, what)
|
578
|
-
|
579
|
-
id = if parent
|
580
|
-
execute :find_child_element, {id: parent}, {using: how, value: what}
|
581
|
-
else
|
582
|
-
execute :find_element, {}, {using: how, value: what}
|
583
|
-
end
|
584
|
-
Element.new self, element_id_from(id)
|
585
|
-
end
|
586
|
-
|
587
|
-
def find_elements_by(how, what, parent = nil)
|
588
|
-
how, what = convert_locators(how, what)
|
589
|
-
|
590
|
-
ids = if parent
|
591
|
-
execute :find_child_elements, {id: parent}, {using: how, value: what}
|
592
|
-
else
|
593
|
-
execute :find_elements, {}, {using: how, value: what}
|
594
|
-
end
|
595
|
-
|
596
|
-
ids.map { |id| Element.new self, element_id_from(id) }
|
597
|
-
end
|
598
|
-
|
599
|
-
private
|
600
|
-
|
601
|
-
def convert_locators(how, what)
|
602
|
-
case how
|
603
|
-
when 'class name'
|
604
|
-
how = 'css selector'
|
605
|
-
what = ".#{escape_css(what)}"
|
606
|
-
when 'id'
|
607
|
-
how = 'css selector'
|
608
|
-
what = "##{escape_css(what)}"
|
609
|
-
when 'name'
|
610
|
-
how = 'css selector'
|
611
|
-
what = "*[name='#{escape_css(what)}']"
|
612
|
-
when 'tag name'
|
613
|
-
how = 'css selector'
|
614
|
-
end
|
615
|
-
[how, what]
|
616
|
-
end
|
617
|
-
|
618
|
-
#
|
619
|
-
# executes a command on the remote server.
|
620
|
-
#
|
621
|
-
#
|
622
|
-
# @return [WebDriver::Remote::Response]
|
623
|
-
#
|
624
|
-
|
625
|
-
def execute(command, opts = {}, command_hash = nil)
|
626
|
-
verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
|
627
|
-
path = path.dup
|
628
|
-
|
629
|
-
path[':session_id'] = @session_id if path.include?(':session_id')
|
630
|
-
|
631
|
-
begin
|
632
|
-
opts.each do |key, value|
|
633
|
-
path[key.inspect] = escaper.escape(value.to_s)
|
634
|
-
end
|
635
|
-
rescue IndexError
|
636
|
-
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
|
637
|
-
end
|
638
|
-
|
639
|
-
WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
|
640
|
-
http.call(verb, path, command_hash)['value']
|
641
|
-
end
|
642
|
-
|
643
|
-
def escaper
|
644
|
-
@escaper ||= defined?(URI::Parser) ? URI::Parser.new : URI
|
645
|
-
end
|
646
|
-
|
647
|
-
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/
|
648
|
-
UNICODE_CODE_POINT = 30
|
649
|
-
|
650
|
-
# Escapes invalid characters in CSS selector.
|
651
|
-
# @see https://mathiasbynens.be/notes/css-escapes
|
652
|
-
def escape_css(string)
|
653
|
-
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
654
|
-
if !string.empty? && string[0] =~ /[[:digit:]]/
|
655
|
-
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}"
|
656
|
-
end
|
657
|
-
|
658
|
-
string
|
659
|
-
end
|
660
|
-
end # W3CBridge
|
661
|
-
end # Remote
|
662
|
-
end # WebDriver
|
663
|
-
end # Selenium
|