From b0851bfea9c3c8dfbe741d512ac4092264766dfa Mon Sep 17 00:00:00 2001 From: Boni Garcia Date: Mon, 3 Jun 2024 16:57:36 +0200 Subject: [PATCH 01/21] [rust] Display driver path in error trace when driver is unavailable --- rust/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/main.rs b/rust/src/main.rs index f47b8c83854c6..4347cf9900a94 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -283,7 +283,7 @@ fn log_driver_and_browser_path( if driver_path.exists() { log.info(format!("{}{}", DRIVER_PATH, driver_path.display())); } else { - log.error(format!("Driver unavailable: {}", DRIVER_PATH)); + log.error(format!("Driver unavailable: {}", driver_path.display())); flush_and_exit(UNAVAILABLE, log, None); } if !browser_path.is_empty() { From 2ad5abdabc5697a0d484c6af16aa39b38ef6e8f4 Mon Sep 17 00:00:00 2001 From: Boni Garcia Date: Mon, 3 Jun 2024 16:58:47 +0200 Subject: [PATCH 02/21] [rust] Include cache paths with non-ascii characters in test (#14066) --- rust/tests/cache_tests.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rust/tests/cache_tests.rs b/rust/tests/cache_tests.rs index 66dbc5293d13b..11b46d33b3f84 100644 --- a/rust/tests/cache_tests.rs +++ b/rust/tests/cache_tests.rs @@ -17,20 +17,23 @@ use crate::common::get_driver_path; use assert_cmd::Command; +use rstest::rstest; use std::fs; use std::path::Path; mod common; -#[test] -fn cache_path_test() { +#[rstest] +#[case("../tmp")] +#[case("../áèîö")] +#[case("../テスト")] +fn cache_path_test(#[case] tmp_cache_folder_name: String) { let mut cmd = Command::new(env!("CARGO_BIN_EXE_selenium-manager")); - let tmp_cache_folder_name = "../tmp"; cmd.args([ "--browser", "chrome", "--cache-path", - tmp_cache_folder_name, + &tmp_cache_folder_name, "--output", "json", ]) @@ -42,7 +45,7 @@ fn cache_path_test() { println!("*** Custom cache path: {}", driver_path); assert!(!driver_path.contains(r"cache\selenium")); - let tmp_cache_path = Path::new(tmp_cache_folder_name); + let tmp_cache_path = Path::new(&tmp_cache_folder_name); fs::remove_dir_all(tmp_cache_path).unwrap(); assert!(!tmp_cache_path.exists()); } From 53ed43c3c7fa388e29726370fc47d3b8ce6fc7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Sautter?= Date: Mon, 3 Jun 2024 17:41:46 +0200 Subject: [PATCH 03/21] [java] let TracedCommandExecutor delegate NeedsLocalLogs calls --- .../org/openqa/selenium/remote/RemoteWebDriver.java | 6 +++--- .../openqa/selenium/remote/TracedCommandExecutor.java | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index 9e518401b8dab..3462231c7f17b 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java @@ -206,11 +206,11 @@ private Capabilities init(Capabilities capabilities) { converter = new JsonToWebElementConverter(this); executeMethod = new RemoteExecuteMethod(this); - Set logTypesToInclude = Set.of(); + Set logTypesToIgnore = Set.of(); - LocalLogs performanceLogger = LocalLogs.getStoringLoggerInstance(logTypesToInclude); + LocalLogs performanceLogger = LocalLogs.getStoringLoggerInstance(logTypesToIgnore); LocalLogs clientLogs = - LocalLogs.getHandlerBasedLoggerInstance(LoggingHandler.getInstance(), logTypesToInclude); + LocalLogs.getHandlerBasedLoggerInstance(LoggingHandler.getInstance(), logTypesToIgnore); localLogs = LocalLogs.getCombinedLogsHolder(clientLogs, performanceLogger); remoteLogs = new RemoteLogs(executeMethod, localLogs); diff --git a/java/src/org/openqa/selenium/remote/TracedCommandExecutor.java b/java/src/org/openqa/selenium/remote/TracedCommandExecutor.java index d09c41715b9f6..a8d0b220ea678 100644 --- a/java/src/org/openqa/selenium/remote/TracedCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/TracedCommandExecutor.java @@ -20,10 +20,12 @@ import java.io.IOException; import java.util.Map; import java.util.Objects; +import org.openqa.selenium.logging.LocalLogs; +import org.openqa.selenium.logging.NeedsLocalLogs; import org.openqa.selenium.remote.tracing.Span; import org.openqa.selenium.remote.tracing.Tracer; -public class TracedCommandExecutor implements CommandExecutor { +public class TracedCommandExecutor implements CommandExecutor, NeedsLocalLogs { private final CommandExecutor delegate; private final Tracer tracer; @@ -51,4 +53,11 @@ public Response execute(Command command) throws IOException { return delegate.execute(command); } } + + @Override + public void setLocalLogs(LocalLogs logs) { + if (delegate instanceof NeedsLocalLogs) { + ((NeedsLocalLogs) delegate).setLocalLogs(logs); + } + } } From cab1dd1e7065614ae903e3ebf42b861b6fc8a80f Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Mon, 3 Jun 2024 23:10:25 -0500 Subject: [PATCH 04/21] [rb] manage bidi instance on the bridge not the driver (#14071) * [rb] manage bidi instance on the bridge not the driver * restore null safety checks --- rb/lib/selenium/webdriver/common/driver.rb | 9 ++------- .../webdriver/common/driver_extensions/has_bidi.rb | 2 +- rb/lib/selenium/webdriver/remote/bridge.rb | 11 ++++++++++- rb/sig/lib/selenium/webdriver/common/driver.rbs | 1 - rb/sig/lib/selenium/webdriver/remote/bridge.rbs | 1 + 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/rb/lib/selenium/webdriver/common/driver.rb b/rb/lib/selenium/webdriver/common/driver.rb index 6a816cb67b301..0a02916c5afe2 100644 --- a/rb/lib/selenium/webdriver/common/driver.rb +++ b/rb/lib/selenium/webdriver/common/driver.rb @@ -70,10 +70,9 @@ def for(browser, opts = {}) def initialize(bridge: nil, listener: nil, **opts) @devtools = nil - @bidi = nil bridge ||= create_bridge(**opts) - add_extensions(bridge.browser) @bridge = listener ? Support::EventFiringBridge.new(bridge, listener) : bridge + add_extensions(@bridge.browser) end def inspect @@ -174,7 +173,6 @@ def quit ensure @service_manager&.stop @devtools&.close - @bidi&.close end # @@ -182,10 +180,7 @@ def quit # def close - # If no top-level browsing contexts are open after calling close, - # it indicates that the WebDriver session is closed. - # If the WebDriver session is closed, the BiDi session also needs to be closed. - bridge.close.tap { |handles| @bidi&.close if handles&.empty? } + bridge&.close end # diff --git a/rb/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb b/rb/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb index b0e435d4b6665..859e44737e705 100644 --- a/rb/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +++ b/rb/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb @@ -28,7 +28,7 @@ module HasBiDi # def bidi - @bidi ||= Selenium::WebDriver::BiDi.new(url: capabilities[:web_socket_url]) + @bridge.bidi end end # HasBiDi end # DriverExtensions diff --git a/rb/lib/selenium/webdriver/remote/bridge.rb b/rb/lib/selenium/webdriver/remote/bridge.rb index 773d3ef37d778..eb0b748b3702c 100644 --- a/rb/lib/selenium/webdriver/remote/bridge.rb +++ b/rb/lib/selenium/webdriver/remote/bridge.rb @@ -213,10 +213,12 @@ def quit http.close rescue *QUIT_ERRORS nil + ensure + @bidi&.close end def close - execute :close_window + execute(:close_window).tap { |handles| @bidi&.close if handles.empty? } end def refresh @@ -602,6 +604,13 @@ def user_verified(verified, authenticator_id) execute :set_user_verified, {authenticatorId: authenticator_id}, {isUserVerified: verified} end + def bidi + msg = 'this operation requires enabling BiDi by setting #web_socket_url to true in options class' + raise(WebDriver::Error::WebDriverError, msg) unless capabilities.web_socket_url + + @bidi ||= Selenium::WebDriver::BiDi.new(url: capabilities[:web_socket_url]) + end + def command_list COMMANDS end diff --git a/rb/sig/lib/selenium/webdriver/common/driver.rbs b/rb/sig/lib/selenium/webdriver/common/driver.rbs index 67f2a9017680f..102ad2c526dd2 100644 --- a/rb/sig/lib/selenium/webdriver/common/driver.rbs +++ b/rb/sig/lib/selenium/webdriver/common/driver.rbs @@ -5,7 +5,6 @@ module Selenium include TakesScreenshot - @bidi: untyped @devtools: untyped @navigate: untyped diff --git a/rb/sig/lib/selenium/webdriver/remote/bridge.rbs b/rb/sig/lib/selenium/webdriver/remote/bridge.rbs index a303ec5316006..32aaa1a21b8ef 100644 --- a/rb/sig/lib/selenium/webdriver/remote/bridge.rbs +++ b/rb/sig/lib/selenium/webdriver/remote/bridge.rbs @@ -5,6 +5,7 @@ module Selenium include _CommandList include _Features + @bidi: WebDriver::BiDi @http: untyped @file_detector: untyped From 635f7e22f566d63ecf68c18607a17623cfc2dd11 Mon Sep 17 00:00:00 2001 From: Alexander Millin Date: Tue, 4 Jun 2024 15:35:16 +0300 Subject: [PATCH 05/21] Fix EOFError when calling the Remote WebDriver download_file method (#14031) --- py/selenium/webdriver/remote/webdriver.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/py/selenium/webdriver/remote/webdriver.py b/py/selenium/webdriver/remote/webdriver.py index c1fa5110898b7..ccb6b0b5e1830 100644 --- a/py/selenium/webdriver/remote/webdriver.py +++ b/py/selenium/webdriver/remote/webdriver.py @@ -20,6 +20,7 @@ import copy import os import pkgutil +import tempfile import types import typing import warnings @@ -1147,12 +1148,13 @@ def download_file(self, file_name: str, target_directory: str) -> None: contents = self.execute(Command.DOWNLOAD_FILE, {"name": file_name})["value"]["contents"] - target_file = os.path.join(target_directory, file_name) - with open(target_file, "wb") as file: - file.write(base64.b64decode(contents)) + with tempfile.TemporaryDirectory() as tmp_dir: + zip_file = os.path.join(tmp_dir, file_name + ".zip") + with open(zip_file, "wb") as file: + file.write(base64.b64decode(contents)) - with zipfile.ZipFile(target_file, "r") as zip_ref: - zip_ref.extractall(target_directory) + with zipfile.ZipFile(zip_file, "r") as zip_ref: + zip_ref.extractall(target_directory) def delete_downloadable_files(self) -> None: """Deletes all downloadable files.""" From e67210469f33d52ba6385f56cc230df1c5e51560 Mon Sep 17 00:00:00 2001 From: Agustin Pequeno <33221555+aguspe@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:56:56 +0200 Subject: [PATCH 06/21] [rb] Expand RBS typing support by replacing untypes with precise typing (#13709) * Update atoms.rb signature types * Start adding bidi types * Extended types of devtools and edge * Added firefox and safari types and removed unused signature * Update chrome features with type and remove Safari error * Update server and remove unused signature * Expand Server type signature * expanding type support on options * Expand options types * add extra options support * Expand options support --------- Co-authored-by: aguspe --- rb/sig/lib/selenium/server.rbs | 24 ++++++------- rb/sig/lib/selenium/webdriver/atoms.rbs | 4 +-- rb/sig/lib/selenium/webdriver/bidi.rbs | 14 ++++---- .../selenium/webdriver/chrome/features.rbs | 6 ++-- .../lib/selenium/webdriver/common/options.rbs | 35 ++++++++++--------- .../webdriver/common/target_locator.rbs | 4 +-- rb/sig/lib/selenium/webdriver/devtools.rbs | 4 +-- rb/sig/lib/selenium/webdriver/edge.rbs | 6 ++-- rb/sig/lib/selenium/webdriver/firefox.rbs | 6 ++-- rb/sig/lib/selenium/webdriver/safari.rbs | 15 ++++---- rb/sig/lib/selenium/webdriver/support.rbs | 0 rb/sig/lib/selenium_webdriver.rbs | 0 12 files changed, 59 insertions(+), 59 deletions(-) delete mode 100644 rb/sig/lib/selenium/webdriver/support.rbs delete mode 100644 rb/sig/lib/selenium_webdriver.rbs diff --git a/rb/sig/lib/selenium/server.rbs b/rb/sig/lib/selenium/server.rbs index 4ae361debc94b..10020d85851f0 100644 --- a/rb/sig/lib/selenium/server.rbs +++ b/rb/sig/lib/selenium/server.rbs @@ -4,36 +4,36 @@ module Selenium self.@available_assets: untyped - @jar: untyped + @jar: String - @host: untyped + @host: String - @role: untyped + @role: String - @port: untyped + @port: Integer - @timeout: untyped + @timeout: Integer - @background: untyped + @background: bool - @additional_args: untyped + @additional_args: Integer | bool | Array[untyped] - @log: untyped + @log: String | bool @log_file: untyped @process: untyped - @socket: untyped + @socket: WebDriver::SocketPoller class Error < StandardError end CL_RESET: untyped - def self.get: (?Symbol required_version, ?Hash[untyped, untyped] opts) -> untyped + def self.get: (Symbol | String required_version, ?Hash[untyped, untyped] opts) -> Server - def self.download: (?Symbol required_version) -> untyped + def self.download: (Symbol | String required_version) -> String def self.latest: () -> untyped @@ -55,7 +55,7 @@ module Selenium attr_accessor log: untyped - def initialize: (untyped jar, ?Hash[untyped, untyped] opts) -> void + def initialize: (String jar, ?Hash[String | Symbol, Integer | bool] opts) -> void def start: () -> untyped diff --git a/rb/sig/lib/selenium/webdriver/atoms.rbs b/rb/sig/lib/selenium/webdriver/atoms.rbs index eacbd40132411..e825969f918c5 100644 --- a/rb/sig/lib/selenium/webdriver/atoms.rbs +++ b/rb/sig/lib/selenium/webdriver/atoms.rbs @@ -4,13 +4,13 @@ module Selenium include _Bridge include _ExecuteScript - def atom_script: (untyped) -> untyped + def atom_script: (Symbol) -> String private def read_atom: (Symbol function) -> String - def execute_atom: (Symbol function_name, *untyped arguments) -> untyped + def execute_atom: (Symbol function_name, [Element | String | Symbol] arguments) -> [Element | Integer | Float | bool | nil | String | Array[untyped]] end end end diff --git a/rb/sig/lib/selenium/webdriver/bidi.rbs b/rb/sig/lib/selenium/webdriver/bidi.rbs index e37040f4d9061..109b658bca24b 100644 --- a/rb/sig/lib/selenium/webdriver/bidi.rbs +++ b/rb/sig/lib/selenium/webdriver/bidi.rbs @@ -1,21 +1,21 @@ module Selenium module WebDriver class BiDi - @ws: untyped + @ws: WebSocketConnection - @session: untyped + @session: Session - def initialize: (url: untyped) -> void + def initialize: (url: String) -> void - def close: () -> untyped + def close: () -> nil - def callbacks: () -> untyped + def callbacks: () -> Hash[untyped, untyped] - def session: () -> untyped + def session: () -> Session def send_cmd: (untyped method, **untyped params) -> untyped - def error_message: (untyped message) -> ::String + def error_message: (Hash[String,String] message) -> String end end end diff --git a/rb/sig/lib/selenium/webdriver/chrome/features.rbs b/rb/sig/lib/selenium/webdriver/chrome/features.rbs index a4523edc32d4b..ab3e9c85707e4 100644 --- a/rb/sig/lib/selenium/webdriver/chrome/features.rbs +++ b/rb/sig/lib/selenium/webdriver/chrome/features.rbs @@ -4,10 +4,10 @@ module Selenium module Features include WebDriver::Chromium::Features - CHROME_COMMANDS: untyped - COMMANDS: Array[Symbol | String] + CHROME_COMMANDS: Hash[Symbol, Array[Symbol | String]] + COMMANDS: Hash[Symbol, Array[Symbol | String]] - def command_list: -> untyped + def command_list: -> Hash[Symbol, Array[Symbol | String]] def commands: (Symbol command) -> Array[Symbol | String] end diff --git a/rb/sig/lib/selenium/webdriver/common/options.rbs b/rb/sig/lib/selenium/webdriver/common/options.rbs index ccb419b4f7902..adf1b2bb473c7 100644 --- a/rb/sig/lib/selenium/webdriver/common/options.rbs +++ b/rb/sig/lib/selenium/webdriver/common/options.rbs @@ -1,43 +1,43 @@ module Selenium module WebDriver class Options - @options: untyped + @options: Hash[String | Symbol, String | Numeric | bool?] W3C_OPTIONS: Array[Symbol] GRID_OPTIONS: Array[Symbol] - BROWSER: untyped + BROWSER: Symbol KEY: untyped - CAPABILITIES: Hash[Symbol, String] + CAPABILITIES: Hash[String | Symbol, String | Numeric | bool?] - attr_reader self.driver_path: untyped + attr_reader self.driver_path: String - def self.chrome: (**untyped opts) -> untyped + def self.chrome: (**String | Symbol | Integer | bool opts) -> Chrome::Options - def self.firefox: (**untyped opts) -> untyped + def self.firefox: (**String | Symbol | Integer | bool opts) -> Firefox::Options - def self.ie: (**untyped opts) -> untyped + def self.ie: (**String | Symbol | Integer | bool opts) -> IE::Options alias self.internet_explorer self.ie - def self.edge: (**untyped opts) -> untyped + def self.edge: (**String | Symbol | Integer | bool opts) -> Edge::Options alias self.microsoftedge self.edge - def self.safari: (**untyped opts) -> untyped + def self.safari: (**String | Symbol | Integer | bool opts) -> Safari::Options def self.set_capabilities: () -> untyped - attr_accessor options: untyped + attr_accessor options: Hash[String | Symbol, String | Numeric | bool?] - def initialize: (**untyped opts) -> void + def initialize: (Hash[String | Symbol, String | Numeric | bool] opts) -> void - def add_option: (untyped name, ?untyped? value) -> untyped + def add_option: (String | Symbol name, String | Numeric | bool? value) -> (String | Numeric | bool)? - def ==: (untyped other) -> (false | untyped) + def ==: (untyped other) -> bool alias eql? == @@ -53,13 +53,14 @@ module Selenium def camelize?: (untyped _key) -> true - def generate_as_json: (untyped value, ?camelize_keys: bool) -> untyped + def generate_as_json: (Array[untyped] | Hash[untyped, untyped] | String | Symbol value, ?camelize_keys: bool) + -> (Array[untyped] | Hash[untyped, untyped] | String | Symbol) - def process_json_hash: (untyped value, untyped camelize_keys) -> untyped + def process_json_hash: (Hash[untyped, untyped] value, bool camelize_keys) -> Hash[untyped, untyped] - def convert_json_key: (untyped key, ?camelize: bool) -> untyped + def convert_json_key: (String | Symbol key, camelize: bool) -> String - def camel_case: (untyped str) -> untyped + def camel_case: (String str) -> String end end end diff --git a/rb/sig/lib/selenium/webdriver/common/target_locator.rbs b/rb/sig/lib/selenium/webdriver/common/target_locator.rbs index 609989c2ca3d8..2e650495a40ff 100644 --- a/rb/sig/lib/selenium/webdriver/common/target_locator.rbs +++ b/rb/sig/lib/selenium/webdriver/common/target_locator.rbs @@ -7,9 +7,9 @@ module Selenium def parent_frame: () -> void - def new_window: (?::Symbol `type`) { (untyped) -> untyped } -> untyped + def new_window: (Symbol type) { (untyped) -> untyped } -> untyped - def window: (untyped id) ?{ () -> untyped } -> untyped + def window: (Integer id) ?{ () -> untyped } -> untyped def active_element: () -> Element diff --git a/rb/sig/lib/selenium/webdriver/devtools.rbs b/rb/sig/lib/selenium/webdriver/devtools.rbs index 44db3c460745f..892e89bc4c8dd 100644 --- a/rb/sig/lib/selenium/webdriver/devtools.rbs +++ b/rb/sig/lib/selenium/webdriver/devtools.rbs @@ -10,7 +10,7 @@ module Selenium def initialize: (url: untyped) -> void - def close: () -> untyped + def close: () -> nil def callbacks: () -> untyped @@ -24,7 +24,7 @@ module Selenium def start_session: () -> untyped - def error_message: (untyped error) -> untyped + def error_message: (Hash[untyped, untyped] error) -> String end end end diff --git a/rb/sig/lib/selenium/webdriver/edge.rbs b/rb/sig/lib/selenium/webdriver/edge.rbs index fea64d68b81e6..e357f141b0061 100644 --- a/rb/sig/lib/selenium/webdriver/edge.rbs +++ b/rb/sig/lib/selenium/webdriver/edge.rbs @@ -1,11 +1,11 @@ module Selenium module WebDriver module Edge - self.@path: untyped + @path: String? - def self.path=: (untyped path) -> untyped + def self.path=: (String path) -> String - def self.path: () -> untyped + def self.path: () -> String? end end end diff --git a/rb/sig/lib/selenium/webdriver/firefox.rbs b/rb/sig/lib/selenium/webdriver/firefox.rbs index 9d25371c0f786..790ac05f42dbd 100644 --- a/rb/sig/lib/selenium/webdriver/firefox.rbs +++ b/rb/sig/lib/selenium/webdriver/firefox.rbs @@ -1,7 +1,7 @@ module Selenium module WebDriver module Firefox - self.@path: untyped + @path: String? DEFAULT_PORT: Integer @@ -13,9 +13,9 @@ module Selenium DEVTOOLS_VERSION: Integer - def self.path=: (untyped path) -> untyped + def self.path=: (String path) -> String - def self.path: () -> untyped + def self.path: () -> String? end end end diff --git a/rb/sig/lib/selenium/webdriver/safari.rbs b/rb/sig/lib/selenium/webdriver/safari.rbs index 80bfbd7a4b4a1..7159df25cc89f 100644 --- a/rb/sig/lib/selenium/webdriver/safari.rbs +++ b/rb/sig/lib/selenium/webdriver/safari.rbs @@ -1,21 +1,20 @@ module Selenium module WebDriver module Safari - self.@use_technology_preview: untyped + @use_technology_preview: bool? + @path: String? - self.@path: untyped - - attr_accessor self.use_technology_preview: untyped + attr_accessor self.use_technology_preview: bool def self.technology_preview: () -> String - def self.technology_preview!: () -> untyped + def self.technology_preview!: () -> bool - def self.technology_preview?: () -> untyped + def self.technology_preview?: () -> bool - def self.path=: (untyped path) -> untyped + def self.path=: (String) -> String - def self.path: () -> untyped + def self.path: () -> String end end end diff --git a/rb/sig/lib/selenium/webdriver/support.rbs b/rb/sig/lib/selenium/webdriver/support.rbs deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/rb/sig/lib/selenium_webdriver.rbs b/rb/sig/lib/selenium_webdriver.rbs deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 8ac19e4470604a73397953195bf6028e0c9fa26c Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Wed, 5 Jun 2024 10:40:18 -0500 Subject: [PATCH 07/21] [rb] Implement High Level Logging API with BiDi (#14073) * add and remove logging handlers with BiDi * use #object_id instead of creating new ids to track callbacks * do not send browsing contexts to subscription if not needed * deprecate Driver#script & LogInspector * error if trying to remove an id that does not exist * do not unsubscribe if never subscribed in the first place * do not deprecate callbacks people don't have to care about getting the id back --- rb/lib/selenium/webdriver/bidi.rb | 10 ++ rb/lib/selenium/webdriver/bidi/log_handler.rb | 63 ++++++++++ .../selenium/webdriver/bidi/log_inspector.rb | 6 +- rb/lib/selenium/webdriver/bidi/session.rb | 14 +-- rb/lib/selenium/webdriver/bidi/struct.rb | 40 ++++++ rb/lib/selenium/webdriver/common.rb | 1 + rb/lib/selenium/webdriver/common/driver.rb | 22 +++- rb/lib/selenium/webdriver/common/script.rb | 45 +++++++ .../webdriver/common/websocket_connection.rb | 12 ++ .../lib/selenium/webdriver/common/driver.rbs | 1 + rb/sig/selenium/web_driver/script.rbs | 20 +++ .../selenium/webdriver/bidi/script_spec.rb | 114 ++++++++++++++++++ .../selenium/webdriver/bidi_spec.rb | 29 +---- .../selenium/webdriver/driver_spec.rb | 11 +- 14 files changed, 350 insertions(+), 38 deletions(-) create mode 100644 rb/lib/selenium/webdriver/bidi/log_handler.rb create mode 100644 rb/lib/selenium/webdriver/bidi/struct.rb create mode 100644 rb/lib/selenium/webdriver/common/script.rb create mode 100644 rb/sig/selenium/web_driver/script.rbs create mode 100644 rb/spec/integration/selenium/webdriver/bidi/script_spec.rb diff --git a/rb/lib/selenium/webdriver/bidi.rb b/rb/lib/selenium/webdriver/bidi.rb index 21a78b0361a4e..0beb1d024578a 100644 --- a/rb/lib/selenium/webdriver/bidi.rb +++ b/rb/lib/selenium/webdriver/bidi.rb @@ -22,7 +22,9 @@ module WebDriver class BiDi autoload :Session, 'selenium/webdriver/bidi/session' autoload :LogInspector, 'selenium/webdriver/bidi/log_inspector' + autoload :LogHandler, 'selenium/webdriver/bidi/log_handler' autoload :BrowsingContext, 'selenium/webdriver/bidi/browsing_context' + autoload :Struct, 'selenium/webdriver/bidi/struct' def initialize(url:) @ws = WebSocketConnection.new(url: url) @@ -36,6 +38,14 @@ def callbacks @ws.callbacks end + def add_callback(event, &block) + @ws.add_callback(event, &block) + end + + def remove_callback(event, id) + @ws.remove_callback(event, id) + end + def session @session ||= Session.new(self) end diff --git a/rb/lib/selenium/webdriver/bidi/log_handler.rb b/rb/lib/selenium/webdriver/bidi/log_handler.rb new file mode 100644 index 0000000000000..5054a28ed416f --- /dev/null +++ b/rb/lib/selenium/webdriver/bidi/log_handler.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Selenium + module WebDriver + class BiDi + class LogHandler + ConsoleLogEntry = BiDi::Struct.new(:level, :text, :timestamp, :method, :args, :type) + JavaScriptLogEntry = BiDi::Struct.new(:level, :text, :timestamp, :stack_trace, :type) + + def initialize(bidi) + @bidi = bidi + @log_entry_subscribed = false + end + + # @return [int] id of the handler + def add_message_handler(type) + subscribe_log_entry unless @log_entry_subscribed + @bidi.add_callback('log.entryAdded') do |params| + if params['type'] == type + log_entry_klass = type == 'console' ? ConsoleLogEntry : JavaScriptLogEntry + yield(log_entry_klass.new(**params)) + end + end + end + + # @param [int] id of the handler previously added + def remove_message_handler(id) + @bidi.remove_callback('log.entryAdded', id) + unsubscribe_log_entry if @log_entry_subscribed && @bidi.callbacks['log.entryAdded'].empty? + end + + private + + def subscribe_log_entry + @bidi.session.subscribe('log.entryAdded') + @log_entry_subscribed = true + end + + def unsubscribe_log_entry + @bidi.session.unsubscribe('log.entryAdded') + @log_entry_subscribed = false + end + end # LogHandler + end # Bidi + end # WebDriver +end # Selenium diff --git a/rb/lib/selenium/webdriver/bidi/log_inspector.rb b/rb/lib/selenium/webdriver/bidi/log_inspector.rb index 76853d785bf97..133666cec0f01 100644 --- a/rb/lib/selenium/webdriver/bidi/log_inspector.rb +++ b/rb/lib/selenium/webdriver/bidi/log_inspector.rb @@ -41,6 +41,10 @@ class LogInspector }.freeze def initialize(driver, browsing_context_ids = nil) + WebDriver.logger.deprecate('LogInspector class', + 'Script class with driver.script', + id: :log_inspector) + unless driver.capabilities.web_socket_url raise Error::WebDriverError, 'WebDriver instance must support BiDi protocol' @@ -92,7 +96,7 @@ def on_log(filter_by = nil, &block) def on(event, &block) event = EVENTS[event] if event.is_a?(Symbol) - @bidi.callbacks["log.#{event}"] << block + @bidi.add_callback("log.#{event}", &block) end def check_valid_filter(filter_by) diff --git a/rb/lib/selenium/webdriver/bidi/session.rb b/rb/lib/selenium/webdriver/bidi/session.rb index a323868e7714c..d3ae6792c46d9 100644 --- a/rb/lib/selenium/webdriver/bidi/session.rb +++ b/rb/lib/selenium/webdriver/bidi/session.rb @@ -29,21 +29,21 @@ def initialize(bidi) def status status = @bidi.send_cmd('session.status') - Status.new(status['ready'], status['message']) + Status.new(**status) end def subscribe(events, browsing_contexts = nil) - events_list = Array(events) - browsing_contexts_list = browsing_contexts.nil? ? nil : Array(browsing_contexts) + opts = {events: Array(events)} + opts[:browsing_contexts] = Array(browsing_contexts) if browsing_contexts - @bidi.send_cmd('session.subscribe', events: events_list, contexts: browsing_contexts_list) + @bidi.send_cmd('session.subscribe', **opts) end def unsubscribe(events, browsing_contexts = nil) - events_list = Array(events) - browsing_contexts_list = browsing_contexts.nil? ? nil : Array(browsing_contexts) + opts = {events: Array(events)} + opts[:browsing_contexts] = Array(browsing_contexts) if browsing_contexts - @bidi.send_cmd('session.unsubscribe', events: events_list, contexts: browsing_contexts_list) + @bidi.send_cmd('session.unsubscribe', **opts) end end # Session end # BiDi diff --git a/rb/lib/selenium/webdriver/bidi/struct.rb b/rb/lib/selenium/webdriver/bidi/struct.rb new file mode 100644 index 0000000000000..1ce3c562e5862 --- /dev/null +++ b/rb/lib/selenium/webdriver/bidi/struct.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Selenium + module WebDriver + class BiDi + class Struct < ::Struct + def self.new(*args, &block) + super(*args) do + define_method(:initialize) do |**kwargs| + converted_kwargs = kwargs.transform_keys { |key| camel_to_snake(key.to_s).to_sym } + super(*converted_kwargs.values_at(*self.class.members)) + end + class_eval(&block) if block + end + end + + def camel_to_snake(camel_str) + camel_str.gsub(/([A-Z])/, '_\1').downcase + end + end + end # BiDi + end # WebDriver +end # Selenium diff --git a/rb/lib/selenium/webdriver/common.rb b/rb/lib/selenium/webdriver/common.rb index c684f208b8846..7473825892268 100644 --- a/rb/lib/selenium/webdriver/common.rb +++ b/rb/lib/selenium/webdriver/common.rb @@ -98,3 +98,4 @@ require 'selenium/webdriver/common/shadow_root' require 'selenium/webdriver/common/websocket_connection' require 'selenium/webdriver/common/child_process' +require 'selenium/webdriver/common/script' diff --git a/rb/lib/selenium/webdriver/common/driver.rb b/rb/lib/selenium/webdriver/common/driver.rb index 0a02916c5afe2..41a079017d8ad 100644 --- a/rb/lib/selenium/webdriver/common/driver.rb +++ b/rb/lib/selenium/webdriver/common/driver.rb @@ -99,6 +99,22 @@ def navigate @navigate ||= WebDriver::Navigation.new(bridge) end + # + # @return [Script] + # @see Script + # + + def script(*args) + if args.any? + WebDriver.logger.deprecate('`Driver#script` as an alias for `#execute_script`', + '`Driver#execute_script`', + id: :driver_script) + execute_script(*args) + else + @script ||= WebDriver::Script.new(bridge) + end + end + # # @return [TargetLocator] # @see TargetLocator @@ -262,12 +278,6 @@ def add_virtual_authenticator(options) alias all find_elements - # - # driver.script('function() { ... };') - # - - alias script execute_script - # Get the first element matching the given selector. If given a # String or Symbol, it will be used as the id of the element. # diff --git a/rb/lib/selenium/webdriver/common/script.rb b/rb/lib/selenium/webdriver/common/script.rb new file mode 100644 index 0000000000000..4b58b1bbea2c7 --- /dev/null +++ b/rb/lib/selenium/webdriver/common/script.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Selenium + module WebDriver + class Script + def initialize(bridge) + @log_handler = BiDi::LogHandler.new(bridge.bidi) + end + + # @return [int] id of the handler + def add_console_message_handler(&block) + @log_handler.add_message_handler('console', &block) + end + + # @return [int] id of the handler + def add_javascript_error_handler(&block) + @log_handler.add_message_handler('javascript', &block) + end + + # @param [int] id of the handler previously added + def remove_console_message_handler(id) + @log_handler.remove_message_handler(id) + end + + alias remove_javascript_error_handler remove_console_message_handler + end # Script + end # WebDriver +end # Selenium diff --git a/rb/lib/selenium/webdriver/common/websocket_connection.rb b/rb/lib/selenium/webdriver/common/websocket_connection.rb index ec0e3d128e3fd..26f02ebc9bf1c 100644 --- a/rb/lib/selenium/webdriver/common/websocket_connection.rb +++ b/rb/lib/selenium/webdriver/common/websocket_connection.rb @@ -52,6 +52,18 @@ def callbacks @callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] } end + def add_callback(event, &block) + callbacks[event] << block + block.object_id + end + + def remove_callback(event, id) + return if callbacks[event].reject! { |callback| callback.object_id == id } + + ids = callbacks[event]&.map(&:object_id) + raise Error::WebDriverError, "Callback with ID #{id} does not exist for event #{event}: #{ids}" + end + def send_cmd(**payload) id = next_id data = payload.merge(id: id) diff --git a/rb/sig/lib/selenium/webdriver/common/driver.rbs b/rb/sig/lib/selenium/webdriver/common/driver.rbs index 102ad2c526dd2..0bc94a74a8008 100644 --- a/rb/sig/lib/selenium/webdriver/common/driver.rbs +++ b/rb/sig/lib/selenium/webdriver/common/driver.rbs @@ -8,6 +8,7 @@ module Selenium @devtools: untyped @navigate: untyped + @script: untyped @service_manager: untyped def self.for: (untyped browser, Hash[untyped, untyped] opts) -> untyped diff --git a/rb/sig/selenium/web_driver/script.rbs b/rb/sig/selenium/web_driver/script.rbs new file mode 100644 index 0000000000000..f2dc066174df0 --- /dev/null +++ b/rb/sig/selenium/web_driver/script.rbs @@ -0,0 +1,20 @@ +module Selenium + module WebDriver + class Script + @bidi: BiDi + @log_entry_subscribed: bool + + def add_console_message_handler: -> untyped + + def add_javascript_error_handler: -> untyped + + def remove_console_message_handler: -> untyped + + alias remove_javascript_error_handler remove_console_message_handler + + private + + def subscribe_log_entry: -> untyped + end + end +end diff --git a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb new file mode 100644 index 0000000000000..e8743217e6af9 --- /dev/null +++ b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +require_relative '../spec_helper' + +module Selenium + module WebDriver + describe Script, only: {browser: %i[chrome edge firefox]} do + before { reset_driver!(web_socket_url: true) } + after(:all) { quit_driver } + + it 'errors when bidi not enabled' do + reset_driver! do |driver| + expect { + driver.script + }.to raise_error(WebDriver::Error::WebDriverError, /this operation requires enabling BiDi/) + end + end + + it 'logs console messages' do + driver.navigate.to url_for('bidi/logEntryAdded.html') + + log_entries = [] + driver.script.add_console_message_handler { |log| log_entries << log } + + driver.find_element(id: 'jsException').click + driver.find_element(id: 'consoleLog').click + + wait.until { log_entries.any? } + expect(log_entries.size).to eq(1) + log_entry = log_entries.first + expect(log_entry).to be_a BiDi::LogHandler::ConsoleLogEntry + expect(log_entry.level).to eq 'info' + expect(log_entry.method).to eq 'log' + expect(log_entry.text).to eq 'Hello, world!' + expect(log_entry.type).to eq 'console' + end + + it 'logs multiple console messages' do + driver.navigate.to url_for('bidi/logEntryAdded.html') + + log_entries = [] + driver.script.add_console_message_handler { |log| log_entries << log } + driver.script.add_console_message_handler { |log| log_entries << log } + + driver.find_element(id: 'jsException').click + driver.find_element(id: 'consoleLog').click + + wait.until { log_entries.size > 1 } + expect(log_entries.size).to eq(2) + end + + it 'logs removes console message handler' do + driver.navigate.to url_for('bidi/logEntryAdded.html') + + log_entries = [] + id = driver.script.add_console_message_handler { |log| log_entries << log } + driver.script.add_console_message_handler { |log| log_entries << log } + + driver.find_element(id: 'consoleLog').click + + wait.until { log_entries.size > 1 } + + driver.script.remove_console_message_handler(id) + + driver.find_element(id: 'consoleLog').click + + wait.until { log_entries.size > 2 } + expect(log_entries.size).to eq(3) + end + + it 'logs javascript errors' do + driver.navigate.to url_for('bidi/logEntryAdded.html') + + log_entries = [] + driver.script.add_javascript_error_handler { |log| log_entries << log } + + driver.find_element(id: 'consoleLog').click + driver.find_element(id: 'jsException').click + + wait.until { log_entries.any? } + expect(log_entries.size).to eq(1) + log_entry = log_entries.first + expect(log_entry).to be_a BiDi::LogHandler::JavaScriptLogEntry + expect(log_entry.level).to eq 'error' + expect(log_entry.type).to eq 'javascript' + expect(log_entry.text).to eq 'Error: Not working' + expect(log_entry.stack_trace).not_to be_empty + end + + it 'errors removing non-existent handler' do + expect { + driver.script.remove_console_message_handler(0) + }.to raise_error(Error::WebDriverError, /Callback with ID 0 does not exist/) + end + end + end +end diff --git a/rb/spec/integration/selenium/webdriver/bidi_spec.rb b/rb/spec/integration/selenium/webdriver/bidi_spec.rb index 6b447803b0546..0a26e1009466d 100644 --- a/rb/spec/integration/selenium/webdriver/bidi_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi_spec.rb @@ -30,35 +30,18 @@ module WebDriver # do nothing end + it 'errors when bidi not enabled' do + reset_driver! do |driver| + expect { driver.bidi }.to raise_error(WebDriver::Error::WebDriverError) + end + end + it 'gets session status' do status = driver.bidi.session.status expect(status).to respond_to(:ready) expect(status.message).not_to be_empty end - it 'can navigate and listen to errors' do - log_entries = [] - log_inspector = BiDi::LogInspector.new(driver) - log_inspector.on_javascript_exception { |log| log_entries << log } - - browsing_context = BiDi::BrowsingContext.new(driver: driver, browsing_context_id: driver.window_handle) - info = browsing_context.navigate(url: url_for('/bidi/logEntryAdded.html')) - - expect(browsing_context.id).not_to be_nil - expect(info.navigation_id).not_to be_nil - expect(info.url).to include('/bidi/logEntryAdded.html') - - js_exception = wait.until { driver.find_element(id: 'jsException') } - js_exception.click - - log_entry = wait.until { log_entries.find { _1.text == 'Error: Not working' } } - expect(log_entry).to have_attributes( - text: 'Error: Not working', - type: 'javascript', - level: BiDi::LogInspector::LOG_LEVEL[:ERROR] - ) - end - it 'does not close BiDi session if at least one window is opened' do status = driver.bidi.session.status expect(status.ready).to be false diff --git a/rb/spec/integration/selenium/webdriver/driver_spec.rb b/rb/spec/integration/selenium/webdriver/driver_spec.rb index 623303445705c..c63685f4ac3d2 100644 --- a/rb/spec/integration/selenium/webdriver/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/driver_spec.rb @@ -243,7 +243,16 @@ module WebDriver end end - describe 'execute script' do + describe '#script' do + it 'executes script with deprecation warning' do + driver.navigate.to url_for('xhtmlTest.html') + expect { + expect(driver.script('return document.title;')).to eq('XHTML Test Page') + }.to have_deprecated(:driver_script) + end + end + + describe '#execute_script' do it 'returns strings' do driver.navigate.to url_for('xhtmlTest.html') expect(driver.execute_script('return document.title;')).to eq('XHTML Test Page') From 987be00dd5e220fecaa78f579e5ae37611bc4685 Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 08:11:38 -0500 Subject: [PATCH 08/21] [rb] make new #camel_to_snake a private class method --- rb/lib/selenium/webdriver/bidi/struct.rb | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/rb/lib/selenium/webdriver/bidi/struct.rb b/rb/lib/selenium/webdriver/bidi/struct.rb index 1ce3c562e5862..1ad8cb66390ef 100644 --- a/rb/lib/selenium/webdriver/bidi/struct.rb +++ b/rb/lib/selenium/webdriver/bidi/struct.rb @@ -21,20 +21,26 @@ module Selenium module WebDriver class BiDi class Struct < ::Struct - def self.new(*args, &block) - super(*args) do - define_method(:initialize) do |**kwargs| - converted_kwargs = kwargs.transform_keys { |key| camel_to_snake(key.to_s).to_sym } - super(*converted_kwargs.values_at(*self.class.members)) + class << self + def new(*args, &block) + super(*args) do + define_method(:initialize) do |**kwargs| + converted_kwargs = kwargs.transform_keys { |key| self.class.camel_to_snake(key.to_s).to_sym } + super(*converted_kwargs.values_at(*self.class.members)) + end + class_eval(&block) if block end - class_eval(&block) if block end - end - def camel_to_snake(camel_str) - camel_str.gsub(/([A-Z])/, '_\1').downcase + private + + def camel_to_snake(camel_str) + camel_str.gsub(/([A-Z])/, '_\1').downcase + end end end - end # BiDi + end + + # BiDi end # WebDriver end # Selenium From bf4027c78c87dae1b26f1dabb4133f5d2a6af108 Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 11:37:37 -0500 Subject: [PATCH 09/21] [rb] #camel_to_snake as a class method cannot be private when called by constructor --- rb/lib/selenium/webdriver/bidi/struct.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/rb/lib/selenium/webdriver/bidi/struct.rb b/rb/lib/selenium/webdriver/bidi/struct.rb index 1ad8cb66390ef..38d3e08e85221 100644 --- a/rb/lib/selenium/webdriver/bidi/struct.rb +++ b/rb/lib/selenium/webdriver/bidi/struct.rb @@ -32,8 +32,6 @@ def new(*args, &block) end end - private - def camel_to_snake(camel_str) camel_str.gsub(/([A-Z])/, '_\1').downcase end From a9ea73a672200870fc68c146962913b3d07d15ac Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 11:48:34 -0500 Subject: [PATCH 10/21] [ci] stop running safari tests in Ruby suite for now --- .github/workflows/ci-ruby.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci-ruby.yml b/.github/workflows/ci-ruby.yml index 653034f947f45..d93d4dc7ad25c 100644 --- a/.github/workflows/ci-ruby.yml +++ b/.github/workflows/ci-ruby.yml @@ -73,7 +73,6 @@ jobs: - chrome - edge - firefox - - safari os: - ubuntu - windows @@ -83,10 +82,6 @@ jobs: os: ubuntu - browser: edge os: macos - - browser: safari - os: ubuntu - - browser: safari - os: windows with: name: Local Tests (${{ matrix.browser }}, ${{ matrix.os }}) browser: ${{ matrix.browser }} @@ -115,8 +110,6 @@ jobs: os: ubuntu - browser: firefox os: ubuntu - - browser: safari - os: macos with: name: Remote Tests (${{ matrix.browser }}, ${{ matrix.os }}) browser: ${{ matrix.browser }} From b519b43f65ad2f073d19957c343146a563a00715 Mon Sep 17 00:00:00 2001 From: Earlopain <14981592+Earlopain@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:49:03 +0200 Subject: [PATCH 11/21] [rb] Add `logger` gem as a runtime dependency (#14082) This is getting the same treatment as the `base64` gem, starting with Ruby 3.4. Also see #13454 Closes #14081 --- rb/Gemfile.lock | 1 + rb/selenium-webdriver.gemspec | 1 + 2 files changed, 2 insertions(+) diff --git a/rb/Gemfile.lock b/rb/Gemfile.lock index da3275587867d..220102ae4b9b0 100644 --- a/rb/Gemfile.lock +++ b/rb/Gemfile.lock @@ -5,6 +5,7 @@ PATH selenium-webdriver (~> 4.2) selenium-webdriver (4.22.0.nightly) base64 (~> 0.2) + logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) diff --git a/rb/selenium-webdriver.gemspec b/rb/selenium-webdriver.gemspec index 5b8fe1df012d4..90e2662aa1e85 100644 --- a/rb/selenium-webdriver.gemspec +++ b/rb/selenium-webdriver.gemspec @@ -48,6 +48,7 @@ Gem::Specification.new do |s| s.require_paths = ['lib'] s.add_runtime_dependency 'base64', ['~> 0.2'] + s.add_runtime_dependency 'logger', ['~> 1.4'] s.add_runtime_dependency 'rexml', ['~> 3.2', '>= 3.2.5'] s.add_runtime_dependency 'rubyzip', ['>= 1.2.2', '< 3.0'] s.add_runtime_dependency 'websocket', ['~> 1.0'] From 16d3905975fc0cbfaf723d9b68c945fc0cfe4f7a Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 15:03:17 -0500 Subject: [PATCH 12/21] [build] need this version to prevent temporary issue with coreutils download --- MODULE.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index ccf0472ab9b5c..c4e8b2e0f5bc7 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,7 +1,7 @@ module(name = "selenium") bazel_dep(name = "apple_rules_lint", version = "0.3.2") -bazel_dep(name = "aspect_bazel_lib", version = "2.7.3") +bazel_dep(name = "aspect_bazel_lib", version = "2.7.6") bazel_dep(name = "aspect_rules_esbuild", version = "0.20.0") bazel_dep(name = "aspect_rules_js", version = "1.42.3") bazel_dep(name = "aspect_rules_ts", version = "2.1.0") From 1328661cea82e0cf8cf595faa27f67ea658d9a86 Mon Sep 17 00:00:00 2001 From: titusfortner Date: Tue, 4 Jun 2024 15:16:15 -0500 Subject: [PATCH 13/21] [rb] only run bidi tests if web_socket_url is set via environment variable --- .../webdriver/bidi/browsing_context_spec.rb | 5 +- .../webdriver/bidi/log_inspector_spec.rb | 346 ++++++++---------- .../selenium/webdriver/bidi/script_spec.rb | 11 +- .../selenium/webdriver/bidi_spec.rb | 12 +- .../selenium/webdriver/spec_helper.rb | 1 + .../spec_support/test_environment.rb | 3 + 6 files changed, 175 insertions(+), 203 deletions(-) diff --git a/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb index adf462b6af059..1d0a554daa8dd 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb @@ -22,9 +22,8 @@ module Selenium module WebDriver class BiDi - describe BrowsingContext, only: {browser: %i[chrome edge firefox]} do - before { reset_driver!(web_socket_url: true) } - after { quit_driver } + describe BrowsingContext, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + after { reset_driver! } it 'can create a browsing context for given id' do id = driver.window_handle diff --git a/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb index 24da3ede70771..e507fd1b1f9c6 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb @@ -22,20 +22,45 @@ module Selenium module WebDriver class BiDi - describe LogInspector, only: {browser: %i[chrome edge firefox]} do - let(:page) { '/bidi/logEntryAdded.html' } + describe LogInspector, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + let(:page) { 'bidi/logEntryAdded.html' } it 'can listen to console log' do - reset_driver!(web_socket_url: true) do |driver| - log_entries = [] - log_inspector = described_class.new(driver) - log_inspector.on_console_entry { |log| log_entries << log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'consoleLog').click - log_entry = wait.until { log_entries.find { _1.text == 'Hello, world!' } } + log_entries = [] + log_inspector = described_class.new(driver) + log_inspector.on_console_entry { |log| log_entries << log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'consoleLog').click + log_entry = wait.until { log_entries.find { _1.text == 'Hello, world!' } } + + expect(log_entry).to have_attributes( + text: 'Hello, world!', + realm: nil, + type: 'console', + level: LogInspector::LOG_LEVEL[:INFO], + method: 'log', + args: [{ + 'type' => 'string', + 'value' => 'Hello, world!' + }] + ) + end - expect(log_entry).to have_attributes( + it 'can listen to console log with different consumers' do + log_entries1 = [] + log_entries2 = [] + log_inspector = described_class.new(driver) + log_inspector.on_console_entry { |log| log_entries1 << log } + log_inspector.on_console_entry { |log| log_entries2 << log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'consoleLog').click + log_entry1 = wait.until { log_entries1.find { _1.text == 'Hello, world!' } } + log_entry2 = wait.until { log_entries2.find { _1.text == 'Hello, world!' } } + + expect([log_entry1, log_entry2]).to all( + have_attributes( text: 'Hello, world!', realm: nil, type: 'console', @@ -46,213 +71,164 @@ class BiDi 'value' => 'Hello, world!' }] ) - end - end - - it 'can listen to console log with different consumers' do - reset_driver!(web_socket_url: true) do |driver| - log_entries1 = [] - log_entries2 = [] - log_inspector = described_class.new(driver) - log_inspector.on_console_entry { |log| log_entries1 << log } - log_inspector.on_console_entry { |log| log_entries2 << log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'consoleLog').click - log_entry1 = wait.until { log_entries1.find { _1.text == 'Hello, world!' } } - log_entry2 = wait.until { log_entries2.find { _1.text == 'Hello, world!' } } - - expect([log_entry1, log_entry2]).to all( - have_attributes( - text: 'Hello, world!', - realm: nil, - type: 'console', - level: LogInspector::LOG_LEVEL[:INFO], - method: 'log', - args: [{ - 'type' => 'string', - 'value' => 'Hello, world!' - }] - ) - ) - end + ) end it 'can filter console info level log' do - reset_driver!(web_socket_url: true) do |driver| - log_entries = [] - log_inspector = described_class.new(driver) - log_inspector.on_console_entry(FilterBy.log_level('info')) { |log| log_entries << log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'consoleLog').click - log_entry = wait.until { log_entries.find { _1.text == 'Hello, world!' } } - - expect(log_entry).to have_attributes( - text: 'Hello, world!', - realm: nil, - type: 'console', - level: LogInspector::LOG_LEVEL[:INFO], - method: 'log', - args: [{ - 'type' => 'string', - 'value' => 'Hello, world!' - }] - ) - end + log_entries = [] + log_inspector = described_class.new(driver) + log_inspector.on_console_entry(FilterBy.log_level('info')) { |log| log_entries << log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'consoleLog').click + log_entry = wait.until { log_entries.find { _1.text == 'Hello, world!' } } + + expect(log_entry).to have_attributes( + text: 'Hello, world!', + realm: nil, + type: 'console', + level: LogInspector::LOG_LEVEL[:INFO], + method: 'log', + args: [{ + 'type' => 'string', + 'value' => 'Hello, world!' + }] + ) end it 'can filter console log' do - reset_driver!(web_socket_url: true) do |driver| - log_entries = [] - log_inspector = described_class.new(driver) - log_inspector.on_console_entry(FilterBy.log_level('error')) { |log| log_entries << log } - - driver.navigate.to url_for(page) - # Generating info level log but we are filtering by error level - wait.until { driver.find_element(id: 'consoleLog').displayed? } - driver.find_element(id: 'consoleLog').click - - expect(log_entries).to be_empty - end + log_entries = [] + log_inspector = described_class.new(driver) + log_inspector.on_console_entry(FilterBy.log_level('error')) { |log| log_entries << log } + + driver.navigate.to url_for(page) + # Generating info level log but we are filtering by error level + wait.until { driver.find_element(id: 'consoleLog').displayed? } + driver.find_element(id: 'consoleLog').click + + expect(log_entries).to be_empty end it 'can listen to javascript log' do - reset_driver!(web_socket_url: true) do |driver| - log_entry = nil - log_inspector = described_class.new(driver) - log_inspector.on_javascript_log { |log| log_entry = log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'jsException').click - wait.until { !log_entry.nil? } - - expect(log_entry).to have_attributes( - text: 'Error: Not working', - type: 'javascript', - level: LogInspector::LOG_LEVEL[:ERROR] - ) - end + log_entry = nil + log_inspector = described_class.new(driver) + log_inspector.on_javascript_log { |log| log_entry = log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'jsException').click + wait.until { !log_entry.nil? } + + expect(log_entry).to have_attributes( + text: 'Error: Not working', + type: 'javascript', + level: LogInspector::LOG_LEVEL[:ERROR] + ) end it 'can filter javascript log at error level' do - reset_driver!(web_socket_url: true) do |driver| - log_entry = nil - log_inspector = described_class.new(driver) - log_inspector.on_javascript_log(FilterBy.log_level('error')) { |log| log_entry = log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'jsException').click - wait.until { !log_entry.nil? } - - expect(log_entry).to have_attributes( - text: 'Error: Not working', - type: 'javascript', - level: LogInspector::LOG_LEVEL[:ERROR] - ) - end + log_entry = nil + log_inspector = described_class.new(driver) + log_inspector.on_javascript_log(FilterBy.log_level('error')) { |log| log_entry = log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'jsException').click + wait.until { !log_entry.nil? } + + expect(log_entry).to have_attributes( + text: 'Error: Not working', + type: 'javascript', + level: LogInspector::LOG_LEVEL[:ERROR] + ) end it 'can filter javascript log' do - reset_driver!(web_socket_url: true) do |driver| - log_entry = nil - log_inspector = described_class.new(driver) - log_inspector.on_javascript_log(FilterBy.log_level('info')) { |log| log_entry = log } - - driver.navigate.to url_for(page) - # Generating js error level log but we are filtering by info level - wait.until { driver.find_element(id: 'jsException').displayed? } - driver.find_element(id: 'jsException').click - - expect(log_entry).to be_nil - end + log_entry = nil + log_inspector = described_class.new(driver) + log_inspector.on_javascript_log(FilterBy.log_level('info')) { |log| log_entry = log } + + driver.navigate.to url_for(page) + # Generating js error level log but we are filtering by info level + wait.until { driver.find_element(id: 'jsException').displayed? } + driver.find_element(id: 'jsException').click + + expect(log_entry).to be_nil end it 'can listen to javascript error log' do - reset_driver!(web_socket_url: true) do |driver| - log_entry = nil - log_inspector = described_class.new(driver) - log_inspector.on_javascript_exception { |log| log_entry = log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'jsException').click - wait.until { !log_entry.nil? } - - expect(log_entry).to have_attributes( - text: 'Error: Not working', - type: 'javascript', - level: LogInspector::LOG_LEVEL[:ERROR] - ) - end + log_entry = nil + log_inspector = described_class.new(driver) + log_inspector.on_javascript_exception { |log| log_entry = log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'jsException').click + wait.until { !log_entry.nil? } + + expect(log_entry).to have_attributes( + text: 'Error: Not working', + type: 'javascript', + level: LogInspector::LOG_LEVEL[:ERROR] + ) end it 'can listen to any log' do - reset_driver!(web_socket_url: true) do |driver| - log_entries = [] - log_inspector = described_class.new(driver) - log_inspector.on_log { |log| log_entries << log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'consoleError').click - log_entry = wait.until { log_entries.find { _1['text'] == 'I am console error' } } - - expect(log_entry).to include( - 'text' => 'I am console error', - 'type' => 'console', - 'level' => LogInspector::LOG_LEVEL[:ERROR] - ) - end + log_entries = [] + log_inspector = described_class.new(driver) + log_inspector.on_log { |log| log_entries << log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'consoleError').click + log_entry = wait.until { log_entries.find { _1['text'] == 'I am console error' } } + + expect(log_entry).to include( + 'text' => 'I am console error', + 'type' => 'console', + 'level' => LogInspector::LOG_LEVEL[:ERROR] + ) end it 'can filter any log' do - reset_driver!(web_socket_url: true) do |driver| - log_entries = [] - log_inspector = described_class.new(driver) - log_inspector.on_log(FilterBy.log_level('info')) { |log| log_entries << log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'consoleLog').click - log_entry = wait.until { log_entries.find { _1['text'] == 'Hello, world!' } } - - expect(log_entry['text']).to eq('Hello, world!') - expect(log_entry['realm']).to be_nil - expect(log_entry['type']).to eq('console') - expect(log_entry['level']).to eq('info') - expect(log_entry['method']).to eq('log') - expect(log_entry['args'].size).to eq(1) - end + log_entries = [] + log_inspector = described_class.new(driver) + log_inspector.on_log(FilterBy.log_level('info')) { |log| log_entries << log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'consoleLog').click + log_entry = wait.until { log_entries.find { _1['text'] == 'Hello, world!' } } + + expect(log_entry['text']).to eq('Hello, world!') + expect(log_entry['realm']).to be_nil + expect(log_entry['type']).to eq('console') + expect(log_entry['level']).to eq('info') + expect(log_entry['method']).to eq('log') + expect(log_entry['args'].size).to eq(1) end it 'can filter any log at error level' do - reset_driver!(web_socket_url: true) do |driver| - log_entry = nil - log_inspector = described_class.new(driver) - log_inspector.on_log(FilterBy.log_level('error')) { |log| log_entry = log } - - driver.navigate.to url_for(page) - driver.find_element(id: 'jsException').click - wait.until { !log_entry.nil? } - - expect(log_entry['text']).to eq('Error: Not working') - expect(log_entry['type']).to eq('javascript') - expect(log_entry['level']).to eq('error') - end + log_entry = nil + log_inspector = described_class.new(driver) + log_inspector.on_log(FilterBy.log_level('error')) { |log| log_entry = log } + + driver.navigate.to url_for(page) + driver.find_element(id: 'jsException').click + wait.until { !log_entry.nil? } + + expect(log_entry['text']).to eq('Error: Not working') + expect(log_entry['type']).to eq('javascript') + expect(log_entry['level']).to eq('error') end it 'can retrieve stack trace for a log' do - reset_driver!(web_socket_url: true) do |driver| - log_entry = nil - log_inspector = described_class.new(driver) - log_inspector.on_javascript_log { |log| log_entry = log } + log_entry = nil + log_inspector = described_class.new(driver) + log_inspector.on_javascript_log { |log| log_entry = log } - driver.navigate.to url_for(page) - driver.find_element(id: 'jsException').click - wait.until { !log_entry.nil? } + driver.navigate.to url_for(page) + driver.find_element(id: 'jsException').click + wait.until { !log_entry.nil? } - stack_trace = log_entry.stack_trace + stack_trace = log_entry.stack_trace - expect(stack_trace).not_to be_nil - end + expect(stack_trace).not_to be_nil end end end diff --git a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb index e8743217e6af9..c080277a5505b 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb @@ -21,12 +21,11 @@ module Selenium module WebDriver - describe Script, only: {browser: %i[chrome edge firefox]} do - before { reset_driver!(web_socket_url: true) } - after(:all) { quit_driver } + describe Script, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + after { reset_driver! } it 'errors when bidi not enabled' do - reset_driver! do |driver| + reset_driver!(web_socket_url: false) do |driver| expect { driver.script }.to raise_error(WebDriver::Error::WebDriverError, /this operation requires enabling BiDi/) @@ -106,8 +105,8 @@ module WebDriver it 'errors removing non-existent handler' do expect { - driver.script.remove_console_message_handler(0) - }.to raise_error(Error::WebDriverError, /Callback with ID 0 does not exist/) + driver.script.remove_console_message_handler(12345) + }.to raise_error(Error::WebDriverError, /Callback with ID 12345 does not exist/) end end end diff --git a/rb/spec/integration/selenium/webdriver/bidi_spec.rb b/rb/spec/integration/selenium/webdriver/bidi_spec.rb index 0a26e1009466d..7259cca35ef34 100644 --- a/rb/spec/integration/selenium/webdriver/bidi_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi_spec.rb @@ -21,17 +21,11 @@ module Selenium module WebDriver - describe BiDi, only: {browser: %i[chrome edge firefox]} do - before { reset_driver!(web_socket_url: true) } - - after do - quit_driver - rescue Selenium::WebDriver::Error::InvalidSessionIdError - # do nothing - end + describe BiDi, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + after { reset_driver! } it 'errors when bidi not enabled' do - reset_driver! do |driver| + reset_driver!(web_socket_url: false) do |driver| expect { driver.bidi }.to raise_error(WebDriver::Error::WebDriverError) end end diff --git a/rb/spec/integration/selenium/webdriver/spec_helper.rb b/rb/spec/integration/selenium/webdriver/spec_helper.rb index bf0a507577ef1..e8b9ee7f84ecd 100644 --- a/rb/spec/integration/selenium/webdriver/spec_helper.rb +++ b/rb/spec/integration/selenium/webdriver/spec_helper.rb @@ -56,6 +56,7 @@ guards.add_condition(:ci, WebDriver::Platform.ci) guards.add_condition(:platform, WebDriver::Platform.os) guards.add_condition(:headless, !ENV['HEADLESS'].nil?) + guards.add_condition(:bidi, !ENV['WEBDRIVER_BIDI'].nil?) results = guards.disposition send(*results) if results diff --git a/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb b/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb index aafa63bcd17bb..c5e7731502215 100644 --- a/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb +++ b/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb @@ -248,6 +248,7 @@ def safari_preview_driver(**opts) end def chrome_options(args: [], **opts) + opts[:web_socket_url] = true if ENV['WEBDRIVER_BIDI'] && !opts.key?(:web_socket_url) opts[:binary] ||= ENV['CHROME_BINARY'] if ENV.key?('CHROME_BINARY') args << '--headless=chrome' if ENV['HEADLESS'] args << '--no-sandbox' if ENV['NO_SANDBOX'] @@ -256,6 +257,7 @@ def chrome_options(args: [], **opts) end def edge_options(args: [], **opts) + opts[:web_socket_url] = true if ENV['WEBDRIVER_BIDI'] && !opts.key?(:web_socket_url) opts[:binary] ||= ENV['EDGE_BINARY'] if ENV.key?('EDGE_BINARY') args << '--headless=chrome' if ENV['HEADLESS'] args << '--no-sandbox' if ENV['NO_SANDBOX'] @@ -264,6 +266,7 @@ def edge_options(args: [], **opts) end def firefox_options(args: [], **opts) + opts[:web_socket_url] = true if ENV['WEBDRIVER_BIDI'] && !opts.key?(:web_socket_url) opts[:binary] ||= ENV['FIREFOX_BINARY'] if ENV.key?('FIREFOX_BINARY') args << '--headless' if ENV['HEADLESS'] WebDriver::Options.firefox(args: args, **opts) From f3e2256e0320a28ff2fa6d79e4243a5faf28b81c Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 08:11:38 -0500 Subject: [PATCH 14/21] [rb] update after hook in specs to reduce unnecessary browser restarts --- .../selenium/webdriver/bidi/browsing_context_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/bidi/script_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/bidi_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/devtools_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/spec_helper.rb | 2 +- .../selenium/webdriver/spec_support/test_environment.rb | 3 +++ 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb index 1d0a554daa8dd..bdec214b2d987 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb @@ -23,7 +23,7 @@ module Selenium module WebDriver class BiDi describe BrowsingContext, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do - after { reset_driver! } + after { |example| reset_driver!(example: example) } it 'can create a browsing context for given id' do id = driver.window_handle diff --git a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb index c080277a5505b..54539ef844273 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver describe Script, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do - after { reset_driver! } + after { |example| reset_driver!(example: example) } it 'errors when bidi not enabled' do reset_driver!(web_socket_url: false) do |driver| diff --git a/rb/spec/integration/selenium/webdriver/bidi_spec.rb b/rb/spec/integration/selenium/webdriver/bidi_spec.rb index 7259cca35ef34..c7a65eda04954 100644 --- a/rb/spec/integration/selenium/webdriver/bidi_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver describe BiDi, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do - after { reset_driver! } + after { |example| reset_driver!(example: example) } it 'errors when bidi not enabled' do reset_driver!(web_socket_url: false) do |driver| diff --git a/rb/spec/integration/selenium/webdriver/devtools_spec.rb b/rb/spec/integration/selenium/webdriver/devtools_spec.rb index ab43c9cfe7bff..9ac7175047a8d 100644 --- a/rb/spec/integration/selenium/webdriver/devtools_spec.rb +++ b/rb/spec/integration/selenium/webdriver/devtools_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver describe DevTools, exclusive: {browser: %i[chrome edge firefox]} do - after { reset_driver! } + after { |example| reset_driver!(example: example) } it 'sends commands' do driver.devtools.page.navigate(url: url_for('xhtmlTest.html')) diff --git a/rb/spec/integration/selenium/webdriver/spec_helper.rb b/rb/spec/integration/selenium/webdriver/spec_helper.rb index e8b9ee7f84ecd..cd93fc5eb0aed 100644 --- a/rb/spec/integration/selenium/webdriver/spec_helper.rb +++ b/rb/spec/integration/selenium/webdriver/spec_helper.rb @@ -64,7 +64,7 @@ c.after do |example| result = example.execution_result - reset_driver! if result.exception || result.pending_exception + reset_driver! if example.exception || result.pending_exception end end diff --git a/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb b/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb index c5e7731502215..123b889bdbb3c 100644 --- a/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb +++ b/rb/spec/integration/selenium/webdriver/spec_support/test_environment.rb @@ -62,6 +62,9 @@ def driver_instance(...) end def reset_driver!(time: 0, **opts, &block) + # do not reset if the test was marked skipped + return if opts.delete(:example)&.metadata&.fetch(:skip, nil) + quit_driver sleep time driver_instance(**opts, &block) From 02a2bfcb923e4e633ee78dd26ecaec3b3e4ffd12 Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 08:12:26 -0500 Subject: [PATCH 15/21] [rb] restrict what tests are run when bidi enabled --- .../integration/selenium/webdriver/action_builder_spec.rb | 2 +- .../selenium/webdriver/bidi/browsing_context_spec.rb | 3 ++- .../selenium/webdriver/bidi/log_inspector_spec.rb | 3 ++- rb/spec/integration/selenium/webdriver/bidi/script_spec.rb | 3 ++- rb/spec/integration/selenium/webdriver/bidi_spec.rb | 3 ++- .../integration/selenium/webdriver/chrome/driver_spec.rb | 2 +- .../integration/selenium/webdriver/chrome/options_spec.rb | 2 +- .../integration/selenium/webdriver/chrome/profile_spec.rb | 2 +- .../integration/selenium/webdriver/chrome/service_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/devtools_spec.rb | 3 ++- rb/spec/integration/selenium/webdriver/driver_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/edge/driver_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/edge/options_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/edge/profile_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/edge/service_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/element_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/error_spec.rb | 2 +- .../integration/selenium/webdriver/firefox/driver_spec.rb | 2 +- .../integration/selenium/webdriver/firefox/profile_spec.rb | 2 +- .../integration/selenium/webdriver/firefox/service_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/guard_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/listener_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/manager_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/navigation_spec.rb | 2 +- .../integration/selenium/webdriver/remote/driver_spec.rb | 2 +- .../integration/selenium/webdriver/remote/element_spec.rb | 6 ++---- .../integration/selenium/webdriver/safari/driver_spec.rb | 3 ++- rb/spec/integration/selenium/webdriver/select_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/shadow_root_spec.rb | 3 ++- rb/spec/integration/selenium/webdriver/storage_spec.rb | 2 +- .../integration/selenium/webdriver/takes_screenshot_spec.rb | 6 ++---- .../integration/selenium/webdriver/target_locator_spec.rb | 4 ++-- rb/spec/integration/selenium/webdriver/timeout_spec.rb | 2 +- .../selenium/webdriver/virtual_authenticator_spec.rb | 3 ++- rb/spec/integration/selenium/webdriver/window_spec.rb | 2 +- rb/spec/integration/selenium/webdriver/zipper_spec.rb | 6 ++---- 36 files changed, 48 insertions(+), 46 deletions(-) diff --git a/rb/spec/integration/selenium/webdriver/action_builder_spec.rb b/rb/spec/integration/selenium/webdriver/action_builder_spec.rb index db187bc696c22..47c2a71c65bb0 100644 --- a/rb/spec/integration/selenium/webdriver/action_builder_spec.rb +++ b/rb/spec/integration/selenium/webdriver/action_builder_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe ActionBuilder do + describe ActionBuilder, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do after { driver.action.clear_all_actions } describe '#send_keys' do diff --git a/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb index bdec214b2d987..902a289441652 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/browsing_context_spec.rb @@ -22,7 +22,8 @@ module Selenium module WebDriver class BiDi - describe BrowsingContext, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + describe BrowsingContext, exclusive: {bidi: true, reason: 'only executed when bidi is enabled'}, + only: {browser: %i[chrome edge firefox]} do after { |example| reset_driver!(example: example) } it 'can create a browsing context for given id' do diff --git a/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb index e507fd1b1f9c6..e0d36be936bab 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb @@ -22,7 +22,8 @@ module Selenium module WebDriver class BiDi - describe LogInspector, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + describe LogInspector, exclusive: {bidi: true, reason: 'only executed when bidi is enabled'}, + only: {browser: %i[chrome edge firefox]} do let(:page) { 'bidi/logEntryAdded.html' } it 'can listen to console log' do diff --git a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb index 54539ef844273..f32aa39118779 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb @@ -21,7 +21,8 @@ module Selenium module WebDriver - describe Script, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + describe Script, exclusive: {bidi: true, reason: 'only executed when bidi is enabled'}, + only: {browser: %i[chrome edge firefox]} do after { |example| reset_driver!(example: example) } it 'errors when bidi not enabled' do diff --git a/rb/spec/integration/selenium/webdriver/bidi_spec.rb b/rb/spec/integration/selenium/webdriver/bidi_spec.rb index c7a65eda04954..1444e9ff52277 100644 --- a/rb/spec/integration/selenium/webdriver/bidi_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi_spec.rb @@ -21,7 +21,8 @@ module Selenium module WebDriver - describe BiDi, exclusive: {bidi: true}, only: {browser: %i[chrome edge firefox]} do + describe BiDi, exclusive: {bidi: true, reason: 'only executed when bidi is enabled'}, + only: {browser: %i[chrome edge firefox]} do after { |example| reset_driver!(example: example) } it 'errors when bidi not enabled' do diff --git a/rb/spec/integration/selenium/webdriver/chrome/driver_spec.rb b/rb/spec/integration/selenium/webdriver/chrome/driver_spec.rb index a6fb7da579df3..1e50fce967419 100644 --- a/rb/spec/integration/selenium/webdriver/chrome/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/chrome/driver_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Chrome - describe Driver, exclusive: {browser: :chrome} do + describe Driver, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :chrome}] do it 'gets and sets network conditions' do driver.network_conditions = {offline: false, latency: 56, throughput: 789} expect(driver.network_conditions).to eq( diff --git a/rb/spec/integration/selenium/webdriver/chrome/options_spec.rb b/rb/spec/integration/selenium/webdriver/chrome/options_spec.rb index 19488798e089b..b1919c9a909da 100644 --- a/rb/spec/integration/selenium/webdriver/chrome/options_spec.rb +++ b/rb/spec/integration/selenium/webdriver/chrome/options_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Chrome - describe Options, exclusive: {browser: :chrome} do + describe Options, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :chrome}] do it 'passes emulated device correctly' do reset_driver!(emulation: {device_name: 'Nexus 5'}) do |driver| ua = driver.execute_script 'return window.navigator.userAgent' diff --git a/rb/spec/integration/selenium/webdriver/chrome/profile_spec.rb b/rb/spec/integration/selenium/webdriver/chrome/profile_spec.rb index a5ff1d1b469de..45e68e1ba5a70 100644 --- a/rb/spec/integration/selenium/webdriver/chrome/profile_spec.rb +++ b/rb/spec/integration/selenium/webdriver/chrome/profile_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Chrome - describe Profile, exclusive: {browser: :chrome} do + describe Profile, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :chrome}] do let(:profile) { described_class.new } it 'adds an extension' do diff --git a/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb b/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb index fe594c1132eb4..aed6ce922258d 100644 --- a/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb +++ b/rb/spec/integration/selenium/webdriver/chrome/service_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Chrome - describe Service, exclusive: {browser: :chrome} do + describe Service, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :chrome}] do let(:service) { described_class.new } let(:service_manager) { service.launch } diff --git a/rb/spec/integration/selenium/webdriver/devtools_spec.rb b/rb/spec/integration/selenium/webdriver/devtools_spec.rb index 9ac7175047a8d..64e03e0851641 100644 --- a/rb/spec/integration/selenium/webdriver/devtools_spec.rb +++ b/rb/spec/integration/selenium/webdriver/devtools_spec.rb @@ -21,7 +21,8 @@ module Selenium module WebDriver - describe DevTools, exclusive: {browser: %i[chrome edge firefox]} do + describe DevTools, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, + {browser: %i[chrome edge firefox]}] do after { |example| reset_driver!(example: example) } it 'sends commands' do diff --git a/rb/spec/integration/selenium/webdriver/driver_spec.rb b/rb/spec/integration/selenium/webdriver/driver_spec.rb index c63685f4ac3d2..8b85148a3e1f8 100644 --- a/rb/spec/integration/selenium/webdriver/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/driver_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Driver do + describe Driver, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do it_behaves_like 'driver that can be started concurrently', exclude: {browser: %i[safari safari_preview]} it 'creates default capabilities', exclude: {browser: %i[safari safari_preview]} do diff --git a/rb/spec/integration/selenium/webdriver/edge/driver_spec.rb b/rb/spec/integration/selenium/webdriver/edge/driver_spec.rb index 232894c01608c..b3f739582a792 100644 --- a/rb/spec/integration/selenium/webdriver/edge/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/edge/driver_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Edge - describe Driver, exclusive: {browser: :edge} do + describe Driver, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :edge}] do it 'gets and sets network conditions' do driver.network_conditions = {offline: false, latency: 56, throughput: 789} expect(driver.network_conditions).to eq( diff --git a/rb/spec/integration/selenium/webdriver/edge/options_spec.rb b/rb/spec/integration/selenium/webdriver/edge/options_spec.rb index ebef9d9277e48..a6c6cacbaaeb2 100644 --- a/rb/spec/integration/selenium/webdriver/edge/options_spec.rb +++ b/rb/spec/integration/selenium/webdriver/edge/options_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Edge - describe Options, exclusive: {browser: :edge} do + describe Options, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :edge}] do it 'passes emulated device correctly' do reset_driver!(emulation: {device_name: 'Nexus 5'}) do |driver| ua = driver.execute_script 'return window.navigator.userAgent' diff --git a/rb/spec/integration/selenium/webdriver/edge/profile_spec.rb b/rb/spec/integration/selenium/webdriver/edge/profile_spec.rb index 4c3269029f0b1..33c933c3d62b9 100644 --- a/rb/spec/integration/selenium/webdriver/edge/profile_spec.rb +++ b/rb/spec/integration/selenium/webdriver/edge/profile_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Edge - describe Profile, exclusive: {browser: :edge} do + describe Profile, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :edge}] do let(:profile) { described_class.new } it 'adds an extension' do diff --git a/rb/spec/integration/selenium/webdriver/edge/service_spec.rb b/rb/spec/integration/selenium/webdriver/edge/service_spec.rb index cb13efaf4d904..39160cf62bcd0 100644 --- a/rb/spec/integration/selenium/webdriver/edge/service_spec.rb +++ b/rb/spec/integration/selenium/webdriver/edge/service_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Edge - describe Service, exclusive: {browser: :edge} do + describe Service, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :edge}] do let(:service) { described_class.new } let(:service_manager) { service.launch } diff --git a/rb/spec/integration/selenium/webdriver/element_spec.rb b/rb/spec/integration/selenium/webdriver/element_spec.rb index 4868e202d3070..d5985d64dcf0d 100644 --- a/rb/spec/integration/selenium/webdriver/element_spec.rb +++ b/rb/spec/integration/selenium/webdriver/element_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Element do + describe Element, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do it 'clicks' do driver.navigate.to url_for('formPage.html') expect { driver.find_element(id: 'imageButton').click }.not_to raise_error diff --git a/rb/spec/integration/selenium/webdriver/error_spec.rb b/rb/spec/integration/selenium/webdriver/error_spec.rb index 577df2c64d2d7..ab168263e2b6e 100644 --- a/rb/spec/integration/selenium/webdriver/error_spec.rb +++ b/rb/spec/integration/selenium/webdriver/error_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Error do + describe Error, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do it 'raises an appropriate error' do driver.navigate.to url_for('xhtmlTest.html') diff --git a/rb/spec/integration/selenium/webdriver/firefox/driver_spec.rb b/rb/spec/integration/selenium/webdriver/firefox/driver_spec.rb index 9ec85e2479ed8..d522b2d6b3cbf 100644 --- a/rb/spec/integration/selenium/webdriver/firefox/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/firefox/driver_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Firefox - describe Driver, exclusive: {browser: :firefox} do + describe Driver, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :firefox}] do let(:extensions) { '../../../../../../common/extensions/' } describe '#print_options' do diff --git a/rb/spec/integration/selenium/webdriver/firefox/profile_spec.rb b/rb/spec/integration/selenium/webdriver/firefox/profile_spec.rb index cb5a488751bf9..9da5a67e957df 100644 --- a/rb/spec/integration/selenium/webdriver/firefox/profile_spec.rb +++ b/rb/spec/integration/selenium/webdriver/firefox/profile_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Firefox - describe Profile, exclusive: {browser: :firefox} do + describe Profile, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :firefox}] do let(:profile) { described_class.new } before do diff --git a/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb b/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb index 74fac7f65e3af..8cd66cc67619c 100644 --- a/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb +++ b/rb/spec/integration/selenium/webdriver/firefox/service_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Firefox - describe Service, exclusive: {browser: :firefox} do + describe Service, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {browser: :firefox}] do let(:service) { described_class.new } let(:service_manager) { service.launch } diff --git a/rb/spec/integration/selenium/webdriver/guard_spec.rb b/rb/spec/integration/selenium/webdriver/guard_spec.rb index d53bacce60665..1353f3bae14b1 100644 --- a/rb/spec/integration/selenium/webdriver/guard_spec.rb +++ b/rb/spec/integration/selenium/webdriver/guard_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Support - describe Guards, exclusive: {driver: :chrome} do + describe Guards, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {driver: :chrome}] do describe '#exclude' do it 'ignores an unrecognized guard parameter', invalid: {browser: :chrome} do # pass diff --git a/rb/spec/integration/selenium/webdriver/listener_spec.rb b/rb/spec/integration/selenium/webdriver/listener_spec.rb index 2b44829345262..2cf960ff00be1 100644 --- a/rb/spec/integration/selenium/webdriver/listener_spec.rb +++ b/rb/spec/integration/selenium/webdriver/listener_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Driver do + describe Driver, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do before { quit_driver } it 'supports listener' do diff --git a/rb/spec/integration/selenium/webdriver/manager_spec.rb b/rb/spec/integration/selenium/webdriver/manager_spec.rb index 600ebc68261c3..f06afd7302cc9 100644 --- a/rb/spec/integration/selenium/webdriver/manager_spec.rb +++ b/rb/spec/integration/selenium/webdriver/manager_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Manager do + describe Manager, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do describe 'cookie management' do before { driver.navigate.to url_for('xhtmlTest.html') } diff --git a/rb/spec/integration/selenium/webdriver/navigation_spec.rb b/rb/spec/integration/selenium/webdriver/navigation_spec.rb index 4b98516c84480..b7586bc1bb9d8 100644 --- a/rb/spec/integration/selenium/webdriver/navigation_spec.rb +++ b/rb/spec/integration/selenium/webdriver/navigation_spec.rb @@ -19,7 +19,7 @@ require_relative 'spec_helper' -describe 'Navigation' do +describe 'Navigation', exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do it 'navigates back and forward' do form_title = 'We Leave From Here' result_title = 'We Arrive Here' diff --git a/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb b/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb index e402fc7d0f1c0..36038859cd303 100644 --- a/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Remote - describe Driver, exclusive: {driver: :remote} do + describe Driver, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, {driver: :remote}] do it 'exposes session_id' do expect(driver.session_id).to be_a(String) end diff --git a/rb/spec/integration/selenium/webdriver/remote/element_spec.rb b/rb/spec/integration/selenium/webdriver/remote/element_spec.rb index fb66a3195f7da..509ba1608c1e8 100644 --- a/rb/spec/integration/selenium/webdriver/remote/element_spec.rb +++ b/rb/spec/integration/selenium/webdriver/remote/element_spec.rb @@ -21,14 +21,12 @@ module Selenium module WebDriver - describe Element do + describe Element, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do before do driver.file_detector = ->(filename) { File.join(__dir__, filename) } end - after do - driver.file_detector = nil - end + after { driver.file_detector = nil } context 'when uploading one file' do it 'uses the provided file detector', exclusive: {driver: :remote}, diff --git a/rb/spec/integration/selenium/webdriver/safari/driver_spec.rb b/rb/spec/integration/selenium/webdriver/safari/driver_spec.rb index 541e35ef4fdb9..520e39792e7b8 100644 --- a/rb/spec/integration/selenium/webdriver/safari/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/safari/driver_spec.rb @@ -22,7 +22,8 @@ module Selenium module WebDriver module Safari - describe Driver, exclusive: {browser: %i[safari safari_preview]} do + describe Driver, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, + {browser: %i[safari safari_preview]}] do it 'gets and sets permissions' do driver.permissions = {'getUserMedia' => false} expect(driver.permissions).to eq('getUserMedia' => false) diff --git a/rb/spec/integration/selenium/webdriver/select_spec.rb b/rb/spec/integration/selenium/webdriver/select_spec.rb index 715dbe5e7ba89..fd14626283c08 100644 --- a/rb/spec/integration/selenium/webdriver/select_spec.rb +++ b/rb/spec/integration/selenium/webdriver/select_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module Support - describe Select do + describe Select, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do let(:select) { described_class.new(driver.find_element(name: 'selectomatic')) } let(:multi_select) { described_class.new(driver.find_element(id: 'multi')) } let(:single_disabled) { described_class.new(driver.find_element(name: 'single_disabled')) } diff --git a/rb/spec/integration/selenium/webdriver/shadow_root_spec.rb b/rb/spec/integration/selenium/webdriver/shadow_root_spec.rb index 91b9f889dccbc..00bada8e87970 100644 --- a/rb/spec/integration/selenium/webdriver/shadow_root_spec.rb +++ b/rb/spec/integration/selenium/webdriver/shadow_root_spec.rb @@ -21,7 +21,8 @@ module Selenium module WebDriver - describe ShadowRoot, only: {browser: %i[chrome firefox edge safari]} do + describe ShadowRoot, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, + {browser: %i[chrome firefox edge safari]}] do before { driver.navigate.to url_for('webComponents.html') } let(:custom_element) { driver.find_element(css: 'custom-checkbox-element') } diff --git a/rb/spec/integration/selenium/webdriver/storage_spec.rb b/rb/spec/integration/selenium/webdriver/storage_spec.rb index 264ea8958e79a..a438984eb9706 100644 --- a/rb/spec/integration/selenium/webdriver/storage_spec.rb +++ b/rb/spec/integration/selenium/webdriver/storage_spec.rb @@ -22,7 +22,7 @@ module Selenium module WebDriver module DriverExtensions - describe HasWebStorage do + describe HasWebStorage, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do shared_examples 'web storage' do before do driver.navigate.to url_for('clicks.html') diff --git a/rb/spec/integration/selenium/webdriver/takes_screenshot_spec.rb b/rb/spec/integration/selenium/webdriver/takes_screenshot_spec.rb index 3cbc87ac970d2..a7082d7f0702b 100644 --- a/rb/spec/integration/selenium/webdriver/takes_screenshot_spec.rb +++ b/rb/spec/integration/selenium/webdriver/takes_screenshot_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe TakesScreenshot do + describe TakesScreenshot, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do before do driver.navigate.to url_for('xhtmlTest.html') end @@ -80,9 +80,7 @@ def save_screenshot_and_assert(source, path) driver.navigate.to url_for('printPage.html') end - after do - FileUtils.rm_rf(path) - end + after { FileUtils.rm_rf(path) } it 'takes viewport screenshot by default' do viewport_width = driver.execute_script('return window.innerWidth;') diff --git a/rb/spec/integration/selenium/webdriver/target_locator_spec.rb b/rb/spec/integration/selenium/webdriver/target_locator_spec.rb index 1f76430b44ec3..5c32083143c5e 100644 --- a/rb/spec/integration/selenium/webdriver/target_locator_spec.rb +++ b/rb/spec/integration/selenium/webdriver/target_locator_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe TargetLocator do + describe TargetLocator, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do before { @original_window = driver.window_handle } after do @@ -325,7 +325,7 @@ module WebDriver end describe 'unhandled alert error' do - after { reset_driver! } + after { |example| reset_driver!(example: example) } it 'raises an UnexpectedAlertOpenError if an alert has not been dealt with' do driver.navigate.to url_for('alerts.html') diff --git a/rb/spec/integration/selenium/webdriver/timeout_spec.rb b/rb/spec/integration/selenium/webdriver/timeout_spec.rb index 843c13d81b2fe..934faafd3bc69 100644 --- a/rb/spec/integration/selenium/webdriver/timeout_spec.rb +++ b/rb/spec/integration/selenium/webdriver/timeout_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Timeouts do + describe Timeouts, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do before do driver.manage.timeouts.implicit_wait = 6 driver.manage.timeouts.page_load = 2 diff --git a/rb/spec/integration/selenium/webdriver/virtual_authenticator_spec.rb b/rb/spec/integration/selenium/webdriver/virtual_authenticator_spec.rb index 594c407a27e77..16c1ce255f5ac 100644 --- a/rb/spec/integration/selenium/webdriver/virtual_authenticator_spec.rb +++ b/rb/spec/integration/selenium/webdriver/virtual_authenticator_spec.rb @@ -21,7 +21,8 @@ module Selenium module WebDriver - describe VirtualAuthenticator, exclusive: {browser: %i[chrome edge]} do + describe VirtualAuthenticator, exclusive: [{bidi: false, reason: 'Not yet implemented with BiDi'}, + {browser: %i[chrome edge]}] do # A pkcs#8 encoded unencrypted EC256 private key as a base64url string. let(:pkcs8_private_key) do 'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q' \ diff --git a/rb/spec/integration/selenium/webdriver/window_spec.rb b/rb/spec/integration/selenium/webdriver/window_spec.rb index 5d01db255da4a..8a5b588dd52ea 100644 --- a/rb/spec/integration/selenium/webdriver/window_spec.rb +++ b/rb/spec/integration/selenium/webdriver/window_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Window do + describe Window, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do after(:all) { reset_driver! } let(:window) { driver.manage.window } diff --git a/rb/spec/integration/selenium/webdriver/zipper_spec.rb b/rb/spec/integration/selenium/webdriver/zipper_spec.rb index 197e1cf5d630d..266c19ed2962b 100644 --- a/rb/spec/integration/selenium/webdriver/zipper_spec.rb +++ b/rb/spec/integration/selenium/webdriver/zipper_spec.rb @@ -21,7 +21,7 @@ module Selenium module WebDriver - describe Zipper do + describe Zipper, exclusive: {bidi: false, reason: 'Not yet implemented with BiDi'} do let(:base_file_name) { 'file.txt' } let(:file_content) { 'content' } let(:zip_file) { File.join(Dir.tmpdir, 'test.zip') } @@ -34,9 +34,7 @@ def create_file filename end - after do - FileUtils.rm_rf zip_file - end + after { FileUtils.rm_rf zip_file } describe '#zip' do it 'a file' do From 40da1a73a76dfefc3e91a7c36c6b24cd75eeb7c9 Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 16:24:19 -0500 Subject: [PATCH 16/21] [rb] do not delete the reason message from the guard, we need it later --- rb/lib/selenium/webdriver/support/guards/guard.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rb/lib/selenium/webdriver/support/guards/guard.rb b/rb/lib/selenium/webdriver/support/guards/guard.rb index 877b1028c055a..a9f0acaccfd52 100644 --- a/rb/lib/selenium/webdriver/support/guards/guard.rb +++ b/rb/lib/selenium/webdriver/support/guards/guard.rb @@ -36,7 +36,7 @@ def initialize(guarded, type, guards = nil) @messages[:unknown] = 'TODO: Investigate why this is failing and file a bug report' @type = type - @reason = @guarded.delete(:reason) + @reason = @guarded[:reason] end def message From 199aaee8b960c2d38186c26ff0a98d761507d1da Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 16:25:16 -0500 Subject: [PATCH 17/21] [rb] when test is guarded just output the whole matching guard --- rb/lib/selenium/webdriver/support/guards/guard.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rb/lib/selenium/webdriver/support/guards/guard.rb b/rb/lib/selenium/webdriver/support/guards/guard.rb index a9f0acaccfd52..6c86483dad555 100644 --- a/rb/lib/selenium/webdriver/support/guards/guard.rb +++ b/rb/lib/selenium/webdriver/support/guards/guard.rb @@ -36,7 +36,7 @@ def initialize(guarded, type, guards = nil) @messages[:unknown] = 'TODO: Investigate why this is failing and file a bug report' @type = type - @reason = @guarded[:reason] + @guarded[:reason] ||= 'No reason given' end def message @@ -45,10 +45,8 @@ def message "Bug Filed: #{@tracker}/#{@reason}" when Symbol @messages[@reason] - when String - @reason else - 'no reason given' + "Guarded by #{guarded};" end case @type From e98cc84f685f4841312a81505b4865755858bf5e Mon Sep 17 00:00:00 2001 From: titusfortner Date: Wed, 5 Jun 2024 18:03:05 -0500 Subject: [PATCH 18/21] [rb] fix the guard tests --- .../selenium/webdriver/support/guards/guard.rb | 13 +++++++------ rb/spec/unit/selenium/webdriver/guard_spec.rb | 17 +++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/rb/lib/selenium/webdriver/support/guards/guard.rb b/rb/lib/selenium/webdriver/support/guards/guard.rb index 6c86483dad555..2bd46fb4494d1 100644 --- a/rb/lib/selenium/webdriver/support/guards/guard.rb +++ b/rb/lib/selenium/webdriver/support/guards/guard.rb @@ -27,7 +27,7 @@ class Guards # class Guard - attr_reader :guarded, :type, :messages, :reason + attr_reader :guarded, :type, :messages, :reason, :tracker def initialize(guarded, type, guards = nil) @guarded = guarded @@ -36,20 +36,21 @@ def initialize(guarded, type, guards = nil) @messages[:unknown] = 'TODO: Investigate why this is failing and file a bug report' @type = type - @guarded[:reason] ||= 'No reason given' + @reason = @guarded[:reason] || 'No reason given' + @guarded[:reason] = @reason end def message - details = case @reason + details = case reason when Integer - "Bug Filed: #{@tracker}/#{@reason}" + "Bug Filed: #{tracker}/#{reason}" when Symbol - @messages[@reason] + messages[reason] else "Guarded by #{guarded};" end - case @type + case type when :exclude "Test skipped because it breaks test run; #{details}" when :flaky diff --git a/rb/spec/unit/selenium/webdriver/guard_spec.rb b/rb/spec/unit/selenium/webdriver/guard_spec.rb index 08b56ee40ffad..4e24252195b69 100644 --- a/rb/spec/unit/selenium/webdriver/guard_spec.rb +++ b/rb/spec/unit/selenium/webdriver/guard_spec.rb @@ -77,14 +77,14 @@ module Support guards = described_class.new(example) guards.add_condition(:foo, false) - expect(guards.disposition).to eq [:pending, 'Test guarded; no reason given'] + expect(guards.disposition).to eq [:pending, 'Test guarded; Guarded by {:foo=>false, :reason=>"No reason given"};'] end it 'is skipped without provided reason', exclusive: {foo: true} do |example| guards = described_class.new(example) guards.add_condition(:foo, false) - message = 'Test does not apply to this configuration; no reason given' + message = 'Test does not apply to this configuration; Guarded by {:foo=>true, :reason=>"No reason given"};' expect(guards.disposition).to eq [:skip, message] end end @@ -138,7 +138,7 @@ module Support describe '#new' do it 'requires guarded Hash and type' do guard = described_class.new({foo: 7}, :only) - expect(guard.guarded).to eq(foo: 7) + expect(guard.guarded).to eq(foo: 7, reason: 'No reason given') expect(guard.type).to eq :only end @@ -157,7 +157,7 @@ module Support it 'defaults to no reason given' do guard = described_class.new({}, :only) - expect(guard.message).to eq('Test guarded; no reason given') + expect(guard.message).to eq('Test guarded; Guarded by {:reason=>"No reason given"};') end it 'accepts integer' do |example| @@ -170,7 +170,7 @@ module Support it 'accepts String' do guard = described_class.new({reason: 'because'}, :only) - expect(guard.message).to eq('Test guarded; because') + expect(guard.message).to eq('Test guarded; Guarded by {:reason=>"because"};') end it 'accepts Symbol of known message' do @@ -190,19 +190,20 @@ module Support it 'has special message for exclude' do guard = described_class.new({reason: 'because'}, :exclude) - expect(guard.message).to eq('Test skipped because it breaks test run; because') + expect(guard.message).to eq('Test skipped because it breaks test run; Guarded by {:reason=>"because"};') end it 'has special message for flaky' do guard = described_class.new({reason: 'because'}, :flaky) - expect(guard.message).to eq('Test skipped because it is unreliable in this configuration; because') + msg = 'Test skipped because it is unreliable in this configuration; Guarded by {:reason=>"because"};' + expect(guard.message).to eq(msg) end it 'has special message for exclusive' do guard = described_class.new({reason: 'because'}, :exclusive) - expect(guard.message).to eq('Test does not apply to this configuration; because') + expect(guard.message).to eq('Test does not apply to this configuration; Guarded by {:reason=>"because"};') end end end From 5c3d526d06efb201c2038c66f616c13f7208794d Mon Sep 17 00:00:00 2001 From: titusfortner Date: Thu, 6 Jun 2024 07:02:56 -0500 Subject: [PATCH 19/21] [rb] fix linting failures --- rb/spec/unit/selenium/webdriver/guard_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rb/spec/unit/selenium/webdriver/guard_spec.rb b/rb/spec/unit/selenium/webdriver/guard_spec.rb index 4e24252195b69..60a17ac67eff4 100644 --- a/rb/spec/unit/selenium/webdriver/guard_spec.rb +++ b/rb/spec/unit/selenium/webdriver/guard_spec.rb @@ -77,7 +77,8 @@ module Support guards = described_class.new(example) guards.add_condition(:foo, false) - expect(guards.disposition).to eq [:pending, 'Test guarded; Guarded by {:foo=>false, :reason=>"No reason given"};'] + expect(guards.disposition).to eq [:pending, + 'Test guarded; Guarded by {:foo=>false, :reason=>"No reason given"};'] end it 'is skipped without provided reason', exclusive: {foo: true} do |example| From 8e268cfaf3997cde043e8d870e73b42703ff0f1d Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Thu, 6 Jun 2024 07:45:47 -0500 Subject: [PATCH 20/21] [build] Add Bazel bidi flag (#14083) * [build] implement bazel targets for running ruby tests with BiDi * add necessary bidi targets to skipped-tests for rbe --- .skipped-tests | 6 ++++++ rb/spec/tests.bzl | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.skipped-tests b/.skipped-tests index 38374bfabbe59..4764a7b9cbe08 100644 --- a/.skipped-tests +++ b/.skipped-tests @@ -28,3 +28,9 @@ -//rb/spec/integration/selenium/webdriver/firefox:service-firefox-beta -//rb/spec/integration/selenium/webdriver:element-chrome -//rb/spec/integration/selenium/webdriver/firefox:driver-firefox-beta +-//rb/spec/integration/selenium/webdriver/chrome:service-chrome-bidi +-//rb/spec/integration/selenium/webdriver/edge:service-edge-bidi +-//rb/spec/integration/selenium/webdriver/firefox:service-firefox-bidi +-//rb/spec/integration/selenium/webdriver/firefox:service-firefox-beta-bidi +-//rb/spec/integration/selenium/webdriver:element-chrome-bidi +-//rb/spec/integration/selenium/webdriver/firefox:driver-firefox-beta-bidi diff --git a/rb/spec/tests.bzl b/rb/spec/tests.bzl index 9c0f1596de6e9..aee4889e49453 100644 --- a/rb/spec/tests.bzl +++ b/rb/spec/tests.bzl @@ -197,6 +197,21 @@ def rb_integration_test(name, srcs, deps = [], data = [], browsers = BROWSERS.ke target_compatible_with = BROWSERS[browser]["target_compatible_with"], ) + # Generate a test target for bidi browser execution. + rb_test( + name = "{}-{}-bidi".format(name, browser), + size = "large", + srcs = srcs, + args = ["rb/spec/"], + data = BROWSERS[browser]["data"] + data + ["//common/src/web"], + env = BROWSERS[browser]["env"] | {"WEBDRIVER_BIDI": "true"}, + main = "@bundle//bin:rspec", + tags = COMMON_TAGS + BROWSERS[browser]["tags"] + tags + ["{}-bidi".format(browser)], + deps = ["//rb/spec/integration/selenium/webdriver:spec_helper"] + BROWSERS[browser]["deps"] + deps, + visibility = ["//rb:__subpackages__"], + target_compatible_with = BROWSERS[browser]["target_compatible_with"], + ) + def rb_unit_test(name, srcs, deps, data = []): rb_test( name = name, From 3597ecdaf86cba927a7f31d379f2fd8bb098889f Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Thu, 6 Jun 2024 11:04:53 -0500 Subject: [PATCH 21/21] [rb] implement toggle for BiDi and Classic implementations (#14092) [rb] use BiDiBridge subclass when web_socket_url is true --- rb/lib/selenium/webdriver/common/driver.rb | 3 +- rb/lib/selenium/webdriver/remote.rb | 1 + .../selenium/webdriver/remote/bidi_bridge.rb | 44 +++++++++++++++++++ rb/lib/selenium/webdriver/remote/bridge.rb | 10 ++--- .../selenium/webdriver/bidi/script_spec.rb | 5 +-- rb/spec/tests.bzl | 6 ++- 6 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 rb/lib/selenium/webdriver/remote/bidi_bridge.rb diff --git a/rb/lib/selenium/webdriver/common/driver.rb b/rb/lib/selenium/webdriver/common/driver.rb index 41a079017d8ad..643d477ef381d 100644 --- a/rb/lib/selenium/webdriver/common/driver.rb +++ b/rb/lib/selenium/webdriver/common/driver.rb @@ -318,7 +318,8 @@ def ref attr_reader :bridge def create_bridge(caps:, url:, http_client: nil) - Remote::Bridge.new(http_client: http_client, url: url).tap do |bridge| + klass = caps['webSocketUrl'] ? Remote::BiDiBridge : Remote::Bridge + klass.new(http_client: http_client, url: url).tap do |bridge| bridge.create_session(caps) end end diff --git a/rb/lib/selenium/webdriver/remote.rb b/rb/lib/selenium/webdriver/remote.rb index 44baebffb3816..1f73c4b93270d 100644 --- a/rb/lib/selenium/webdriver/remote.rb +++ b/rb/lib/selenium/webdriver/remote.rb @@ -25,6 +25,7 @@ module WebDriver module Remote autoload :Features, 'selenium/webdriver/remote/features' autoload :Bridge, 'selenium/webdriver/remote/bridge' + autoload :BiDiBridge, 'selenium/webdriver/remote/bidi_bridge' autoload :Driver, 'selenium/webdriver/remote/driver' autoload :Response, 'selenium/webdriver/remote/response' autoload :Capabilities, 'selenium/webdriver/remote/capabilities' diff --git a/rb/lib/selenium/webdriver/remote/bidi_bridge.rb b/rb/lib/selenium/webdriver/remote/bidi_bridge.rb new file mode 100644 index 0000000000000..4207577cf14dc --- /dev/null +++ b/rb/lib/selenium/webdriver/remote/bidi_bridge.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +module Selenium + module WebDriver + module Remote + class BiDiBridge < Bridge + attr_reader :bidi + + def create_session(capabilities) + super(capabilities) + socket_url = @capabilities[:web_socket_url] + @bidi = Selenium::WebDriver::BiDi.new(url: socket_url) + end + + def quit + super + ensure + bidi.close + end + + def close + execute(:close_window).tap { |handles| bidi.close if handles.empty? } + end + end # BiDiBridge + end # Remote + end # WebDriver +end # Selenium diff --git a/rb/lib/selenium/webdriver/remote/bridge.rb b/rb/lib/selenium/webdriver/remote/bridge.rb index eb0b748b3702c..3dd8a799c5249 100644 --- a/rb/lib/selenium/webdriver/remote/bridge.rb +++ b/rb/lib/selenium/webdriver/remote/bridge.rb @@ -213,12 +213,10 @@ def quit http.close rescue *QUIT_ERRORS nil - ensure - @bidi&.close end def close - execute(:close_window).tap { |handles| @bidi&.close if handles.empty? } + execute :close_window end def refresh @@ -605,10 +603,8 @@ def user_verified(verified, authenticator_id) end def bidi - msg = 'this operation requires enabling BiDi by setting #web_socket_url to true in options class' - raise(WebDriver::Error::WebDriverError, msg) unless capabilities.web_socket_url - - @bidi ||= Selenium::WebDriver::BiDi.new(url: capabilities[:web_socket_url]) + msg = 'BiDi must be enabled by setting #web_socket_url to true in options class' + raise(WebDriver::Error::WebDriverError, msg) end def command_list diff --git a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb index f32aa39118779..34fef2e314393 100644 --- a/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb +++ b/rb/spec/integration/selenium/webdriver/bidi/script_spec.rb @@ -27,9 +27,8 @@ module WebDriver it 'errors when bidi not enabled' do reset_driver!(web_socket_url: false) do |driver| - expect { - driver.script - }.to raise_error(WebDriver::Error::WebDriverError, /this operation requires enabling BiDi/) + msg = /BiDi must be enabled by setting #web_socket_url to true in options class/ + expect { driver.script }.to raise_error(WebDriver::Error::WebDriverError, msg) end end diff --git a/rb/spec/tests.bzl b/rb/spec/tests.bzl index aee4889e49453..bb1604161659a 100644 --- a/rb/spec/tests.bzl +++ b/rb/spec/tests.bzl @@ -207,7 +207,11 @@ def rb_integration_test(name, srcs, deps = [], data = [], browsers = BROWSERS.ke env = BROWSERS[browser]["env"] | {"WEBDRIVER_BIDI": "true"}, main = "@bundle//bin:rspec", tags = COMMON_TAGS + BROWSERS[browser]["tags"] + tags + ["{}-bidi".format(browser)], - deps = ["//rb/spec/integration/selenium/webdriver:spec_helper"] + BROWSERS[browser]["deps"] + deps, + deps = depset( + ["//rb/spec/integration/selenium/webdriver:spec_helper", "//rb/lib/selenium/webdriver:bidi"] + + BROWSERS[browser]["deps"] + + deps, + ), visibility = ["//rb:__subpackages__"], target_compatible_with = BROWSERS[browser]["target_compatible_with"], )