selenium-webdriver 4.35.0 → 4.47.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 +111 -0
- data/Gemfile +1 -4
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/README.md +43 -23
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/selenium-manager-THIRD-PARTY-NOTICES.txt +5971 -0
- data/bin/selenium-manager.cdx.json +11629 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +30 -5
- data/lib/selenium/webdriver/atoms/findElements.js +272 -54
- data/lib/selenium/webdriver/atoms/getAttribute.js +152 -6
- data/lib/selenium/webdriver/atoms/isDisplayed.js +385 -28
- data/lib/selenium/webdriver/bidi/browser.rb +71 -0
- data/lib/selenium/webdriver/bidi/browsing_context.rb +3 -2
- data/lib/selenium/webdriver/bidi/{log/filter_by.rb → error.rb} +20 -16
- data/lib/selenium/webdriver/bidi/log_handler.rb +5 -0
- data/lib/selenium/webdriver/bidi/network/cookies.rb +13 -9
- data/lib/selenium/webdriver/bidi/network/credentials.rb +4 -0
- data/lib/selenium/webdriver/bidi/network/headers.rb +4 -0
- data/lib/selenium/webdriver/bidi/network/intercepted_auth.rb +4 -0
- data/lib/selenium/webdriver/bidi/network/intercepted_item.rb +4 -0
- data/lib/selenium/webdriver/bidi/network/intercepted_request.rb +20 -4
- data/lib/selenium/webdriver/bidi/network/intercepted_response.rb +24 -6
- data/lib/selenium/webdriver/bidi/network/url_pattern.rb +4 -0
- data/lib/selenium/webdriver/bidi/network.rb +18 -9
- data/lib/selenium/webdriver/bidi/protocol/bluetooth.rb +495 -0
- data/lib/selenium/webdriver/bidi/protocol/browser.rb +250 -0
- data/lib/selenium/webdriver/bidi/protocol/browsing_context.rb +798 -0
- data/lib/selenium/webdriver/bidi/{log/base_log_entry.rb → protocol/domain.rb} +22 -11
- data/lib/selenium/webdriver/bidi/protocol/emulation.rb +448 -0
- data/lib/selenium/webdriver/bidi/protocol/error_code.rb +66 -0
- data/lib/selenium/webdriver/bidi/protocol/input.rb +354 -0
- data/lib/selenium/webdriver/bidi/protocol/log.rb +115 -0
- data/lib/selenium/webdriver/bidi/protocol/network.rb +720 -0
- data/lib/selenium/webdriver/bidi/protocol/permissions.rb +75 -0
- data/lib/selenium/webdriver/bidi/protocol/script.rb +1052 -0
- data/lib/selenium/webdriver/bidi/protocol/session.rb +286 -0
- data/lib/selenium/webdriver/bidi/protocol/speculation.rb +58 -0
- data/lib/selenium/webdriver/bidi/protocol/storage.rb +183 -0
- data/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb +88 -0
- data/lib/selenium/webdriver/bidi/protocol/web_extension.rb +132 -0
- data/lib/selenium/webdriver/bidi/protocol.rb +42 -0
- data/lib/selenium/webdriver/bidi/serialization/record.rb +402 -0
- data/lib/selenium/webdriver/bidi/serialization/union.rb +156 -0
- data/lib/selenium/webdriver/bidi/serialization.rb +90 -0
- data/lib/selenium/webdriver/bidi/session.rb +4 -0
- data/lib/selenium/webdriver/bidi/support/bidi_generate.rb +1420 -0
- data/lib/selenium/webdriver/bidi/support/check_generated.rb +63 -0
- data/lib/selenium/webdriver/bidi/transport.rb +57 -0
- data/lib/selenium/webdriver/bidi.rb +4 -1
- data/lib/selenium/webdriver/chrome/driver.rb +4 -3
- data/lib/selenium/webdriver/chrome/service.rb +13 -0
- data/lib/selenium/webdriver/chromium/driver.rb +0 -1
- data/lib/selenium/webdriver/chromium/options.rb +23 -1
- data/lib/selenium/webdriver/chromium/profile.rb +6 -0
- data/lib/selenium/webdriver/common/child_process.rb +2 -1
- data/lib/selenium/webdriver/common/client_config.rb +97 -0
- data/lib/selenium/webdriver/common/driver.rb +13 -7
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +0 -7
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +0 -7
- data/lib/selenium/webdriver/common/driver_extensions/{has_bidi.rb → has_session_events.rb} +19 -7
- data/lib/selenium/webdriver/common/driver_finder.rb +35 -25
- data/lib/selenium/webdriver/common/error.rb +10 -3
- data/lib/selenium/webdriver/common/local_driver.rb +29 -12
- data/lib/selenium/webdriver/common/logger.rb +28 -0
- data/lib/selenium/webdriver/common/manager.rb +2 -0
- data/lib/selenium/webdriver/common/options.rb +41 -2
- data/lib/selenium/webdriver/common/platform.rb +1 -3
- data/lib/selenium/webdriver/common/proxy.rb +1 -9
- data/lib/selenium/webdriver/common/selenium_manager.rb +2 -1
- data/lib/selenium/webdriver/common/service.rb +4 -8
- data/lib/selenium/webdriver/common/service_manager.rb +36 -4
- 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 +1 -1
- data/lib/selenium/webdriver/common/wait.rb +4 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +73 -37
- data/lib/selenium/webdriver/common.rb +2 -1
- data/lib/selenium/webdriver/devtools/response.rb +3 -3
- data/lib/selenium/webdriver/devtools.rb +1 -1
- data/lib/selenium/webdriver/edge/driver.rb +4 -3
- data/lib/selenium/webdriver/edge/service.rb +14 -0
- data/lib/selenium/webdriver/firefox/driver.rb +4 -6
- data/lib/selenium/webdriver/firefox/options.rb +19 -0
- data/lib/selenium/webdriver/firefox/profile.rb +13 -6
- data/lib/selenium/webdriver/firefox/service.rb +31 -2
- data/lib/selenium/webdriver/ie/driver.rb +4 -3
- data/lib/selenium/webdriver/ie/service.rb +10 -0
- data/lib/selenium/webdriver/remote/bidi_bridge.rb +44 -10
- data/lib/selenium/webdriver/remote/bridge.rb +20 -14
- data/lib/selenium/webdriver/remote/driver.rb +15 -4
- data/lib/selenium/webdriver/remote/features.rb +26 -1
- data/lib/selenium/webdriver/remote/http/common.rb +57 -12
- data/lib/selenium/webdriver/remote/http/curb.rb +6 -8
- data/lib/selenium/webdriver/remote/http/default.rb +56 -39
- data/lib/selenium/webdriver/safari/driver.rb +4 -3
- data/lib/selenium/webdriver/safari/options.rb +23 -4
- data/lib/selenium/webdriver/safari.rb +1 -6
- data/lib/selenium/webdriver/support/block_event_listener.rb +5 -1
- data/lib/selenium/webdriver/support/color.rb +14 -14
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +5 -1
- data/lib/selenium/webdriver/support/guards/guard.rb +30 -14
- data/lib/selenium/webdriver/support/guards.rb +11 -4
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +7 -2
- data/selenium-webdriver.gemspec +1 -1
- metadata +33 -11
- data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +0 -35
- data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +0 -33
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +0 -33
- data/lib/selenium/webdriver/bidi/log_inspector.rb +0 -147
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
require 'json'
|
|
21
|
+
require_relative 'bidi_generate'
|
|
22
|
+
|
|
23
|
+
module BiDiGenerate
|
|
24
|
+
# Verifies the checked-in protocol .rb match what the generator would produce from the
|
|
25
|
+
# current schema — catching a hand-edit or a forgotten regeneration. Re-renders each module
|
|
26
|
+
# in memory (no file writes) and compares. The .rbs are covered by Steep.
|
|
27
|
+
def self.check!(schema_rootpath)
|
|
28
|
+
schema = Schema.new(JSON.parse(File.read(schema_path(schema_rootpath))))
|
|
29
|
+
protocol_dir = File.expand_path('../protocol', __dir__)
|
|
30
|
+
template = File.join(__dir__, 'templates', 'module.rb.erb')
|
|
31
|
+
|
|
32
|
+
stale = build_ir(schema).filter_map do |mod|
|
|
33
|
+
path = File.join(protocol_dir, "#{mod.filename}.rb")
|
|
34
|
+
"#{mod.filename}.rb" unless File.exist?(path) && File.read(path) == render(mod, template)
|
|
35
|
+
end
|
|
36
|
+
stale << 'error_code.rb' unless error_module_current?(schema, protocol_dir)
|
|
37
|
+
return if stale.empty?
|
|
38
|
+
|
|
39
|
+
warn "Generated BiDi protocol code is stale or hand-edited: #{stale.sort.join(', ')}"
|
|
40
|
+
warn 'Regenerate with: bazel run //rb/lib/selenium/webdriver:bidi-generate'
|
|
41
|
+
exit 1
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Whether the checked-in protocol/error_code.rb matches what the generator would render now.
|
|
45
|
+
def self.error_module_current?(schema, protocol_dir)
|
|
46
|
+
mod = ErrorModule.new(filename: 'error_code', codes: error_code_map(schema))
|
|
47
|
+
path = File.join(protocol_dir, 'error_code.rb')
|
|
48
|
+
File.exist?(path) && File.read(path) == render(mod, File.join(__dir__, 'templates', 'error_code.rb.erb'))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# $(rootpath) is relative to the runfiles root; __dir__ anchors us there so it resolves the
|
|
52
|
+
# same way locally and on RBE (an execpath would not). This file lives at
|
|
53
|
+
# rb/lib/selenium/webdriver/bidi/support, so expand six levels up to the root — File.expand_path
|
|
54
|
+
# is separator-agnostic, unlike stripping a "/"-spelled suffix (which would miss on Windows).
|
|
55
|
+
# The cwd-relative rootpath fallback matches how spec_support's `rlocation` resolves.
|
|
56
|
+
def self.schema_path(rootpath)
|
|
57
|
+
runfiles_root = File.expand_path('../../../../../..', __dir__)
|
|
58
|
+
[File.join(runfiles_root, rootpath), rootpath].find { |p| File.exist?(p) } ||
|
|
59
|
+
raise("BiDi schema not found (looked for #{rootpath})")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
BiDiGenerate.check!(ARGV.fetch(0)) if $PROGRAM_NAME == __FILE__
|
|
@@ -0,0 +1,57 @@
|
|
|
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 BiDi
|
|
23
|
+
# The seam between the generated Protocol layer and the websocket: serializes a
|
|
24
|
+
# command's params, sends it, and parses the reply into its declared type.
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
class Transport
|
|
28
|
+
# The websocket the transport sends over. Exposed so a domain can build a sibling
|
|
29
|
+
# domain (e.g. a vendor variant) over the same connection without Transport ever
|
|
30
|
+
# becoming a public constructor argument.
|
|
31
|
+
attr_reader :connection
|
|
32
|
+
|
|
33
|
+
def initialize(connection)
|
|
34
|
+
@connection = connection
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def execute(cmd:, params: nil, result: nil)
|
|
38
|
+
reply = @connection.send_cmd(method: cmd, params: serialize(params))
|
|
39
|
+
raise error_for(reply) if reply['error']
|
|
40
|
+
|
|
41
|
+
value = reply['result']
|
|
42
|
+
result ? result.from_json(value) : value
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def serialize(params)
|
|
48
|
+
params&.as_json || {}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def error_for(reply)
|
|
52
|
+
Protocol::ErrorCode.for(reply['error']).new("#{reply['message']}\n#{reply['stacktrace']}")
|
|
53
|
+
end
|
|
54
|
+
end # Transport
|
|
55
|
+
end # BiDi
|
|
56
|
+
end # WebDriver
|
|
57
|
+
end # Selenium
|
|
@@ -21,8 +21,8 @@ module Selenium
|
|
|
21
21
|
module WebDriver
|
|
22
22
|
class BiDi
|
|
23
23
|
autoload :Session, 'selenium/webdriver/bidi/session'
|
|
24
|
-
autoload :LogInspector, 'selenium/webdriver/bidi/log_inspector'
|
|
25
24
|
autoload :LogHandler, 'selenium/webdriver/bidi/log_handler'
|
|
25
|
+
autoload :Browser, 'selenium/webdriver/bidi/browser'
|
|
26
26
|
autoload :BrowsingContext, 'selenium/webdriver/bidi/browsing_context'
|
|
27
27
|
autoload :Struct, 'selenium/webdriver/bidi/struct'
|
|
28
28
|
autoload :Network, 'selenium/webdriver/bidi/network'
|
|
@@ -35,6 +35,9 @@ module Selenium
|
|
|
35
35
|
@ws = WebSocketConnection.new(url: url)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# @api private
|
|
39
|
+
attr_reader :ws
|
|
40
|
+
|
|
38
41
|
def close
|
|
39
42
|
@ws.close
|
|
40
43
|
end
|
|
@@ -30,9 +30,10 @@ module Selenium
|
|
|
30
30
|
class Driver < Chromium::Driver
|
|
31
31
|
include LocalDriver
|
|
32
32
|
|
|
33
|
-
def initialize(options: nil, service: nil, url: nil, **)
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
def initialize(options: nil, service: nil, url: nil, http_client: nil, client_config: nil, **)
|
|
34
|
+
initialize_local_driver(options, service, url, http_client, client_config) do |caps, client|
|
|
35
|
+
super(caps: caps, http_client: client, **)
|
|
36
|
+
end
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def browser
|
|
@@ -26,6 +26,19 @@ module Selenium
|
|
|
26
26
|
SHUTDOWN_SUPPORTED = true
|
|
27
27
|
DRIVER_PATH_ENV_KEY = 'SE_CHROMEDRIVER'
|
|
28
28
|
|
|
29
|
+
def initialize(args: nil, **)
|
|
30
|
+
args = Array(args.dup)
|
|
31
|
+
# skip when CHROME_LOG_FILE is set; --enable-chrome-logs would override the user's log file
|
|
32
|
+
args << '--enable-chrome-logs' unless ENV.key?('CHROME_LOG_FILE') || args.include?('--enable-chrome-logs')
|
|
33
|
+
|
|
34
|
+
if ENV.key?('SE_DEBUG')
|
|
35
|
+
warn_driver_log_override if args.reject! { |arg| arg.include?('log-level') || arg.include?('silent') }
|
|
36
|
+
args << '--verbose'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
29
42
|
def log
|
|
30
43
|
return @log unless @log.is_a? String
|
|
31
44
|
|
|
@@ -49,7 +49,9 @@ module Selenium
|
|
|
49
49
|
# options = Selenium::WebDriver::Chrome::Options.new(args: ['start-maximized', 'user-data-dir=/tmp/temp_profile'])
|
|
50
50
|
# driver = Selenium::WebDriver.for(:chrome, options: options)
|
|
51
51
|
#
|
|
52
|
-
# @param [Profile] profile An instance of a Chrome::Profile
|
|
52
|
+
# @param [Profile] profile (Deprecated) An instance of a Chrome::Profile class.
|
|
53
|
+
# Use {#add_argument} with `--user-data-dir=...`, {#add_preference}, and
|
|
54
|
+
# {#add_extension} instead.
|
|
53
55
|
# @param [Hash] opts the pre-defined options to create the Chrome::Options with
|
|
54
56
|
# @option opts [Array] encoded_extensions List of extensions that do not need to be Base64 encoded
|
|
55
57
|
# @option opts [Array<String>] args List of command-line arguments to use when starting Chrome
|
|
@@ -84,6 +86,7 @@ module Selenium
|
|
|
84
86
|
@logging_prefs = options.delete(:logging_prefs) || {}
|
|
85
87
|
@encoded_extensions = @options.delete(:encoded_extensions) || []
|
|
86
88
|
@extensions = []
|
|
89
|
+
@vendor_options = {}
|
|
87
90
|
@options.delete(:extensions).each { |ext| validate_extension(ext) }
|
|
88
91
|
end
|
|
89
92
|
|
|
@@ -182,6 +185,24 @@ module Selenium
|
|
|
182
185
|
@options[:emulation] = opts
|
|
183
186
|
end
|
|
184
187
|
|
|
188
|
+
#
|
|
189
|
+
# Add a Chromium-specific capability nested in the browser options object
|
|
190
|
+
# (e.g. `goog:chromeOptions` / `ms:edgeOptions`) that is not yet exposed
|
|
191
|
+
# through a dedicated method. This is the escape hatch for legitimate
|
|
192
|
+
# vendor capabilities the bindings do not model.
|
|
193
|
+
#
|
|
194
|
+
# @example Set an option not handled by other methods
|
|
195
|
+
# options = Selenium::WebDriver::Chrome::Options.new
|
|
196
|
+
# options.add_chromium_option('unhandledCapability', 'value')
|
|
197
|
+
#
|
|
198
|
+
# @param [String, Symbol] name Name of the capability, as expected by the driver
|
|
199
|
+
# @param [Object] value Value of the capability
|
|
200
|
+
#
|
|
201
|
+
|
|
202
|
+
def add_chromium_option(name, value)
|
|
203
|
+
@vendor_options[name.to_s] = value
|
|
204
|
+
end
|
|
205
|
+
|
|
185
206
|
#
|
|
186
207
|
# Enables mobile browser use on Android.
|
|
187
208
|
#
|
|
@@ -207,6 +228,7 @@ module Selenium
|
|
|
207
228
|
enable_logging(browser_options) unless @logging_prefs.empty?
|
|
208
229
|
|
|
209
230
|
options = browser_options[self.class::KEY]
|
|
231
|
+
options.merge!(@vendor_options)
|
|
210
232
|
options['binary'] ||= binary_path if binary_path
|
|
211
233
|
|
|
212
234
|
if @profile
|
|
@@ -28,6 +28,12 @@ module Selenium
|
|
|
28
28
|
include ProfileHelper
|
|
29
29
|
|
|
30
30
|
def initialize(model = nil)
|
|
31
|
+
WebDriver.logger.deprecate(
|
|
32
|
+
'Chromium::Profile (including Chrome::Profile and Edge::Profile)',
|
|
33
|
+
"Options#add_argument('--user-data-dir=...'), Options#add_preference, and Options#add_extension",
|
|
34
|
+
id: :chromium_profile
|
|
35
|
+
)
|
|
36
|
+
|
|
31
37
|
@model = verify_model(model)
|
|
32
38
|
@extensions = []
|
|
33
39
|
@encoded_extensions = []
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
require 'uri'
|
|
21
|
+
|
|
22
|
+
module Selenium
|
|
23
|
+
module WebDriver
|
|
24
|
+
#
|
|
25
|
+
# Configuration for HTTP clients.
|
|
26
|
+
#
|
|
27
|
+
|
|
28
|
+
class ClientConfig
|
|
29
|
+
class << self
|
|
30
|
+
attr_accessor :default_extra_headers
|
|
31
|
+
attr_writer :default_user_agent
|
|
32
|
+
|
|
33
|
+
def default_user_agent
|
|
34
|
+
@default_user_agent ||= "selenium/#{WebDriver::VERSION} (ruby #{Platform.os})"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attr_accessor :open_timeout, :read_timeout, :max_redirects, :proxy
|
|
39
|
+
attr_writer :extra_headers, :user_agent
|
|
40
|
+
attr_reader :server_url
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# @param [Numeric] open_timeout Seconds to wait for the connection to open.
|
|
44
|
+
# @param [Numeric] read_timeout Seconds to wait for a response.
|
|
45
|
+
# @param [Integer] max_redirects Maximum number of redirects to follow.
|
|
46
|
+
# @param [Proxy] proxy Proxy to use for the connection.
|
|
47
|
+
# @param [Hash] extra_headers Additional headers to send with each request.
|
|
48
|
+
# @param [String] user_agent Value to send as the User-Agent header.
|
|
49
|
+
# @param [String, URI] server_url URL of the server to connect to.
|
|
50
|
+
#
|
|
51
|
+
def initialize(open_timeout: 60,
|
|
52
|
+
read_timeout: 120,
|
|
53
|
+
max_redirects: 20,
|
|
54
|
+
proxy: nil,
|
|
55
|
+
extra_headers: nil,
|
|
56
|
+
user_agent: nil,
|
|
57
|
+
server_url: nil)
|
|
58
|
+
@open_timeout = open_timeout
|
|
59
|
+
@read_timeout = read_timeout
|
|
60
|
+
@max_redirects = max_redirects
|
|
61
|
+
@proxy = proxy || proxy_from_environment
|
|
62
|
+
@extra_headers = extra_headers
|
|
63
|
+
@user_agent = user_agent
|
|
64
|
+
self.server_url = server_url
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def user_agent
|
|
68
|
+
@user_agent || self.class.default_user_agent
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def extra_headers
|
|
72
|
+
@extra_headers || self.class.default_extra_headers
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def server_url=(url)
|
|
76
|
+
if url.nil?
|
|
77
|
+
@server_url = nil
|
|
78
|
+
else
|
|
79
|
+
url = url.to_s
|
|
80
|
+
url += '/' unless url.end_with?('/')
|
|
81
|
+
@server_url = URI.parse(url)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def proxy_from_environment
|
|
88
|
+
proxy = ENV.fetch('http_proxy', nil) || ENV.fetch('HTTP_PROXY', nil)
|
|
89
|
+
return unless proxy
|
|
90
|
+
|
|
91
|
+
no_proxy = ENV.fetch('no_proxy', nil) || ENV.fetch('NO_PROXY', nil)
|
|
92
|
+
proxy = "http://#{proxy}" unless proxy.match?(%r{\Ahttps?://})
|
|
93
|
+
Proxy.new(http: proxy, no_proxy: no_proxy)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -90,6 +90,17 @@ module Selenium
|
|
|
90
90
|
@bridge.status
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
#
|
|
94
|
+
# Returns the WebDriver BiDi connection for this session.
|
|
95
|
+
#
|
|
96
|
+
# @return [BiDi]
|
|
97
|
+
# @raise [Error::WebDriverError] if BiDi was not enabled for this session
|
|
98
|
+
#
|
|
99
|
+
|
|
100
|
+
def bidi
|
|
101
|
+
@bridge.bidi
|
|
102
|
+
end
|
|
103
|
+
|
|
93
104
|
#
|
|
94
105
|
# @return [Navigation]
|
|
95
106
|
# @see Navigation
|
|
@@ -319,18 +330,13 @@ module Selenium
|
|
|
319
330
|
|
|
320
331
|
attr_reader :bridge
|
|
321
332
|
|
|
322
|
-
def create_bridge(caps:,
|
|
333
|
+
def create_bridge(caps:, http_client:)
|
|
323
334
|
klass = caps['webSocketUrl'] ? Remote::BiDiBridge : Remote::Bridge
|
|
324
|
-
klass.new(http_client: http_client
|
|
335
|
+
klass.new(http_client: http_client).tap do |bridge|
|
|
325
336
|
bridge.create_session(caps)
|
|
326
337
|
end
|
|
327
338
|
end
|
|
328
339
|
|
|
329
|
-
def service_url(service)
|
|
330
|
-
@service_manager = service.launch
|
|
331
|
-
@service_manager.uri
|
|
332
|
-
end
|
|
333
|
-
|
|
334
340
|
def screenshot
|
|
335
341
|
bridge.screenshot
|
|
336
342
|
end
|
|
@@ -57,13 +57,6 @@ module Selenium
|
|
|
57
57
|
#
|
|
58
58
|
|
|
59
59
|
def on_log_event(kind, &block)
|
|
60
|
-
if browser == :firefox
|
|
61
|
-
WebDriver.logger.deprecate(
|
|
62
|
-
'Driver#on_log_event on Firefox',
|
|
63
|
-
'the script.add_console_message_handler or the script.add_javascript_error_handler methods',
|
|
64
|
-
id: :on_log_event
|
|
65
|
-
)
|
|
66
|
-
end
|
|
67
60
|
raise Error::WebDriverError, "Don't know how to handle #{kind} events" unless KINDS.include?(kind)
|
|
68
61
|
|
|
69
62
|
enabled = log_listeners[kind].any?
|
|
@@ -60,13 +60,6 @@ module Selenium
|
|
|
60
60
|
#
|
|
61
61
|
|
|
62
62
|
def intercept(&block)
|
|
63
|
-
if browser == :firefox
|
|
64
|
-
WebDriver.logger.deprecate(
|
|
65
|
-
'Driver#intercept on Firefox',
|
|
66
|
-
'the new bidi.network.add_intercept method',
|
|
67
|
-
id: :intercept
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
63
|
@interceptor ||= DevTools::NetworkInterceptor.new(devtools)
|
|
71
64
|
@interceptor.intercept(&block)
|
|
72
65
|
end
|
|
@@ -20,17 +20,29 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module DriverExtensions
|
|
23
|
-
module
|
|
23
|
+
module HasSessionEvents
|
|
24
24
|
#
|
|
25
|
-
#
|
|
25
|
+
# Fires a custom session event to the remote server event bus.
|
|
26
|
+
# This allows test code to trigger server-side utilities that subscribe to
|
|
27
|
+
# the event bus.
|
|
26
28
|
#
|
|
27
|
-
# @
|
|
29
|
+
# @param [String] event_type The type of event (e.g., "test:failed", "log:collect")
|
|
30
|
+
# @param [Hash] payload Optional data to include with the event
|
|
31
|
+
# @return [Hash] Response data including success status, event type, and timestamp
|
|
28
32
|
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
# @example Fire a simple event
|
|
34
|
+
# driver.fire_session_event("test:started")
|
|
35
|
+
#
|
|
36
|
+
# @example Fire an event with payload
|
|
37
|
+
# driver.fire_session_event("test:failed", {
|
|
38
|
+
# testName: "LoginTest",
|
|
39
|
+
# error: "Element not found"
|
|
40
|
+
# })
|
|
41
|
+
#
|
|
42
|
+
def fire_session_event(event_type, payload = nil)
|
|
43
|
+
@bridge.fire_session_event(event_type, payload)
|
|
32
44
|
end
|
|
33
|
-
end #
|
|
45
|
+
end # HasSessionEvents
|
|
34
46
|
end # DriverExtensions
|
|
35
47
|
end # WebDriver
|
|
36
48
|
end # Selenium
|
|
@@ -26,6 +26,8 @@ module Selenium
|
|
|
26
26
|
new(options, service_class.new).driver_path
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# @param options [Options, nil] when nil driver parsed from Service::EXECUTABLE
|
|
30
|
+
# @param service [Service]
|
|
29
31
|
def initialize(options, service)
|
|
30
32
|
@options = options
|
|
31
33
|
@service = service
|
|
@@ -47,36 +49,44 @@ module Selenium
|
|
|
47
49
|
|
|
48
50
|
def paths
|
|
49
51
|
@paths ||= begin
|
|
50
|
-
path = @service.
|
|
51
|
-
path
|
|
52
|
-
exe = @service.class::EXECUTABLE
|
|
53
|
-
if path
|
|
54
|
-
WebDriver.logger.debug("Skipping Selenium Manager; path to #{exe} specified in service class: #{path}")
|
|
55
|
-
Platform.assert_executable(path)
|
|
56
|
-
{driver_path: path}
|
|
57
|
-
else
|
|
58
|
-
output = SeleniumManager.binary_paths(*to_args)
|
|
59
|
-
formatted = {driver_path: Platform.cygwin_path(output['driver_path'], only_cygwin: true),
|
|
60
|
-
browser_path: Platform.cygwin_path(output['browser_path'], only_cygwin: true)}
|
|
61
|
-
Platform.assert_executable(formatted[:driver_path])
|
|
62
|
-
|
|
63
|
-
browser_path = formatted[:browser_path]
|
|
64
|
-
Platform.assert_executable(browser_path)
|
|
65
|
-
if @options.respond_to?(:binary) && @options.binary.nil?
|
|
66
|
-
@options.binary = browser_path
|
|
67
|
-
@options.browser_version = nil
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
formatted
|
|
71
|
-
end
|
|
52
|
+
path = @service.executable_path || env_path || class_path
|
|
53
|
+
path ? paths_from_service(path) : paths_from_manager
|
|
72
54
|
rescue StandardError => e
|
|
73
|
-
WebDriver.logger.error("Exception occurred: #{e.message}")
|
|
74
|
-
WebDriver.logger.error("Backtrace:\n\t#{e.backtrace&.join("\n\t")}")
|
|
75
|
-
raise Error::NoSuchDriverError, "Unable to obtain #{
|
|
55
|
+
WebDriver.logger.error("Exception occurred: #{e.message}", id: :driver_finder)
|
|
56
|
+
WebDriver.logger.error("Backtrace:\n\t#{e.backtrace&.join("\n\t")}", id: :driver_finder)
|
|
57
|
+
raise Error::NoSuchDriverError, "Unable to obtain #{@service.class::EXECUTABLE}"
|
|
76
58
|
end
|
|
77
59
|
end
|
|
78
60
|
|
|
61
|
+
def env_path
|
|
62
|
+
ENV.fetch(@service.class::DRIVER_PATH_ENV_KEY, nil)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def class_path
|
|
66
|
+
path = @service.class.driver_path
|
|
67
|
+
path.is_a?(Proc) ? path.call : path
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def paths_from_service(path)
|
|
71
|
+
exe = @service.class::EXECUTABLE
|
|
72
|
+
WebDriver.logger.debug("Skipping Selenium Manager; path to #{exe} specified in service class: #{path}",
|
|
73
|
+
id: :driver_finder)
|
|
74
|
+
Platform.assert_executable(path)
|
|
75
|
+
{driver_path: path}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def paths_from_manager
|
|
79
|
+
output = SeleniumManager.binary_paths(*to_args)
|
|
80
|
+
formatted = {driver_path: Platform.cygwin_path(output['driver_path'], only_cygwin: true),
|
|
81
|
+
browser_path: Platform.cygwin_path(output['browser_path'], only_cygwin: true)}
|
|
82
|
+
Platform.assert_executable(formatted[:driver_path])
|
|
83
|
+
Platform.assert_executable(formatted[:browser_path])
|
|
84
|
+
formatted
|
|
85
|
+
end
|
|
86
|
+
|
|
79
87
|
def to_args
|
|
88
|
+
return ['--driver', @service.class::EXECUTABLE] unless @options
|
|
89
|
+
|
|
80
90
|
args = ['--browser', @options.browser_name]
|
|
81
91
|
if @options.browser_version
|
|
82
92
|
args << '--browser-version'
|
|
@@ -38,9 +38,9 @@ module Selenium
|
|
|
38
38
|
ERROR_URL = 'https://www.selenium.dev/documentation/webdriver/troubleshooting/errors'
|
|
39
39
|
|
|
40
40
|
URLS = {
|
|
41
|
-
NoSuchElementError: "#{ERROR_URL}#
|
|
42
|
-
StaleElementReferenceError: "#{ERROR_URL}#
|
|
43
|
-
InvalidSelectorError: "#{ERROR_URL}#
|
|
41
|
+
NoSuchElementError: "#{ERROR_URL}#nosuchelementexception",
|
|
42
|
+
StaleElementReferenceError: "#{ERROR_URL}#staleelementreferenceexception",
|
|
43
|
+
InvalidSelectorError: "#{ERROR_URL}#invalidselectorexception",
|
|
44
44
|
NoSuchDriverError: "#{ERROR_URL}/driver_location"
|
|
45
45
|
}.freeze
|
|
46
46
|
|
|
@@ -119,6 +119,13 @@ module Selenium
|
|
|
119
119
|
|
|
120
120
|
class NoSuchWindowError < WebDriverError; end
|
|
121
121
|
|
|
122
|
+
#
|
|
123
|
+
# A command to find a devtools target could not be satisfied because
|
|
124
|
+
# the target could not be found.
|
|
125
|
+
#
|
|
126
|
+
|
|
127
|
+
class NoSuchTargetError < WebDriverError; end
|
|
128
|
+
|
|
122
129
|
#
|
|
123
130
|
# The element does not have a shadow root.
|
|
124
131
|
#
|
|
@@ -20,14 +20,25 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module LocalDriver
|
|
23
|
-
def initialize_local_driver(options, service, url)
|
|
24
|
-
|
|
23
|
+
def initialize_local_driver(options, service, url, http_client, client_config)
|
|
24
|
+
assert_local_arguments(url, http_client, client_config)
|
|
25
25
|
|
|
26
26
|
service ||= Service.send(browser)
|
|
27
27
|
caps = process_options(options, service)
|
|
28
|
-
|
|
28
|
+
http_client ||= Remote::Http::Default.new(client_config: client_config)
|
|
29
|
+
http_client.server_url = service_url(service)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
begin
|
|
32
|
+
yield(caps, http_client) if block_given?
|
|
33
|
+
rescue Selenium::WebDriver::Error::WebDriverError
|
|
34
|
+
@service_manager&.stop
|
|
35
|
+
raise
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def service_url(service)
|
|
40
|
+
@service_manager = service.launch
|
|
41
|
+
@service_manager.uri
|
|
31
42
|
end
|
|
32
43
|
|
|
33
44
|
def process_options(options, service)
|
|
@@ -38,16 +49,22 @@ module Selenium
|
|
|
38
49
|
raise ArgumentError, ":options must be an instance of #{default_options.class}"
|
|
39
50
|
end
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
options.browser_version = nil
|
|
46
|
-
end
|
|
47
|
-
finder.driver_path
|
|
48
|
-
end
|
|
52
|
+
finder = WebDriver::DriverFinder.new(options, service)
|
|
53
|
+
options.binary = finder.browser_path if options.respond_to?(:binary) && finder.browser_path?
|
|
54
|
+
service.executable_path = finder.driver_path
|
|
55
|
+
options.browser_version = nil if options.respond_to?(:binary) && options.binary
|
|
49
56
|
options.as_json
|
|
50
57
|
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def assert_local_arguments(url, http_client, client_config)
|
|
62
|
+
if url || client_config&.server_url
|
|
63
|
+
raise ArgumentError, "Can't set the server URL for #{self.class}; the service provides it"
|
|
64
|
+
elsif http_client && client_config
|
|
65
|
+
raise ArgumentError, 'Cannot use both :http_client and :client_config'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
51
68
|
end
|
|
52
69
|
end
|
|
53
70
|
end
|