selenium-webdriver 3.142.7 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +432 -5
  3. data/Gemfile +3 -1
  4. data/LICENSE +1 -1
  5. data/NOTICE +2 -0
  6. data/README.md +4 -5
  7. data/lib/selenium/server.rb +75 -64
  8. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +100 -7
  10. data/lib/selenium/webdriver/atoms/isDisplayed.js +76 -78
  11. data/lib/selenium/webdriver/atoms/mutationListener.js +55 -0
  12. data/lib/selenium/webdriver/bidi/session.rb +38 -0
  13. data/lib/selenium/webdriver/bidi.rb +55 -0
  14. data/lib/selenium/webdriver/chrome/driver.rb +26 -83
  15. data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +55 -12
  16. data/lib/selenium/webdriver/chrome/options.rb +138 -67
  17. data/lib/selenium/webdriver/chrome/profile.rb +6 -3
  18. data/lib/selenium/webdriver/chrome/service.rb +8 -15
  19. data/lib/selenium/webdriver/chrome.rb +5 -18
  20. data/lib/selenium/webdriver/common/action_builder.rb +171 -236
  21. data/lib/selenium/webdriver/common/driver.rb +76 -29
  22. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +43 -0
  23. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +51 -0
  24. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +89 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +10 -8
  26. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +87 -0
  27. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +38 -0
  28. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +44 -0
  29. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +43 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +38 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -8
  32. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +144 -0
  33. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_logs.rb} +4 -4
  34. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +17 -0
  35. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
  36. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +136 -0
  37. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -11
  38. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +77 -0
  39. data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
  40. data/lib/selenium/webdriver/common/element.rb +83 -23
  41. data/lib/selenium/webdriver/common/error.rb +32 -196
  42. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  43. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -22
  44. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  45. data/lib/selenium/webdriver/common/interactions/key_actions.rb +10 -6
  46. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  47. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  48. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  49. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +68 -78
  50. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  51. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  52. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  53. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  54. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  55. data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
  56. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  57. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  58. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  59. data/lib/selenium/webdriver/common/{w3c_manager.rb → interactions/wheel_input.rb} +14 -17
  60. data/lib/selenium/webdriver/common/keys.rb +1 -0
  61. data/lib/selenium/webdriver/common/log_entry.rb +2 -2
  62. data/lib/selenium/webdriver/common/logger.rb +50 -15
  63. data/lib/selenium/webdriver/common/manager.rb +11 -38
  64. data/lib/selenium/webdriver/common/options.rb +147 -23
  65. data/lib/selenium/webdriver/common/platform.rb +10 -5
  66. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  67. data/lib/selenium/webdriver/common/profile_helper.rb +11 -9
  68. data/lib/selenium/webdriver/common/proxy.rb +6 -3
  69. data/lib/selenium/webdriver/common/search_context.rb +7 -9
  70. data/lib/selenium/webdriver/common/service.rb +17 -125
  71. data/lib/selenium/webdriver/common/service_manager.rb +150 -0
  72. data/lib/selenium/webdriver/common/shadow_root.rb +87 -0
  73. data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
  74. data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
  75. data/lib/selenium/webdriver/common/takes_screenshot.rb +66 -0
  76. data/lib/selenium/webdriver/common/target_locator.rb +32 -4
  77. data/lib/selenium/webdriver/common/timeouts.rb +31 -4
  78. data/lib/selenium/webdriver/common/wait.rb +1 -1
  79. data/lib/selenium/webdriver/common/websocket_connection.rb +149 -0
  80. data/lib/selenium/webdriver/common/window.rb +0 -4
  81. data/lib/selenium/webdriver/common/zipper.rb +3 -9
  82. data/lib/selenium/webdriver/common.rb +35 -18
  83. data/lib/selenium/webdriver/devtools/console_event.rb +38 -0
  84. data/lib/selenium/webdriver/devtools/exception_event.rb +36 -0
  85. data/lib/selenium/webdriver/devtools/mutation_event.rb +37 -0
  86. data/lib/selenium/webdriver/devtools/pinned_script.rb +59 -0
  87. data/lib/selenium/webdriver/devtools/request.rb +67 -0
  88. data/lib/selenium/webdriver/devtools/response.rb +66 -0
  89. data/lib/selenium/webdriver/devtools.rb +86 -0
  90. data/lib/selenium/webdriver/edge/driver.rb +7 -29
  91. data/lib/selenium/webdriver/edge/features.rb +45 -0
  92. data/lib/selenium/webdriver/edge/options.rb +11 -48
  93. data/lib/selenium/webdriver/edge/profile.rb +33 -0
  94. data/lib/selenium/webdriver/edge/service.rb +10 -26
  95. data/lib/selenium/webdriver/edge.rb +11 -14
  96. data/lib/selenium/webdriver/firefox/driver.rb +32 -19
  97. data/lib/selenium/webdriver/firefox/extension.rb +8 -0
  98. data/lib/selenium/webdriver/firefox/features.rb +63 -0
  99. data/lib/selenium/webdriver/firefox/options.rb +73 -50
  100. data/lib/selenium/webdriver/firefox/profile.rb +16 -70
  101. data/lib/selenium/webdriver/firefox/service.rb +5 -9
  102. data/lib/selenium/webdriver/firefox/util.rb +1 -1
  103. data/lib/selenium/webdriver/firefox.rb +17 -28
  104. data/lib/selenium/webdriver/ie/driver.rb +1 -47
  105. data/lib/selenium/webdriver/ie/options.rb +15 -46
  106. data/lib/selenium/webdriver/ie/service.rb +13 -15
  107. data/lib/selenium/webdriver/ie.rb +3 -16
  108. data/lib/selenium/webdriver/remote/bridge.rb +563 -86
  109. data/lib/selenium/webdriver/remote/capabilities.rb +159 -123
  110. data/lib/selenium/webdriver/remote/commands.rb +158 -0
  111. data/lib/selenium/webdriver/remote/driver.rb +22 -13
  112. data/lib/selenium/webdriver/remote/http/common.rb +0 -5
  113. data/lib/selenium/webdriver/remote/http/default.rb +22 -31
  114. data/lib/selenium/webdriver/remote/response.rb +18 -49
  115. data/lib/selenium/webdriver/remote.rb +15 -12
  116. data/lib/selenium/webdriver/safari/driver.rb +3 -31
  117. data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -3
  118. data/lib/selenium/webdriver/safari/options.rb +10 -29
  119. data/lib/selenium/webdriver/safari/service.rb +4 -8
  120. data/lib/selenium/webdriver/safari.rb +13 -19
  121. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
  122. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +63 -0
  123. data/lib/selenium/webdriver/support/cdp_client_generator.rb +108 -0
  124. data/lib/selenium/webdriver/support/color.rb +9 -9
  125. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  126. data/lib/selenium/webdriver/support/guards/guard.rb +89 -0
  127. data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +22 -19
  128. data/lib/selenium/webdriver/support/guards.rb +95 -0
  129. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  130. data/lib/selenium/webdriver/support/select.rb +3 -3
  131. data/lib/selenium/webdriver/support.rb +1 -0
  132. data/lib/selenium/webdriver/version.rb +1 -1
  133. data/lib/selenium/webdriver.rb +14 -13
  134. data/selenium-webdriver.gemspec +32 -13
  135. metadata +176 -69
  136. data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
  137. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
  138. data/lib/selenium/webdriver/common/keyboard.rb +0 -70
  139. data/lib/selenium/webdriver/common/mouse.rb +0 -89
  140. data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
  141. data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
  142. data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
  143. data/lib/selenium/webdriver/edge/bridge.rb +0 -76
  144. data/lib/selenium/webdriver/firefox/binary.rb +0 -187
  145. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
  146. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  147. data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
  148. data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
  149. data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
  150. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  151. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  152. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -60
  153. data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
  154. data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
  155. data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
  156. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
  157. data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -19,25 +19,14 @@
