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.
Files changed (44) hide show
  1. data/CHANGES +35 -1
  2. data/Gemfile.lock +54 -0
  3. data/lib/selenium/webdriver.rb +8 -10
  4. data/lib/selenium/webdriver/chrome.rb +2 -1
  5. data/lib/selenium/webdriver/chrome/{bridge.rb → driver.rb} +47 -32
  6. data/lib/selenium/webdriver/chrome/options.rb +170 -0
  7. data/lib/selenium/webdriver/common/driver.rb +20 -39
  8. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +5 -0
  9. data/lib/selenium/webdriver/common/element.rb +5 -1
  10. data/lib/selenium/webdriver/common/logger.rb +10 -0
  11. data/lib/selenium/webdriver/common/w3c_error.rb +2 -2
  12. data/lib/selenium/webdriver/edge.rb +2 -1
  13. data/lib/selenium/webdriver/edge/bridge.rb +2 -91
  14. data/lib/selenium/webdriver/edge/driver.rb +80 -0
  15. data/lib/selenium/webdriver/firefox.rb +6 -3
  16. data/lib/selenium/webdriver/firefox/driver.rb +50 -0
  17. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  18. data/lib/selenium/webdriver/firefox/legacy/driver.rb +81 -0
  19. data/lib/selenium/webdriver/firefox/marionette/driver.rb +101 -0
  20. data/lib/selenium/webdriver/firefox/options.rb +139 -0
  21. data/lib/selenium/webdriver/ie.rb +1 -1
  22. data/lib/selenium/webdriver/ie/{bridge.rb → driver.rb} +13 -13
  23. data/lib/selenium/webdriver/phantomjs.rb +1 -1
  24. data/lib/selenium/webdriver/phantomjs/{bridge.rb → driver.rb} +10 -13
  25. data/lib/selenium/webdriver/remote.rb +11 -13
  26. data/lib/selenium/webdriver/remote/bridge.rb +87 -586
  27. data/lib/selenium/webdriver/remote/capabilities.rb +3 -1
  28. data/lib/selenium/webdriver/remote/driver.rb +44 -0
  29. data/lib/selenium/webdriver/remote/http/default.rb +1 -1
  30. data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
  31. data/lib/selenium/webdriver/remote/{commands.rb → oss/commands.rb} +9 -5
  32. data/lib/selenium/webdriver/remote/oss/driver.rb +44 -0
  33. data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
  34. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +266 -0
  35. data/lib/selenium/webdriver/remote/{w3c_commands.rb → w3c/commands.rb} +9 -4
  36. data/lib/selenium/webdriver/remote/w3c/driver.rb +41 -0
  37. data/lib/selenium/webdriver/safari.rb +3 -6
  38. data/lib/selenium/webdriver/safari/{bridge.rb → driver.rb} +14 -6
  39. data/selenium-webdriver.gemspec +1 -3
  40. metadata +29 -45
  41. data/lib/selenium/webdriver/firefox/bridge.rb +0 -70
  42. data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -114
  43. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -663
  44. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -231
