selenium-webdriver 4.17.0 → 4.26.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 +92 -0
- data/Gemfile +1 -0
- data/README.md +2 -2
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +2 -1
- data/lib/selenium/webdriver/atoms/findElements.js +26 -26
- data/lib/selenium/webdriver/atoms/getAttribute.js +2 -2
- data/lib/selenium/webdriver/atoms/isDisplayed.js +24 -97
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +1 -1
- data/lib/selenium/webdriver/bidi/log_handler.rb +63 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +5 -1
- data/lib/selenium/webdriver/bidi/session.rb +7 -7
- data/lib/selenium/webdriver/bidi/struct.rb +44 -0
- data/lib/selenium/webdriver/bidi.rb +10 -0
- data/lib/selenium/webdriver/chrome/service.rb +1 -0
- data/lib/selenium/webdriver/chromium/driver.rb +1 -0
- data/lib/selenium/webdriver/common/child_process.rb +8 -2
- data/lib/selenium/webdriver/common/driver.rb +21 -15
- data/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +1 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_fedcm_dialog.rb +55 -0
- data/lib/selenium/webdriver/common/driver_finder.rb +66 -14
- data/lib/selenium/webdriver/common/error.rb +21 -21
- data/lib/selenium/webdriver/common/fedcm/account.rb +50 -0
- data/lib/selenium/webdriver/common/fedcm/dialog.rb +74 -0
- data/lib/selenium/webdriver/common/fedcm.rb +27 -0
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +1 -1
- data/lib/selenium/webdriver/common/interactions/wheel_input.rb +1 -1
- data/lib/selenium/webdriver/common/local_driver.rb +8 -1
- data/lib/selenium/webdriver/common/logger.rb +2 -2
- data/lib/selenium/webdriver/common/manager.rb +1 -1
- data/lib/selenium/webdriver/common/options.rb +1 -1
- data/lib/selenium/webdriver/common/platform.rb +3 -1
- data/lib/selenium/webdriver/common/script.rb +45 -0
- data/lib/selenium/webdriver/common/search_context.rb +10 -2
- data/lib/selenium/webdriver/common/selenium_manager.rb +36 -73
- data/lib/selenium/webdriver/common/service.rb +11 -4
- data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
- data/lib/selenium/webdriver/common/target_locator.rb +1 -2
- data/lib/selenium/webdriver/common/wait.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +12 -0
- data/lib/selenium/webdriver/common.rb +4 -0
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +1 -1
- data/lib/selenium/webdriver/edge/service.rb +1 -1
- data/lib/selenium/webdriver/firefox/options.rb +3 -0
- data/lib/selenium/webdriver/firefox/profile.rb +11 -5
- data/lib/selenium/webdriver/firefox/profiles_ini.rb +1 -1
- data/lib/selenium/webdriver/firefox/service.rb +1 -0
- data/lib/selenium/webdriver/ie/options.rb +3 -2
- data/lib/selenium/webdriver/ie/service.rb +1 -0
- data/lib/selenium/webdriver/remote/bidi_bridge.rb +44 -0
- data/lib/selenium/webdriver/remote/bridge/commands.rb +13 -1
- data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
- data/lib/selenium/webdriver/remote/bridge.rb +87 -46
- data/lib/selenium/webdriver/remote/capabilities.rb +1 -1
- data/lib/selenium/webdriver/remote/http/common.rb +21 -3
- data/lib/selenium/webdriver/remote/http/curb.rb +11 -5
- data/lib/selenium/webdriver/remote/response.rb +12 -19
- data/lib/selenium/webdriver/remote/server_error.rb +1 -1
- data/lib/selenium/webdriver/remote.rb +2 -1
- data/lib/selenium/webdriver/safari/service.rb +1 -1
- data/lib/selenium/webdriver/support/guards/guard.rb +8 -9
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +1 -1
- data/selenium-webdriver.gemspec +9 -6
- metadata +70 -7
@@ -34,88 +34,33 @@ module Selenium
|
|
34
34
|
@bin_path ||= '../../../../../bin'
|
35
35
|
end
|
36
36
|
|
37
|
-
# @param [
|
38
|
-
# @return [
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
driver_path = Platform.cygwin? ? Platform.cygwin_path(output['driver_path']) : output['driver_path']
|
46
|
-
Platform.assert_executable driver_path
|
47
|
-
|
48
|
-
if options.respond_to?(:binary) && browser_path && !browser_path.empty?
|
49
|
-
options.binary = browser_path
|
50
|
-
options.browser_version = nil
|
51
|
-
end
|
52
|
-
|
53
|
-
driver_path
|
37
|
+
# @param [Array] arguments what gets sent to to Selenium Manager binary.
|
38
|
+
# @return [Hash] paths to the requested assets.
|
39
|
+
def binary_paths(*arguments)
|
40
|
+
arguments += %w[--language-binding ruby]
|
41
|
+
arguments += %w[--output json]
|
42
|
+
arguments << '--debug' if WebDriver.logger.debug?
|
43
|
+
|
44
|
+
run(binary, *arguments)
|
54
45
|
end
|
55
46
|
|
56
47
|
private
|
57
48
|
|
58
|
-
def generate_command(binary, options)
|
59
|
-
command = [binary, '--browser', options.browser_name]
|
60
|
-
if options.browser_version
|
61
|
-
command << '--browser-version'
|
62
|
-
command << options.browser_version
|
63
|
-
end
|
64
|
-
if options.respond_to?(:binary) && !options.binary.nil?
|
65
|
-
command << '--browser-path'
|
66
|
-
command << options.binary.gsub('\\', '\\\\\\')
|
67
|
-
end
|
68
|
-
if options.proxy
|
69
|
-
command << '--proxy'
|
70
|
-
command << (options.proxy.ssl || options.proxy.http)
|
71
|
-
end
|
72
|
-
command
|
73
|
-
end
|
74
|
-
|
75
49
|
# @return [String] the path to the correct selenium manager
|
76
50
|
def binary
|
77
51
|
@binary ||= begin
|
78
|
-
location = ENV.fetch('SE_MANAGER_PATH',
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
elsif Platform.mac?
|
83
|
-
"#{directory}/macos/selenium-manager"
|
84
|
-
elsif Platform.linux?
|
85
|
-
"#{directory}/linux/selenium-manager"
|
86
|
-
elsif Platform.unix?
|
87
|
-
WebDriver.logger.warn('Selenium Manager binary may not be compatible with Unix; verify settings',
|
88
|
-
id: %i[selenium_manager unix_binary])
|
89
|
-
"#{directory}/linux/selenium-manager"
|
90
|
-
end
|
91
|
-
rescue Error::WebDriverError => e
|
92
|
-
raise Error::WebDriverError, "Unable to obtain Selenium Manager binary for #{e.message}"
|
93
|
-
end)
|
52
|
+
if (location = ENV.fetch('SE_MANAGER_PATH', nil))
|
53
|
+
WebDriver.logger.debug("Selenium Manager set by ENV['SE_MANAGER_PATH']: #{location}")
|
54
|
+
end
|
55
|
+
location ||= platform_location
|
94
56
|
|
95
|
-
validate_location(location)
|
96
|
-
location
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def validate_location(location)
|
101
|
-
begin
|
102
|
-
Platform.assert_file(location)
|
103
57
|
Platform.assert_executable(location)
|
104
|
-
|
105
|
-
|
106
|
-
"Unable to locate or obtain Selenium Manager binary; #{location} is not a valid file object"
|
107
|
-
rescue Error::WebDriverError => e
|
108
|
-
raise Error::WebDriverError, "Selenium Manager binary located, but #{e.message}"
|
58
|
+
WebDriver.logger.debug("Selenium Manager binary found at #{location}", id: :selenium_manager)
|
59
|
+
location
|
109
60
|
end
|
110
|
-
|
111
|
-
WebDriver.logger.debug("Selenium Manager binary found at #{location}", id: :selenium_manager)
|
112
61
|
end
|
113
62
|
|
114
63
|
def run(*command)
|
115
|
-
command += %w[--language-binding ruby]
|
116
|
-
command += %w[--output json]
|
117
|
-
command << '--debug' if WebDriver.logger.debug?
|
118
|
-
|
119
64
|
WebDriver.logger.debug("Executing Process #{command}", id: :selenium_manager)
|
120
65
|
|
121
66
|
begin
|
@@ -124,16 +69,34 @@ module Selenium
|
|
124
69
|
raise Error::WebDriverError, "Unsuccessful command executed: #{command}; #{e.message}"
|
125
70
|
end
|
126
71
|
|
127
|
-
json_output = stdout.empty? ? {} : JSON.parse(stdout)
|
128
|
-
|
72
|
+
json_output = stdout.empty? ? {'logs' => [], 'result' => {}} : JSON.parse(stdout)
|
73
|
+
json_output['logs'].each do |log|
|
129
74
|
level = log['level'].casecmp('info').zero? ? 'debug' : log['level'].downcase
|
130
75
|
WebDriver.logger.send(level, log['message'], id: :selenium_manager)
|
131
76
|
end
|
132
77
|
|
133
78
|
result = json_output['result']
|
134
|
-
return result unless status.exitstatus.positive?
|
79
|
+
return result unless status.exitstatus.positive? || result.nil?
|
135
80
|
|
136
|
-
raise Error::WebDriverError,
|
81
|
+
raise Error::WebDriverError,
|
82
|
+
"Unsuccessful command executed: #{command} - Code #{status.exitstatus}\n#{result}\n#{stderr}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def platform_location
|
86
|
+
directory = File.expand_path(bin_path, __FILE__)
|
87
|
+
if Platform.windows?
|
88
|
+
"#{directory}/windows/selenium-manager.exe"
|
89
|
+
elsif Platform.mac?
|
90
|
+
"#{directory}/macos/selenium-manager"
|
91
|
+
elsif Platform.linux?
|
92
|
+
"#{directory}/linux/selenium-manager"
|
93
|
+
elsif Platform.unix?
|
94
|
+
WebDriver.logger.warn('Selenium Manager binary may not be compatible with Unix',
|
95
|
+
id: %i[selenium_manager unix_binary])
|
96
|
+
"#{directory}/linux/selenium-manager"
|
97
|
+
else
|
98
|
+
raise Error::WebDriverError, "unsupported platform: #{Platform.os}"
|
99
|
+
end
|
137
100
|
end
|
138
101
|
end
|
139
102
|
end # SeleniumManager
|
@@ -69,6 +69,7 @@ module Selenium
|
|
69
69
|
def initialize(path: nil, port: nil, log: nil, args: nil)
|
70
70
|
port ||= self.class::DEFAULT_PORT
|
71
71
|
args ||= []
|
72
|
+
path ||= env_path
|
72
73
|
|
73
74
|
@executable_path = path
|
74
75
|
@host = Platform.localhost
|
@@ -87,16 +88,22 @@ module Selenium
|
|
87
88
|
end
|
88
89
|
|
89
90
|
def launch
|
90
|
-
@executable_path ||=
|
91
|
-
default_options = WebDriver.const_get("#{self.class.name.split('::')[2]}::Options").new
|
92
|
-
DriverFinder.path(default_options, self.class)
|
93
|
-
end
|
91
|
+
@executable_path ||= env_path || find_driver_path
|
94
92
|
ServiceManager.new(self).tap(&:start)
|
95
93
|
end
|
96
94
|
|
97
95
|
def shutdown_supported
|
98
96
|
self.class::SHUTDOWN_SUPPORTED
|
99
97
|
end
|
98
|
+
|
99
|
+
def find_driver_path
|
100
|
+
default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
|
101
|
+
DriverFinder.new(default_options, self).driver_path
|
102
|
+
end
|
103
|
+
|
104
|
+
def env_path
|
105
|
+
ENV.fetch(self.class::DRIVER_PATH_ENV_KEY, nil)
|
106
|
+
end
|
100
107
|
end # Service
|
101
108
|
end # WebDriver
|
102
109
|
end # Selenium
|
@@ -78,7 +78,7 @@ module Selenium
|
|
78
78
|
def listening?
|
79
79
|
addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)
|
80
80
|
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
81
|
-
sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3])
|
81
|
+
sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3].to_s)
|
82
82
|
|
83
83
|
begin
|
84
84
|
sock.connect_nonblock sockaddr
|
@@ -96,12 +96,11 @@ module Selenium
|
|
96
96
|
@bridge.switch_to_window id
|
97
97
|
|
98
98
|
begin
|
99
|
-
|
99
|
+
yield
|
100
100
|
ensure
|
101
101
|
current_handles = @bridge.window_handles
|
102
102
|
original = current_handles.first unless current_handles.include? original
|
103
103
|
@bridge.switch_to_window original
|
104
|
-
returned
|
105
104
|
end
|
106
105
|
else
|
107
106
|
@bridge.switch_to_window id
|
@@ -52,6 +52,18 @@ module Selenium
|
|
52
52
|
@callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] }
|
53
53
|
end
|
54
54
|
|
55
|
+
def add_callback(event, &block)
|
56
|
+
callbacks[event] << block
|
57
|
+
block.object_id
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_callback(event, id)
|
61
|
+
return if callbacks[event].reject! { |callback| callback.object_id == id }
|
62
|
+
|
63
|
+
ids = callbacks[event]&.map(&:object_id)
|
64
|
+
raise Error::WebDriverError, "Callback with ID #{id} does not exist for event #{event}: #{ids}"
|
65
|
+
end
|
66
|
+
|
55
67
|
def send_cmd(**payload)
|
56
68
|
id = next_id
|
57
69
|
data = payload.merge(id: id)
|
@@ -89,6 +89,7 @@ require 'selenium/webdriver/common/driver_extensions/has_pinned_scripts'
|
|
89
89
|
require 'selenium/webdriver/common/driver_extensions/has_cdp'
|
90
90
|
require 'selenium/webdriver/common/driver_extensions/has_casting'
|
91
91
|
require 'selenium/webdriver/common/driver_extensions/has_launching'
|
92
|
+
require 'selenium/webdriver/common/driver_extensions/has_fedcm_dialog'
|
92
93
|
require 'selenium/webdriver/common/keys'
|
93
94
|
require 'selenium/webdriver/common/profile_helper'
|
94
95
|
require 'selenium/webdriver/common/options'
|
@@ -98,3 +99,6 @@ require 'selenium/webdriver/common/element'
|
|
98
99
|
require 'selenium/webdriver/common/shadow_root'
|
99
100
|
require 'selenium/webdriver/common/websocket_connection'
|
100
101
|
require 'selenium/webdriver/common/child_process'
|
102
|
+
require 'selenium/webdriver/common/script'
|
103
|
+
require 'selenium/webdriver/common/fedcm/account'
|
104
|
+
require 'selenium/webdriver/common/fedcm/dialog'
|
@@ -98,7 +98,7 @@ module Selenium
|
|
98
98
|
original = DevTools::Request.from(id, params)
|
99
99
|
mutable = DevTools::Request.from(id, params)
|
100
100
|
|
101
|
-
block.call(mutable) do |&continue|
|
101
|
+
block.call(mutable) do |&continue|
|
102
102
|
pending_response_requests[id] = continue
|
103
103
|
|
104
104
|
if original == mutable
|
@@ -64,6 +64,9 @@ module Selenium
|
|
64
64
|
|
65
65
|
@options[:args] ||= []
|
66
66
|
@options[:prefs] ||= {}
|
67
|
+
# Firefox 129 onwards the CDP protocol will not be enabled by default. Setting this preference will enable it.
|
68
|
+
# https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/.
|
69
|
+
@options[:prefs]['remote.active-protocols'] = 3
|
67
70
|
@options[:env] ||= {}
|
68
71
|
@options[:log] ||= {level: log_level} if log_level
|
69
72
|
|
@@ -24,6 +24,10 @@ module Selenium
|
|
24
24
|
include ProfileHelper
|
25
25
|
|
26
26
|
VALID_PREFERENCE_TYPES = [TrueClass, FalseClass, Integer, Float, String].freeze
|
27
|
+
WEBDRIVER_PREFS = {
|
28
|
+
port: 'webdriver_firefox_port',
|
29
|
+
log_file: 'webdriver.log.file'
|
30
|
+
}.freeze
|
27
31
|
|
28
32
|
DEFAULT_PREFERENCES = {
|
29
33
|
'browser.newtabpage.enabled' => false,
|
@@ -35,8 +39,8 @@ module Selenium
|
|
35
39
|
|
36
40
|
LOCK_FILES = %w[.parentlock parent.lock lock].freeze
|
37
41
|
|
38
|
-
attr_reader
|
39
|
-
attr_writer
|
42
|
+
attr_reader :name, :log_file
|
43
|
+
attr_writer :secure_ssl, :load_no_focus_lib
|
40
44
|
|
41
45
|
class << self
|
42
46
|
def ini
|
@@ -206,8 +210,8 @@ module Selenium
|
|
206
210
|
File.read(path).split("\n").each do |line|
|
207
211
|
next unless line =~ /user_pref\("([^"]+)"\s*,\s*(.+?)\);/
|
208
212
|
|
209
|
-
key = Regexp.last_match(1)
|
210
|
-
value = Regexp.last_match(2)
|
213
|
+
key = Regexp.last_match(1)&.strip
|
214
|
+
value = Regexp.last_match(2)&.strip
|
211
215
|
|
212
216
|
# wrap the value in an array to make it a valid JSON string.
|
213
217
|
prefs[key] = JSON.parse("[#{value}]").first
|
@@ -223,7 +227,9 @@ module Selenium
|
|
223
227
|
end
|
224
228
|
end
|
225
229
|
end
|
226
|
-
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Profile
|
227
233
|
end # Firefox
|
228
234
|
end # WebDriver
|
229
235
|
end # Selenium
|
@@ -52,7 +52,7 @@ module Selenium
|
|
52
52
|
when /^\[Profile/
|
53
53
|
name, path = nil if path_for(name, is_relative, path)
|
54
54
|
when /^Name=(.+)$/
|
55
|
-
name = Regexp.last_match(1)
|
55
|
+
name = Regexp.last_match(1)&.strip
|
56
56
|
when /^IsRelative=(.+)$/
|
57
57
|
is_relative = Regexp.last_match(1).strip == '1'
|
58
58
|
when /^Path=(.+)$/
|
@@ -42,7 +42,8 @@ module Selenium
|
|
42
42
|
use_legacy_file_upload_dialog_handling: 'ie.useLegacyFileUploadDialogHandling',
|
43
43
|
attach_to_edge_chrome: 'ie.edgechromium',
|
44
44
|
edge_executable_path: 'ie.edgepath',
|
45
|
-
ignore_process_match: 'ie.ignoreprocessmatch'
|
45
|
+
ignore_process_match: 'ie.ignoreprocessmatch',
|
46
|
+
silent: 'silent'
|
46
47
|
}.freeze
|
47
48
|
BROWSER = 'internet explorer'
|
48
49
|
|
@@ -81,7 +82,7 @@ module Selenium
|
|
81
82
|
|
82
83
|
def initialize(**opts)
|
83
84
|
@args = (opts.delete(:args) || []).to_set
|
84
|
-
super
|
85
|
+
super
|
85
86
|
|
86
87
|
@options[:native_events] = true if @options[:native_events].nil?
|
87
88
|
end
|
@@ -0,0 +1,44 @@
|
|
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 Remote
|
23
|
+
class BiDiBridge < Bridge
|
24
|
+
attr_reader :bidi
|
25
|
+
|
26
|
+
def create_session(capabilities)
|
27
|
+
super
|
28
|
+
socket_url = @capabilities[:web_socket_url]
|
29
|
+
@bidi = Selenium::WebDriver::BiDi.new(url: socket_url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def quit
|
33
|
+
super
|
34
|
+
ensure
|
35
|
+
bidi.close
|
36
|
+
end
|
37
|
+
|
38
|
+
def close
|
39
|
+
execute(:close_window).tap { |handles| bidi.close if handles.empty? }
|
40
|
+
end
|
41
|
+
end # BiDiBridge
|
42
|
+
end # Remote
|
43
|
+
end # WebDriver
|
44
|
+
end # Selenium
|
@@ -155,8 +155,20 @@ module Selenium
|
|
155
155
|
remove_credential: [:delete,
|
156
156
|
'session/:session_id/webauthn/authenticator/:authenticatorId/credentials/:credentialId'],
|
157
157
|
remove_all_credentials: [:delete, 'session/:session_id/webauthn/authenticator/:authenticatorId/credentials'],
|
158
|
-
set_user_verified: [:post, 'session/:session_id/webauthn/authenticator/:authenticatorId/uv']
|
158
|
+
set_user_verified: [:post, 'session/:session_id/webauthn/authenticator/:authenticatorId/uv'],
|
159
159
|
|
160
|
+
#
|
161
|
+
# federated-credential management
|
162
|
+
#
|
163
|
+
|
164
|
+
get_fedcm_title: [:get, 'session/:session_id/fedcm/gettitle'],
|
165
|
+
get_fedcm_dialog_type: [:get, 'session/:session_id/fedcm/getdialogtype'],
|
166
|
+
get_fedcm_account_list: [:get, 'session/:session_id/fedcm/accountlist'],
|
167
|
+
click_fedcm_dialog_button: [:post, 'session/:session_id/fedcm/clickdialogbutton'],
|
168
|
+
cancel_fedcm_dialog: [:post, 'session/:session_id/fedcm/canceldialog'],
|
169
|
+
select_fedcm_account: [:post, 'session/:session_id/fedcm/selectaccount'],
|
170
|
+
set_fedcm_delay: [:post, 'session/:session_id/fedcm/setdelayenabled'],
|
171
|
+
reset_fedcm_cooldown: [:post, 'session/:session_id/fedcm/resetcooldown']
|
160
172
|
}.freeze
|
161
173
|
end # Bridge
|
162
174
|
end # Remote
|
@@ -0,0 +1,76 @@
|
|
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 Remote
|
23
|
+
class Bridge
|
24
|
+
class LocatorConverter
|
25
|
+
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]()])/
|
26
|
+
UNICODE_CODE_POINT = 30
|
27
|
+
|
28
|
+
#
|
29
|
+
# Converts a locator to a specification compatible one.
|
30
|
+
# @param [String, Symbol] how
|
31
|
+
# @param [String] what
|
32
|
+
#
|
33
|
+
|
34
|
+
def convert(how, what)
|
35
|
+
how = SearchContext.finders[how.to_sym] || how
|
36
|
+
|
37
|
+
case how
|
38
|
+
when 'class name'
|
39
|
+
how = 'css selector'
|
40
|
+
what = ".#{escape_css(what.to_s)}"
|
41
|
+
when 'id'
|
42
|
+
how = 'css selector'
|
43
|
+
what = "##{escape_css(what.to_s)}"
|
44
|
+
when 'name'
|
45
|
+
how = 'css selector'
|
46
|
+
what = "*[name='#{escape_css(what.to_s)}']"
|
47
|
+
end
|
48
|
+
|
49
|
+
if what.is_a?(Hash)
|
50
|
+
what = what.each_with_object({}) do |(h, w), hash|
|
51
|
+
h, w = convert(h.to_s, w)
|
52
|
+
hash[h] = w
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
[how, what]
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
#
|
62
|
+
# Escapes invalid characters in CSS selector.
|
63
|
+
# @see https://mathiasbynens.be/notes/css-escapes
|
64
|
+
#
|
65
|
+
|
66
|
+
def escape_css(string)
|
67
|
+
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
68
|
+
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..]}" if string[0]&.match?(/[[:digit:]]/)
|
69
|
+
|
70
|
+
string
|
71
|
+
end
|
72
|
+
end # LocatorConverter
|
73
|
+
end # Bridge
|
74
|
+
end # Remote
|
75
|
+
end # WebDriver
|
76
|
+
end # Selenium
|