19
19
 
20
20
  require 'net/http'
21
21
 
22
- require 'selenium/webdriver/chrome/bridge'
23
- require 'selenium/webdriver/chrome/driver'
24
- require 'selenium/webdriver/chrome/profile'
25
- require 'selenium/webdriver/chrome/options'
26
-
27
22
  module Selenium
28
23
  module WebDriver
29
24
  module Chrome
30
- def self.driver_path=(path)
31
- WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path=',
32
- 'Selenium::WebDriver::Chrome::Service#driver_path='
33
- Selenium::WebDriver::Chrome::Service.driver_path = path
34
- end
35
-
36
- def self.driver_path
37
- WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path',
38
- 'Selenium::WebDriver::Chrome::Service#driver_path'
39
- Selenium::WebDriver::Chrome::Service.driver_path
40
- end
25
+ autoload :Features, 'selenium/webdriver/chrome/features'
26
+ autoload :Driver, 'selenium/webdriver/chrome/driver'
27
+ autoload :Profile, 'selenium/webdriver/chrome/profile'
28
+ autoload :Options, 'selenium/webdriver/chrome/options'
29
+ autoload :Service, 'selenium/webdriver/chrome/service'
41
30
 
42
31
  def self.path=(path)
43
32
  Platform.assert_executable path
