selenium-webdriver 4.1.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.
- checksums.yaml +4 -4
- data/CHANGES +85 -1
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/lib/selenium/server.rb +15 -10
- data/lib/selenium/webdriver/bidi/session.rb +38 -0
- data/lib/selenium/webdriver/bidi.rb +55 -0
- data/lib/selenium/webdriver/chrome/features.rb +5 -0
- data/lib/selenium/webdriver/chrome/options.rb +33 -19
- data/lib/selenium/webdriver/chrome.rb +0 -14
- data/lib/selenium/webdriver/common/action_builder.rb +108 -21
- data/lib/selenium/webdriver/common/driver.rb +22 -55
- data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +12 -5
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -67
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -1
- data/lib/selenium/webdriver/common/element.rb +1 -1
- data/lib/selenium/webdriver/common/error.rb +1 -1
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
- data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
- data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
- data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -70
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
- data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
- data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
- data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
- data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
- data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
- data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/manager.rb +0 -27
- data/lib/selenium/webdriver/common/options.rb +2 -9
- data/lib/selenium/webdriver/common/platform.rb +4 -4
- data/lib/selenium/webdriver/common/search_context.rb +0 -6
- data/lib/selenium/webdriver/common/service_manager.rb +2 -3
- data/lib/selenium/webdriver/common/shadow_root.rb +1 -1
- data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
- data/lib/selenium/webdriver/common/takes_screenshot.rb +1 -1
- data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +83 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +73 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
- data/lib/selenium/webdriver/common/websocket_connection.rb +156 -0
- data/lib/selenium/webdriver/common/window.rb +6 -6
- data/lib/selenium/webdriver/common/zipper.rb +1 -1
- data/lib/selenium/webdriver/common.rb +17 -3
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +176 -0
- data/lib/selenium/webdriver/devtools/request.rb +1 -1
- data/lib/selenium/webdriver/devtools/response.rb +1 -1
- data/lib/selenium/webdriver/devtools.rb +6 -112
- data/lib/selenium/webdriver/edge/features.rb +1 -0
- data/lib/selenium/webdriver/firefox/driver.rb +1 -0
- data/lib/selenium/webdriver/firefox/features.rb +2 -5
- data/lib/selenium/webdriver/firefox/options.rb +3 -1
- data/lib/selenium/webdriver/firefox/profile.rb +1 -5
- data/lib/selenium/webdriver/firefox/util.rb +46 -0
- data/lib/selenium/webdriver/firefox.rb +1 -14
- data/lib/selenium/webdriver/ie.rb +0 -14
- data/lib/selenium/webdriver/remote/bridge.rb +54 -19
- data/lib/selenium/webdriver/remote/commands.rb +15 -6
- data/lib/selenium/webdriver/remote/driver.rb +0 -1
- data/lib/selenium/webdriver/remote/http/default.rb +6 -12
- data/lib/selenium/webdriver/remote/response.rb +2 -2
- data/lib/selenium/webdriver/safari.rb +0 -14
- data/lib/selenium/webdriver/support/cdp_client_generator.rb +4 -4
- data/lib/selenium/webdriver/support/color.rb +7 -7
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -1
- data/lib/selenium/webdriver/support/guards.rb +1 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +1 -0
- data/selenium-webdriver.gemspec +9 -6
- metadata +64 -12
- data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
module WheelActions
|
|
23
|
+
class ScrollOrigin
|
|
24
|
+
class << self
|
|
25
|
+
def element(element, x_offset = 0, y_offset = 0)
|
|
26
|
+
new(element, x_offset, y_offset)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def viewport(x_offset = 0, y_offset = 0)
|
|
30
|
+
new(:viewport, x_offset, y_offset)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
attr_reader :origin, :x_offset, :y_offset
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Use a static method to access
|
|
38
|
+
# @api private
|
|
39
|
+
#
|
|
40
|
+
def initialize(origin, x_offset, y_offset)
|
|
41
|
+
@origin = origin
|
|
42
|
+
@x_offset = x_offset
|
|
43
|
+
@y_offset = y_offset
|
|
44
|
+
end
|
|
45
|
+
end # ScrollOrigin
|
|
46
|
+
end # WheelActions
|
|
47
|
+
end # WebDriver
|
|
48
|
+
end # Selenium
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
module Interactions
|
|
23
|
+
#
|
|
24
|
+
# Actions related to pressing keys.
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
class TypingInteraction < Interaction
|
|
30
|
+
attr_reader :type
|
|
31
|
+
|
|
32
|
+
def initialize(source, type, key)
|
|
33
|
+
super(source)
|
|
34
|
+
@type = assert_type(type)
|
|
35
|
+
@key = Keys.encode_key(key)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def assert_source(source)
|
|
39
|
+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? KeyInput
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def assert_type(type)
|
|
43
|
+
raise TypeError, "#{type.inspect} is not a valid key subtype" unless KeyInput::SUBTYPES.key? type
|
|
44
|
+
|
|
45
|
+
KeyInput::SUBTYPES[type]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def encode
|
|
49
|
+
{type: @type, value: @key}
|
|
50
|
+
end
|
|
51
|
+
end # TypingInteraction
|
|
52
|
+
end # Interactions
|
|
53
|
+
end # WebDriver
|
|
54
|
+
end # Selenium
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
module WheelActions
|
|
23
|
+
attr_writer :default_scroll_duration
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# By default this is set to 250ms in the ActionBuilder constructor
|
|
27
|
+
# It can be overridden with default_scroll_duration=
|
|
28
|
+
#
|
|
29
|
+
|
|
30
|
+
def default_scroll_duration
|
|
31
|
+
@default_scroll_duration ||= @duration / 1000.0 # convert ms to seconds
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport.
|
|
36
|
+
#
|
|
37
|
+
# @example Scroll to element
|
|
38
|
+
# el = driver.find_element(id: "some_id")
|
|
39
|
+
# driver.action.scroll_to(element).perform
|
|
40
|
+
#
|
|
41
|
+
# @param [Object] Which element to scroll into the viewport.
|
|
42
|
+
# @return [Selenium::WebDriver::WheelActions] A self reference.
|
|
43
|
+
def scroll_to(element, device: nil)
|
|
44
|
+
scroll(origin: element, device: device)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# Scrolls by provided amounts with the origin in the top left corner of the viewport.
|
|
49
|
+
#
|
|
50
|
+
# @example Scroll viewport by a specified amount
|
|
51
|
+
# el = driver.find_element(id: "some_id")
|
|
52
|
+
# driver.action.scroll_by(100, 200).perform
|
|
53
|
+
#
|
|
54
|
+
# @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
|
|
55
|
+
# @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
|
|
56
|
+
# @return [Selenium::WebDriver::WheelActions] A self reference.
|
|
57
|
+
def scroll_by(delta_x, delta_y, device: nil)
|
|
58
|
+
scroll(delta_x: delta_x, delta_y: delta_y, device: device)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Scrolls by provided amount based on a provided origin.
|
|
63
|
+
#
|
|
64
|
+
# The scroll origin is either the center of an element or the upper left of the viewport plus any offsets.
|
|
65
|
+
# If the origin is an element, and the element is not in the viewport, the bottom of the element will first
|
|
66
|
+
# be scrolled to the bottom of the viewport.
|
|
67
|
+
#
|
|
68
|
+
# @example Scroll from element by a specified amount
|
|
69
|
+
# el = driver.find_element(id: "some_id")
|
|
70
|
+
# origin = WheelActions::ScrollOrigin.element(el)
|
|
71
|
+
# driver.action.scroll_from(origin, 0, 200).perform
|
|
72
|
+
#
|
|
73
|
+
# @example Scroll from element by a specified amount with an offset
|
|
74
|
+
# el = driver.find_element(id: "some_id")
|
|
75
|
+
# origin = WheelActions::ScrollOrigin.element(el, 10, 10)
|
|
76
|
+
# driver.action.scroll_from(origin, 100, 200).perform
|
|
77
|
+
#
|
|
78
|
+
# @example Scroll viewport by a specified amount with an offset
|
|
79
|
+
# origin = WheelActions::ScrollOrigin.viewport(10, 10)
|
|
80
|
+
# driver.action.scroll_from(origin, 0, 200).perform
|
|
81
|
+
#
|
|
82
|
+
# @param [ScrollOrigin] scroll_origin Where scroll originates (viewport or element center) plus provided offsets.
|
|
83
|
+
# @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
|
|
84
|
+
# @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
|
|
85
|
+
# @return [Selenium::WebDriver::WheelActions] A self reference.
|
|
86
|
+
# @raise [Error::MoveTargetOutOfBoundsError] If the origin with offset is outside the viewport.
|
|
87
|
+
def scroll_from(scroll_origin, delta_x, delta_y, device: nil)
|
|
88
|
+
raise TypeError, "#{scroll_origin.inspect} isn't a valid ScrollOrigin" unless scroll_origin.is_a?(ScrollOrigin)
|
|
89
|
+
|
|
90
|
+
scroll(x: scroll_origin.x_offset,
|
|
91
|
+
y: scroll_origin.y_offset,
|
|
92
|
+
delta_x: delta_x,
|
|
93
|
+
delta_y: delta_y,
|
|
94
|
+
origin: scroll_origin.origin,
|
|
95
|
+
device: device)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def scroll(**opts)
|
|
101
|
+
opts[:duration] = default_scroll_duration
|
|
102
|
+
wheel = wheel_input(opts.delete(:device))
|
|
103
|
+
wheel.create_scroll(**opts)
|
|
104
|
+
tick(wheel)
|
|
105
|
+
self
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def wheel_input(name = nil)
|
|
109
|
+
device(name: name, type: Interactions::WHEEL) || add_wheel_input('wheel')
|
|
110
|
+
end
|
|
111
|
+
end # WheelActions
|
|
112
|
+
end # WebDriver
|
|
113
|
+
end # Selenium
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
module Interactions
|
|
23
|
+
#
|
|
24
|
+
# Creates actions specific to Pointer Input devices
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
#
|
|
28
|
+
|
|
29
|
+
class WheelInput < InputDevice
|
|
30
|
+
def initialize(name = nil)
|
|
31
|
+
super(name)
|
|
32
|
+
@type = Interactions::WHEEL
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_scroll(**opts)
|
|
36
|
+
opts[:source] = self
|
|
37
|
+
add_action(Scroll.new(**opts))
|
|
38
|
+
end
|
|
39
|
+
end # PointerInput
|
|
40
|
+
end # Interactions
|
|
41
|
+
end # WebDriver
|
|
42
|
+
end # Selenium
|
|
@@ -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(
|
|
69
|
+
def initialize(**opts)
|
|
70
70
|
self.class.set_capabilities
|
|
71
71
|
|
|
72
|
-
@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
|
|
|
@@ -104,7 +104,7 @@ module Selenium
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def cygwin?
|
|
107
|
-
RUBY_PLATFORM
|
|
107
|
+
RUBY_PLATFORM.include?('cygwin')
|
|
108
108
|
!Regexp.last_match.nil?
|
|
109
109
|
end
|
|
110
110
|
|
|
@@ -177,9 +177,9 @@ module Selenium
|
|
|
177
177
|
|
|
178
178
|
def find_in_program_files(*binary_names)
|
|
179
179
|
paths = [
|
|
180
|
-
ENV
|
|
181
|
-
ENV
|
|
182
|
-
ENV
|
|
180
|
+
ENV.fetch('PROGRAMFILES', '\\Program Files'),
|
|
181
|
+
ENV.fetch('ProgramFiles(x86)', '\\Program Files (x86)'),
|
|
182
|
+
ENV.fetch('ProgramW6432', '\\Program Files')
|
|
183
183
|
]
|
|
184
184
|
|
|
185
185
|
paths.each do |root|
|
|
@@ -61,9 +61,6 @@ module Selenium
|
|
|
61
61
|
raise ArgumentError, "cannot find element by #{how.inspect}" unless by
|
|
62
62
|
|
|
63
63
|
bridge.find_element_by by, what, ref
|
|
64
|
-
rescue Selenium::WebDriver::Error::TimeoutError
|
|
65
|
-
# Implicit Wait times out in Edge
|
|
66
|
-
raise Selenium::WebDriver::Error::NoSuchElementError
|
|
67
64
|
end
|
|
68
65
|
|
|
69
66
|
#
|
|
@@ -79,9 +76,6 @@ module Selenium
|
|
|
79
76
|
raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
|
|
80
77
|
|
|
81
78
|
bridge.find_elements_by by, what, ref
|
|
82
|
-
rescue Selenium::WebDriver::Error::TimeoutError
|
|
83
|
-
# Implicit Wait times out in Edge
|
|
84
|
-
[]
|
|
85
79
|
end
|
|
86
80
|
|
|
87
81
|
private
|
|
@@ -49,7 +49,7 @@ module Selenium
|
|
|
49
49
|
def start
|
|
50
50
|
raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
|
|
51
51
|
|
|
52
|
-
Platform.exit_hook
|
|
52
|
+
Platform.exit_hook { stop } # make sure we don't leave the server running
|
|
53
53
|
|
|
54
54
|
socket_lock.locked do
|
|
55
55
|
find_free_port
|
|
@@ -60,6 +60,7 @@ module Selenium
|
|
|
60
60
|
|
|
61
61
|
def stop
|
|
62
62
|
return unless @shutdown_supported
|
|
63
|
+
return if process_exited?
|
|
63
64
|
|
|
64
65
|
stop_server
|
|
65
66
|
@process.poll_for_exit STOP_TIMEOUT
|
|
@@ -116,8 +117,6 @@ module Selenium
|
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
def stop_server
|
|
119
|
-
return if process_exited?
|
|
120
|
-
|
|
121
120
|
connect_to_server do |http|
|
|
122
121
|
headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
|
|
123
122
|
http.get('/shutdown', headers)
|
|
@@ -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
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
# Options for the creation of virtual authenticators.
|
|
22
|
+
# @see http://w3c.github.io/webauthn/#sctn-automation
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
module Selenium
|
|
26
|
+
module WebDriver
|
|
27
|
+
class VirtualAuthenticatorOptions
|
|
28
|
+
|
|
29
|
+
PROTOCOL = {ctap2: "ctap2", u2f: "ctap1/u2f"}.freeze
|
|
30
|
+
TRANSPORT = {ble: "ble", usb: "usb", nfc: "nfc", internal: "internal"}.freeze
|
|
31
|
+
|
|
32
|
+
attr_accessor :protocol, :transport, :resident_key, :user_verification, :user_consenting, :user_verified
|
|
33
|
+
alias_method :resident_key?, :resident_key
|
|
34
|
+
alias_method :user_verification?, :user_verification
|
|
35
|
+
alias_method :user_consenting?, :user_consenting
|
|
36
|
+
alias_method :user_verified?, :user_verified
|
|
37
|
+
|
|
38
|
+
def initialize(protocol: :ctap2, transport: :usb, resident_key: false,
|
|
39
|
+
user_verification: false, user_consenting: true, user_verified: false)
|
|
40
|
+
@protocol = protocol
|
|
41
|
+
@transport = transport
|
|
42
|
+
@resident_key = resident_key
|
|
43
|
+
@user_verification = user_verification
|
|
44
|
+
@user_consenting = user_consenting
|
|
45
|
+
@user_verified = user_verified
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
#
|
|
49
|
+
# @api private
|
|
50
|
+
#
|
|
51
|
+
|
|
52
|
+
def as_json(*)
|
|
53
|
+
{'protocol' => PROTOCOL[protocol],
|
|
54
|
+
'transport' => TRANSPORT[transport],
|
|
55
|
+
'hasResidentKey' => resident_key?,
|
|
56
|
+
'hasUserVerification' => user_verification?,
|
|
57
|
+
'isUserConsenting' => user_consenting?,
|
|
58
|
+
'isUserVerified' => user_verified?}
|
|
59
|
+
end
|
|
60
|
+
end # VirtualAuthenticatorOptions
|
|
61
|
+
end # WebDriver
|
|
62
|
+
end # Selenium
|