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
@@ -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
|
@@ -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
|
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.
|
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.
|
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
|
-
|
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
|
-
|
76
|
+
|
77
|
+
end # Driver
|
78
78
|
end # IE
|
79
79
|
end # WebDriver
|
80
80
|
end # Selenium
|
@@ -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
|
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.
|
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
|
-
|
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/
|
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/
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
#
|
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
|
43
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
291
|
-
|
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
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
-
|
436
|
-
|
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
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
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
|
-
|
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
|
-
#
|
127
|
+
# Returns the current session ID.
|
526
128
|
#
|
527
129
|
|
528
|
-
def
|
529
|
-
|
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
|
534
|
-
|
535
|
-
|
536
|
-
|
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
|
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'] =
|
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
|
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
|