selenium-webdriver 4.1.0 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +141 -1
  3. data/LICENSE +1 -1
  4. data/NOTICE +1 -1
  5. data/bin/linux/selenium-manager +0 -0
  6. data/bin/macos/selenium-manager +0 -0
  7. data/bin/windows/selenium-manager.exe +0 -0
  8. data/lib/selenium/server.rb +34 -26
  9. data/lib/selenium/webdriver/atoms/findElements.js +0 -0
  10. data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
  11. data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
  12. data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
  13. data/lib/selenium/webdriver/bidi/session.rb +38 -0
  14. data/lib/selenium/webdriver/bidi.rb +55 -0
  15. data/lib/selenium/webdriver/chrome/driver.rb +1 -0
  16. data/lib/selenium/webdriver/chrome/features.rb +5 -0
  17. data/lib/selenium/webdriver/chrome/options.rb +33 -19
  18. data/lib/selenium/webdriver/chrome/service.rb +1 -1
  19. data/lib/selenium/webdriver/chrome.rb +0 -14
  20. data/lib/selenium/webdriver/common/action_builder.rb +108 -21
  21. data/lib/selenium/webdriver/common/child_process.rb +126 -0
  22. data/lib/selenium/webdriver/common/driver.rb +22 -55
  23. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +12 -5
  24. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -2
  26. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
  27. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -67
  28. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -1
  29. data/lib/selenium/webdriver/common/element.rb +2 -2
  30. data/lib/selenium/webdriver/common/error.rb +1 -1
  31. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  32. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  33. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  34. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  35. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  36. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  37. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  38. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -70
  39. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  40. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  41. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  42. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  43. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  44. data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
  45. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  46. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  47. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  48. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  49. data/lib/selenium/webdriver/common/keys.rb +1 -0
  50. data/lib/selenium/webdriver/common/manager.rb +0 -27
  51. data/lib/selenium/webdriver/common/options.rb +2 -9
  52. data/lib/selenium/webdriver/common/platform.rb +8 -5
  53. data/lib/selenium/webdriver/common/search_context.rb +0 -6
  54. data/lib/selenium/webdriver/common/selenium_manager.rb +91 -0
  55. data/lib/selenium/webdriver/common/service.rb +7 -3
  56. data/lib/selenium/webdriver/common/service_manager.rb +3 -12
  57. data/lib/selenium/webdriver/common/shadow_root.rb +1 -1
  58. data/lib/selenium/webdriver/common/socket_lock.rb +1 -2
  59. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  60. data/lib/selenium/webdriver/common/takes_screenshot.rb +1 -1
  61. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +83 -0
  62. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +73 -0
  63. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  64. data/lib/selenium/webdriver/common/websocket_connection.rb +165 -0
  65. data/lib/selenium/webdriver/common/window.rb +6 -6
  66. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  67. data/lib/selenium/webdriver/common.rb +19 -3
  68. data/lib/selenium/webdriver/devtools/network_interceptor.rb +176 -0
  69. data/lib/selenium/webdriver/devtools/request.rb +1 -1
  70. data/lib/selenium/webdriver/devtools/response.rb +1 -1
  71. data/lib/selenium/webdriver/devtools.rb +6 -112
  72. data/lib/selenium/webdriver/edge/features.rb +1 -0
  73. data/lib/selenium/webdriver/firefox/driver.rb +1 -0
  74. data/lib/selenium/webdriver/firefox/features.rb +6 -5
  75. data/lib/selenium/webdriver/firefox/options.rb +5 -2
  76. data/lib/selenium/webdriver/firefox/profile.rb +1 -5
  77. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  78. data/lib/selenium/webdriver/firefox.rb +1 -14
  79. data/lib/selenium/webdriver/ie.rb +0 -14
  80. data/lib/selenium/webdriver/remote/bridge.rb +54 -19
  81. data/lib/selenium/webdriver/remote/commands.rb +15 -6
  82. data/lib/selenium/webdriver/remote/driver.rb +0 -1
  83. data/lib/selenium/webdriver/remote/http/default.rb +6 -12
  84. data/lib/selenium/webdriver/remote/response.rb +2 -2
  85. data/lib/selenium/webdriver/safari/options.rb +1 -1
  86. data/lib/selenium/webdriver/safari.rb +0 -14
  87. data/lib/selenium/webdriver/support/cdp_client_generator.rb +4 -4
  88. data/lib/selenium/webdriver/support/color.rb +7 -7
  89. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -1
  90. data/lib/selenium/webdriver/support/guards.rb +1 -1
  91. data/lib/selenium/webdriver/support/nightly_version_generator.rb +60 -0
  92. data/lib/selenium/webdriver/support/select.rb +3 -1
  93. data/lib/selenium/webdriver/version.rb +1 -1
  94. data/lib/selenium/webdriver.rb +1 -1
  95. data/selenium-webdriver.gemspec +14 -10
  96. metadata +66 -42
  97. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  98. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