@@ -0,0 +1,139 @@
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
+ class Options
24
+ attr_reader :args, :prefs, :options, :profile
25
+ attr_accessor :binary, :log_level
26
+
27
+ KEY = 'moz:firefoxOptions'.freeze
28
+
29
+ #
30
+ # Create a new Options instance, only for W3C-capable versions of Firefox.
31
+ #
32
+ # @example
33
+ # options = Selenium::WebDriver::Firefox::Options.new(args: ['--host=127.0.0.1'])
34
+ # driver = Selenium::WebDriver.for :firefox, options: options
35
+ #
36
+ # @param [Hash] opts the pre-defined options to create the Firefox::Options with
37
+ # @option opts [String] :binary Path to the Firefox executable to use
38
+ # @option opts [Array<String>] :args List of command-line arguments to use when starting geckodriver
39
+ # @option opts [Profile, String] :profile Encoded profile string or Profile instance
40
+ # @option opts [String, Symbol] :log_level Log level for geckodriver
41
+ # @option opts [Hash] :prefs A hash with each entry consisting of the key of the preference and its value
42
+ # @option opts [Hash] :options A hash for raw options
43
+ #
44
+
45
+ def initialize(**opts)
46
+ @args = opts.delete(:args) || []
47
+ @binary = opts.delete(:binary)
48
+ @profile = opts.delete(:profile)
49
+ @log_level = opts.delete(:log_level)
50
+ @prefs = opts.delete(:prefs) || {}
51
+ @options = opts.delete(:options) || {}
52
+ end
53
+
54
+ #
55
+ # Add a command-line argument to use when starting Firefox.
56
+ #
57
+ # @example Start geckodriver on a specific host
58
+ # options = Selenium::WebDriver::Firefox::Options.new
59
+ # options.add_argument('--host=127.0.0.1')
60
+ #
61
+ # @param [String] arg The command-line argument to add
62
+ #
63
+
64
+ def add_argument(arg)
65
+ @args << arg
66
+ end
67
+
68
+ #
69
+ # Add a new option not yet handled by these bindings.
70
+ #
71
+ # @example
72
+ # options = Selenium::WebDriver::Firefox::Options.new
73
+ # options.add_option(:foo, 'bar')
74
+ #
75
+ # @param [String, Symbol] name Name of the option
76
+ # @param [Boolean, String, Integer] value Value of the option
77
+ #
78
+
79
+ def add_option(name, value)
80
+ @options[name] = value
81
+ end
82
+
83
+ #
84
+ # Add a preference that is only applied to the user profile in use.
85
+ #
86
+ # @example Set the default homepage
87
+ # options = Selenium::WebDriver::Firefox::Options.new
88
+ # options.add_preference('browser.startup.homepage', 'http://www.seleniumhq.com/')
89
+ #
90
+ # @param [String] name Key of the preference
91
+ # @param [Boolean, String, Integer] value Value of the preference
92
+ #
93
+
94
+ def add_preference(name, value)
95
+ prefs[name] = value
96
+ end
97
+
98
+ #
99
+ # Sets Firefox profile.
100
+ #
101
+ # @example Set the custom profile
102
+ # profile = Selenium::WebDriver::Firefox::Profile.new
103
+ # options = Selenium::WebDriver::Firefox::Options.new
104
+ # options.profile = profile
105
+ #
106
+ # @example Use existing profile
107
+ # options = Selenium::WebDriver::Firefox::Options.new
108
+ # options.profile = 'myprofile'
109
+ #
110
+ # @param [Profile, String] profile Profile to be used
111
+ #
112
+
113
+ def profile=(profile)
114
+ @profile = if profile.is_a?(Profile)
115
+ profile
116
+ else
117
+ Profile.from_name(profile)
118
+ end
119
+ end
120
+
121
+ #
122
+ # @api private
123
+ #
124
+
125
+ def as_json(*)
126
+ opts = @options
127
+
128
+ opts[:profile] = @profile.encoded if @profile
129
+ opts[:args] = @args if @args.any?
130
+ opts[:binary] = @binary if @binary
131
+ opts[:prefs] = @prefs unless @prefs.empty?
132
+ opts[:log] = {level: @log_level} if @log_level
133
+
134
+ {KEY => opts}
135
+ end
136
+ end # Profile
137
+ end # Firefox
138
+ end # WebDriver
139
+ end # Selenium
@@ -17,7 +17,7 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- require 'selenium/webdriver/ie/bridge'
20
+ require 'selenium/webdriver/ie/driver'
21
21
  require 'selenium/webdriver/ie/service'
22
22
 
23
23
  module Selenium
@@ -20,11 +20,16 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module IE
23
+
23
24
  #
25
+ # Driver implementation for Internet Explorer supporting
26
+ # both OSS and W3C dialects of JSON wire protocol.
24
27
  # @api private
25
28
  #
26
29
 
27
- class Bridge < Remote::Bridge
30
+ class Driver < WebDriver::Driver
31
+ include DriverExtensions::TakesScreenshot
32
+
28
33
  def initialize(opts = {})
29
34
  opts[:desired_capabilities] ||= Remote::Capabilities.internet_explorer
30
35
 
@@ -34,17 +39,13 @@ module Selenium
34
39
 
35
40
  opts[:driver_opts] ||= {}
36
41
  if opts.key? :service_args
37
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
38
- [DEPRECATION] `:service_args` is deprecated. Pass switches using `driver_opts`
39
- DEPRECATE
42
+ WebDriver.logger.deprecate ':service_args', "driver_opts: {args: #{opts[:service_args]}}"
40
43
  opts[:driver_opts][:args] = opts.delete(:service_args)
41
44
  end
42
45
 
43
46
  %i[log_level log_file implementation].each do |method|
44
47
  next unless opts.key? method
45
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
46
- [DEPRECATION] `#{method}` is deprecated. Pass switches using `driver_opts`
47
- DEPRECATE
48
+ WebDriver.logger.deprecate ":#{method}", "driver_opts: {#{method}: '#{opts[method]}'}"
48
49
  opts[:driver_opts][method] = opts.delete(method)
