selenium-webdriver 3.142.7 → 4.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +21 -43
  3. data/lib/selenium/webdriver.rb +2 -4
  4. data/lib/selenium/webdriver/chrome/bridge.rb +3 -21
  5. data/lib/selenium/webdriver/chrome/driver.rb +12 -39
  6. data/lib/selenium/webdriver/chrome/options.rb +3 -7
  7. data/lib/selenium/webdriver/chrome/profile.rb +2 -2
  8. data/lib/selenium/webdriver/chrome/service.rb +4 -9
  9. data/lib/selenium/webdriver/common.rb +7 -16
  10. data/lib/selenium/webdriver/common/action_builder.rb +97 -249
  11. data/lib/selenium/webdriver/common/driver.rb +2 -4
  12. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +1 -1
  13. data/lib/selenium/webdriver/common/element.rb +3 -6
  14. data/lib/selenium/webdriver/common/error.rb +27 -203
  15. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -5
  16. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +13 -13
  17. data/lib/selenium/webdriver/common/manager.rb +1 -1
  18. data/lib/selenium/webdriver/common/options.rb +148 -24
  19. data/lib/selenium/webdriver/common/service.rb +16 -34
  20. data/lib/selenium/webdriver/common/socket_poller.rb +2 -2
  21. data/lib/selenium/webdriver/common/w3c_options.rb +45 -0
  22. data/lib/selenium/webdriver/edge.rb +0 -1
  23. data/lib/selenium/webdriver/edge/driver.rb +14 -10
  24. data/lib/selenium/webdriver/edge/service.rb +6 -7
  25. data/lib/selenium/webdriver/firefox.rb +2 -6
  26. data/lib/selenium/webdriver/firefox/binary.rb +3 -80
  27. data/lib/selenium/webdriver/firefox/bridge.rb +47 -0
  28. data/lib/selenium/webdriver/firefox/driver.rb +44 -22
  29. data/lib/selenium/webdriver/firefox/marionette/driver.rb +1 -1
  30. data/lib/selenium/webdriver/firefox/options.rb +2 -2
  31. data/lib/selenium/webdriver/firefox/profile.rb +25 -14
  32. data/lib/selenium/webdriver/firefox/service.rb +4 -4
  33. data/lib/selenium/webdriver/ie/driver.rb +5 -18
  34. data/lib/selenium/webdriver/ie/options.rb +2 -2
  35. data/lib/selenium/webdriver/ie/service.rb +4 -4
  36. data/lib/selenium/webdriver/remote.rb +2 -6
  37. data/lib/selenium/webdriver/remote/bridge.rb +515 -69
  38. data/lib/selenium/webdriver/remote/capabilities.rb +77 -99
  39. data/lib/selenium/webdriver/remote/commands.rb +156 -0
  40. data/lib/selenium/webdriver/remote/driver.rb +12 -5
  41. data/lib/selenium/webdriver/remote/http/default.rb +0 -9
  42. data/lib/selenium/webdriver/remote/response.rb +16 -47
  43. data/lib/selenium/webdriver/safari.rb +1 -1
  44. data/lib/selenium/webdriver/safari/bridge.rb +3 -3
  45. data/lib/selenium/webdriver/safari/driver.rb +4 -1
  46. data/lib/selenium/webdriver/safari/service.rb +4 -4
  47. data/lib/selenium/webdriver/support/select.rb +1 -1
  48. data/lib/selenium/webdriver/version.rb +1 -1
  49. data/selenium-webdriver.gemspec +3 -3
  50. metadata +14 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c9f40e154da198fff9e277f0613042c22fff6c98
4
- data.tar.gz: 33cc734e78b08e66c00a4c73f51c635d97c86fe6
3
+ metadata.gz: bc17a0dfe5aefbf024e5e6c64082ba16209c7f36
4
+ data.tar.gz: 6e7cda3f69ed3fcaaa591b9720b7674a3f8d7a23
5
5
  SHA512:
