selenium-webdriver 2.47.1 → 2.48.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/CHANGES +18 -0
  2. data/Gemfile.lock +1 -1
  3. data/lib/selenium/client/base.rb +3 -3
  4. data/lib/selenium/client/extensions.rb +15 -18
  5. data/lib/selenium/client/idiomatic.rb +26 -26
  6. data/lib/selenium/client/javascript_expression_builder.rb +8 -8
  7. data/lib/selenium/webdriver/common.rb +1 -1
  8. data/lib/selenium/webdriver/common/driver.rb +5 -1
  9. data/lib/selenium/webdriver/common/error.rb +5 -8
  10. data/lib/selenium/webdriver/common/search_context.rb +6 -0
  11. data/lib/selenium/webdriver/common/target_locator.rb +4 -0
  12. data/lib/selenium/webdriver/common/w3c_error.rb +194 -0
  13. data/lib/selenium/webdriver/firefox.rb +3 -0
  14. data/lib/selenium/webdriver/firefox/binary.rb +13 -0
  15. data/lib/selenium/webdriver/firefox/extension/prefs.json +2 -1
  16. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  17. data/lib/selenium/webdriver/firefox/service.rb +120 -0
  18. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +97 -0
  19. data/lib/selenium/webdriver/remote.rb +3 -0
  20. data/lib/selenium/webdriver/remote/bridge.rb +17 -10
  21. data/lib/selenium/webdriver/remote/capabilities.rb +1 -2
  22. data/lib/selenium/webdriver/remote/commands.rb +5 -0
  23. data/lib/selenium/webdriver/remote/response.rb +5 -6
  24. data/lib/selenium/webdriver/remote/w3c_bridge.rb +676 -0
  25. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +208 -0
  26. data/lib/selenium/webdriver/remote/w3c_commands.rb +133 -0
  27. data/lib/selenium/webdriver/safari/resources/client.js +653 -629
  28. data/selenium-webdriver.gemspec +1 -1
  29. metadata +8 -4
  30. data/lib/selenium/webdriver/common/core_ext/string.rb +0 -24
  31. data/lib/selenium/webdriver/safari/resources/SafariDriver.safariextz +0 -0
@@ -28,6 +28,9 @@ require 'selenium/webdriver/firefox/profiles_ini'
28
28
  require 'selenium/webdriver/firefox/profile'
29
29
  require 'selenium/webdriver/firefox/launcher'
30
30
  require 'selenium/webdriver/firefox/bridge'
31
+ require 'selenium/webdriver/firefox/w3c_bridge'
32
+ require 'selenium/webdriver/firefox/binary'
33
+ require 'selenium/webdriver/firefox/service'
31
34
 
32
35
  module Selenium
33
36
  module WebDriver
@@ -149,6 +149,19 @@ module Selenium
149
149
  @path
150
150
  end
151
151
 
152
+ def version
153
+ @version = case Platform.os
154
+ when :macosx
155
+ `#{path} -v`.strip[/[^\s]*$/][/^\d+/].to_i
156
+ when :windows
157
+ `\"#{path}\" -v | more`.strip[/[^\s]*$/][/^\d+/].to_i
158
+ when :linux
159
+ `#{path} -v`.strip[/[^\s]*$/][/^\d+/].to_i
160
+ else
161
+ 0
162
+ end
163
+ end
164
+
152
165
  private
153
166
 
154
167
  def windows_path
@@ -52,7 +52,8 @@
52
52
  "toolkit.networkmanager.disable": true,
53
53
  "toolkit.telemetry.prompted": 2,
54
54
  "toolkit.telemetry.enabled": false,
55
- "toolkit.telemetry.rejected": true
55
+ "toolkit.telemetry.rejected": true,
56
+ "xpinstall.signatures.required": false
56
57
  },