@@ -22,6 +22,7 @@ module Selenium
22
22
  class ActionBuilder
23
23
  include KeyActions # Actions specific to key inputs
24
24
  include PointerActions # Actions specific to pointer inputs
25
+ include WheelActions # Actions specific to wheel inputs
25
26
 
26
27
  attr_reader :devices
27
28
 
@@ -31,19 +32,40 @@ module Selenium
31
32
  # the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions
32
33
  # correctly when using asynchronous.
33
34
  #
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.
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.
39
42
  # @return [ActionBuilder] A self reference.
40
43
  #
41
44
 
42
- def initialize(bridge, mouse, keyboard, async = false)
43
- # For backwards compatibility, automatically include mouse & keyboard
45
+ def initialize(bridge, deprecated_mouse = nil, deprecated_keyboard = nil, deprecated_async = nil,
46
+ devices: [], async: false, duration: 250)
44
47
  @bridge = bridge
45
- @devices = [mouse, keyboard]
46
- @async = async
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
47
69
  end
48
70
 
49
71
  #
@@ -61,9 +83,7 @@ module Selenium
61
83
  #
62
84
 
63
85
  def add_pointer_input(kind, name)
64
- new_input = Interactions.pointer(kind, name: name)
65
- add_input(new_input)
66
- new_input
86
+ add_input(Interactions.pointer(kind, name: name))
67
87
  end
68
88
 
69
89
  #
@@ -79,9 +99,23 @@ module Selenium
79
99
  #
80
100
 
81
101
  def add_key_input(name)
82
- new_input = Interactions.key(name)
83
- add_input(new_input)
84
- new_input
102
+ add_input(Interactions.key(name))
103
+ end
104
+
105
+ #
106
+ # Adds a WheelInput device
107
+ #
108
+ # @example Add a wheel input device
109
+ #
110
+ # builder = device.action
111
+ # builder.add_wheel_input('wheel2')
112
+ #
113
+ # @param [String] name name for the device
114
+ # @return [Interactions::WheelInput] The wheel input added
115
+ #
116
+
117
+ def add_wheel_input(name)
118
+ add_input(Interactions.wheel(name))
85
119
  end
86
120
 
87
121
  #
@@ -92,7 +126,26 @@ module Selenium
92
126
  #
93
127
 
94
128
  def get_device(name)
95
- @devices.find { |device| device.name == name.to_s }
129
+ WebDriver.logger.deprecate('#get_device with name parameter',
130
+ '#device with :name or :type keyword',
131
+ id: :get_device)
132
+ device(name: name)
133
+ end
134
+
135
+ #
136
+ # Retrieves the input device for the given name or type
137
+ #
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
141
+ #
142
+
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
96
149
  end
97
150
 
98
151
  #
@@ -115,6 +168,16 @@ module Selenium
115
168
  @devices.select { |device| device.type == Interactions::KEY }
