selenium-webdriver 3.142.7 → 4.0.3

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +350 -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 +69 -63
  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/chrome/driver.rb +26 -83
  13. data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +50 -12
  14. data/lib/selenium/webdriver/chrome/options.rb +129 -58
  15. data/lib/selenium/webdriver/chrome/profile.rb +6 -3
  16. data/lib/selenium/webdriver/chrome/service.rb +8 -15
  17. data/lib/selenium/webdriver/chrome.rb +10 -9
  18. data/lib/selenium/webdriver/common/action_builder.rb +97 -249
  19. data/lib/selenium/webdriver/common/driver.rb +112 -23
  20. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +43 -0
  21. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +51 -0
  22. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +89 -0
  23. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +77 -0
  24. data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_cdp.rb} +10 -8
  25. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +45 -0
  26. data/lib/selenium/webdriver/{firefox/util.rb → common/driver_extensions/has_devtools.rb} +16 -19
  27. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +38 -0
  28. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -8
  29. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +144 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_logs.rb +30 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +17 -0
  32. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
  33. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +136 -0
  34. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -11
  35. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +77 -0
  36. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +1 -0
  37. data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
  38. data/lib/selenium/webdriver/common/element.rb +82 -22
  39. data/lib/selenium/webdriver/common/error.rb +32 -196
  40. data/lib/selenium/webdriver/common/interactions/interaction.rb +4 -1
  41. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -5
  42. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +13 -13
  43. data/lib/selenium/webdriver/common/log_entry.rb +2 -2
  44. data/lib/selenium/webdriver/common/logger.rb +50 -15
  45. data/lib/selenium/webdriver/common/manager.rb +15 -15
  46. data/lib/selenium/webdriver/common/options.rb +154 -23
  47. data/lib/selenium/webdriver/common/platform.rb +6 -1
  48. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  49. data/lib/selenium/webdriver/common/profile_helper.rb +11 -9
  50. data/lib/selenium/webdriver/common/proxy.rb +6 -3
  51. data/lib/selenium/webdriver/common/search_context.rb +7 -3
  52. data/lib/selenium/webdriver/common/service.rb +17 -125
  53. data/lib/selenium/webdriver/common/service_manager.rb +151 -0
  54. data/lib/selenium/webdriver/common/shadow_root.rb +87 -0
  55. data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
  56. data/lib/selenium/webdriver/common/socket_poller.rb +2 -2
  57. data/lib/selenium/webdriver/common/takes_screenshot.rb +66 -0
  58. data/lib/selenium/webdriver/common/target_locator.rb +32 -4
  59. data/lib/selenium/webdriver/common/timeouts.rb +31 -4
  60. data/lib/selenium/webdriver/common/wait.rb +1 -1
  61. data/lib/selenium/webdriver/common/window.rb +0 -4
  62. data/lib/selenium/webdriver/common/zipper.rb +1 -9
  63. data/lib/selenium/webdriver/common.rb +23 -17
  64. data/lib/selenium/webdriver/devtools/console_event.rb +38 -0
  65. data/lib/selenium/webdriver/devtools/exception_event.rb +36 -0
  66. data/lib/selenium/webdriver/devtools/mutation_event.rb +37 -0
  67. data/lib/selenium/webdriver/devtools/pinned_script.rb +59 -0
  68. data/lib/selenium/webdriver/devtools/request.rb +67 -0
  69. data/lib/selenium/webdriver/devtools/response.rb +66 -0
  70. data/lib/selenium/webdriver/devtools.rb +182 -0
  71. data/lib/selenium/webdriver/edge/driver.rb +7 -29
  72. data/lib/selenium/webdriver/edge/features.rb +44 -0
  73. data/lib/selenium/webdriver/edge/options.rb +11 -48
  74. data/lib/selenium/webdriver/{common/w3c_manager.rb → edge/profile.rb} +7 -19
  75. data/lib/selenium/webdriver/edge/service.rb +10 -26
  76. data/lib/selenium/webdriver/edge.rb +11 -14
  77. data/lib/selenium/webdriver/firefox/driver.rb +31 -19
  78. data/lib/selenium/webdriver/firefox/extension.rb +8 -0
  79. data/lib/selenium/webdriver/firefox/features.rb +66 -0
  80. data/lib/selenium/webdriver/firefox/options.rb +70 -49
  81. data/lib/selenium/webdriver/firefox/profile.rb +21 -71
  82. data/lib/selenium/webdriver/firefox/service.rb +5 -9
  83. data/lib/selenium/webdriver/firefox.rb +22 -20
  84. data/lib/selenium/webdriver/ie/driver.rb +1 -47
  85. data/lib/selenium/webdriver/ie/options.rb +13 -44
  86. data/lib/selenium/webdriver/ie/service.rb +13 -15
  87. data/lib/selenium/webdriver/ie.rb +8 -7
  88. data/lib/selenium/webdriver/remote/bridge.rb +558 -86
  89. data/lib/selenium/webdriver/remote/capabilities.rb +159 -123
  90. data/lib/selenium/webdriver/remote/commands.rb +163 -0
  91. data/lib/selenium/webdriver/remote/driver.rb +22 -12
  92. data/lib/selenium/webdriver/remote/http/common.rb +0 -5
  93. data/lib/selenium/webdriver/remote/http/default.rb +17 -20
  94. data/lib/selenium/webdriver/remote/http/persistent.rb +11 -6
  95. data/lib/selenium/webdriver/remote/response.rb +16 -47
  96. data/lib/selenium/webdriver/remote.rb +15 -13
  97. data/lib/selenium/webdriver/safari/driver.rb +3 -31
  98. data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -3
  99. data/lib/selenium/webdriver/safari/options.rb +10 -29
  100. data/lib/selenium/webdriver/safari/service.rb +4 -8
  101. data/lib/selenium/webdriver/safari.rb +17 -9
  102. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
  103. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +63 -0
  104. data/lib/selenium/webdriver/support/cdp_client_generator.rb +108 -0
  105. data/lib/selenium/webdriver/support/color.rb +2 -2
  106. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  107. data/lib/selenium/webdriver/support/guards/guard.rb +89 -0
  108. data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +22 -19
  109. data/lib/selenium/webdriver/support/guards.rb +95 -0
  110. data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
  111. data/lib/selenium/webdriver/support/select.rb +3 -3
  112. data/lib/selenium/webdriver/support.rb +1 -0
  113. data/lib/selenium/webdriver/version.rb +1 -1
  114. data/lib/selenium/webdriver.rb +12 -12
  115. data/selenium-webdriver.gemspec +28 -12
  116. metadata +128 -69
  117. data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
  118. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
  119. data/lib/selenium/webdriver/common/keyboard.rb +0 -70
  120. data/lib/selenium/webdriver/common/mouse.rb +0 -89
  121. data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
  122. data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
  123. data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
  124. data/lib/selenium/webdriver/edge/bridge.rb +0 -76
  125. data/lib/selenium/webdriver/firefox/binary.rb +0 -187
  126. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
  127. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  128. data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
  129. data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
  130. data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
  131. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  132. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  133. data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
  134. data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
  135. data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
  136. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
  137. data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -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