@@ -50,5 +39,3 @@ module Selenium
50
39
  end # Chrome
51
40
  end # WebDriver
52
41
  end # Selenium
53
-
54
- require 'selenium/webdriver/chrome/service'
@@ -19,347 +19,282 @@
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
25
+ include WheelActions # Actions specific to wheel inputs
42
26
 
43
- def initialize(mouse, keyboard)
44
- @devices = {
45
- mouse: mouse,
46
- keyboard: keyboard
47
- }
27
+ attr_reader :devices
48
28
 
49
- @actions = []
50
- end
51
-
52
- #
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.
57
29
  #
58
- # Equivalent to:
59
- # driver.action.click(element).send_keys(key)
60
- # # or
61
- # driver.action.click.send_keys(key)
30
+ # Initialize a W3C Action Builder. Differs from previous by requiring a bridge and allowing asynchronous actions.
31
+ # The W3C implementation allows asynchronous actions per device. e.g. A key can be pressed at the same time that
32
+ # the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions
33
+ # correctly when using asynchronous.
62
34
  #
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
35
+ # @param [Selenium::WebDriver::Remote::Bridge] bridge the bridge for the current driver instance.
36
+ # @param [Selenium::WebDriver::Interactions::PointerInput] deprecated_mouse PointerInput for the mouse.
37
+ # @param [Selenium::WebDriver::Interactions::KeyInput] deprecated_keyboard KeyInput for the keyboard.
38
+ # @param [Boolean] deprecated_async Whether to perform the actions asynchronously per device.
39
+ # Defaults to false for backwards compatibility.
40
+ # @param [Array<Selenium::WebDriver::Interactions::InputDevices>] devices list of valid sources of input.
41
+ # @param [Boolean] async Whether to perform the actions asynchronously per device.
42
+ # @return [ActionBuilder] A self reference.
71
43
  #
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
-
80
- def key_down(*args)
81
- @actions << [:mouse, :click, [args.shift]] if args.first.is_a? Element
82
44
 
