selenium-webdriver 4.2.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
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