57
58
  "mutable": {
58
59
  "browser.dom.window.dump.enabled": true,
@@ -0,0 +1,120 @@
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
+
24
+ #
25
+ # @api private
26
+ #
27
+ class Service
28
+ START_TIMEOUT = 20
29
+ SOCKET_LOCK_TIMEOUT = 45
30
+ STOP_TIMEOUT = 5
31
+ DEFAULT_PORT = 4444
32
+ MISSING_TEXT = "Unable to find Mozilla Wires. Please download the executable from https://github.com/jgraham/wires/releases"
33
+
34
+ def self.executable_path
35
+ @executable_path ||= (
36
+ path = Platform.find_binary "wires"
37
+ path or raise Error::WebDriverError, MISSING_TEXT
38
+ Platform.assert_executable path
39
+
40
+ path
41
+ )
42
+ end
43
+
44
+ def self.executable_path=(path)
45
+ Platform.assert_executable path
46
+ @executable_path = path
47
+ end
48
+
49
+ def self.default_service(*extra_args)
50
+ new executable_path, DEFAULT_PORT, *extra_args
51
+ end
52
+
53
+ def initialize(executable_path, port, *extra_args)
54
+ @executable_path = executable_path
55
+ @host = Platform.localhost
56
+ @port = Integer(port)
57
+
58
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
59
+
60
+ @extra_args = extra_args
61
+ end
62
+
63
+ def start
64
+ Platform.exit_hook { stop } # make sure we don't leave the server running
65
+
66
+ socket_lock.locked do
67
+ find_free_port
68
+ start_process
69
+ connect_until_stable
70
+ end
71
+ end
72
+
73
+ def stop
74
+ return if @process.nil? || @process.exited?
75
+
76
+ Net::HTTP.start(@host, @port) do |http|
77
+ http.open_timeout = STOP_TIMEOUT / 2
78
+ http.read_timeout = STOP_TIMEOUT / 2
79
+
80
+ http.head("/shutdown")
81
+ end
82
+
83
+ @process.poll_for_exit STOP_TIMEOUT
84
+ rescue ChildProcess::TimeoutError
85
+ # ok, force quit
86
+ @process.stop STOP_TIMEOUT
87
+ end
88
+
89
+ def uri
90
+ URI.parse "http://#{@host}:#{@port}"
91
+ end
92
+
93
+ def find_free_port
94
+ @port = PortProber.above @port
95
+ end
96
+
97
+ def start_process
98
+ server_command = [@executable_path, "--binary=#{Firefox::Binary.path}", "--webdriver-port=#{@port}", *@extra_args]
99
+ @process = ChildProcess.build(*server_command)
100
+
101
+ @process.io.inherit! if $DEBUG || Platform.os == :windows
102
+ @process.start
103
+ end
104
+
105
+ def connect_until_stable
106
+ @socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
107
+
108
+ unless @socket_poller.connected?
109
+ raise Error::WebDriverError, "unable to connect to Mozilla Wires #{@host}:#{@port}"
110
+ end
111
+ end
112
+
113
+ def socket_lock
114
+ @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
115
+ end
116
+
117
+ end # Service
118
+ end # Firefox
119
+ end # WebDriver
120
+ end # Service
@@ -0,0 +1,97 @@
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
+
24
+ # @api private
25
+ class W3CBridge < Remote::W3CBridge
26
+
27
+ def initialize(opts = {})
28
+ http_client = opts.delete(:http_client)
29
+
30
+ if opts.has_key?(:url)
31
+ url = opts.delete(:url)
32
+ else
33
+ @service = Service.default_service(*extract_service_args(opts))
34
+
35
+ if @service.instance_variable_get("@host") == "127.0.0.1"
36
+ @service.instance_variable_set("@host", 'localhost')
37
+ end
38
+
39
+ @service.start
40
+
41
+ url = @service.uri
42
+ end
43
+
44
+ caps = create_capabilities(opts)
45
+
46
+ remote_opts = {
47
+ :url => url,
48
+ :desired_capabilities => caps
49
+ }
50
+
51
+ remote_opts.merge!(:http_client => http_client) if http_client
52
+ super(remote_opts)
53
+ end
54
+
55
+ def browser
56
+ :firefox
57
+ end
58
+
59
+ def driver_extensions
60
+ [
61
+ DriverExtensions::TakesScreenshot,
62
+ DriverExtensions::HasInputDevices
63
+ ]
64
+ end
65
+
66
+ def quit
67
+ super
68
+ ensure
69
+ @service.stop if @service
70
+ end
71
+
72
+ private
73
+
74
+ def create_capabilities(opts)
75
+ caps = opts.delete(:desired_capabilities) { Remote::W3CCapabilities.firefox }
76
+
77
+ unless opts.empty?
78
+ raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
79
+ end
80
+
81
+ caps
82
+ end
83
+
84
+ def extract_service_args(opts)
85
+ args = []
86
+
87
+ if opts.has_key?(:service_log_path)
88
+ args << "--log-path=#{opts.delete(:service_log_path)}"
89
+ end
90
+
91
+ args
92
+ end
93
+
94
+ end # W3CBridge
95
+ end # Firefox
96
+ end # WebDriver
97
+ end # Selenium
@@ -20,10 +20,13 @@
20
20
  require 'uri'
21
21
 
22
22
  require 'selenium/webdriver/remote/capabilities'
23
+ require 'selenium/webdriver/remote/w3c_capabilities'
23
24
  require 'selenium/webdriver/remote/bridge'
25
+ require 'selenium/webdriver/remote/w3c_bridge'
24
26
  require 'selenium/webdriver/remote/server_error'
25
27
  require 'selenium/webdriver/remote/response'
26
28
  require 'selenium/webdriver/remote/commands'
29
+ require 'selenium/webdriver/remote/w3c_commands'
27
30
  require 'selenium/webdriver/remote/http/common'
28
31
  require 'selenium/webdriver/remote/http/default'
29
32
 
@@ -21,8 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  module Remote
23
23
 
24
- COMMANDS = {}
25
-
26
24
  #
27
25
  # Low level bridge to the remote server, through which the rest of the API works.
28
26
  #
@@ -32,6 +30,8 @@ module Selenium
32
30
  class Bridge
33
31
  include BridgeHelper
34
32
 
33
+ COMMANDS = {}
34
+
35
35
  #
36
36
  # Defines a wrapper method for a command, which ultimately calls #execute.
37
37
  #
@@ -158,19 +158,23 @@ module Selenium
158
158
  end
159
159
 
160
160
  def acceptAlert
161
- execute :acceptAlert
161
+ command = :acceptAlert
162
+ execute command
162
163
  end
163
164
 
164
165
  def dismissAlert
165
- execute :dismissAlert
166
+ command = :dismissAlert
167
+ execute command
166
168
  end
167
169
 
168
170
  def setAlertValue(keys)
169
- execute :setAlertValue, {}, :text => keys.to_s
171
+ command = capabilities.browser_name == 'MicrosoftEdge' ? :setAlertValueW3C : :setAlertValue
172
+ execute command, {}, :text => keys.to_s
170
173
  end
171
174
 
172
175
  def getAlertText
173
- execute :getAlertText
176
+ command = :getAlertText
177
+ execute command
174
178
  end
175
179
 
176
180
  #
@@ -206,7 +210,11 @@ module Selenium
206
210
  end
207
211
 
208
212
  def switchToWindow(name)
209
- execute :switchToWindow, {}, :name => name
213
+ if capabilities.browser_name == 'MicrosoftEdge'
214
+ execute :switchToWindow, {}, :handle => name
215
+ else
216
+ execute :switchToWindow, {}, :name => name
217
+ end
210
218
  end
211
219
 
212
220
  def switchToFrame(id)
@@ -619,9 +627,8 @@ module Selenium
619
627
  private
620
628
 
621
629
  def assert_javascript_enabled
622
- unless capabilities.javascript_enabled?
623
- raise Error::UnsupportedOperationError, "underlying webdriver instance does not support javascript"
624
- end
630
+ return if capabilities.browser_name == 'MicrosoftEdge' || capabilities.javascript_enabled?
631
+ raise Error::UnsupportedOperationError, "underlying webdriver instance does not support javascript"
625
632
  end
626
633
 
627
634
  #
@@ -80,9 +80,8 @@ module Selenium
80
80
 
81
81
  def edge(opts = {})
82
82
  new({
83
- :browser_name => "edge",
83
+ :browser_name => "MicrosoftEdge",
84
84
  :platform => :windows,
85
- :javascript_enabled => true
86
85
  }.merge(opts))
87
86
  end
88
87
 
@@ -78,6 +78,11 @@ class Selenium::WebDriver::Remote::Bridge
78
78
  command :getAlertText, :get, "session/:session_id/alert_text"
79
79
  command :setAlertValue, :post, "session/:session_id/alert_text"
80
80
 
81
+ command :dismissAlertW3C, :post, "session/:session_id/alert/dismiss"
82
+ command :acceptAlertW3C, :post, "session/:session_id/alert/accept"
83
+ command :getAlertTextW3C, :get, "session/:session_id/alert/text"
84
+ command :setAlertValueW3C, :post, "session/:session_id/alert/text"
85
+
81
86
  #
82
87
  # target locator
83
88
  #
@@ -52,13 +52,12 @@ module Selenium
52
52
  msg = val['message'] or return "unknown error"
53
53
  msg << ": #{val['alert']['text'].inspect}" if val['alert'].kind_of?(Hash) && val['alert']['text']
54
54
  msg << " (#{ val['class'] })" if val['class']
55
+ msg
55
56
  when String
56
- msg = val
57
+ val
57
58
  else
58
- msg = "unknown error, status=#{status}: #{val.inspect}"
59
+ "unknown error, status=#{status}: #{val.inspect}"
59
60
  end
60
-
61
- msg
62
61
  end
63
62
 
64
63
  def [](key)
@@ -104,11 +103,11 @@ module Selenium
104
103
  end
105
104
 
106
105
  def status
107
- @payload['status']
106
+ @payload['status'] || @payload['error']
108
107
  end
109
108
 
110
109
  def value
111
- @payload['value']
110
+ @payload['value'] || @payload['message']
112
111
  end
113
112
 
114
113
  end # Response
@@ -0,0 +1,676 @@
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 Remote
23
+
24
+ #
25
+ # Low level bridge to the remote server, through which the rest of the API works.
26
+ #
27
+ # @api private
28
+ #
29
+
30
+ class W3CBridge
31
+ include BridgeHelper
32
+
33
+ COMMANDS = {}
34
+
35
+ #
36
+ # Defines a wrapper method for a command, which ultimately calls #execute.
37
+ #
38
+ # @param name [Symbol]
39
+ # name of the resulting method
40
+ # @param verb [Symbol]
41
+ # the appropriate http verb, such as :get, :post, or :delete
42
+ # @param url [String]
43
+ # a URL template, which can include some arguments, much like the definitions on the server.
44
+ # the :session_id parameter is implicitly handled, but the remainder will become required method arguments.
45
+ #
46
+
47
+ def self.command(name, verb, url)
48
+ COMMANDS[name] = [verb, url.freeze]
49
+ end
50
+
51
+ attr_accessor :context, :http, :file_detector
52
+ attr_reader :capabilities
53
+
54
+ #
55
+ # Initializes the bridge with the given server URL.
56
+ #
57
+ # @param url [String] url for the remote server
58
+ # @param http_client [Object] an HTTP client instance that implements the same protocol as Http::Default
59
+ # @param desired_capabilities [Capabilities] an instance of Remote::Capabilities describing the capabilities you want
60
+ #
61
+
62
+ def initialize(opts = {})
63
+ opts = opts.dup
64
+
65
+ http_client = opts.delete(:http_client) { Http::Default.new }
66
+ desired_capabilities = opts.delete(:desired_capabilities) { W3CCapabilities.firefox }
67
+ url = opts.delete(:url) { "http://#{Platform.localhost}:4444/wd/hub" }
68
+
69
+ unless opts.empty?
70
+ raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
71
+ end
72
+
73
+ if desired_capabilities.kind_of?(Symbol)
74
+ unless W3CCapabilities.respond_to?(desired_capabilities)
75
+ raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
76
+ end
77
+
78
+ desired_capabilities = W3CCapabilities.send(desired_capabilities)
79
+ end
80
+
81
+ uri = url.kind_of?(URI) ? url : URI.parse(url)
82
+ uri.path += "/" unless uri.path =~ /\/$/
83
+
84
+ http_client.server_url = uri
85
+
86
+ @http = http_client
87
+ @capabilities = create_session(desired_capabilities)
88
+
89
+ @file_detector = nil
90
+ end
91
+
92
+ def browser
93
+ @browser ||= (
94
+ name = @capabilities.browser_name
95
+ name ? name.gsub(" ", "_").to_sym : 'unknown'
96
+ )
97
+ end
98
+
99
+ def driver_extensions
100
+ [
101
+ DriverExtensions::HasInputDevices,
102
+ DriverExtensions::UploadsFiles,
103
+ DriverExtensions::TakesScreenshot,
104
+ DriverExtensions::HasSessionId,
105
+ DriverExtensions::Rotatable,
106
+ DriverExtensions::HasTouchScreen,
107
+ DriverExtensions::HasLocation,
108
+ DriverExtensions::HasNetworkConnection,
109
+ DriverExtensions::HasRemoteStatus
110
+ ]
111
+ end
112
+
113
+ #
114
+ # Returns the current session ID.
115
+ #
116
+
117
+ def session_id
118
+ @session_id || raise(Error::WebDriverError, "no current session exists")
119
+ end
120
+
121
+ def create_session(desired_capabilities)
122
+ resp = raw_execute :newSession, {}, :desiredCapabilities => desired_capabilities
123
+ @session_id = resp['sessionId'] or raise Error::WebDriverError, 'no sessionId in returned payload'
124
+
125
+ W3CCapabilities.json_create resp['value']
126
+ end
127
+
128
+ def status
129
+ execute :status
130
+ end
131
+
132
+ def get(url)
133
+ execute :get, {}, :url => url
134
+ end
135
+
136
+ def getCapabilities
137
+ W3CCapabilities.json_create execute(:getCapabilities)
138
+ end
139
+
140
+ def setImplicitWaitTimeout(milliseconds)
141
+ setTimeout('implicit', milliseconds)
142
+ end
143
+
144
+ def setScriptTimeout(milliseconds)
145
+ setTimeout('script', milliseconds)
146
+ end
147
+
148
+ def setTimeout(type, milliseconds)
149
+ execute :setTimeout, {}, :type => type, :ms => milliseconds
150
+ end
151
+
152
+ #
153
+ # alerts
154
+ #
155
+
156
+ def getAlert
157
+ execute :getAlert
158
+ end
159
+
160
+ def acceptAlert
161
+ execute :acceptAlert
162
+ end
163
+
164
+ def dismissAlert
165
+ execute :dismissAlert
166
+ end
167
+
168
+ def setAlertValue(keys)
169
+ execute :sendAlertText, {}, {:handler => 'prompt', :message => keys}
170
+ end
171
+
172
+ def getAlertText
173
+ execute :getAlertText
174
+ end
175
+
176
+ #
177
+ # navigation
178
+ #
179
+
180
+ def goBack
181
+ execute :back
182
+ end
183
+
184
+ def goForward
185
+ execute :forward
186
+ end
187
+
188
+ def getCurrentUrl
189
+ execute :getCurrentUrl
190
+ end
191
+
192
+ def getTitle
193
+ execute :getTitle
194
+ end
195
+
196
+ def getPageSource
197
+ execute :getPageSource
198
+ end
199
+
200
+ def getVisible
201
+ execute :getVisible
202
+ end
203
+
204
+ def setVisible(bool)
205
+ execute :setVisible, {}, bool
206
+ end
207
+
208
+ def switchToWindow(name)
209
+ execute :switchToWindow, {}, :handle => name
210
+ end
211
+
212
+ def switchToFrame(id)
213
+ locator = case id
214
+ when String
215
+ find_element_by('id', id)
216
+ when Hash
217
+ find_element_by(id.keys.first.to_s, id.values.first)
218
+ else
219
+ id
220
+ end
221
+
222
+ execute :switchToFrame, {}, :id => locator
223
+ end
224
+
225
+ def switchToParentFrame
226
+ execute :switchToParentFrame
227
+ end
228
+
229
+ def switchToDefaultContent
230
+ switchToFrame nil
231
+ end
232
+
233
+ QUIT_ERRORS = [IOError]
234
+
235
+ def quit
236
+ execute :deleteSession
237
+ http.close
238
+ rescue *QUIT_ERRORS
239
+ end
240
+
241
+ def close
242
+ execute :closeWindow
243
+ end
244
+
245
+ def refresh
246
+ execute :refresh
247
+ end
248
+
249
+ #
250
+ # window handling
251
+ #
252
+
253
+ def getWindowHandles
254
+ execute :getWindowHandles
255
+ end
256
+
257
+ def getCurrentWindowHandle
258
+ execute :getWindowHandle
259
+ end
260
+
261
+ # TODO - These Commands might require checking for being
262
+ # current window before performing
263
+ def setWindowSize(width, height, handle = :current)
264
+ execute :setWindowSize, {}, {:width => width,
265
+ :height => height}
266
+ end
267
+
268
+ def maximizeWindow(handle = :current)
269
+ execute :maximizeWindow
270
+ end
271
+
272
+ def getWindowSize(handle = :current)
273
+ data = execute :getWindowSize
274
+
275
+ Dimension.new data['width'], data['height']
276
+ end
277
+
278
+ def setWindowPosition(x, y, handle = :current)
279
+ execute :setWindowPosition, :x => x, :y => y
280
+ end
281
+
282
+ def getWindowPosition(handle = :current)
283
+ data = execute :getWindowPosition
284
+
285
+ Point.new data['x'], data['y']
286
+ end
287
+
288
+ def getScreenshot
289
+ execute :takeScreenshot
290
+ end
291
+
292
+ #
293
+ # HTML 5
294
+ #
295
+
296
+ def getLocalStorageItem(key)
297
+ execute :getLocalStorageItem, :key => key
298
+ end
299
+
300
+ def removeLocalStorageItem(key)
301
+ execute :removeLocalStorageItem, :key => key
302
+ end
303
+
304
+ def getLocalStorageKeys
305
+ execute :getLocalStorageKeys
306
+ end
307
+
308
+ def setLocalStorageItem(key, value)
309
+ execute :setLocalStorageItem, {}, :key => key, :value => value
310
+ end
311
+
312
+ def clearLocalStorage
313
+ execute :clearLocalStorage
314
+ end
315
+
316
+ def getLocalStorageSize
317
+ execute :getLocalStorageSize
318
+ end
319
+
320
+ def getSessionStorageItem(key)
321
+ execute :getSessionStorageItem, :key => key
322
+ end
323
+
324
+ def removeSessionStorageItem(key)
325
+ execute :removeSessionStorageItem, :key => key
326
+ end
327
+
328
+ def getSessionStorageKeys
329
+ execute :getSessionStorageKeys
330
+ end
331
+
332
+ def setSessionStorageItem(key, value)
333
+ execute :setSessionStorageItem, {}, :key => key, :value => value
334
+ end
335
+
336
+ def clearSessionStorage
337
+ execute :clearSessionStorage
338
+ end
339
+
340
+ def getSessionStorageSize
341
+ execute :getSessionStorageSize
342
+ end
343
+
344
+ def getLocation
345
+ obj = execute(:getLocation) || {} # android returns null
346
+ Location.new obj['latitude'], obj['longitude'], obj['altitude']
347
+ end
348
+
349
+ def setLocation(lat, lon, alt)
350
+ loc = {:latitude => lat, :longitude => lon, :altitude => alt}
351
+ execute :setLocation, {}, :location => loc
352
+ end
353
+
354
+ def getNetworkConnection
355
+ execute :getNetworkConnection
356
+ end
357
+
358
+ def setNetworkConnection(type)
359
+ execute :setNetworkConnection, {}, :parameters => {:type => type}
360
+ end
361
+
362
+ #
363
+ # javascript execution
364
+ #
365
+
366
+ def executeScript(script, *args)
367
+ result = execute :executeScript, {}, :script => script, :args => args
368
+ unwrap_script_result result
369
+ end
370
+
371
+ def executeAsyncScript(script, *args)
372
+ result = execute :executeAsyncScript, {}, :script => script, :args => args
373
+ unwrap_script_result result
374
+ end
375
+
376
+ #
377
+ # cookies
378
+ #
379
+
380
+ def addCookie(cookie)
381
+ execute :addCookie, {}, cookie
382
+ end
383
+
384
+ def deleteCookie(name)
385
+ execute :deleteCookie, :name => name
386
+ end
387
+
388
+ # TODO - write specs
389
+ def getCookie(name)
390
+ execute :getCookie, :name => name
391
+ end
392
+
393
+ def getAllCookies
394
+ execute :getAllCookies
395
+ end
396
+
397
+ def deleteAllCookies
398
+ getAllCookies.each { |cookie| deleteCookie(cookie['name'])}
399
+ end
400
+
401
+ #
402
+ # actions
403
+ #
404
+
405
+ def clickElement(element)
406
+ execute :elementClick, :id => element
407
+ end
408
+
409
+ def click
410
+ execute :click, {}, :button => 0
411
+ end
412
+
413
+ def doubleClick
414
+ execute :doubleClick
415
+ end
416
+
417
+ def contextClick
418
+ execute :click, {}, :button => 2
419
+ end
420
+
421
+ def mouseDown
422
+ execute :mouseDown
423
+ end
424
+
425
+ def mouseUp
426
+ execute :mouseUp
427
+ end
428
+
429
+ def mouseMoveTo(element, x = nil, y = nil)
430
+ params = { :element => element }
431
+
432
+ if x && y
433
+ params.merge! :xoffset => x, :yoffset => y
434
+ end
435
+
436
+ execute :mouseMoveTo, {}, params
437
+ end
438
+
439
+ def sendKeysToActiveElement(keys)
440
+ sendKeysToElement(getActiveElement, keys)
441
+ end
442
+
443
+ def sendKeysToElement(element, keys)
444
+ execute :elementSendKeys, {:id => element}, {:value => keys.join('').split(//)}
445
+ end
446
+
447
+ def upload(local_file)
448
+ unless File.file?(local_file)
449
+ raise Error::WebDriverError, "you may only upload files: #{local_file.inspect}"
450
+ end
451
+
452
+ execute :uploadFile, {}, :file => Zipper.zip_file(local_file)
453
+ end
454
+
455
+ def clearElement(element)
456
+ execute :elementClear, :id => element
457
+ end
458
+
459
+
460
+ def submitElement(element)
461
+ executeScript("var e = arguments[0].ownerDocument.createEvent('Event');" +
462
+ "e.initEvent('submit', true, true);" +
463
+ "if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }", element)
464
+ end
465
+
466
+ def dragElement(element, right_by, down_by)
467
+ execute :dragElement, {:id => element}, :x => right_by, :y => down_by
468
+ end
469
+
470
+ def touchSingleTap(element)
471
+ execute :touchSingleTap, {}, :element => element
472
+ end
473
+
474
+ def touchDoubleTap(element)
475
+ execute :touchDoubleTap, {}, :element => element
476
+ end
477
+
478
+ def touchLongPress(element)
479
+ execute :touchLongPress, {}, :element => element
480
+ end
481
+
482
+ def touchDown(x, y)
483
+ execute :touchDown, {}, :x => x, :y => y
484
+ end
485
+
486
+ def touchUp(x, y)
487
+ execute :touchUp, {}, :x => x, :y => y
488
+ end
489
+
490
+ def touchMove(x, y)
491
+ execute :touchMove, {}, :x => x, :y => y
492
+ end
493
+
494
+ def touchScroll(element, x, y)
495
+ if element
496
+ execute :touchScroll, {}, :element => element,
497
+ :xoffset => x,
498
+ :yoffset => y
499
+ else
500
+ execute :touchScroll, {}, :xoffset => x, :yoffset => y
501
+ end
502
+ end
503
+
504
+ def touchFlick(xspeed, yspeed)
505
+ execute :touchFlick, {}, :xspeed => xspeed, :yspeed => yspeed
506
+ end
507
+
508
+ def touchElementFlick(element, right_by, down_by, speed)
509
+ execute :touchFlick, {}, :element => element,
510
+ :xoffset => right_by,
511
+ :yoffset => down_by,
512
+ :speed => speed
513
+
514
+ end
515
+
516
+ def setScreenOrientation(orientation)
517
+ execute :setScreenOrientation, {}, :orientation => orientation
518
+ end
519
+
520
+ def getScreenOrientation
521
+ execute :getScreenOrientation
522
+ end
523
+
524
+ #
525
+ # element properties
526
+ #
527
+
528
+ def getElementTagName(element)
529
+ execute :getElementTagName, :id => element
530
+ end
531
+
532
+ def getElementAttribute(element, name)
533
+ execute :getElementAttribute, :id => element, :name => name
534
+ end
535
+
536
+ def getElementValue(element)
537
+ execute :getElementProperty, :id => element, :name => 'value'
538
+ end
539
+
540
+ def getElementText(element)
541
+ execute :getElementText, :id => element
542
+ end
543
+
544
+ def getElementLocation(element)
545
+ data = execute :getElementLocation, :id => element
546
+
547
+ Point.new data['x'], data['y']
548
+ end
549
+
550
+ def getElementLocationOnceScrolledIntoView(element)
551
+ data = execute :getElementLocationOnceScrolledIntoView, :id => element
552
+
553
+ Point.new data['x'], data['y']
554
+ end
555
+
556
+ def getElementSize(element)
557
+ data = execute :getElementSize, :id => element
558
+
559
+ Dimension.new data['width'], data['height']
560
+ end
561
+
562
+ def isElementEnabled(element)
563
+ execute :isElementEnabled, :id => element
564
+ end
565
+
566
+ def isElementSelected(element)
567
+ execute :isElementSelected, :id => element
568
+ end
569
+
570
+ def isElementDisplayed(element)
571
+ execute :isElementDisplayed, :id => element
572
+ end
573
+ def getElementValueOfCssProperty(element, prop)
574
+ execute :getElementCssValue, :id => element, :property_name => prop
575
+ end
576
+
577
+ def elementEquals(element, other)
578
+ element.ref == other.ref
579
+ end
580
+
581
+ #
582
+ # finding elements
583
+ #
584
+
585
+ def getActiveElement
586
+ Element.new self, element_id_from(execute(:getActiveElement))
587
+ end
588
+ alias_method :switchToActiveElement, :getActiveElement
589
+
590
+ def find_element_by(how, what, parent = nil)
591
+ how, what = convert_locators(how, what)
592
+
593
+ if parent
594
+ id = execute :findChildElement, {:id => parent}, {:using => how, :value => what}
595
+ else
596
+ id = execute :findElement, {}, {:using => how, :value => what}
597
+ end
598
+
599
+ Element.new self, element_id_from(id)
600
+ end
601
+
602
+ def find_elements_by(how, what, parent = nil)
603
+ how, what = convert_locators(how, what)
604
+
605
+ if parent
606
+ ids = execute :findChildElements, {:id => parent}, {:using => how, :value => what}
607
+ else
608
+ ids = execute :findElements, {}, {:using => how, :value => what}
609
+ end
610
+
611
+ ids.map { |id| Element.new self, element_id_from(id) }
612
+ end
613
+
614
+ private
615
+
616
+ def convert_locators(how, what)
617
+ case how
618
+ when 'class name'
619
+ how = 'css selector'
620
+ what = ".#{what}"
621
+ when 'id'
622
+ how = 'css selector'
623
+ what = "##{what}"
624
+ when 'name'
625
+ how = 'css selector'
626
+ what = "*[name='#{what}']"
627
+ when 'tag name'
628
+ how = 'css selector'
629
+ end
630
+ return how, what
631
+ end
632
+
633
+ #
634
+ # executes a command on the remote server.
635
+ #
636
+ #
637
+ # Returns the 'value' of the returned payload
638
+ #
639
+
640
+ def execute(*args)
641
+ result = raw_execute(*args)
642
+ result.payload.key?('value') ? result['value'] : result
643
+ end
644
+
645
+ #
646
+ # executes a command on the remote server.
647
+ #
648
+ # @return [WebDriver::Remote::Response]
649
+ #
650
+
651
+ def raw_execute(command, opts = {}, command_hash = nil)
652
+ verb, path = COMMANDS[command] || raise(ArgumentError, "unknown command: #{command.inspect}")
653
+ path = path.dup
654
+
655
+ path[':session_id'] = @session_id if path.include?(":session_id")
656
+
657
+ begin
658
+ opts.each { |key, value|
659
+ path[key.inspect] = escaper.escape(value.to_s)
660
+ }
661
+ rescue IndexError
662
+ raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
663
+ end
664
+
665
+ puts "-> #{verb.to_s.upcase} #{path}" if $DEBUG
666
+ http.call verb, path, command_hash
667
+ end
668
+
669
+ def escaper
670
+ @escaper ||= defined?(URI::Parser) ? URI::Parser.new : URI
671
+ end
672
+
673
+ end # W3CBridge
674
+ end # Remote
675
+ end # WebDriver
676
+ end # Selenium