83
- @actions << [:keyboard, :press, args]
84
- self
45
+ def initialize(bridge, deprecated_mouse = nil, deprecated_keyboard = nil, deprecated_async = nil,
46
+ devices: [], async: false, duration: 250)
47
+ @bridge = bridge
48
+ @duration = duration
49
+
50
+ @async = if deprecated_async.nil?
51
+ async
52
+ else
53
+ WebDriver.logger.deprecate('initializing ActionBuilder with async parameter',
54
+ ':async keyword',
55
+ id: :action_async)
56
+ deprecated_async
57
+ end
58
+
59
+ @devices = []
60
+ if deprecated_keyboard || deprecated_mouse
61
+ WebDriver.logger.deprecate "initializing ActionBuilder with keyboard and mouse parameters",
62
+ "devices keyword or, even better, Driver#action",
63
+ id: :action_devices
64
+ add_input(deprecated_mouse)
65
+ add_input(deprecated_keyboard)
66
+ else
67
+ Array(devices).each { |device| add_input(device) }
68
+ end
85
69
  end
86
70
 
87
71
  #
88
- # Performs a modifier key release.
89
- # Releasing a non-depressed modifier key will yield undefined behaviour.
72
+ # Adds a PointerInput device of the given kind
90
73
  #
91
- # @example Release a key
74
+ # @example Add a touch pointer input device
92
75
  #
93
- # driver.action.key_up(:shift).perform
76
+ # builder = device.action
77
+ # builder.add_pointer_input('touch', :touch)
94
78
  #
95
- # @example Release a key from an element
79
+ # @param [String] name name for the device
80
+ # @param [Symbol] kind kind of pointer device to create
81
+ # @return [Interactions::PointerInput] The pointer input added
96
82
  #
97
- # el = driver.find_element(id: "some_id")
98
- # driver.action.key_up(el, :alt).perform
99
83
  #
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
84
 
112
- @actions << [:keyboard, :release, args]
113
- self
85
+ def add_pointer_input(kind, name)
86
+ add_input(Interactions.pointer(kind, name: name))
114
87
  end
115
88
 
116
89
  #
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
90
+ # Adds a KeyInput device
127
91
  #
128
- # @example Send the text "help" to the currently focused element
92
+ # @example Add a key input device
129
93
  #
130
- # driver.action.send_keys("help").perform
94
+ # builder = device.action
95
+ # builder.add_key_input('keyboard2')
131
96
  #
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
97
+ # @param [String] name name for the device
98
+ # @return [Interactions::KeyInput] The key input added
138
99
  #
139
100
 
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
101
+ def add_key_input(name)
102
+ add_input(Interactions.key(name))
145
103
  end
146
104
 
147
105
  #
148
- # Clicks (without releasing) in the middle of the given element. This is
149
- # equivalent to:
106
+ # Adds a WheelInput device
150
107
  #
151
- # driver.action.move_to(element).click_and_hold
108
+ # @example Add a wheel input device
152
109
  #
153
- # @example Clicking and holding on some element
110
+ # builder = device.action
111
+ # builder.add_wheel_input('wheel2')
154
112
  #
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.
113
+ # @param [String] name name for the device
114
+ # @return [Interactions::WheelInput] The wheel input added
160
115
  #
161
116
 
162
- def click_and_hold(element = nil)
163
- @actions << [:mouse, :down, [element]]
164
- self
117
+ def add_wheel_input(name)
118
+ add_input(Interactions.wheel(name))
165
119
  end
166
120
 
167
121
  #
168
- # Releases the depressed left mouse button at the current mouse location.
122
+ # Retrieves the input device for the given name
169
123
  #
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.
124
+ # @param [String] name name of the input device
125
+ # @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name
176
126
  #
177
127
 
178
- def release(element = nil)
179
- @actions << [:mouse, :up, [element]]
180
- self
128
+ def get_device(name)
129
+ WebDriver.logger.deprecate('#get_device with name parameter',
130
+ '#device with :name or :type keyword',
131
+ id: :get_device)
132
+ device(name: name)
181
133
  end
182
134
 
183
135
  #
184
- # Clicks in the middle of the given element. Equivalent to:
185
- #
186
- # driver.action.move_to(element).click
136
+ # Retrieves the input device for the given name or type
187
137
  #