49
50
  end
50
51
 
@@ -58,23 +59,22 @@ module Selenium
58
59
  end
59
60
  opts[:desired_capabilities][:native_events] = opts.delete(:native_events) != false
60
61
 
61
- super(opts)
62
+ listener = opts.delete(:listener)
63
+ @bridge = Remote::Bridge.handshake(opts)
64
+ super(@bridge, listener: listener)
62
65
  end
63
66
 
64
67
  def browser
65
68
  :internet_explorer
66
69
  end
67
70
 
68
- def driver_extensions
69
- [DriverExtensions::TakesScreenshot]
70
- end
71
-
72
71
  def quit
73
72
  super
74
73
  ensure
75
74
  @service.stop if @service
76
75
  end
77
- end # Bridge
76
+
77
+ end # Driver
78
78
  end # IE
79
79
  end # WebDriver
80
80
  end # Selenium
@@ -20,7 +20,7 @@
20
20
  require 'net/http'
21
21
 
22
22
  require 'selenium/webdriver/phantomjs/service'
23
- require 'selenium/webdriver/phantomjs/bridge'
23
+ require 'selenium/webdriver/phantomjs/driver'
24
24
 
25
25
  module Selenium
26
26
  module WebDriver
@@ -20,11 +20,15 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module PhantomJS
23
+
23
24
  #
25
+ # Driver implementation for PhantomJS.
24
26
  # @api private
25
27
  #
26
28
 
27
- class Bridge < Remote::Bridge
29
+ class Driver < WebDriver::Driver
30
+ include DriverExtensions::TakesScreenshot
31
+
28
32
  def initialize(opts = {})
29
33
  opts[:desired_capabilities] ||= Remote::Capabilities.phantomjs
30
34
 
@@ -34,9 +38,7 @@ module Selenium
34
38
 
35
39
  opts[:driver_opts] ||= {}
36
40
  if opts.key? :args
37
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
38
- [DEPRECATION] `:args` is deprecated. Pass switches using `driver_opts`
39
- DEPRECATE
41
+ WebDriver.logger.deprecate ':args', "driver_opts: {args: #{opts[:args]}}"
40
42
  opts[:driver_opts][:args] = opts.delete(:args)
41
43
  elsif opts[:desired_capabilities]['phantomjs.cli.args']
42
44
  opts[:driver_opts][:args] = opts[:desired_capabilities]['phantomjs.cli.args']
@@ -47,26 +49,21 @@ module Selenium
47
49
  opts[:url] = @service.uri
48
50
  end
49
51
 
50
- super(opts)
52
+ listener = opts.delete(:listener)
53
+ @bridge = Remote::Bridge.handshake(opts)
54
+ super(@bridge, listener: listener)
51
55
  end
52
56
 
53
57
  def browser
54
58
  :phantomjs
55
59
  end
56
60
 
57
- def driver_extensions
58
- [DriverExtensions::TakesScreenshot]
59
- end
60
-
61
- def capabilities
62
- @capabilities ||= Remote::Capabilities.phantomjs
63
- end
64
-
65
61
  def quit
66
62
  super
67
63
  ensure
68
64
  @service.stop if @service
69
65
  end
66
+
70
67
  end # Bridge
71
68
  end # PhantomJS
72
69
  end # WebDriver
@@ -19,21 +19,19 @@
19
19
 
20
20
  require 'uri'
21
21
 
22
- require 'selenium/webdriver/remote/capabilities'
23
- require 'selenium/webdriver/remote/w3c_capabilities'
24
22
  require 'selenium/webdriver/remote/bridge'
25
- require 'selenium/webdriver/remote/w3c_bridge'
26
- require 'selenium/webdriver/remote/server_error'
23
+ require 'selenium/webdriver/remote/driver'
27
24
  require 'selenium/webdriver/remote/response'
28
- require 'selenium/webdriver/remote/commands'
29
- require 'selenium/webdriver/remote/w3c_commands'
25
+ require 'selenium/webdriver/remote/server_error'
30
26
  require 'selenium/webdriver/remote/http/common'
31
27
  require 'selenium/webdriver/remote/http/default'
32
28
 
