selenium-webdriver 3.142.6 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +369 -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 -70
  13. data/lib/selenium/webdriver/chrome/{bridge.rb → features.rb} +50 -12
  14. data/lib/selenium/webdriver/chrome/options.rb +128 -53
  15. data/lib/selenium/webdriver/chrome/profile.rb +8 -5
  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 +184 -0
  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 +3 -9
  63. data/lib/selenium/webdriver/common.rb +24 -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 +193 -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 +71 -50
  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 +15 -46
  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 +561 -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 -12
  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 +13 -13
  115. data/selenium-webdriver.gemspec +28 -12
  116. metadata +129 -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