188
- # When no element is passed, the current mouse position will be clicked.
189
- #
190
- # @example Clicking on an element
191
- #
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.
138
+ # @param [String] name name of the input device
139
+ # @param [String] type name of the input device
140
+ # @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name or type
201
141
  #
202
142
 
203
- def click(element = nil)
204
- @actions << [:mouse, :click, [element]]
205
- self
143
+ def device(name: nil, type: nil)
144
+ input = @devices.find { |device| (device.name == name.to_s || name.nil?) && (device.type == type || type.nil?) }
145
+
146
+ raise(ArgumentError, "Can not find device: #{name}") if name && input.nil?
147
+
148
+ input
206
149
  end
207
150
 
208
151
  #
209
- # Performs a double-click at middle of the given element. Equivalent to:
152
+ # Retrieves the current PointerInput devices
210
153
  #
211
- # driver.action.move_to(element).double_click
154
+ # @return [Array] array of current PointerInput devices
212
155
  #
213
- # @example Double click an element
156
+
157
+ def pointer_inputs
158
+ @devices.select { |device| device.type == Interactions::POINTER }
159
+ end
160
+
214
161
  #
215
- # el = driver.find_element(id: "some_id")
216
- # driver.action.double_click(el).perform
162
+ # Retrieves the current KeyInput device
217
163
  #
218
- # @param [Selenium::WebDriver::Element] element An optional element to move to.
219
- # @return [ActionBuilder] A self reference.
164
+ # @return [Selenium::WebDriver::Interactions::InputDevice] current KeyInput device
220
165
  #
221
166
 
222
- def double_click(element = nil)
223
- @actions << [:mouse, :double_click, [element]]
224
- self
167
+ def key_inputs
168
+ @devices.select { |device| device.type == Interactions::KEY }
225
169
  end
226
170
 
227
171
  #
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.
172
+ # Retrieves the current WheelInput device
231
173
  #
232
- # Note that when using offsets, both coordinates need to be passed.
174
+ # @return [Selenium::WebDriver::Interactions::InputDevice] current WheelInput devices
233
175
  #
234
- # @example Scroll element into view and move the mouse to it
176
+
177
+ def wheel_inputs
178
+ @devices.select { |device| device.type == Interactions::WHEEL }
179
+ end
180
+
235
181
  #
236
- # el = driver.find_element(id: "some_id")
237
- # driver.action.move_to(el).perform
182
+ # Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait
183
+ # for all actions to complete in that tick.
238
184
  #
239
- # @example
185
+ # @example Send keys to an element
240
186
  #
187
+ # action_builder = driver.action
188
+ # keyboard = action_builder.key_input
241
189
  # el = driver.find_element(id: "some_id")
242
- # driver.action.move_to(el, 100, 100).perform
190
+ # driver.action.click(el).pause(keyboard).pause(keyboard).pause(keyboard).send_keys('keys').perform
243
191
  #
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.
192
+ # @param [InputDevice] device Input device to pause
193
+ # @param [Float] duration Duration to pause
249
194
  # @return [ActionBuilder] A self reference.
250
195
  #
251
196
 
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
197
+ def pause(deprecated_device = nil, deprecated_duration = nil, device: nil, duration: 0)
198
+ deprecate_method(deprecated_device, deprecated_duration)
258
199
 
200
+ device ||= deprecated_device || pointer_input
201
+ device.create_pause(deprecated_duration || duration)
259
202
  self
260
203
  end
261
204
 
262
205
  #
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.
206
+ # Creates multiple pauses for the given device of the given duration.
267
207
  #
268
- # @example Move the mouse to a certain offset from its current position
208
+ # @example Send keys to an element
269
209
  #