33
- module Selenium
34
- module WebDriver
35
- # @api private
36
- module Remote
37
- end # Remote
38
- end # WebDriver
39
- end # Selenium
29
+ require 'selenium/webdriver/remote/capabilities'
30
+ require 'selenium/webdriver/remote/oss/bridge'
31
+ require 'selenium/webdriver/remote/oss/commands'
32
+ require 'selenium/webdriver/remote/oss/driver'
33
+
34
+ require 'selenium/webdriver/remote/w3c/bridge'
35
+ require 'selenium/webdriver/remote/w3c/capabilities'
36
+ require 'selenium/webdriver/remote/w3c/commands'
37
+ require 'selenium/webdriver/remote/w3c/driver'
@@ -20,653 +20,137 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Remote
23
- #
24
- # Low level bridge to the remote server, through which the rest of the API works.
25
- #
26
- # @api private
27
- #
28
-
29
23
  class Bridge
24
+ include Atoms
30
25
  include BridgeHelper
31
26
 
27
+ COMMANDS = {
28
+ new_session: [:post, 'session'.freeze]
29
+ }.freeze
30
+
32
31
  attr_accessor :context, :http, :file_detector
33
- attr_reader :capabilities
32
+ attr_reader :capabilities, :dialect
34
33
 
35
34
  #
36
- # @see W3CBridge#Initialize
35
+ # Implements protocol handshake which:
36
+ #
37
+ # 1. Creates session with driver.
38
+ # 2. Sniffs response.
39
+ # 3. Based on the response, understands which dialect we should use.
40
+ #
41
+ # @return [OSS:Bridge, W3C::Bridge]
42
+ #
43
+ def self.handshake(**opts)
44
+ desired_capabilities = opts.delete(:desired_capabilities)
45
+ bridge = new(opts)
46
+ capabilities = bridge.create_session(desired_capabilities)
47
+
48
+ case bridge.dialect
49
+ when :oss
50
+ Remote::OSS::Bridge.new(capabilities, bridge.session_id, opts)
51
+ when :w3c
52
+ Remote::W3C::Bridge.new(capabilities, bridge.session_id, opts)
53
+ else
54
+ raise WebDriverError, 'cannot understand dialect'
55
+ end
56
+ end
57
+
58
+ #
59
+ # Initializes the bridge with the given server URL
60
+ # @param [Hash] opts options for the driver
61
+ # @option opts [String] :url url for the remote server
62
+ # @option opts [Integer] :port port number for the remote server
63
+ # @option opts [Object] :http_client an HTTP client instance that implements the same protocol as Http::Default
64
+ # @option opts [Capabilities] :desired_capabilities an instance of Remote::Capabilities describing the capabilities you want
65
+ # @api private
37
66
  #
38
67
 
39
68
  def initialize(opts = {})
40
69
  opts = opts.dup
41
70
 
42
- port = if opts.key?(:port)
43
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
44
- [DEPRECATION] `:port` is deprecated. Use `:url` with full path
45
- DEPRECATE
46
- opts.delete(:port)
47
- else
48
- 4444
49
- end
71
+ WebDriver.logger.deprecate ':port', 'full URL' if opts.key?(:port)
72
+ port = opts.delete(:port) || 4444
50
73
 
51
74
  http_client = opts.delete(:http_client) { Http::Default.new }
52
- desired_capabilities = opts.delete(:desired_capabilities) { Capabilities.firefox }
53
75
  url = opts.delete(:url) { "http://#{Platform.localhost}:#{port}/wd/hub" }
54
76
 
55
77
  unless opts.empty?
56
78
  raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
57
79
  end
58
80
 
59
- if desired_capabilities.is_a?(Symbol)
60
- unless Capabilities.respond_to?(desired_capabilities)
61
- raise Error::WebDriverError, "invalid desired capability: #{desired_capabilities.inspect}"
62
- end
63
-
64
- desired_capabilities = Capabilities.send(desired_capabilities)
65
- end
66
-
67
81
  uri = url.is_a?(URI) ? url : URI.parse(url)
68
82
  uri.path += '/' unless uri.path =~ %r{\/$}
69
83
 
70
84
  http_client.server_url = uri
71
85
 
72
86
  @http = http_client
73
- @capabilities = create_session(desired_capabilities)
74
-
75
87
  @file_detector = nil
76
88
  end
77
89
 