6
- metadata.gz: b40eb2e74a284b21ee31924f7cf20b878c7ab6ebcfe7848b21ad163165c80467c9d3a6fa6252ca74680b82db7186e8c1b73a2ac5f4d37501b0dfa16763ec19b9
7
- data.tar.gz: bc9e9e0128888f3efe33f59160b677cc88655bf986877c534053adf915f3b39a78ff1d88e9b2d80753c1aba49af92f1796bb4f774ca68114027594813cc26ad3
6
+ metadata.gz: 2dfdf006f048202deedb179a6279c5000082e298ecc29c97806dc1199517bfa625ee45d6be2a2f88ece82869a5b4f593795b3940aa781539740a91d2a8a33523
7
+ data.tar.gz: 85a754811a838367f4a93699b1e44b58eb3493a0ec44d9367d7be566a28ec29a820f89befda4d7f73383443d999f87d76f4cc0b484b920600c30d2b15289e496
data/CHANGES CHANGED
@@ -1,53 +1,31 @@
1
- 3.142.7 (2019-12-27)
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
- * Loosen RubyZip dependency so that 1.3+ can be used (thanks @vtamara)
18
-
19
- 3.142.4 (2019-09-02)
20
- ====================
21
-
22
- Chrome:
23
- * Added support for new command for getting logs in ChromeDriver 76+
24
- with W3C mode on
25
-
26
- 3.142.3 (2019-05-21)
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
- * Fixed an issue when getting/setting network conditions and sending CDP
39
- commands didn't work with Grid (issue #7174)
17
+ * Removed support for OSS mode - use W3C mode instead by using
18
+ Selenium::WebDriver::Chrome::Options.new(options: {w3c: true})
40
19
 
41
- Safari:
42
- * Fixed an issue when getting/setting permissions and attaching debugger
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
- * Fixed an issue when processing error in legacy driver would result
50
- in NoMethodError (issue #7178)
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
  ====================
@@ -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, :phantomjs, :safari] browser The browser to
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, :phantomjs, :safari] browser The browser to
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
- @bridge = Remote::Bridge.handshake(**opts)
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['extensions']
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['extensions'].each do |extension|
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
- if opts.key?(:detach)
98
- WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome::Driver#new with `:detach` parameter',
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
- if opts.key?(:proxy) || opts.key?('proxy')
115
- WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome::Driver#new with `:proxy` parameter',
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 < WebDriver::Common::Options
23
+ class Options
24
24
  attr_reader :args, :prefs, :options, :emulation, :extensions, :encoded_extensions
25
- attr_accessor :binary, :profile, :detach
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 => generate_as_json(opts)}
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 = {'directory' => directory || layout_on_disk}
81
- opts['extensions'] = extensions if extensions.any?
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
- @default_port = 9515
29
- @executable = 'chromedriver'
30
- @missing_text = <<~ERROR
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
- @shutdown_supported = true
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/w3c_action_builder'
48
- require 'selenium/webdriver/common/touch_action_builder'
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
- # @api private
41
- #
23
+ include KeyActions # Actions specific to key inputs
24
+ include PointerActions # Actions specific to pointer inputs
42
25
 
43
- def initialize(mouse, keyboard)
44
- @devices = {
45
- mouse: mouse,
46
- keyboard: keyboard
47
- }
48
-
49
- @actions = []
50
- end
26
+ attr_reader :devices
51
27
 
52
28
  #
53
- # Performs a modifier key press. Does not release
54
- # the modifier key - subsequent interactions may assume it's kept pressed.
55
- # Note that the modifier key is never released implicitly - either
56
- # #key_up(key) or #send_keys(:null) must be called to release the modifier.
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
- # Equivalent to:
59
- # driver.action.click(element).send_keys(key)
60
- # # or
61
- # driver.action.click.send_keys(key)
62
- #
63
- # @example Press a key
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 key_down(*args)
81
- @actions << [:mouse, :click, [args.shift]] if args.first.is_a? Element
82
-
83
- @actions << [:keyboard, :press, args]
84
- self
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
- # Performs a modifier key release.
89
- # Releasing a non-depressed modifier key will yield undefined behaviour.
50
+ # Adds a PointerInput device of the given kind
90
51
  #
91
- # @example Release a key
52
+ # @example Add a touch pointer input device
92
53
  #
93
- # driver.action.key_up(:shift).perform
54
+ # builder = device.action
55
+ # builder.add_pointer_input('touch', :touch)
94
56
  #
