selenium-webdriver 4.2.0 → 4.4.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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +40 -1
  3. data/lib/selenium/server.rb +1 -1
  4. data/lib/selenium/webdriver/chrome/options.rb +14 -0
  5. data/lib/selenium/webdriver/chrome.rb +0 -14
  6. data/lib/selenium/webdriver/common/driver.rb +20 -53
  7. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -67
  8. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +17 -34
  9. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +1 -1
  10. data/lib/selenium/webdriver/common/manager.rb +0 -27
  11. data/lib/selenium/webdriver/common/options.rb +2 -9
  12. data/lib/selenium/webdriver/common/takes_screenshot.rb +1 -1
  13. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +83 -0
  14. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +73 -0
  15. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  16. data/lib/selenium/webdriver/common/websocket_connection.rb +13 -6
  17. data/lib/selenium/webdriver/common/window.rb +6 -6
  18. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  19. data/lib/selenium/webdriver/common.rb +3 -1
  20. data/lib/selenium/webdriver/devtools/network_interceptor.rb +176 -0
  21. data/lib/selenium/webdriver/devtools.rb +1 -0
  22. data/lib/selenium/webdriver/firefox.rb +0 -14
  23. data/lib/selenium/webdriver/ie.rb +0 -14
  24. data/lib/selenium/webdriver/remote/bridge.rb +35 -2
  25. data/lib/selenium/webdriver/remote/commands.rb +15 -1
  26. data/lib/selenium/webdriver/remote/driver.rb +0 -1
  27. data/lib/selenium/webdriver/safari.rb +0 -14
  28. data/lib/selenium/webdriver/version.rb +1 -1
  29. data/selenium-webdriver.gemspec +3 -3
  30. metadata +17 -15
  31. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +0 -31
  32. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5767633e16bd3296a248757bd01f157c6441a7780d3bcfd972a7c03f94237d54
4
- data.tar.gz: 6e82108ff5bd955eeb68e2b7a8e23aa9c766d900f46bbb4e7d3a9475b00a7ceb
3
+ metadata.gz: 2be9fb14d69ce82170e3ddbe0cbcf2f2428e1978544d8a7c11191ebd37cf0241
4
+ data.tar.gz: 2e40d6ad67ccfb269ee1ae9d3f47d024e90081da9881f757f22170751875fa69
5
5
  SHA512:
6
- metadata.gz: 792f2a87bb8c1f78c8e7fef7242d9b42026473e9fcb2c8a586e5d1af04188f0026b7b7e0c300b3d9cc3d709a8a7efe0dc6050edd69df1af3b1c397f40a1b0fd8
7
- data.tar.gz: 840b9619f535ea1f67d603de69d88e5fcf8ca324a9198b68f9e418f1ebde25bae9d40cfda6c65c8fce9321ae21a84b88ef91c57ae99105a95d191dbf28f1f0e5
6
+ metadata.gz: b7f419d9b1349a1afbf68999eecaa53391503cc793026228577fbcb40ac4958a35c539286f7c4c3c913992336dc797481f7c57f6aec9d58813671e24d1cd09ad
7
+ data.tar.gz: '0093bd2d0fbb0c1c8e7abee9d1a632f80d458b1dc0d1b22635f403230ed7c40a2be498915d8abed40bd4992295a1444a69c3e653ebf1d4cf106886c8a9d5a499'
data/CHANGES CHANGED
@@ -1,4 +1,43 @@
1
- 4.2.0 (unreleased)
1
+ 4.4.0 (Unreleased)
2
+ =========================
3
+
4
+ BiDi:
5
+ * Released selenium-devtools 0.103.1 to fix websocket dependency requirement
6
+ * Released selenium-devtools 0.104.0 (supports CDP v85, v102, v103, v104)
7
+ * Have network interceptor ignore cancelled requests (#10856)
8
+ * Improve websocket message handling
9
+
10
+ Chromium:
11
+ * Prevent users from setting w3c: false and warn for true (thanks Tamsil Amani!)
12
+
13
+ Ruby:
14
+ * Implement Virtual Authenticator (#10903, #10541) (thanks Tamsil Amani!)
15
+
16
+ 4.3.0 (2022-06-23)
17
+ =========================
18
+
19
+ BiDi:
20
+ * Released selenium-devtools 0.103.0 (supports CDP v85, v101, v102, v103)
21
+
22
+ Ruby:
23
+ * Allow specifying which button is clicked in pointer action class methods
24
+ * Remove deprecated `Persistent` http class
25
+ * Remove deprecated HasRemoteStatus module
26
+ * Remove deprecated `Manager#new_window` and `Manager#logs`
27
+ * `ActionBuilder#move_to` no longer attempts to move to top left corner of element
28
+ * Remove deprecated support for sending Service parameters directly to Driver constructor
29
+ * Remove deprecated setters and getters for driver path on Browser modules
30
+ * Remove deprecated support for passing in options argument to Options class
31
+ * Allow `:options` parameter to take `Options` instance argument like other languages
32
+ * Remove deprecated support for `:desired_capabilities` & `:options` with `Hash` argument
33
+
34
+ 4.2.1 (2022-05-31)
35
+ =========================
36
+
37
+ Ruby
38
+ * Fix bug in setting default duration in Actions constructor
39
+
40
+ 4.2.0 (2022-05-27)
2
41
  =========================
3
42
 
4
43
  BiDi:
@@ -92,7 +92,7 @@ module Selenium
92
92
  download_server(redirected, destination)
93
93
  end
94
94
  rescue StandardError
95
- FileUtils.rm download_file_name if File.exist? download_file_name
95
+ FileUtils.rm_rf download_file_name
96
96
  raise
97
97
  end
98
98
 
@@ -227,6 +227,9 @@ module Selenium
227
227
 
228
228
  options = browser_options[self.class::KEY]
229
229
  options['binary'] ||= binary_path if binary_path
230
+
231
+ check_w3c(options[:w3c]) if options.key?(:w3c)
232
+
230
233
  if @profile
231
234
  options['args'] ||= []
232
235
  options['args'] << "--user-data-dir=#{@profile.directory}"
@@ -237,6 +240,17 @@ module Selenium
237
240
  options['extensions'] = @encoded_extensions + @extensions.map { |ext| encode_extension(ext) }
238
241
  end
239
242
 
243
+ def check_w3c(w3c)
244
+ if w3c
245
+ WebDriver.logger.warn("Setting 'w3c: true' is redundant and will no longer be allowed", id: :w3c)
246
+ return
247
+ end
248
+
249
+ raise Error::InvalidArgumentError,
250
+ "Setting 'w3c: false' is not allowed.\n" \
251
+ "Please update to W3C Syntax: https://www.selenium.dev/blog/2022/legacy-protocol-support/"
252
+ end
253
+
240
254
  def binary_path
241
255
  Chrome.path
242
256
  end
@@ -28,20 +28,6 @@ module Selenium
28
28
  autoload :Options, 'selenium/webdriver/chrome/options'
29
29
  autoload :Service, 'selenium/webdriver/chrome/service'
30
30
 
31
- def self.driver_path=(path)
32
- WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path=',
33
- 'Selenium::WebDriver::Chrome::Service#driver_path=',
34
- id: :driver_path
35
- Selenium::WebDriver::Chrome::Service.driver_path = path
36
- end
37
-
38
- def self.driver_path
39
- WebDriver.logger.deprecate 'Selenium::WebDriver::Chrome#driver_path',
40
- 'Selenium::WebDriver::Chrome::Service#driver_path',
41
- id: :driver_path
42
- Selenium::WebDriver::Chrome::Service.driver_path
43
- end
44
-
45
31
  def self.path=(path)
46
32
  Platform.assert_executable path
47
33
  @path = path
@@ -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
 
@@ -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
@@ -28,7 +28,7 @@ module Selenium
28
28
  #
29
29
 
30
30
  def default_move_duration
31
- @default_move_duration ||= @duration / 1000 # convert ms to seconds
31
+ @default_move_duration ||= @duration / 1000.0 # convert ms to seconds
32
32
  end
33
33
 
34
34
  #
@@ -68,9 +68,8 @@ module Selenium
68
68
  end
69
69
 
70
70
  #
71
- # Moves the pointer to the middle of the given element.
72
- # its location is calculated using getBoundingClientRect.
73
- # Then the pointer is moved to optional offset coordinates from the element.
71
+ # Moves the pointer to the in-view center point of the given element.
72
+ # Then the pointer is moved to optional offset coordinates.
74
73
  #
75
74
  # The element is not scrolled into view.
76
75
  # MoveTargetOutOfBoundsError will be raised if element with offset is outside the viewport
@@ -88,32 +87,19 @@ module Selenium
88
87
  # driver.action.move_to(el, 100, 100).perform
89
88
  #
90
89
  # @param [Selenium::WebDriver::Element] element to move to.
91
- # @param [Integer] right_by Optional offset from the top-left corner. A negative value means
92
- # coordinates to the left of the element.
93
- # @param [Integer] down_by Optional offset from the top-left corner. A negative value means
94
- # coordinates above the element.
90
+ # @param [Integer] right_by Optional offset from the in-view center of the
91
+ # element. A negative value means coordinates to the left of the center.
92
+ # @param [Integer] down_by Optional offset from the in-view center of the
93
+ # element. A negative value means coordinates to the top of the center.
95
94
  # @param [Symbol || String] device optional name of the PointerInput device to move.
96
95
  # @return [ActionBuilder] A self reference.
97
96
  #
98
97
 
99
98
  def move_to(element, right_by = nil, down_by = nil, device: nil, duration: default_move_duration, **opts)
100
99
  pointer = pointer_input(device)
101
- if right_by || down_by
102
- WebDriver.logger.warn("moving to an element with offset currently tries to use
103
- the top left corner of the element as the origin; in Selenium 4.3 it will use the in-view
104
- center point of the element as the origin.")
105
- size = element.size
106
- left_offset = (size[:width] / 2).to_i
107
- top_offset = (size[:height] / 2).to_i
108
- left = -left_offset + (right_by || 0)
109
- top = -top_offset + (down_by || 0)
110
- else
111
- left = 0
112
- top = 0
113
- end
114
100
  pointer.create_pointer_move(duration: duration,
115
- x: left,
116
- y: top,
101
+ x: right_by || 0,
102
+ y: down_by || 0,
117
103
  origin: element,
118
104
  **opts)
119
105
  tick(pointer)
@@ -192,9 +178,9 @@ center point of the element as the origin.")
192
178
  # @return [ActionBuilder] A self reference.
193
179
  #
194
180
 
195
- def click_and_hold(element = nil, device: nil)
181
+ def click_and_hold(element = nil, button: nil, device: nil)
196
182
  move_to(element, device: device) if element
197
- pointer_down(:left, device: device)
183
+ pointer_down(button || :left, device: device)
198
184
  self
199
185
  end
200
186
 
@@ -211,8 +197,8 @@ center point of the element as the origin.")
211
197
  # @return [ActionBuilder] A self reference.
212
198
  #
213
199
 
214
- def release(device: nil)
215
- pointer_up(:left, device: device)
200
+ def release(button: nil, device: nil)
201
+ pointer_up(button || :left, device: device)
216
202
  self
217
203
  end
218
204
 
@@ -238,10 +224,10 @@ center point of the element as the origin.")
238
224
  # @return [ActionBuilder] A self reference.
239
225
  #
240
226
 
241
- def click(element = nil, device: nil)
227
+ def click(element = nil, button: nil, device: nil)
242
228
  move_to(element, device: device) if element
243
- pointer_down(:left, device: device)
244
- pointer_up(:left, device: device)
229
+ pointer_down(button || :left, device: device)
230
+ pointer_up(button || :left, device: device)
245
231
  self
246
232
  end
247
233
 
@@ -296,10 +282,7 @@ center point of the element as the origin.")
296
282
  #
297
283
 
298
284
  def context_click(element = nil, device: nil)
299
- move_to(element, device: device) if element
300
- pointer_down(:right, device: device)
301
- pointer_up(:right, device: device)
302
- self
285
+ click(element, button: :right, device: device)
303
286
  end
304
287
 
305
288
  #
@@ -28,7 +28,7 @@ module Selenium
28
28
  #
29
29
 
30
30
  def default_scroll_duration
31
- @default_scroll_duration ||= @duration / 1000 # convert ms to seconds
31
+ @default_scroll_duration ||= @duration / 1000.0 # convert ms to seconds
32
32
  end
33
33
 
34
34
  #
@@ -104,33 +104,6 @@ module Selenium
104
104
  @timeouts ||= Timeouts.new(@bridge)
105
105
  end
106
106
 
107
- def logs
108
- WebDriver.logger.deprecate('Manager#logs', 'Chrome::Driver#logs')
109
- @logs ||= Logs.new(@bridge)
110
- end
111
-
112
- #
113
- # @param type [Symbol] Supports two values: :tab and :window.
114
- # @return [String] The value of the window handle
115
- #
116
- def new_window(type = :tab)
117
- WebDriver.logger.deprecate('Manager#new_window', 'TargetLocator#new_window', id: :new_window) do
118
- 'e.g., `driver.switch_to.new_window(:tab)`'
119
- end
120
- case type
121
- when :tab, :window
122
- result = @bridge.new_window(type)
123
- unless result.key?('handle')
124
- raise UnknownError, "the driver did not return a handle. " \
125
- "The returned result: #{result.inspect}"
126
- end
127
- result['handle']
128
- else
129
- raise ArgumentError, "invalid argument for type. Got: '#{type.inspect}'. " \
130
- "Try :tab or :window"
131
- end
132
- end
133
-
134
107
  def window
135
108
  @window ||= Window.new(@bridge)
136
109
  end
@@ -66,17 +66,10 @@ module Selenium
66
66
 
67
67
  attr_accessor :options
68
68
 
69
- def initialize(options: nil, **opts)
69
+ def initialize(**opts)
70
70
  self.class.set_capabilities
71
71
 
72
- @options = if options
73
- WebDriver.logger.deprecate(":options as keyword for initializing #{self.class}",
74
- "custom values directly in #new constructor",
75
- id: :options_options)
76
- opts.merge(options)
77
- else
78
- opts
79
- end
72
+ @options = opts
80
73
  @options[:browser_name] = self.class::BROWSER
81
74
  end
82
75
 
@@ -32,7 +32,7 @@ module Selenium
32
32
  def save_screenshot(png_path, full_page: false)
33
33
  extension = File.extname(png_path).downcase
34
34
  if extension != '.png'
35
- WebDriver.logger.warn "name used for saved screenshot does not match file type. "\
35
+ WebDriver.logger.warn "name used for saved screenshot does not match file type. " \
36
36
  "It should end with .png extension",
37
37
  id: :screenshot
38
38
  end
@@ -0,0 +1,83 @@
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
+ #
21
+ # A credential stored in a virtual authenticator.
22
+ # @see https://w3c.github.io/webauthn/#credential-parameters
23
+ #
24
+
25
+ module Selenium
26
+ module WebDriver
27
+ class Credential
28
+ class << self
29
+ def resident(**opts)
30
+ Credential.new(resident_credential: true, **opts)
31
+ end
32
+
33
+ def non_resident(**opts)
34
+ Credential.new(resident_credential: false, **opts)
35
+ end
36
+
37
+ def encode(byte_array)
38
+ Base64.urlsafe_encode64(byte_array&.pack('C*'))
39
+ end
40
+
41
+ def decode(base64)
42
+ Base64.urlsafe_decode64(base64).unpack('C*')
43
+ end
44
+
45
+ def from_json(opts)
46
+ user_handle = opts['userHandle'] ? decode(opts['userHandle']) : nil
47
+ new(id: decode(opts["credentialId"]),
48
+ resident_credential: opts["isResidentCredential"],
49
+ rp_id: opts['rpId'],
50
+ private_key: opts['privateKey'],
51
+ sign_count: opts['signCount'],
52
+ user_handle: user_handle)
53
+ end
54
+ end
55
+
56
+ attr_reader :id, :resident_credential, :rp_id, :user_handle, :private_key, :sign_count
57
+ alias_method :resident_credential?, :resident_credential
58
+
59
+ def initialize(id:, resident_credential:, rp_id:, private_key:, user_handle: nil, sign_count: 0)
60
+ @id = id
61
+ @resident_credential = resident_credential
62
+ @rp_id = rp_id
63
+ @user_handle = user_handle
64
+ @private_key = private_key
65
+ @sign_count = sign_count
66
+ end
67
+
68
+ #
69
+ # @api private
70
+ #
71
+
72
+ def as_json(*)
73
+ credential_data = {'credentialId' => Credential.encode(id),
74
+ 'isResidentCredential' => resident_credential?,
75
+ 'rpId' => rp_id,
76
+ 'privateKey' => Credential.encode(private_key),
77
+ 'signCount' => sign_count}
78
+ credential_data['userHandle'] = Credential.encode(user_handle) if user_handle
79
+ credential_data
80
+ end
81
+ end # Credential
82
+ end # WebDriver
83
+ end # Selenium
@@ -0,0 +1,73 @@
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
+ class VirtualAuthenticator
23
+
24
+ attr_reader :options
25
+
26
+ #
27
+ # api private
28
+ # Use `Driver#add_virtual_authenticator`
29
+ #
30
+
31
+ def initialize(bridge, authenticator_id, options)
32
+ @id = authenticator_id
33
+ @bridge = bridge
34
+ @options = options
35
+ @valid = true
36
+ end
37
+
38
+ def add_credential(credential)
39
+ credential = credential.as_json
40
+ @bridge.add_credential credential, @id
41
+ end
42
+
43
+ def credentials
44
+ credential_data = @bridge.credentials @id
45
+ credential_data.map do |cred|
46
+ Credential.from_json(cred)
47
+ end
48
+ end
49
+
50
+ def remove_credential(credential_id)
51
+ credential_id = Credential.encode(credential_id) if credential_id.instance_of?(Array)
52
+ @bridge.remove_credential credential_id, @id
53
+ end
54
+
55
+ def remove_all_credentials
56
+ @bridge.remove_all_credentials @id
57
+ end
58
+
59
+ def user_verified=(verified)
60
+ @bridge.user_verified verified, @id
61
+ end
62
+
63
+ def remove!
64
+ @bridge.remove_virtual_authenticator(@id)
65
+ @valid = false
66
+ end
67
+
68
+ def valid?
69
+ @valid
70
+ end
71
+ end # VirtualAuthenticator
72
+ end # WebDriver
73
+ end # Selenium