78
- def browser
79
- @browser ||= (
80
- name = @capabilities.browser_name
81
- name ? name.tr(' ', '_').to_sym : 'unknown'
82
- )
83
- end
84
-
85
- def driver_extensions
86
- [DriverExtensions::UploadsFiles,
87
- DriverExtensions::TakesScreenshot,
88
- DriverExtensions::HasSessionId,
89
- DriverExtensions::Rotatable,
90
- DriverExtensions::HasTouchScreen,
91
- DriverExtensions::HasLocation,
92
- DriverExtensions::HasNetworkConnection,
93
- DriverExtensions::HasRemoteStatus,
94
- DriverExtensions::HasWebStorage]
95
- end
96
-
97
- def commands(command)
98
- COMMANDS[command]
99
- end
100
-
101
90
  #
102
- # Returns the current session ID.
91
+ # Creates session handling both OSS and W3C dialects.
103
92
  #
104
93
 
105
- def session_id
106
- @session_id || raise(Error::WebDriverError, 'no current session exists')
107
- end
108
-
109
94
  def create_session(desired_capabilities)
110
- resp = raw_execute :new_session, {}, {desiredCapabilities: desired_capabilities}
111
- @session_id = resp['sessionId']
112
- return Capabilities.json_create resp['value'] if @session_id
113
-
114
- raise Error::WebDriverError, 'no sessionId in returned payload'
115
- end
116
-
117
- def status
118
- execute :status
119
- end
120
-
121
- def get(url)
122
- execute :get, {}, {url: url}
123
- end
124
-
125
- def session_capabilities
126
- Capabilities.json_create execute(:get_capabilities)
127
- end
128
-
129
- def implicit_wait_timeout=(milliseconds)
130
- execute :implicitly_wait, {}, {ms: milliseconds}
131
- end
132
-
133
- def script_timeout=(milliseconds)
134
- execute :set_script_timeout, {}, {ms: milliseconds}
135
- end
136
-
137
- def timeout(type, milliseconds)
138
- execute :set_timeout, {}, {type: type, ms: milliseconds}
139
- end
140
-
141
- #
142
- # alerts
143
- #
144
-
145
- def accept_alert
146
- execute :accept_alert
147
- end
148
-
149
- def dismiss_alert
150
- execute :dismiss_alert
151
- end
152
-
153
- def alert=(keys)
154
- execute :set_alert_value, {}, {text: keys.to_s}
155
- end
156
-
157
- def alert_text
158
- execute :get_alert_text
159
- end
160
-
161
- def authentication(credentials)
162
- execute :set_authentication, {}, credentials
163
- end
164
-
165
- #
166
- # navigation
167
- #
168
-
169
- def go_back
170
- execute :go_back
171
- end
172
-
173
- def go_forward
174
- execute :go_forward
175
- end
176
-
177
- def url
178
- execute :get_current_url
179
- end
180
-
181
- def title
182
- execute :get_title
183
- end
184
-
185
- def page_source
186
- execute :get_page_source
187
- end
188
-
189
- def switch_to_window(name)
190
- execute :switch_to_window, {}, {name: name}
191
- end
95
+ response = execute(:new_session, {}, merged_capabilties(desired_capabilities))
192
96
 
193
- def switch_to_frame(id)
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
- def quit
206
- execute :quit
207
- http.close
208
- rescue *http.quit_errors
209
- end
210
-
211
- def close
212
- execute :close
213
- end
214
-
215
- def refresh
216
- execute :refresh
217
- end
218
-
219
- #
220
- # window handling
221
- #
222
-
223
- def window_handles
224
- execute :get_window_handles
225
- end
226
-
227
- def window_handle
228
- execute :get_current_window_handle
229
- end
230
-
231
- def resize_window(width, height, handle = :current)
232
- execute :set_window_size, {window_handle: handle},
233
- {width: width,
234
- height: height}
235
- end
236
-
237
- def maximize_window(handle = :current)
238
- execute :maximize_window, window_handle: handle
239
- end
240
-
241
- def window_size(handle = :current)
242
- data = execute :get_window_size, window_handle: handle
243
-
244
- Dimension.new data['width'], data['height']
245
- end
246
-
247
- def reposition_window(x, y, handle = :current)
248
- execute :set_window_position, {window_handle: handle},
249
- {x: x, y: y}
250
- end
251
-
252
- def window_position(handle = :current)
253
- data = execute :get_window_position, window_handle: handle
254
-
255
- Point.new data['x'], data['y']
256
- end
257
-
258
- def screenshot
259
- execute :screenshot
260
- end
261
-
262
- #
263
- # HTML 5
264
- #
265
-
266
- def local_storage_item(key, value = nil)
267
- if value
268
- execute :set_local_storage_item, {}, {key: key, value: value}
269
- else
270
- execute :get_local_storage_item, key: key
271
- end
272
- end
273
-
274
- def remove_local_storage_item(key)
275
- execute :remove_local_storage_item, key: key
276
- end
277
-
278
- def local_storage_keys
279
- execute :get_local_storage_keys
280
- end
281
-
282
- def clear_local_storage
283
- execute :clear_local_storage
284
- end
285
-
286
- def local_storage_size
287
- execute :get_local_storage_size
288
- end
97
+ @session_id = response['sessionId']
98
+ oss_status = response['status']
99
+ value = response['value']
289
100
 