116
169
  end
117
170
 
171
+ #
172
+ # Retrieves the current WheelInput device
173
+ #
174
+ # @return [Selenium::WebDriver::Interactions::InputDevice] current WheelInput devices
175
+ #
176
+
177
+ def wheel_inputs
178
+ @devices.select { |device| device.type == Interactions::WHEEL }
179
+ end
180
+
118
181
  #
119
182
  # Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait
120
183
  # for all actions to complete in that tick.
@@ -131,8 +194,11 @@ module Selenium
131
194
  # @return [ActionBuilder] A self reference.
132
195
  #
133
196
 
134
- def pause(device, duration = nil)
135
- device.create_pause(duration)
197
+ def pause(deprecated_device = nil, deprecated_duration = nil, device: nil, duration: 0)
198
+ deprecate_method(deprecated_device, deprecated_duration)
199
+
200
+ device ||= deprecated_device || pointer_input
201
+ device.create_pause(deprecated_duration || duration)
136
202
  self
137
203
  end
138
204
 
@@ -152,7 +218,14 @@ module Selenium
152
218
  # @return [ActionBuilder] A self reference.
153
219
  #
154
220
 
155
- def pauses(device, number, duration = nil)
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
+
156
229
  number.times { device.create_pause(duration) }
157
230
  self
158
231
  end
@@ -162,7 +235,7 @@ module Selenium
162
235
  #
163
236
 
164
237
  def perform
165
- @bridge.send_actions @devices.map(&:encode).compact
238
+ @bridge.send_actions @devices.filter_map(&:encode)
166
239
  clear_all_actions
167
240
  nil
168
241
  end
@@ -202,12 +275,26 @@ module Selenium
202
275
  #
203
276
 
204
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
+
205
282
  unless @async
206
283
  max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }
207
- pauses(device, max_device.actions.length)
284
+ pauses(device: device, number: max_device.actions.length) if max_device
208
285
  end
209
286
  @devices << device
287
+ device
210
288
  end
289
+
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
296
+ end
297
+
211
298
  end # ActionBuilder
212
299
  end # WebDriver
213
300
  end # Selenium
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed to the Software Freedom Conservancy (SFC) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The SFC licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+
20
+ module Selenium
21
+ module WebDriver
22
+
23
+ #
24
+ # @api private
25
+ #
26
+
27
+ class ChildProcess
28
+ TimeoutError = Class.new(StandardError)
29
+
30
+ SIGTERM = 'TERM'
31
+ SIGKILL = 'KILL'
32
+
33
+ POLL_INTERVAL = 0.1
34
+
35
+ attr_accessor :detach
36
+ attr_writer :io
37
+
38
+ def self.build(*command)
39
+ new(*command)
40
+ end
41
+
42
+ def initialize(*command)
43
+ @command = command
44
+ @detach = false
45
+ @pid = nil
46
+ @status = nil
47
+ end
48
+
49
+ def io
50
+ @io ||= Platform.null_device
51
+ end
52
+
53
+ def start
54
+ options = {%i[out err] => io}
55
+ options[:pgroup] = true unless Platform.windows? # NOTE: this is a bug only in Windows 7
56
+
57
+ WebDriver.logger.debug("Starting process: #{@command} with #{options}")
58
+ @pid = Process.spawn(*@command, options)
59
+ WebDriver.logger.debug(" -> pid: #{@pid}")
60
+
61
+ Process.detach(@pid) if detach
62
+ end
63
+
64
+ def stop(timeout = 3)
65
+ return unless @pid
66
+ return if exited?
67
+
68
+ WebDriver.logger.debug("Sending TERM to process: #{@pid}")
69
+ terminate(@pid)
70
+ poll_for_exit(timeout)
71
+
72
+ WebDriver.logger.debug(" -> stopped #{@pid}")
73
+ rescue TimeoutError, Errno::EINVAL
74
+ WebDriver.logger.debug(" -> sending KILL to process: #{@pid}")
75
+ kill(@pid)
76
+ wait
77
+ WebDriver.logger.debug(" -> killed #{@pid}")
78
+ end
79
+
80
+ def alive?
81
+ @pid && !exited?
82
+ end
83
+
84
+ def exited?
85
+ return unless @pid
86
+
87
+ WebDriver.logger.debug("Checking if #{@pid} is exited:")
88
+ _, @status = Process.waitpid2(@pid, Process::WNOHANG | Process::WUNTRACED) if @status.nil?
89
+ return if @status.nil?
90
+
91
+ exit_code = @status.exitstatus || @status.termsig
92
+ WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}")
93
+
94
+ !!exit_code
95
+ end
96
+
97
+ def poll_for_exit(timeout)
98
+ WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}")
99
+
100
+ end_time = Time.now + timeout
101
+ sleep POLL_INTERVAL until exited? || Time.now > end_time
102
+
103
+ raise TimeoutError, " -> #{@pid} still alive after #{timeout} seconds" unless exited?
104
+ end
105
+
106
+ def wait
107
+ return if exited?
108
+
109
+ _, @status = Process.waitpid2(@pid)
110
+ end
111
+
112
+ private
113
+
114
+ def terminate(pid)
115
+ Process.kill(SIGTERM, pid)
116
+ end
117
+
118
+ def kill(pid)
119
+ Process.kill(SIGKILL, pid)
120
+ rescue Errno::ECHILD, Errno::ESRCH
121
+ # already dead
122
+ end
123
+
124
+ end # ChildProcess
125
+ end # WebDriver
126
+ end # Selenium
@@ -123,8 +123,8 @@ module Selenium
123
123
  # @see ActionBuilder
