selenium-webdriver 3.142.7 → 4.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +21 -43
- data/lib/selenium/webdriver.rb +2 -4
- data/lib/selenium/webdriver/chrome/bridge.rb +3 -21
- data/lib/selenium/webdriver/chrome/driver.rb +12 -39
- data/lib/selenium/webdriver/chrome/options.rb +3 -7
- data/lib/selenium/webdriver/chrome/profile.rb +2 -2
- data/lib/selenium/webdriver/chrome/service.rb +4 -9
- data/lib/selenium/webdriver/common.rb +7 -16
- data/lib/selenium/webdriver/common/action_builder.rb +97 -249
- data/lib/selenium/webdriver/common/driver.rb +2 -4
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +1 -1
- data/lib/selenium/webdriver/common/element.rb +3 -6
- data/lib/selenium/webdriver/common/error.rb +27 -203
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -5
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +13 -13
- data/lib/selenium/webdriver/common/manager.rb +1 -1
- data/lib/selenium/webdriver/common/options.rb +148 -24
- data/lib/selenium/webdriver/common/service.rb +16 -34
- data/lib/selenium/webdriver/common/socket_poller.rb +2 -2
- data/lib/selenium/webdriver/common/w3c_options.rb +45 -0
- data/lib/selenium/webdriver/edge.rb +0 -1
- data/lib/selenium/webdriver/edge/driver.rb +14 -10
- data/lib/selenium/webdriver/edge/service.rb +6 -7
- data/lib/selenium/webdriver/firefox.rb +2 -6
- data/lib/selenium/webdriver/firefox/binary.rb +3 -80
- data/lib/selenium/webdriver/firefox/bridge.rb +47 -0
- data/lib/selenium/webdriver/firefox/driver.rb +44 -22
- data/lib/selenium/webdriver/firefox/marionette/driver.rb +1 -1
- data/lib/selenium/webdriver/firefox/options.rb +2 -2
- data/lib/selenium/webdriver/firefox/profile.rb +25 -14
- data/lib/selenium/webdriver/firefox/service.rb +4 -4
- data/lib/selenium/webdriver/ie/driver.rb +5 -18
- data/lib/selenium/webdriver/ie/options.rb +2 -2
- data/lib/selenium/webdriver/ie/service.rb +4 -4
- data/lib/selenium/webdriver/remote.rb +2 -6
- data/lib/selenium/webdriver/remote/bridge.rb +515 -69
- data/lib/selenium/webdriver/remote/capabilities.rb +77 -99
- data/lib/selenium/webdriver/remote/commands.rb +156 -0
- data/lib/selenium/webdriver/remote/driver.rb +12 -5
- data/lib/selenium/webdriver/remote/http/default.rb +0 -9
- data/lib/selenium/webdriver/remote/response.rb +16 -47
- data/lib/selenium/webdriver/safari.rb +1 -1
- data/lib/selenium/webdriver/safari/bridge.rb +3 -3
- data/lib/selenium/webdriver/safari/driver.rb +4 -1
- data/lib/selenium/webdriver/safari/service.rb +4 -4
- data/lib/selenium/webdriver/support/select.rb +1 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/selenium-webdriver.gemspec +3 -3
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc17a0dfe5aefbf024e5e6c64082ba16209c7f36
|
4
|
+
data.tar.gz: 6e7cda3f69ed3fcaaa591b9720b7674a3f8d7a23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dfdf006f048202deedb179a6279c5000082e298ecc29c97806dc1199517bfa625ee45d6be2a2f88ece82869a5b4f593795b3940aa781539740a91d2a8a33523
|
7
|
+
data.tar.gz: 85a754811a838367f4a93699b1e44b58eb3493a0ec44d9367d7be566a28ec29a820f89befda4d7f73383443d999f87d76f4cc0b484b920600c30d2b15289e496
|
data/CHANGES
CHANGED
@@ -1,53 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
Ruby:
|
5
|
-
* Fix keyword argument deprecations in Ruby 2.7 (thanks @connorshea)
|
6
|
-
|
7
|
-
3.142.6 (2019-10-04)
|
8
|
-
====================
|
9
|
-
|
10
|
-
Ruby:
|
11
|
-
* Loosen ChildProcess dependency so that 3.0+ can be used (thanks @jaredbeck)
|
12
|
-
|
13
|
-
3.142.5 (2019-10-01)
|
14
|
-
====================
|
1
|
+
4.0.0.alpha1 (unreleased)
|
2
|
+
=========================
|
15
3
|
|
16
4
|
Ruby:
|
17
|
-
*
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
*
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
====================
|
28
|
-
|
29
|
-
Firefox:
|
30
|
-
* Fixed a regression when Firefox binary path was not sent to GeckoDriver
|
31
|
-
by default and browser could not be located (issue #7219)
|
32
|
-
|
33
|
-
|
34
|
-
3.142.2 (2019-05-11)
|
35
|
-
====================
|
5
|
+
* Removed Mouse and Keyboard as their implementations are not compliant to WebDriver specification
|
6
|
+
* Removed TouchActionBuilder as its implementation is not compliant to WebDriver specification
|
7
|
+
* Change ActionBuilder to be fully compliant to WebDriver specification
|
8
|
+
* Change Manager to be fully compliant to WebDriver specification
|
9
|
+
* Removed all deprecated errors that are not compliant to WebDriver specification
|
10
|
+
* Update minimum required Ruby version to 2.4
|
11
|
+
* Changed capabilities to use only those that are compliant to WebDriver specification
|
12
|
+
* Removed deprecated timeout setter on default HTTP client
|
13
|
+
* Removed deprecated Remote::W3C::Capabilities
|
14
|
+
* Fixed an issue when services are not shutdown properly
|
36
15
|
|
37
16
|
Chrome:
|
38
|
-
*
|
39
|
-
|
17
|
+
* Removed support for OSS mode - use W3C mode instead by using
|
18
|
+
Selenium::WebDriver::Chrome::Options.new(options: {w3c: true})
|
40
19
|
|
41
|
-
|
42
|
-
*
|
43
|
-
didn't work with Grid (issue #7174)
|
44
|
-
|
45
|
-
3.142.1 (2019-05-07)
|
46
|
-
====================
|
20
|
+
PhantomJS:
|
21
|
+
* Removed support for PhantomJS driver
|
47
22
|
|
48
23
|
Firefox:
|
49
|
-
*
|
50
|
-
|
24
|
+
* Removed support for legacy Firefox driver - use GeckoDriver instead
|
25
|
+
* Removed deprecated outdated capabilities
|
26
|
+
* Fixed an issue when passing :profile string to Firefox::Options.new would
|
27
|
+
result in NoMethodError. Now it will find a profile with such name on your
|
28
|
+
system and use it accordingly (issue #7119)
|
51
29
|
|
52
30
|
3.142.0 (2019-04-24)
|
53
31
|
====================
|
data/lib/selenium/webdriver.rb
CHANGED
@@ -39,7 +39,6 @@ module Selenium
|
|
39
39
|
autoload :Edge, 'selenium/webdriver/edge'
|
40
40
|
autoload :Firefox, 'selenium/webdriver/firefox'
|
41
41
|
autoload :IE, 'selenium/webdriver/ie'
|
42
|
-
autoload :PhantomJS, 'selenium/webdriver/phantomjs'
|
43
42
|
autoload :Remote, 'selenium/webdriver/remote'
|
44
43
|
autoload :Safari, 'selenium/webdriver/safari'
|
45
44
|
autoload :Support, 'selenium/webdriver/support'
|
@@ -54,10 +53,10 @@ module Selenium
|
|
54
53
|
# Create a new Driver instance with the correct bridge for the given browser
|
55
54
|
#
|
56
55
|
# @overload for(browser)
|
57
|
-
# @param [:ie, :internet_explorer, :edge, :remote, :chrome, :firefox, :ff, :
|
56
|
+
# @param [:ie, :internet_explorer, :edge, :remote, :chrome, :firefox, :ff, :safari] browser The browser to
|
58
57
|
# create the driver for
|
59
58
|
# @overload for(browser, opts)
|
60
|
-
# @param [:ie, :internet_explorer, :edge, :remote, :chrome, :firefox, :ff, :
|
59
|
+
# @param [:ie, :internet_explorer, :edge, :remote, :chrome, :firefox, :ff, :safari] browser The browser to
|
61
60
|
# create the driver for
|
62
61
|
# @param [Hash] opts Options passed to Driver.new
|
63
62
|
#
|
@@ -68,7 +67,6 @@ module Selenium
|
|
68
67
|
# @see Selenium::WebDriver::IE::Driver
|
69
68
|
# @see Selenium::WebDriver::Edge::Driver
|
70
69
|
# @see Selenium::WebDriver::Chrome::Driver
|
71
|
-
# @see Selenium::WebDriver::PhantomJS::Driver
|
72
70
|
# @see Selenium::WebDriver::Safari::Driver
|
73
71
|
#
|
74
72
|
# @example
|
@@ -23,11 +23,9 @@ module Selenium
|
|
23
23
|
module Bridge
|
24
24
|
|
25
25
|
COMMANDS = {
|
26
|
-
get_network_conditions: [:get, 'session/:session_id/chromium/network_conditions'],
|
27
|
-
set_network_conditions: [:post, 'session/:session_id/chromium/network_conditions'],
|
28
|
-
send_command: [:post, 'session/:session_id/goog/cdp/execute']
|
29
|
-
get_available_log_types: [:get, 'session/:session_id/se/log/types'],
|
30
|
-
get_log: [:post, 'session/:session_id/se/log']
|
26
|
+
get_network_conditions: [:get, '/session/:session_id/chromium/network_conditions'],
|
27
|
+
set_network_conditions: [:post, '/session/:session_id/chromium/network_conditions'],
|
28
|
+
send_command: [:post, '/session/:session_id/goog/cdp/execute']
|
31
29
|
}.freeze
|
32
30
|
|
33
31
|
def commands(command)
|
@@ -46,22 +44,6 @@ module Selenium
|
|
46
44
|
execute :set_network_conditions, {}, {network_conditions: conditions}
|
47
45
|
end
|
48
46
|
|
49
|
-
def available_log_types
|
50
|
-
types = execute :get_available_log_types
|
51
|
-
Array(types).map(&:to_sym)
|
52
|
-
end
|
53
|
-
|
54
|
-
def log(type)
|
55
|
-
data = execute :get_log, {}, {type: type.to_s}
|
56
|
-
|
57
|
-
Array(data).map do |l|
|
58
|
-
begin
|
59
|
-
LogEntry.new l.fetch('level', 'UNKNOWN'), l.fetch('timestamp'), l.fetch('message')
|
60
|
-
rescue KeyError
|
61
|
-
next
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
47
|
end # Bridge
|
66
48
|
end # Chrome
|
67
49
|
end # WebDriver
|
@@ -28,7 +28,6 @@ module Selenium
|
|
28
28
|
|
29
29
|
class Driver < WebDriver::Driver
|
30
30
|
include DriverExtensions::HasNetworkConditions
|
31
|
-
include DriverExtensions::HasTouchScreen
|
32
31
|
include DriverExtensions::HasWebStorage
|
33
32
|
include DriverExtensions::HasLocation
|
34
33
|
include DriverExtensions::TakesScreenshot
|
@@ -40,8 +39,11 @@ module Selenium
|
|
40
39
|
opts[:url] ||= service_url(opts)
|
41
40
|
|
42
41
|
listener = opts.delete(:listener)
|
43
|
-
|
42
|
+
desired_capabilities = opts.delete(:desired_capabilities)
|
43
|
+
|
44
|
+
@bridge = Remote::Bridge.new(opts)
|
44
45
|
@bridge.extend Bridge
|
46
|
+
@bridge.create_session(desired_capabilities)
|
45
47
|
|
46
48
|
super(@bridge, listener: listener)
|
47
49
|
end
|
@@ -66,58 +68,29 @@ module Selenium
|
|
66
68
|
caps = opts.delete(:desired_capabilities) { Remote::Capabilities.chrome }
|
67
69
|
options = opts.delete(:options) { Options.new }
|
68
70
|
|
69
|
-
args = opts.delete(:args) || opts.delete(:switches)
|
70
|
-
if args
|
71
|
-
WebDriver.logger.deprecate ':args or :switches', 'Selenium::WebDriver::Chrome::Options#add_argument'
|
72
|
-
raise ArgumentError, ':args must be an Array of Strings' unless args.is_a? Array
|
73
|
-
|
74
|
-
args.each { |arg| options.add_argument(arg.to_s) }
|
75
|
-
end
|
76
|
-
|
77
71
|
profile = opts.delete(:profile)
|
78
72
|
if profile
|
79
|
-
WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome::Driver#new with `:profile` parameter',
|
80
|
-
'Selenium::WebDriver::Chrome::Options#profile or Options#add_option'
|
81
|
-
|
82
73
|
profile = profile.as_json
|
83
74
|
|
84
|
-
if options.args.none?(&/user-data-dir/.method(:match?))
|
85
|
-
options.add_argument("--user-data-dir=#{profile['directory']}")
|
86
|
-
end
|
75
|
+
options.add_argument("--user-data-dir=#{profile[:directory]}") if options.args.none?(&/user-data-dir/.method(:match?))
|
87
76
|
|
88
|
-
if profile[
|
89
|
-
WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome::Profile#extensions',
|
77
|
+
if profile[:extensions]
|
78
|
+
WebDriver.logger.deprecate 'Using Selenium::WebDriver::Chrome::Profile#extensions',
|
90
79
|
'Selenium::WebDriver::Chrome::Options#add_extension'
|
91
|
-
profile[
|
80
|
+
profile[:extensions].each do |extension|
|
92
81
|
options.add_encoded_extension(extension)
|
93
82
|
end
|
94
83
|
end
|
95
84
|
end
|
96
85
|
|
97
|
-
|
98
|
-
|
99
|
-
'Selenium::WebDriver::Chrome::Options#new or Options#add_option'
|
100
|
-
options.add_option(:detach, opts.delete(:detach))
|
101
|
-
end
|
102
|
-
|
103
|
-
prefs = opts.delete(:prefs)
|
104
|
-
if prefs
|
105
|
-
WebDriver.logger.deprecate ':prefs', 'Selenium::WebDriver::Chrome::Options#add_preference'
|
106
|
-
prefs.each do |key, value|
|
107
|
-
options.add_preference(key, value)
|
108
|
-
end
|
109
|
-
end
|
86
|
+
detach = opts.delete(:detach)
|
87
|
+
options.add_option(:detach, true) if detach
|
110
88
|
|
111
89
|
options = options.as_json
|
112
90
|
caps.merge!(options) unless options[Options::KEY].empty?
|
113
91
|
|
114
|
-
|
115
|
-
|
116
|
-
'Selenium::WebDriver::Chrome::Capabilities#proxy='
|
117
|
-
|
118
|
-
caps[:proxy] = opts.delete(:proxy) if opts.key?(:proxy)
|
119
|
-
caps[:proxy] ||= opts.delete('proxy') if opts.key?('proxy')
|
120
|
-
end
|
92
|
+
caps[:proxy] = opts.delete(:proxy) if opts.key?(:proxy)
|
93
|
+
caps[:proxy] ||= opts.delete('proxy') if opts.key?('proxy')
|
121
94
|
|
122
95
|
caps
|
123
96
|
end
|
@@ -20,9 +20,9 @@
|
|
20
20
|
module Selenium
|
21
21
|
module WebDriver
|
22
22
|
module Chrome
|
23
|
-
class Options
|
23
|
+
class Options
|
24
24
|
attr_reader :args, :prefs, :options, :emulation, :extensions, :encoded_extensions
|
25
|
-
attr_accessor :binary
|
25
|
+
attr_accessor :binary
|
26
26
|
|
27
27
|
KEY = 'goog:chromeOptions'
|
28
28
|
|
@@ -49,8 +49,6 @@ module Selenium
|
|
49
49
|
@extensions = opts.delete(:extensions) || []
|
50
50
|
@options = opts.delete(:options) || {}
|
51
51
|
@emulation = opts.delete(:emulation) || {}
|
52
|
-
@detach = opts.delete(:detach)
|
53
|
-
@profile = opts.delete(:profile)
|
54
52
|
@encoded_extensions = []
|
55
53
|
end
|
56
54
|
|
@@ -172,7 +170,6 @@ module Selenium
|
|
172
170
|
File.open(crx_path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
|
173
171
|
end
|
174
172
|
extensions.concat(@encoded_extensions)
|
175
|
-
add_argument("--user-data-dir=#{@profile[:directory]}") if @profile
|
176
173
|
|
177
174
|
opts = @options
|
178
175
|
opts[:binary] = @binary if @binary
|
@@ -180,9 +177,8 @@ module Selenium
|
|
180
177
|
opts[:extensions] = extensions if extensions.any?
|
181
178
|
opts[:mobileEmulation] = @emulation unless @emulation.empty?
|
182
179
|
opts[:prefs] = @prefs unless @prefs.empty?
|
183
|
-
opts[:detach] = @detach if !@detach.nil? && @detach != false
|
184
180
|
|
185
|
-
{KEY =>
|
181
|
+
{KEY => opts}
|
186
182
|
end
|
187
183
|
end # Options
|
188
184
|
end # Chrome
|
@@ -77,8 +77,8 @@ module Selenium
|
|
77
77
|
|
78
78
|
extensions.concat(@encoded_extensions)
|
79
79
|
|
80
|
-
opts = {
|
81
|
-
opts[
|
80
|
+
opts = {directory: directory || layout_on_disk}
|
81
|
+
opts[:extensions] = extensions if extensions.any?
|
82
82
|
opts
|
83
83
|
end
|
84
84
|
|
@@ -25,19 +25,14 @@ module Selenium
|
|
25
25
|
#
|
26
26
|
|
27
27
|
class Service < WebDriver::Service
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
DEFAULT_PORT = 9515
|
29
|
+
EXECUTABLE = 'chromedriver'
|
30
|
+
MISSING_TEXT = <<~ERROR
|
31
31
|
Unable to find chromedriver. Please download the server from
|
32
32
|
https://chromedriver.storage.googleapis.com/index.html and place it somewhere on your PATH.
|
33
33
|
More info at https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver.
|
34
34
|
ERROR
|
35
|
-
|
36
|
-
|
37
|
-
def self.driver_path=(path)
|
38
|
-
Platform.assert_executable path if path.is_a?(String)
|
39
|
-
@driver_path = path
|
40
|
-
end
|
35
|
+
SHUTDOWN_SUPPORTED = true
|
41
36
|
|
42
37
|
private
|
43
38
|
|
@@ -29,9 +29,6 @@ require 'selenium/webdriver/common/port_prober'
|
|
29
29
|
require 'selenium/webdriver/common/zipper'
|
30
30
|
require 'selenium/webdriver/common/wait'
|
31
31
|
require 'selenium/webdriver/common/alert'
|
32
|
-
require 'selenium/webdriver/common/mouse'
|
33
|
-
require 'selenium/webdriver/common/keyboard'
|
34
|
-
require 'selenium/webdriver/common/touch_screen'
|
35
32
|
require 'selenium/webdriver/common/target_locator'
|
36
33
|
require 'selenium/webdriver/common/navigation'
|
37
34
|
require 'selenium/webdriver/common/timeouts'
|
@@ -39,13 +36,16 @@ require 'selenium/webdriver/common/window'
|
|
39
36
|
require 'selenium/webdriver/common/logger'
|
40
37
|
require 'selenium/webdriver/common/logs'
|
41
38
|
require 'selenium/webdriver/common/manager'
|
42
|
-
require 'selenium/webdriver/common/w3c_manager'
|
43
39
|
require 'selenium/webdriver/common/search_context'
|
44
|
-
require 'selenium/webdriver/common/action_builder'
|
45
40
|
require 'selenium/webdriver/common/interactions/key_actions'
|
46
41
|
require 'selenium/webdriver/common/interactions/pointer_actions'
|
47
|
-
require 'selenium/webdriver/common/
|
48
|
-
require 'selenium/webdriver/common/
|
42
|
+
require 'selenium/webdriver/common/interactions/interactions'
|
43
|
+
require 'selenium/webdriver/common/interactions/input_device'
|
44
|
+
require 'selenium/webdriver/common/interactions/interaction'
|
45
|
+
require 'selenium/webdriver/common/interactions/none_input'
|
46
|
+
require 'selenium/webdriver/common/interactions/key_input'
|
47
|
+
require 'selenium/webdriver/common/interactions/pointer_input'
|
48
|
+
require 'selenium/webdriver/common/action_builder'
|
49
49
|
require 'selenium/webdriver/common/html5/shared_web_storage'
|
50
50
|
require 'selenium/webdriver/common/html5/local_storage'
|
51
51
|
require 'selenium/webdriver/common/html5/session_storage'
|
@@ -55,7 +55,6 @@ require 'selenium/webdriver/common/driver_extensions/has_web_storage'
|
|
55
55
|
require 'selenium/webdriver/common/driver_extensions/downloads_files'
|
56
56
|
require 'selenium/webdriver/common/driver_extensions/has_location'
|
57
57
|
require 'selenium/webdriver/common/driver_extensions/has_session_id'
|
58
|
-
require 'selenium/webdriver/common/driver_extensions/has_touch_screen'
|
59
58
|
require 'selenium/webdriver/common/driver_extensions/has_remote_status'
|
60
59
|
require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
|
61
60
|
require 'selenium/webdriver/common/driver_extensions/has_network_connection'
|
@@ -63,15 +62,7 @@ require 'selenium/webdriver/common/driver_extensions/has_permissions'
|
|
63
62
|
require 'selenium/webdriver/common/driver_extensions/has_debugger'
|
64
63
|
require 'selenium/webdriver/common/driver_extensions/uploads_files'
|
65
64
|
require 'selenium/webdriver/common/driver_extensions/has_addons'
|
66
|
-
require 'selenium/webdriver/common/interactions/interactions'
|
67
|
-
require 'selenium/webdriver/common/interactions/input_device'
|
68
|
-
require 'selenium/webdriver/common/interactions/interaction'
|
69
|
-
require 'selenium/webdriver/common/interactions/none_input'
|
70
|
-
require 'selenium/webdriver/common/interactions/key_input'
|
71
|
-
require 'selenium/webdriver/common/interactions/pointer_input'
|
72
65
|
require 'selenium/webdriver/common/keys'
|
73
|
-
require 'selenium/webdriver/common/bridge_helper'
|
74
66
|
require 'selenium/webdriver/common/profile_helper'
|
75
|
-
require 'selenium/webdriver/common/options'
|
76
67
|
require 'selenium/webdriver/common/driver'
|
77
68
|
require 'selenium/webdriver/common/element'
|
@@ -19,346 +19,194 @@
|
|
19
19
|
|
20
20
|
module Selenium
|
21
21
|
module WebDriver
|
22
|
-
#
|
23
|
-
# The ActionBuilder provides the user a way to set up and perform
|
24
|
-
# complex user interactions.
|
25
|
-
#
|
26
|
-
# This class should not be instantiated directly, but is created by Driver#action
|
27
|
-
#
|
28
|
-
# @example
|
29
|
-
#
|
30
|
-
# driver.action.key_down(:shift).
|
31
|
-
# click(element).
|
32
|
-
# click(second_element).
|
33
|
-
# key_up(:shift).
|
34
|
-
# drag_and_drop(element, third_element).
|
35
|
-
# perform
|
36
|
-
#
|
37
|
-
|
38
22
|
class ActionBuilder
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
23
|
+
include KeyActions # Actions specific to key inputs
|
24
|
+
include PointerActions # Actions specific to pointer inputs
|
42
25
|
|
43
|
-
|
44
|
-
@devices = {
|
45
|
-
mouse: mouse,
|
46
|
-
keyboard: keyboard
|
47
|
-
}
|
48
|
-
|
49
|
-
@actions = []
|
50
|
-
end
|
26
|
+
attr_reader :devices
|
51
27
|
|
52
28
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
29
|
+
# Initialize a W3C Action Builder. Differs from previous by requiring a bridge and allowing asynchronous actions.
|
30
|
+
# The W3C implementation allows asynchronous actions per device. e.g. A key can be pressed at the same time that
|
31
|
+
# the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions
|
32
|
+
# correctly when using asynchronous.
|
57
33
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# @
|
64
|
-
#
|
65
|
-
# driver.action.key_down(:control).perform
|
66
|
-
#
|
67
|
-
# @example Press a key on an element
|
68
|
-
#
|
69
|
-
# el = driver.find_element(id: "some_id")
|
70
|
-
# driver.action.key_down(el, :shift).perform
|
34
|
+
# @param [Selenium::WebDriver::Remote::Bridge] bridge the bridge for the current driver instance
|
35
|
+
# @param [Selenium::WebDriver::Interactions::PointerInput] mouse PointerInput for the mouse.
|
36
|
+
# @param [Selenium::WebDriver::Interactions::KeyInput] keyboard KeyInput for the keyboard.
|
37
|
+
# @param [Boolean] async Whether to perform the actions asynchronously per device. Defaults to false for
|
38
|
+
# backwards compatibility.
|
39
|
+
# @return [ActionBuilder] A self reference.
|
71
40
|
#
|
72
|
-
# @overload key_down(key)
|
73
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to press
|
74
|
-
# @overload key_down(element, key)
|
75
|
-
# @param [Element] element An optional element to move to first
|
76
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to press
|
77
|
-
# @raise [ArgumentError] if the given key is not a modifier
|
78
|
-
# @return [ActionBuilder] A self reference
|
79
41
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
@
|
84
|
-
|
42
|
+
def initialize(bridge, mouse, keyboard, async = false)
|
43
|
+
# For backwards compatibility, automatically include mouse & keyboard
|
44
|
+
@bridge = bridge
|
45
|
+
@devices = [mouse, keyboard]
|
46
|
+
@async = async
|
85
47
|
end
|
86
48
|
|
87
49
|
#
|
88
|
-
#
|
89
|
-
# Releasing a non-depressed modifier key will yield undefined behaviour.
|
50
|
+
# Adds a PointerInput device of the given kind
|
90
51
|
#
|
91
|
-
# @example
|
52
|
+
# @example Add a touch pointer input device
|
92
53
|
#
|
93
|
-
#
|
54
|
+
# builder = device.action
|
55
|
+
# builder.add_pointer_input('touch', :touch)
|
94
56
|
#
|
95
|
-
# @
|
57
|
+
# @param [String] name name for the device
|
58
|
+
# @param [Symbol] kind kind of pointer device to create
|
59
|
+
# @return [Interactions::PointerInput] The pointer input added
|
96
60
|
#
|
97
|
-
# el = driver.find_element(id: "some_id")
|
98
|
-
# driver.action.key_up(el, :alt).perform
|
99
61
|
#
|
100
|
-
# @overload key_up(key)
|
101
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to release
|
102
|
-
# @overload key_up(element, key)
|
103
|
-
# @param [Element] element An optional element to move to first
|
104
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to release
|
105
|
-
# @raise [ArgumentError] if the given key is not a modifier
|
106
|
-
# @return [ActionBuilder] A self reference
|
107
|
-
#
|
108
|
-
|
109
|
-
def key_up(*args)
|
110
|
-
@actions << [:mouse, :click, [args.shift]] if args.first.is_a? Element
|
111
62
|
|
112
|
-
|
113
|
-
|
63
|
+
def add_pointer_input(kind, name)
|
64
|
+
new_input = Interactions.pointer(kind, name: name)
|
65
|
+
add_input(new_input)
|
66
|
+
new_input
|
114
67
|
end
|
115
68
|
|
116
69
|
#
|
117
|
-
#
|
118
|
-
# Element#send_keys(keys) on the active element in two ways:
|
119
|
-
#
|
120
|
-
# * The modifier keys included in this call are not released.
|
121
|
-
# * There is no attempt to re-focus the element - so send_keys(:tab) for switching elements should work.
|
122
|
-
#
|
123
|
-
# @example Send the text "help" to an element
|
124
|
-
#
|
125
|
-
# el = driver.find_element(id: "some_id")
|
126
|
-
# driver.action.send_keys(el, "help").perform
|
70
|
+
# Adds a KeyInput device
|
127
71
|
#
|
128
|
-
# @example
|
72
|
+
# @example Add a key input device
|
129
73
|
#
|
130
|
-
#
|
74
|
+
# builder = device.action
|
75
|
+
# builder.add_key_input('keyboard2')
|
131
76
|
#
|
132
|
-
# @
|
133
|
-
#
|
134
|
-
# @overload send_keys(element, keys)
|
135
|
-
# @param [Element] element An optional element to move to first
|
136
|
-
# @param [Array, Symbol, String] keys The key(s) to press and release
|
137
|
-
# @return [ActionBuilder] A self reference
|
77
|
+
# @param [String] name name for the device
|
78
|
+
# @return [Interactions::KeyInput] The key input added
|
138
79
|
#
|
139
80
|
|
140
|
-
def
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
self
|
81
|
+
def add_key_input(name)
|
82
|
+
new_input = Interactions.key(name)
|
83
|
+
add_input(new_input)
|
84
|
+
new_input
|
145
85
|
end
|
146
86
|
|
147
87
|
#
|
148
|
-
#
|
149
|
-
# equivalent to:
|
150
|
-
#
|
151
|
-
# driver.action.move_to(element).click_and_hold
|
152
|
-
#
|
153
|
-
# @example Clicking and holding on some element
|
88
|
+
# Retrieves the input device for the given name
|
154
89
|
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
# @param [Element] element the element to move to and click.
|
159
|
-
# @return [ActionBuilder] A self reference.
|
90
|
+
# @param [String] name name of the input device
|
91
|
+
# @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name
|
160
92
|
#
|
161
93
|
|
162
|
-
def
|
163
|
-
@
|
164
|
-
self
|
94
|
+
def get_device(name)
|
95
|
+
@devices.find { |device| device.name == name.to_s }
|
165
96
|
end
|
166
97
|
|
167
98
|
#
|
168
|
-
#
|
99
|
+
# Retrieves the current PointerInput devices
|
169
100
|
#
|
170
|
-
# @
|
171
|
-
#
|
172
|
-
# el = driver.find_element(id: "some_id")
|
173
|
-
# driver.action.click_and_hold(el).release.perform
|
174
|
-
#
|
175
|
-
# @return [ActionBuilder] A self reference.
|
101
|
+
# @return [Array] array of current PointerInput devices
|
176
102
|
#
|
177
103
|
|
178
|
-
def
|
179
|
-
@
|
180
|
-
self
|
104
|
+
def pointer_inputs
|
105
|
+
@devices.select { |device| device.type == Interactions::POINTER }
|
181
106
|
end
|
182
107
|
|
183
108
|
#
|
184
|
-
#
|
185
|
-
#
|
186
|
-
# driver.action.move_to(element).click
|
187
|
-
#
|
188
|
-
# When no element is passed, the current mouse position will be clicked.
|
189
|
-
#
|
190
|
-
# @example Clicking on an element
|
109
|
+
# Retrieves the current KeyInput device
|
191
110
|
#
|
192
|
-
#
|
193
|
-
# driver.action.click(el).perform
|
194
|
-
#
|
195
|
-
# @example Clicking at the current mouse position
|
196
|
-
#
|
197
|
-
# driver.action.click.perform
|
198
|
-
#
|
199
|
-
# @param [Selenium::WebDriver::Element] element An optional element to click.
|
200
|
-
# @return [ActionBuilder] A self reference.
|
111
|
+
# @return [Selenium::WebDriver::Interactions::InputDevice] current KeyInput device
|
201
112
|
#
|
202
113
|
|
203
|
-
def
|
204
|
-
@
|
205
|
-
self
|
114
|
+
def key_inputs
|
115
|
+
@devices.select { |device| device.type == Interactions::KEY }
|
206
116
|
end
|
207
117
|
|
208
118
|
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
# driver.action.move_to(element).double_click
|
119
|
+
# Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait
|
120
|
+
# for all actions to complete in that tick.
|
212
121
|
#
|
213
|
-
# @example
|
122
|
+
# @example Send keys to an element
|
214
123
|
#
|
215
|
-
#
|
216
|
-
#
|
124
|
+
# action_builder = driver.action
|
125
|
+
# keyboard = action_builder.key_input
|
126
|
+
# el = driver.find_element(id: "some_id")
|
127
|
+
# driver.action.click(el).pause(keyboard).pause(keyboard).pause(keyboard).send_keys('keys').perform
|
217
128
|
#
|
218
|
-
# @param [
|
129
|
+
# @param [InputDevice] device Input device to pause
|
130
|
+
# @param [Float] duration Duration to pause
|
219
131
|
# @return [ActionBuilder] A self reference.
|
220
132
|
#
|
221
133
|
|
222
|
-
def
|
223
|
-
|
134
|
+
def pause(device, duration = nil)
|
135
|
+
device.create_pause(duration)
|
224
136
|
self
|
225
137
|
end
|
226
138
|
|
227
139
|
#
|
228
|
-
#
|
229
|
-
# view and its location is calculated using getBoundingClientRect. Then the
|
230
|
-
# mouse is moved to optional offset coordinates from the element.
|
231
|
-
#
|
232
|
-
# Note that when using offsets, both coordinates need to be passed.
|
233
|
-
#
|
234
|
-
# @example Scroll element into view and move the mouse to it
|
140
|
+
# Creates multiple pauses for the given device of the given duration.
|
235
141
|
#
|
236
|
-
#
|
237
|
-
# driver.action.move_to(el).perform
|
238
|
-
#
|
239
|
-
# @example
|
142
|
+
# @example Send keys to an element
|
240
143
|
#
|
144
|
+
# action_builder = driver.action
|
145
|
+
# keyboard = action_builder.key_input
|
241
146
|
# el = driver.find_element(id: "some_id")
|
242
|
-
# driver.action.
|
147
|
+
# driver.action.click(el).pauses(keyboard, 3).send_keys('keys').perform
|
243
148
|
#
|
244
|
-
# @param [
|
245
|
-
# @param [Integer]
|
246
|
-
#
|
247
|
-
# @param [Integer] down_by Optional offset from the top-left corner. A negative value means
|
248
|
-
# coordinates above the element.
|
149
|
+
# @param [InputDevice] device Input device to pause
|
150
|
+
# @param [Integer] number of pauses to add for the device
|
151
|
+
# @param [Float] duration Duration to pause
|
249
152
|
# @return [ActionBuilder] A self reference.
|
250
153
|
#
|
251
154
|
|
252
|
-
def
|
253
|
-
|
254
|
-
[:mouse, :move_to, [element, Integer(right_by), Integer(down_by)]]
|
255
|
-
else
|
256
|
-
[:mouse, :move_to, [element]]
|
257
|
-
end
|
258
|
-
|
155
|
+
def pauses(device, number, duration = nil)
|
156
|
+
number.times { device.create_pause(duration) }
|
259
157
|
self
|
260
158
|
end
|
261
159
|
|
262
160
|
#
|
263
|
-
#
|
264
|
-
# If the coordinates provided are outside the viewport (the mouse will
|
265
|
-
# end up outside the browser window) then the viewport is scrolled to
|
266
|
-
# match.
|
267
|
-
#
|
268
|
-
# @example Move the mouse to a certain offset from its current position
|
269
|
-
#
|
270
|
-
# driver.action.move_by(100, 100).perform
|
271
|
-
#
|
272
|
-
# @param [Integer] right_by horizontal offset. A negative value means moving the
|
273
|
-
# mouse left.
|
274
|
-
# @param [Integer] down_by vertical offset. A negative value means moving the mouse
|
275
|
-
# up.
|
276
|
-
# @return [ActionBuilder] A self reference.
|
277
|
-
# @raise [MoveTargetOutOfBoundsError] if the provided offset is outside
|
278
|
-
# the document's boundaries.
|
161
|
+
# Executes the actions added to the builder.
|
279
162
|
#
|
280
163
|
|
281
|
-
def
|
282
|
-
@
|
283
|
-
|
164
|
+
def perform
|
165
|
+
@bridge.send_actions @devices.map(&:encode).compact
|
166
|
+
clear_all_actions
|
167
|
+
nil
|
284
168
|
end
|
285
169
|
|
286
170
|
#
|
287
|
-
#
|
288
|
-
# a move_to to the location of the element.
|
289
|
-
#
|
290
|
-
# @example Context-click at middle of given element
|
291
|
-
#
|
292
|
-
# el = driver.find_element(id: "some_id")
|
293
|
-
# driver.action.context_click(el).perform
|
294
|
-
#
|
295
|
-
# @param [Selenium::WebDriver::Element] element An element to context click.
|
296
|
-
# @return [ActionBuilder] A self reference.
|
171
|
+
# Clears all actions from the builder.
|
297
172
|
#
|
298
173
|
|
299
|
-
def
|
300
|
-
@
|
301
|
-
self
|
174
|
+
def clear_all_actions
|
175
|
+
@devices.each(&:clear_actions)
|
302
176
|
end
|
303
177
|
|
304
178
|
#
|
305
|
-
#
|
306
|
-
# source element, moves to the location of the target element, then
|
307
|
-
# releases the mouse.
|
308
|
-
#
|
309
|
-
# @example Drag and drop one element onto another
|
310
|
-
#
|
311
|
-
# el1 = driver.find_element(id: "some_id1")
|
312
|
-
# el2 = driver.find_element(id: "some_id2")
|
313
|
-
# driver.action.drag_and_drop(el1, el2).perform
|
314
|
-
#
|
315
|
-
# @param [Selenium::WebDriver::Element] source element to emulate button down at.
|
316
|
-
# @param [Selenium::WebDriver::Element] target element to move to and release the
|
317
|
-
# mouse at.
|
318
|
-
# @return [ActionBuilder] A self reference.
|
179
|
+
# Releases all action states from the browser.
|
319
180
|
#
|
320
181
|
|
321
|
-
def
|
322
|
-
|
323
|
-
move_to target
|
324
|
-
release
|
325
|
-
|
326
|
-
self
|
182
|
+
def release_actions
|
183
|
+
@bridge.release_actions
|
327
184
|
end
|
328
185
|
|
186
|
+
private
|
187
|
+
|
329
188
|
#
|
330
|
-
#
|
331
|
-
# the source element, moves by a given offset, then releases the mouse.
|
332
|
-
#
|
333
|
-
# @example Drag and drop an element by offset
|
189
|
+
# Adds pauses for all devices but the given devices
|
334
190
|
#
|
335
|
-
#
|
336
|
-
# driver.action.drag_and_drop_by(el, 100, 100).perform
|
337
|
-
#
|
338
|
-
# @param [Selenium::WebDriver::Element] source Element to emulate button down at.
|
339
|
-
# @param [Integer] right_by horizontal move offset.
|
340
|
-
# @param [Integer] down_by vertical move offset.
|
341
|
-
# @return [ActionBuilder] A self reference.
|
191
|
+
# @param [Array[InputDevice]] action_devices Array of Input Devices performing an action in this tick.
|
342
192
|
#
|
343
193
|
|
344
|
-
def
|
345
|
-
|
346
|
-
move_by right_by, down_by
|
347
|
-
release
|
194
|
+
def tick(*action_devices)
|
195
|
+
return if @async
|
348
196
|
|
349
|
-
|
197
|
+
@devices.each { |device| device.create_pause unless action_devices.include? device }
|
350
198
|
end
|
351
199
|
|
352
200
|
#
|
353
|
-
#
|
201
|
+
# Adds an InputDevice
|
354
202
|
#
|
355
203
|
|
356
|
-
def
|
357
|
-
@
|
358
|
-
@devices.
|
204
|
+
def add_input(device)
|
205
|
+
unless @async
|
206
|
+
max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }
|
207
|
+
pauses(device, max_device.actions.length)
|
359
208
|
end
|
360
|
-
|
361
|
-
nil
|
209
|
+
@devices << device
|
362
210
|
end
|
363
211
|
end # ActionBuilder
|
364
212
|
end # WebDriver
|