290
- def session_storage_item(key, value = nil)
291
- if value
292
- execute :set_session_storage_item, {}, {key: key, value: value}
293
- else
294
- execute :get_session_storage_item, key: key
295
- end
296
- end
297
-
298
- def remove_session_storage_item(key)
299
- execute :remove_session_storage_item, key: key
300
- end
301
-
302
- def session_storage_keys
303
- execute :get_session_storage_keys
304
- end
305
-
306
- def clear_session_storage
307
- execute :clear_session_storage
308
- end
101
+ if value.is_a?(Hash)
102
+ @session_id = value['sessionId'] if value.key?('sessionId')
309
103
 
310
- def session_storage_size
311
- execute :get_session_storage_size
312
- end
313
-
314
- def location
315
- obj = execute(:get_location) || {}
316
- Location.new obj['latitude'], obj['longitude'], obj['altitude']
317
- end
318
-
319
- def set_location(lat, lon, alt)
320
- loc = {latitude: lat, longitude: lon, altitude: alt}
321
- execute :set_location, {}, {location: loc}
322
- end
323
-
324
- def network_connection
325
- execute :get_network_connection
326
- end
327
-
328
- def network_connection=(type)
329
- execute :set_network_connection, {}, {parameters: {type: type}}
330
- end
331
-
332
- #
333
- # javascript execution
334
- #
335
-
336
- def execute_script(script, *args)
337
- assert_javascript_enabled
338
-
339
- result = execute :execute_script, {}, {script: script, args: args}
340
- unwrap_script_result result
341
- end
342
-
343
- def execute_async_script(script, *args)
344
- assert_javascript_enabled
345
-
346
- result = execute :execute_async_script, {}, {script: script, args: args}
347
- unwrap_script_result result
348
- end
349
-
350
- #
351
- # cookies
352
- #
353
-
354
- def options
355
- @options ||= WebDriver::Options.new(self)
356
- end
357
-
358
- def add_cookie(cookie)
359
- execute :add_cookie, {}, {cookie: cookie}
360
- end
361
-
362
- def delete_cookie(name)
363
- execute :delete_cookie, name: name
364
- end
365
-
366
- def cookies
367
- execute :get_cookies
368
- end
369
-
370
- def delete_all_cookies
371
- execute :delete_all_cookies
372
- end
373
-
374
- #
375
- # actions
376
- #
377
-
378
- #
379
- # @return [ActionBuilder]
380
- # @api public
381
- #
382
-
383
- def action
384
- ActionBuilder.new Mouse.new(self), Keyboard.new(self)
385
- end
386
-
387
- def mouse
388
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
389
- [DEPRECATION] `Driver#mouse` is deprecated with w3c implementation. Instead use
390
- driver.action.<command>.perform
391
- DEPRECATE
392
- Mouse.new self
393
- end
394
-
395
- def keyboard
396
- WebDriver.logger.warn <<-DEPRECATE.gsub(/\n +| {2,}/, ' ').freeze
397
- [DEPRECATION] `Driver#keyboard` is deprecated with w3c implementation. Instead use
398
- driver.action.<command>.perform
399
- DEPRECATE
400
- Keyboard.new self
401
- end
402
-
403
- def click_element(element)
404
- execute :click_element, id: element
405
- end
406
-
407
- def click
408
- execute :click, {}, {button: 0}
409
- end
410
-
411
- def double_click
412
- execute :double_click
413
- end
414
-
415
- def context_click
416
- execute :click, {}, {button: 2}
417
- end
418
-
419
- def mouse_down
420
- execute :mouse_down
421
- end
422
-
423
- def mouse_up
424
- execute :mouse_up
425
- end
426
-
427
- def mouse_move_to(element, x = nil, y = nil)
428
- params = {element: element}
429
-
430
- if x && y
431
- params[:xoffset] = x
432
- params[:yoffset] = y
104
+ if value.key?('capabilities')
105
+ value = value['capabilities']
106
+ elsif value.key?('value')
107
+ value = value['value']
108
+ end
433
109
  end