@@ -30,6 +30,7 @@ module Selenium
30
30
 
31
31
  class Driver
32
32
  include SearchContext
33
+ include TakesScreenshot
33
34
 
34
35
  class << self
35
36
  #
@@ -43,19 +44,17 @@ module Selenium
43
44
  def for(browser, opts = {})
44
45
  case browser
45
46
  when :chrome
46
- Chrome::Driver.new(opts)
47
+ Chrome::Driver.new(**opts)
47
48
  when :internet_explorer, :ie
48
- IE::Driver.new(opts)
49
+ IE::Driver.new(**opts)
49
50
  when :safari
50
- Safari::Driver.new(opts)
51
- when :phantomjs
52
- PhantomJS::Driver.new(opts)
51
+ Safari::Driver.new(**opts)
53
52
  when :firefox, :ff
54
- Firefox::Driver.new(opts)
53
+ Firefox::Driver.new(**opts)
55
54
  when :edge
56
- Edge::Driver.new(opts)
55
+ Edge::Driver.new(**opts)
57
56
  when :remote
58
- Remote::Driver.new(opts)
57
+ Remote::Driver.new(**opts)
59
58
  else
60
59
  raise ArgumentError, "unknown driver: #{browser.inspect}"
61
60
  end
@@ -69,13 +68,27 @@ module Selenium
69
68
  # @api private
70
69
  #
71
70
 
72
- def initialize(bridge, listener: nil)
73
- @bridge = bridge
74
- @bridge = Support::EventFiringBridge.new(bridge, listener) if listener
71
+ def initialize(bridge: nil, listener: nil, **opts)
72
+ @service = nil
73
+ @devtools = nil
74
+ bridge ||= create_bridge(**opts)
75
+ add_extensions(bridge.browser)
76
+ @bridge = listener ? Support::EventFiringBridge.new(bridge, listener) : bridge
75
77
  end
76
78
 
77
79
  def inspect
78
- format '#<%<class>s:0x%<hash>x browser=%<browser>s>', class: self.class, hash: hash * 2, browser: bridge.browser.inspect
80
+ format '#<%<class>s:0x%<hash>x browser=%<browser>s>', class: self.class, hash: hash * 2,
81
+ browser: bridge.browser.inspect
82
+ end
83
+
84
+ #
85
+ # information about whether a remote end is in a state in which it can create new sessions,
86
+ # and may include additional meta information.
87
+ #
88
+ # @return [Hash]
89
+ #
90
+ def status
91
+ @bridge.status
79
92
  end
80
93
 
81
94
  #
@@ -106,8 +119,8 @@ module Selenium
106
119
  end
107
120
 