270
- # driver.action.move_by(100, 100).perform
210
+ # action_builder = driver.action
211
+ # keyboard = action_builder.key_input
212
+ # el = driver.find_element(id: "some_id")
213
+ # driver.action.click(el).pauses(keyboard, 3).send_keys('keys').perform
271
214
  #
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.
215
+ # @param [InputDevice] device Input device to pause
216
+ # @param [Integer] number of pauses to add for the device
217
+ # @param [Float] duration Duration to pause
276
218
  # @return [ActionBuilder] A self reference.
277
- # @raise [MoveTargetOutOfBoundsError] if the provided offset is outside
278
- # the document's boundaries.
279
219
  #
280
220
 
281
- def move_by(right_by, down_by)
282
- @actions << [:mouse, :move_by, [Integer(right_by), Integer(down_by)]]
221
+ def pauses(deprecated_device = nil, deprecated_number = nil, deprecated_duration = nil,
222
+ device: nil, number: nil, duration: 0)
223
+ deprecate_method(deprecated_device, deprecated_duration, deprecated_number, method: :pauses)
224
+
225
+ number ||= deprecated_number || 2
226
+ device ||= deprecated_device || pointer_input
227
+ duration ||= deprecated_duration || 0
228
+
229
+ number.times { device.create_pause(duration) }
283
230
  self
284
231
  end
285
232
 
286
233
  #
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.
234
+ # Executes the actions added to the builder.
297
235
  #
298
236
 
299
- def context_click(element = nil)
300
- @actions << [:mouse, :context_click, [element]]
301
- self
237
+ def perform
238
+ @bridge.send_actions @devices.filter_map(&:encode)
239
+ clear_all_actions
240
+ nil
302
241
  end
303
242
 
304
243
  #
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.
244
+ # Clears all actions from the builder.
308
245
  #
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.
319
- #
320
-
321
- def drag_and_drop(source, target)
322
- click_and_hold source
323
- move_to target
324
- release
325
246
 
326
- self
247
+ def clear_all_actions
248
+ @devices.each(&:clear_actions)
327
249
  end
328
250
 
329
251
  #
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.
252
+ # Releases all action states from the browser.
332
253
  #
333
- # @example Drag and drop an element by offset
254
+
255
+ def release_actions
256
+ @bridge.release_actions
257
+ end
258
+
259
+ private
260
+
334
261
  #
335
- # el = driver.find_element(id: "some_id1")
336
- # driver.action.drag_and_drop_by(el, 100, 100).perform
262
+ # Adds pauses for all devices but the given devices
337
263
  #
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.
264
+ # @param [Array[InputDevice]] action_devices Array of Input Devices performing an action in this tick.
342
265
  #
343
266
 
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
267
+ def tick(*action_devices)
268
+ return if @async
348
269
 
349
- self
270
+ @devices.each { |device| device.create_pause unless action_devices.include? device }
350
271
  end
351
272
 
352
273
  #
353
- # Executes the actions added to the builder.
274
+ # Adds an InputDevice
354
275
  #
355
276
 
356
- def perform
357
- @actions.each do |receiver, method, args|
358
- @devices.fetch(receiver).__send__(method, *args)
277
+ def add_input(device)
278
+ device = Interactions.send(device) if device.is_a?(Symbol) && Interactions.respond_to?(device)
279
+
280
+ raise TypeError, "#{device.inspect} is not a valid InputDevice" unless device.is_a?(Interactions::InputDevice)
281
+
282
+ unless @async
283
+ max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }
284
+ pauses(device: device, number: max_device.actions.length) if max_device
359
285
  end
286
+ @devices << device
287
+ device
288
+ end
360
289
 
361
- nil
290
+ def deprecate_method(device = nil, duration = nil, number = nil, method: :pause)
291
+ return unless device || number || duration
292
+
293
+ WebDriver.logger.deprecate "ActionBuilder##{method} with ordered parameters",
294
+ ':device, :duration, :number keywords',
295
+ id: method
362
296
  end
297
+
363
298
  end # ActionBuilder
364
299
  end # WebDriver
365
300
  end # Selenium