434
110
 
435
- execute :mouse_move_to, {}, params
436
- end
437
-
438
- def send_keys_to_active_element(key)
439
- execute :send_keys_to_active_element, {}, {value: key}
440
- end
441
-
442
- def send_keys_to_element(element, keys)
443
- if @file_detector
444
- local_file = @file_detector.call(keys)
445
- keys = upload(local_file) if local_file
111
+ unless @session_id
112
+ raise Error::WebDriverError, 'no sessionId in returned payload'
446
113
  end
447
114
 
448
- execute :send_keys_to_element, {id: element}, {value: Array(keys)}
449
- end
450
-
451
- def upload(local_file)
452
- unless File.file?(local_file)
453
- raise Error::WebDriverError, "you may only upload files: #{local_file.inspect}"
454
- end
455
-
456
- execute :upload_file, {}, {file: Zipper.zip_file(local_file)}
457
- end
458
-
459
- def clear_element(element)
460
- execute :clear_element, id: element
461
- end
462
-
463
- def submit_element(element)
464
- execute :submit_element, id: element
465
- end
466
-
467
- def drag_element(element, right_by, down_by)
468
- execute :drag_element, {id: element}, {x: right_by, y: down_by}
469
- end
470
-
471
- def touch_single_tap(element)
472
- execute :touch_single_tap, {}, {element: element}
473
- end
474
-
475
- def touch_double_tap(element)
476
- execute :touch_double_tap, {}, {element: element}
477
- end
478
-
479
- def touch_long_press(element)
480
- execute :touch_long_press, {}, {element: element}
481
- end
482
-
483
- def touch_down(x, y)
484
- execute :touch_down, {}, {x: x, y: y}
485
- end
486
-
487
- def touch_up(x, y)
488
- execute :touch_up, {}, {x: x, y: y}
489
- end
490
-
491
- def touch_move(x, y)
492
- execute :touch_move, {}, {x: x, y: y}
493
- end
494
-
495
- def touch_scroll(element, x, y)
496
- if element
497
- execute :touch_scroll, {}, {element: element,
498
- xoffset: x,
499
- yoffset: y}
115
+ if oss_status
116
+ WebDriver.logger.info 'Detected OSS dialect.'
117
+ @dialect = :oss
118
+ Capabilities.json_create(value)
500
119
  else
501
- execute :touch_scroll, {}, {xoffset: x, yoffset: y}
120
+ WebDriver.logger.info 'Detected W3C dialect.'
121
+ @dialect = :w3c
122
+ W3C::Capabilities.json_create(value)
502
123
  end
503
124
  end
504
125
 
505
- def touch_flick(xspeed, yspeed)
506
- execute :touch_flick, {}, {xspeed: xspeed, yspeed: yspeed}
507
- end
508
-
509
- def touch_element_flick(element, right_by, down_by, speed)
510
- execute :touch_flick, {}, {element: element,
511
- xoffset: right_by,
512
- yoffset: down_by,
513
- speed: speed}
514
- end
515
-
516
- def screen_orientation=(orientation)
517
- execute :set_screen_orientation, {}, {orientation: orientation}
518
- end
519
-
520
- def screen_orientation
521
- execute :get_screen_orientation
522
- end
523
-
524
126
  #
525
- # logs
127
+ # Returns the current session ID.
526
128
  #
527
129
 
528
- def available_log_types
529
- types = execute :get_available_log_types
530
- Array(types).map(&:to_sym)
130
+ def session_id
131
+ @session_id || raise(Error::WebDriverError, 'no current session exists')
531
132
  end
532
133
 
533
- def log(type)
534
- data = execute :get_log, {}, {type: type.to_s}
535
-
536
- Array(data).map do |l|
537
- begin
538
- LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
539
- rescue KeyError
540
- next
541
- end
134
+ def browser
135
+ @browser ||= begin
136
+ name = @capabilities.browser_name
137
+ name ? name.tr(' ', '_').to_sym : 'unknown'
542
138
  end
543
139
  end
544
140
 