108
121
  #
109
- # @return [ActionBuilder, W3CActionBuilder]
110
- # @see ActionBuilder, W3CActionBuilder
122
+ # @return [ActionBuilder]
123
+ # @see ActionBuilder
111
124
  #
112
125
 
113
126
  def action
@@ -166,6 +179,9 @@ module Selenium
166
179
 
167
180
  def quit
168
181
  bridge.quit
182
+ ensure
183
+ @service&.stop
184
+ @devtools&.close
169
185
  end
170
186
 
171
187
  #
@@ -271,7 +287,7 @@ module Selenium
271
287
  end
272
288
 
273
289
  def browser
274
- bridge.browser
290
+ bridge&.browser
275
291
  end
276
292
 
277
293
  def capabilities
@@ -283,26 +299,99 @@ module Selenium
283
299
  # @see SearchContext
284
300
  #
285
301
 
286
- def ref; end
302
+ def ref
303
+ [:driver, nil]
304
+ end
287
305
 
288
306
  private
289
307
 
290
308
  attr_reader :bridge
291
309
 
310
+ def create_bridge(**opts)
311
+ opts[:url] ||= service_url(opts)
312
+ caps = opts.delete(:capabilities)
313
+ # NOTE: This is deprecated
314
+ cap_array = caps.is_a?(Hash) ? [caps] : Array(caps)
315
+
316
+ desired_capabilities = opts.delete(:desired_capabilities)
317
+ if desired_capabilities
318
+ WebDriver.logger.deprecate(':desired_capabilities as a parameter for driver initialization',
319
+ ':capabilities with an Array value of capabilities/options if necessary',
320
+ id: :desired_capabilities)
321
+ desired_capabilities = Remote::Capabilities.new(desired_capabilities) if desired_capabilities.is_a?(Hash)
322
+ cap_array << desired_capabilities
323
+ end
324
+
325
+ options = opts.delete(:options)
326
+ if options
327
+ WebDriver.logger.deprecate(':options as a parameter for driver initialization',
328
+ ':capabilities with an Array of value capabilities/options if necessary',
329
+ id: :browser_options)
330
+ cap_array << options
331
+ end
332
+
333
+ capabilities = generate_capabilities(cap_array)
334
+
335
+ bridge_opts = {http_client: opts.delete(:http_client), url: opts.delete(:url)}
336
+ raise ArgumentError, "Unable to create a driver with parameters: #{opts}" unless opts.empty?
337
+
338
+ bridge = Remote::Bridge.new(**bridge_opts)
339
+
340
+ bridge.create_session(capabilities)
341
+ bridge
342
+ end
343
+
344
+ def generate_capabilities(cap_array)
345
+ cap_array.map { |cap|
346
+ if cap.is_a? Symbol
347
+ cap = Remote::Capabilities.send(cap)
348
+ elsif cap.is_a? Hash
349
+ new_message = 'Capabilities instance initialized with the Hash, or build values with Options class'
350
+ WebDriver.logger.deprecate("passing a Hash value to :capabilities",
351
+ new_message,
352
+ id: :capabilities_hash)
353
+ cap = Remote::Capabilities.new(cap)
354
+ elsif !cap.respond_to? :as_json
355
+ msg = ":capabilities parameter only accepts objects responding to #as_json which #{cap.class} does not"
356
+ raise ArgumentError, msg
357
+ end
358
+ cap&.as_json
359
+ }.inject(:merge) || Remote::Capabilities.send(browser || :new)
360
+ end
361
+
292
362
  def service_url(opts)
293
- @service = opts.delete(:service)
363
+ service_config = opts.delete(:service)
294
364
  %i[driver_opts driver_path port].each do |key|
295
365
  next unless opts.key? key
296
366
 
297
- WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service')
367
+ WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service',
368
+ id: "service_#{key}".to_sym)
298
369
  end
299
- @service ||= Service.send(browser,
300
- args: opts.delete(:driver_opts),
301
- path: opts.delete(:driver_path),
302
- port: opts.delete(:port))
303
- @service.start
370
+ service_config ||= Service.send(browser,
371
+ args: opts.delete(:driver_opts),
372
+ path: opts.delete(:driver_path),
373
+ port: opts.delete(:port))
374
+ @service = service_config.launch
304
375
  @service.uri
305
376
  end
377
+
378
+ def screenshot
379
+ bridge.screenshot
380
+ end
381
+
382
+ def add_extensions(browser)
383
+ extensions = case browser
384
+ when :chrome, :msedge
385
+ Chrome::Driver::EXTENSIONS
386
+ when :firefox
387
+ Firefox::Driver::EXTENSIONS
388
+ when :safari, :safari_technology_preview
389
+ Safari::Driver::EXTENSIONS
390
+ else
391
+ []
392
+ end
393
+ extensions.each { |extension| extend extension }
394
+ end
306
395
  end # Driver
307
396
  end # WebDriver
308
397
  end # Selenium