124
124
  #
125
125
 
126
- def action
127
- bridge.action
126
+ def action(**opts)
127
+ bridge.action(**opts)
128
128
  end
129
129
 
130
130
  def mouse
@@ -248,6 +248,15 @@ module Selenium
248
248
  bridge.execute_async_script(script, *args)
249
249
  end
250
250
 
251
+ #
252
+ # @return [VirtualAuthenticator]
253
+ # @see VirtualAuthenticator
254
+ #
255
+
256
+ def add_virtual_authenticator(options)
257
+ bridge.add_virtual_authenticator(options)
258
+ end
259
+
251
260
  #-------------------------------- sugar --------------------------------
252
261
 
253
262
  #
@@ -307,71 +316,29 @@ module Selenium
307
316
 
308
317
  attr_reader :bridge
309
318
 
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
319
+ def create_bridge(capabilities: nil, options: nil, url: nil, service: nil, http_client: nil)
320
+ Remote::Bridge.new(http_client: http_client,
321
+ url: url || service_url(service)).tap do |bridge|
322
+ generated_caps = options ? options.as_json : generate_capabilities(capabilities)
323
+ bridge.create_session(generated_caps)
331
324
  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
325
  end
343
326
 
344
- def generate_capabilities(cap_array)
345
- cap_array.map { |cap|
327
+ def generate_capabilities(capabilities)
328
+ Array(capabilities).map { |cap|
346
329
  if cap.is_a? Symbol
347
330
  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
331
  elsif !cap.respond_to? :as_json
355
332
  msg = ":capabilities parameter only accepts objects responding to #as_json which #{cap.class} does not"
356
333
  raise ArgumentError, msg
357
334
  end
358
- cap&.as_json
335
+ cap.as_json
359
336
  }.inject(:merge) || Remote::Capabilities.send(browser || :new)
360
337
  end
361
338
 
362
- def service_url(opts)
363
- service_config = opts.delete(:service)
364
- %i[driver_opts driver_path port].each do |key|
365
- next unless opts.key? key
366
-
367
- WebDriver.logger.deprecate(":#{key}", ':service with an instance of Selenium::WebDriver::Service',
368
- id: "service_#{key}".to_sym)
369
- end
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
339
+ def service_url(service)
340
+ service ||= Service.send(browser)
341
+ @service = service.launch
375
342
  @service.uri
376
343
  end
377
344
 
@@ -20,12 +20,19 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module DriverExtensions
23
- module HasRemoteStatus
24
- def remote_status
25
- WebDriver.logger.deprecate('#remote_status', '#status')
26
- @bridge.status
23
+ module HasBiDi
24
+
25
+ #
26
+ # Retrieves WebDriver BiDi connection.
27
+ #
28
+ # @return [BiDi]
29
+ #
30
+
31
+ def bidi
32
+ @bidi ||= Selenium::WebDriver::BiDi.new(url: capabilities[:web_socket_url])
27
33
  end
28
- end # HasRemoteStatus
34
+
35
+ end # HasBiDi
29
36
  end # DriverExtensions
30
37
  end # WebDriver
31
38
  end # Selenium
@@ -52,6 +52,16 @@ module Selenium
52
52
  @bridge.start_cast_tab_mirroring(name)
53
53
  end
54
54
 
55
+ #
56
+ # Starts a tab mirroring session on a specific receiver target.
57
+ #
58
+ # @param [String] name the sink to use as the target
59
+ #
60
+
61
+ def start_cast_desktop_mirroring(name)
62
+ @bridge.start_cast_desktop_mirroring(name)
63
+ end
64
+
55
65
  #
56
66
  # Gets error messages when there is any issue in a Cast session.
57
67
  #
@@ -27,8 +27,7 @@ module Selenium
27
27
  # a `with` statement. The state of the context on the server is
28
28
  # saved before entering the block, and restored upon exiting it.
29
29
  #
30
- # @param [String] name which permission to set
31
- # @param [String] value what to set the permission to
30
+ # @param [String] value which context gets set (either 'chrome' or 'content')
32
31
  #
33
32
 
34
33
  def context=(value)
@@ -114,7 +114,7 @@ module Selenium
114
114
  execute_script(mutation_listener)
115
115
  devtools.page.add_script_to_evaluate_on_new_document(source: mutation_listener)
116
116
 
117
- devtools.runtime.on(:binding_called, &method(:log_mutation_event))
117
+ devtools.runtime.on(:binding_called) { |event| log_mutation_event(event) }
118
118
  end
119
119
 
120
120
  def log_mutation_event(params)
@@ -61,75 +61,10 @@ module Selenium
61
61
  #
62
62
 
63
63
  def intercept(&block)
64
- devtools.network.set_cache_disabled(cache_disabled: true)
65
- devtools.fetch.on(:request_paused) do |params|
66
- id = params['requestId']
67
- if params.key?('responseStatusCode') || params.key?('responseErrorReason')
68
- intercept_response(id, params, &pending_response_requests.delete(id))
69
- else
70
- intercept_request(id, params, &block)
71
- end
72
- end
73
- devtools.fetch.enable(patterns: [{requestStage: 'Request'}, {requestStage: 'Response'}])
64
+ @interceptor ||= DevTools::NetworkInterceptor.new(devtools)
65
+ @interceptor.intercept(&block)
74
66
  end
75
67
 
76
- private
77
-
78
- def pending_response_requests
79
- @pending_response_requests ||= {}
80
- end
81
-
82
- def intercept_request(id, params, &block)
83
- original = DevTools::Request.from(id, params)
84
- mutable = DevTools::Request.from(id, params)
85
-
86
- block.call(mutable) do |&continue| # rubocop:disable Performance/RedundantBlockCall
87
- pending_response_requests[id] = continue
88
-
89
- if original == mutable
90
- devtools.fetch.continue_request(request_id: id)
91
- else
92
- devtools.fetch.continue_request(
93
- request_id: id,
94
- url: mutable.url,
95
- method: mutable.method,
96
- post_data: mutable.post_data,
97
- headers: mutable.headers.map do |k, v|
98
- {name: k, value: v}
99
- end
100
- )
101
- end
102
- end
103
- end
104
-
105
- def intercept_response(id, params)
106
- return devtools.fetch.continue_request(request_id: id) unless block_given?
107
-
108
- body = fetch_response_body(id)
109
- original = DevTools::Response.from(id, body, params)
110
- mutable = DevTools::Response.from(id, body, params)
111
- yield mutable
112
-
113
- if original == mutable
114
- devtools.fetch.continue_request(request_id: id)
115
- else
116
- devtools.fetch.fulfill_request(
117
- request_id: id,
118
- body: (Base64.strict_encode64(mutable.body) if mutable.body),
119
- response_code: mutable.code,
120
- response_headers: mutable.headers.map do |k, v|
121
- {name: k, value: v}
122
- end
123
- )
124
- end
125
- end
126
-
127
- def fetch_response_body(id)
128
- devtools.fetch.get_response_body(request_id: id).dig('result', 'body')
129
- rescue Error::WebDriverError
130
- # CDP fails to get body on certain responses (301) and raises:
131
- # Can only get response body on requests captured after headers received.
132
- end
133
68
  end # HasNetworkInterception
134
69
  end # DriverExtensions
135
70
  end # WebDriver
@@ -62,7 +62,7 @@ module Selenium
62
62
  #
63
63
  # Unpins script making it undefined for the subsequent calls.
64
64
  #
65
- # @param [DevTools::PinnedScript]
65
+ # @param [DevTools::PinnedScript] script
66
66
  #
67
67
 
68
68
  def unpin_script(script)
@@ -46,7 +46,7 @@ module Selenium
46
46
  alias_method :eql?, :==
47
47
 
48
48
  def hash
49
- @id.hash ^ @bridge.hash
49
+ [@id, @bridge].hash
50
50
  end
51
51
 
52
52
  #
@@ -101,7 +101,7 @@ module Selenium
101
101
  # if it exists. If it does not, then the value of the attribute with the given name is returned.
102
102
  # If neither exists, null is returned.
103
103
  #
104
- # The "style" attribute is converted as best can be to a text representation with a trailing semi-colon.
104
+ # The "style" attribute is converted as best can be to a text representation with a trailing semicolon.
105
105
  #
106
106
  # The following are deemed to be "boolean" attributes, and will return either "true" or "false":
107
107
  #
@@ -29,7 +29,7 @@ module Selenium
29
29
  def self.for_error(error)
30
30
  return if error.nil?
31
31
 
32
- klass_name = error.split(' ').map(&:capitalize).join.sub(/Error$/, '')
32
+ klass_name = error.split.map(&:capitalize).join.sub(/Error$/, '')
33
33
  const_get("#{klass_name}Error", false)
34
34
  rescue NameError
35
35
  WebDriverError
@@ -22,8 +22,15 @@ require 'securerandom'
22
22
  module Selenium
23
23
  module WebDriver
24
24
  module Interactions
25
+ #
26
+ # Superclass for the input device sources
27
+ # Manages Array of Interaction instances for the device
28
+ #
29
+ # @api private
30
+ #
31
+
25
32
  class InputDevice
26
- attr_reader :name, :actions
33
+ attr_reader :name, :actions, :type
27
34
 
28
35
  def initialize(name = nil)
29
36
  @name = name || SecureRandom.uuid
@@ -44,9 +51,8 @@ module Selenium
44
51
  add_action(Pause.new(self, duration))
45
52
  end
46
53
 
47
- def no_actions? # Determine if only pauses are present
48
- actions = @actions.reject { |action| action.type == Interaction::PAUSE }
49
- actions.empty?
54
+ def encode
55
+ {type: type, id: name, actions: @actions.map(&:encode)} unless @actions.empty?
50
56
  end
51
57
  end # InputDevice
52
58
  end # Interactions