545
- #
546
- # element properties
547
- #
548
-
549
- def element_tag_name(element)
550
- execute :get_element_tag_name, id: element
551
- end
552
-
553
- def element_attribute(element, name)
554
- execute :get_element_attribute, id: element.ref, name: name
555
- end
556
-
557
- # Backwards compatibility for w3c
558
- def element_property(element, name)
559
- execute_script 'return arguments[0][arguments[1]]', element, name
560
- end
561
-
562
- def element_value(element)
563
- execute :get_element_value, id: element
564
- end
565
-
566
- def element_text(element)
567
- execute :get_element_text, id: element
568
- end
569
-
570
- def element_location(element)
571
- data = execute :get_element_location, id: element
572
-
573
- Point.new data['x'], data['y']
574
- end
575
-
576
- def element_rect(element)
577
- loc = execute :get_element_location, id: element
578
- size = execute :get_element_size, id: element
579
-
580
- Rectangle.new loc['x'], loc['y'], size['width'], size['height']
581
- end
582
-
583
- def element_location_once_scrolled_into_view(element)
584
- data = execute :get_element_location_once_scrolled_into_view, id: element
585
-
586
- Point.new data['x'], data['y']
587
- end
588
-
589
- def element_size(element)
590
- data = execute :get_element_size, id: element
591
-
592
- Dimension.new data['width'], data['height']
593
- end
594
-
595
- def element_enabled?(element)
596
- execute :is_element_enabled, id: element
597
- end
598
-
599
- def element_selected?(element)
600
- execute :is_element_selected, id: element
601
- end
602
-
603
- def element_displayed?(element)
604
- execute :is_element_displayed, id: element
605
- end
606
-
607
- def element_value_of_css_property(element, prop)
608
- execute :get_element_value_of_css_property, id: element, property_name: prop
609
- end
610
-
611
- #
612
- # finding elements
613
- #
614
-
615
- def active_element
616
- Element.new self, element_id_from(execute(:get_active_element))
617
- end
618
-
619
- alias_method :switch_to_active_element, :active_element
620
-
621
- def find_element_by(how, what, parent = nil)
622
- id = if parent
623
- execute :find_child_element, {id: parent}, {using: how, value: what}
624
- else
625
- execute :find_element, {}, {using: how, value: what}
626
- end
627
-
628
- Element.new self, element_id_from(id)
629
- end
630
-
631
- def find_elements_by(how, what, parent = nil)
632
- ids = if parent
633
- execute :find_child_elements, {id: parent}, {using: how, value: what}
634
- else
635
- execute :find_elements, {}, {using: how, value: what}
636
- end
637
-
638
- ids.map { |id| Element.new self, element_id_from(id) }
639
- end
640
-
641
141
  private
642
142
 
643
- def assert_javascript_enabled
644
- return if capabilities.javascript_enabled?
645
- raise Error::UnsupportedOperationError, 'underlying webdriver instance does not support javascript'
646
- end
647
-
648
- #
649
- # executes a command on the remote server.
650
- #
651
- #
652
- # Returns the 'value' of the returned payload
653
- #
654
-
655
- def execute(*args)
656
- raw_execute(*args)['value']
657
- end
658
-
659
143
  #
660
144
  # executes a command on the remote server.
661
145
  #
662
146
  # @return [WebDriver::Remote::Response]
663
147
  #
664
148
 
665
- def raw_execute(command, opts = {}, command_hash = nil)
149
+ def execute(command, opts = {}, command_hash = nil)
666
150
  verb, path = commands(command) || raise(ArgumentError, "unknown command: #{command.inspect}")
667
151
  path = path.dup
668
152
 
669
- path[':session_id'] = @session_id if path.include?(':session_id')
153
+ path[':session_id'] = session_id if path.include?(':session_id')
670
154
 
671
155
  begin
672
156
  opts.each { |key, value| path[key.inspect] = escaper.escape(value.to_s) }
@@ -675,12 +159,29 @@ module Selenium
675
159
  end
676
160
 
677
161
  WebDriver.logger.info("-> #{verb.to_s.upcase} #{path}")
678
- http.call verb, path, command_hash
162
+ http.call(verb, path, command_hash)
679
163
  end
680
164
 
681
165
  def escaper
682
166
  @escaper ||= defined?(URI::Parser) ? URI::Parser.new : URI
683
167
  end
168
+
169
+ def commands(command)
170
+ raise NotImplementedError unless command == :new_session
171
+ COMMANDS[command]
172
+ end
173
+
174
+ def merged_capabilties(oss_capabilities)
175
+ w3c_capabilties = W3C::Capabilities.from_oss(oss_capabilities)
176
+
177
+ {
178
+ desiredCapabilities: oss_capabilities,
179
+ capabilities: {
180
+ firstMatch: [w3c_capabilties]
181
+ }
182
+ }
183
+ end
184
+
684
185
  end # Bridge
685
186
  end # Remote
686
187
  end # WebDriver