95
- # @example Release a key from an element
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
- @actions << [:keyboard, :release, args]
113
- self
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
- # Sends keys to the active element. This differs from calling
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 Send the text "help" to the currently focused element
72
+ # @example Add a key input device
129
73
  #
130
- # driver.action.send_keys("help").perform
74
+ # builder = device.action
75
+ # builder.add_key_input('keyboard2')
131
76
  #
132
- # @overload send_keys(keys)
133
- # @param [Array, Symbol, String] keys The key(s) to press and release
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 send_keys(*args)
141
- @actions << [:mouse, :click, [args.shift]] if args.first.is_a? Element
142
-
143
- @actions << [:keyboard, :send_keys, args]
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
- # Clicks (without releasing) in the middle of the given element. This is
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
- # el = driver.find_element(id: "some_id")
156
- # driver.action.click_and_hold(el).perform
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 click_and_hold(element = nil)
163
- @actions << [:mouse, :down, [element]]
164
- self
94
+ def get_device(name)
95
+ @devices.find { |device| device.name == name.to_s }
165
96
  end
166
97
 
167
98
  #
168
- # Releases the depressed left mouse button at the current mouse location.
99
+ # Retrieves the current PointerInput devices
169
100
  #
170
- # @example Releasing an element after clicking and holding it
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 release(element = nil)
179
- @actions << [:mouse, :up, [element]]
180
- self
104
+ def pointer_inputs
105
+ @devices.select { |device| device.type == Interactions::POINTER }
181
106
  end
182
107
 
183
108
  #
184
- # Clicks in the middle of the given element. Equivalent to:
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
- # el = driver.find_element(id: "some_id")
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 click(element = nil)
204
- @actions << [:mouse, :click, [element]]
205
- self
114
+ def key_inputs
115
+ @devices.select { |device| device.type == Interactions::KEY }
206
116
  end
207
117
 
208
118
  #
209
- # Performs a double-click at middle of the given element. Equivalent to:
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 Double click an element
122
+ # @example Send keys to an element
214
123
  #
215
- # el = driver.find_element(id: "some_id")
216
- # driver.action.double_click(el).perform
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 [Selenium::WebDriver::Element] element An optional element to move to.
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 double_click(element = nil)
223
- @actions << [:mouse, :double_click, [element]]
134
+ def pause(device, duration = nil)
135
+ device.create_pause(duration)
224
136
  self
225
137
  end
226
138
 
227
139
  #
228
- # Moves the mouse to the middle of the given element. The element is scrolled into
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
- # el = driver.find_element(id: "some_id")
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.move_to(el, 100, 100).perform
147
+ # driver.action.click(el).pauses(keyboard, 3).send_keys('keys').perform
243
148
  #
244
- # @param [Selenium::WebDriver::Element] element to move to.
245
- # @param [Integer] right_by Optional offset from the top-left corner. A negative value means
246
- # coordinates right from the element.
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 move_to(element, right_by = nil, down_by = nil)
253
- @actions << if right_by && down_by
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
- # Moves the mouse from its current position (or 0,0) by the given offset.
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 move_by(right_by, down_by)
282
- @actions << [:mouse, :move_by, [Integer(right_by), Integer(down_by)]]
283
- self
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
- # Performs a context-click at middle of the given element. First performs
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 context_click(element = nil)
300
- @actions << [:mouse, :context_click, [element]]
301
- self
174
+ def clear_all_actions
175
+ @devices.each(&:clear_actions)
302
176
  end
303
177
 
304
178
  #
305
- # A convenience method that performs click-and-hold at the location of the
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 drag_and_drop(source, target)
322
- click_and_hold source
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
- # A convenience method that performs click-and-hold at the location of
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
- # el = driver.find_element(id: "some_id1")
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 drag_and_drop_by(source, right_by, down_by)
345
- click_and_hold source
346
- move_by right_by, down_by
347
- release
194
+ def tick(*action_devices)
195
+ return if @async
348
196
 
349
- self
197
+ @devices.each { |device| device.create_pause unless action_devices.include? device }
350
198
  end
351
199
 
352
200
  #
353
- # Executes the actions added to the builder.
201
+ # Adds an InputDevice
354
202
  #
355
203
 
356
- def perform
357
- @actions.each do |receiver, method, args|
358
- @devices.fetch(receiver).__send__(method, *args)
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