From 2c037640627f8d058336f0e54ec64b0b066b3aa5 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:26:09 +0300 Subject: [PATCH 1/4] Start session async --- dotnet/src/webdriver/WebDriver.cs | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 78c76f08ebadb..32991250039a6 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -88,7 +88,7 @@ protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) /// /// Gets the that the driver session was created with, which may be different from those requested. /// - public ICapabilities Capabilities { get; private set; } + public ICapabilities Capabilities { get; private set; } = null!; /// /// Gets or sets the URL the browser is currently displaying. @@ -180,7 +180,7 @@ public ReadOnlyCollection WindowHandles /// /// Gets the for the current session of this driver. /// - public SessionId SessionId { get; private set; } + public SessionId SessionId { get; private set; } = null!; /// /// Gets or sets the responsible for detecting @@ -584,11 +584,20 @@ protected internal virtual async Task ExecuteAsync(string driverComman /// Starts a session with the driver /// /// Capabilities of the browser - [MemberNotNull(nameof(SessionId))] - [MemberNotNull(nameof(Capabilities))] protected void StartSession(ICapabilities capabilities) { - Dictionary parameters = new Dictionary(); + Task.Run(() => this.StartSessionAsync(capabilities)).GetAwaiter().GetResult(); + } + + /// + /// Asynchronously starts a session with the driver. + /// + /// Capabilities of the browser. + /// A task that represents the asynchronous operation. + /// If the session cannot be started or the response is invalid. + protected async Task StartSessionAsync(ICapabilities capabilities) + { + Dictionary parameters = []; // If the object passed into the RemoteWebDriver constructor is a // RemoteSessionSettings object, it is expected that all intermediate @@ -599,11 +608,12 @@ protected void StartSession(ICapabilities capabilities) { Dictionary matchCapabilities = this.GetCapabilitiesDictionary(capabilities); - List firstMatchCapabilitiesList = new List(); - firstMatchCapabilitiesList.Add(matchCapabilities); + List firstMatchCapabilitiesList = [matchCapabilities]; - Dictionary specCompliantCapabilitiesDictionary = new Dictionary(); - specCompliantCapabilitiesDictionary["firstMatch"] = firstMatchCapabilitiesList; + Dictionary specCompliantCapabilitiesDictionary = new() + { + ["firstMatch"] = firstMatchCapabilitiesList + }; parameters.Add("capabilities", specCompliantCapabilitiesDictionary); } @@ -612,9 +622,10 @@ protected void StartSession(ICapabilities capabilities) parameters.Add("capabilities", remoteSettings.ToDictionary()); } - Response response = this.Execute(DriverCommand.NewSession, parameters); + Response response = await this.ExecuteAsync(DriverCommand.NewSession, parameters).ConfigureAwait(false); response.EnsureValueIsNotNull(); + if (response.Value is not Dictionary rawCapabilities) { string errorMessage = string.Format(CultureInfo.InvariantCulture, "The new session command returned a value ('{0}') that is not a valid JSON object.", response.Value); From 863eec43791c9fc9d7ac5978e94a6a418fe1a23f Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:37:29 +0300 Subject: [PATCH 2/4] Flag to autostart session --- dotnet/src/webdriver/WebDriver.cs | 34 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 32991250039a6..f94a578306f4b 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -48,6 +48,17 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds /// The object used to execute commands. /// The object used to configure the driver session. protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) + : this(executor, capabilities, true) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The object used to execute commands. + /// The object used to configure the driver session. + /// Whether to automatically start the driver session. + protected WebDriver(ICommandExecutor executor, ICapabilities capabilities, bool autoStartSession) { this.CommandExecutor = executor; this.elementFactory = new WebElementFactory(this); @@ -61,22 +72,25 @@ protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) this.RegisterDriverCommand(DriverCommand.GetLog, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/log"), true); } - try - { - this.StartSession(capabilities); - } - catch (Exception) + if (autoStartSession) { try { - // Failed to start driver session, disposing of driver - this.Dispose(); + this.StartSession(capabilities); } - catch + catch (Exception) { - // Ignore the clean-up exception. We'll propagate the original failure. + try + { + // Failed to start driver session, disposing of driver + this.Dispose(); + } + catch + { + // Ignore the clean-up exception. We'll propagate the original failure. + } + throw; } - throw; } } From c4b6bf9d4b310dd0bfdeae4993777842bd22fb7b Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Wed, 18 Feb 2026 23:44:24 +0300 Subject: [PATCH 3/4] Docs --- dotnet/src/webdriver/WebDriver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index f94a578306f4b..27583a06c10f2 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -43,7 +43,7 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds private readonly List registeredCommands = new List(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class and automatically starts the driver session. /// /// The object used to execute commands. /// The object used to configure the driver session. @@ -53,11 +53,11 @@ protected WebDriver(ICommandExecutor executor, ICapabilities capabilities) } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with optional automatic session initialization. /// /// The object used to execute commands. /// The object used to configure the driver session. - /// Whether to automatically start the driver session. + /// Whether to automatically start the driver session. When , the session is started immediately; when , the session must be started manually. protected WebDriver(ICommandExecutor executor, ICapabilities capabilities, bool autoStartSession) { this.CommandExecutor = executor; From 6a74f0305c83f37c1744cd92e478322e3dc44c14 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Thu, 19 Feb 2026 00:24:27 +0300 Subject: [PATCH 4/4] Start Async! --- dotnet/src/webdriver/Chrome/ChromeDriver.cs | 55 +++++++++++++++++++ .../src/webdriver/Chromium/ChromiumDriver.cs | 24 +++++++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/Chrome/ChromeDriver.cs b/dotnet/src/webdriver/Chrome/ChromeDriver.cs index 80108a05187ff..ae8619f11d75d 100644 --- a/dotnet/src/webdriver/Chrome/ChromeDriver.cs +++ b/dotnet/src/webdriver/Chrome/ChromeDriver.cs @@ -157,6 +157,61 @@ public ChromeDriver(ChromeDriverService service, ChromeOptions options, TimeSpan this.AddCustomChromeCommands(); } + /// + /// Initializes a new instance of the class using the specified command executor and options. + /// + /// The to use for executing commands. + /// The to use for this driver. + /// Whether to automatically start the session. + protected ChromeDriver(ICommandExecutor commandExecutor, ChromeOptions options, bool autoStartSession) + : base(commandExecutor, options, autoStartSession) + { + if (autoStartSession) + { + this.AddCustomChromeCommands(); + } + } + + /// + /// Asynchronously creates and starts a new instance of the class with default options. + /// + /// A task that represents the asynchronous operation. The task result contains the initialized . + public static Task StartAsync() + { + return StartAsync(new ChromeOptions()); + } + + /// + /// Asynchronously creates and starts a new instance of the class using the specified options. + /// + /// The to be used with the Chrome driver. + /// A task that represents the asynchronous operation. The task result contains the initialized . + /// If is . + public static async Task StartAsync(ChromeOptions options) + { + if (options is null) + { + throw new ArgumentNullException(nameof(options), "Chrome options must not be null"); + } + + ChromeDriverService service = ChromeDriverService.CreateDefaultService(); + ICommandExecutor executor = await GenerateDriverServiceCommandExecutorAsync(service, options, DefaultCommandTimeout).ConfigureAwait(false); + + ChromeDriver driver = new(executor, options, autoStartSession: false); + driver.AddCustomChromeCommands(); + + try + { + await driver.StartSessionAsync(options.ToCapabilities()).ConfigureAwait(false); + return driver; + } + catch + { + driver.Dispose(); + throw; + } + } + /// /// Gets a read-only dictionary of the custom WebDriver commands defined for ChromeDriver. /// The keys of the dictionary are the names assigned to the command; the values are the diff --git a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs index 8857f5229b254..e9be48602a7e4 100644 --- a/dotnet/src/webdriver/Chromium/ChromiumDriver.cs +++ b/dotnet/src/webdriver/Chromium/ChromiumDriver.cs @@ -132,6 +132,20 @@ protected ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, this.optionsCapabilityName = options.CapabilityName ?? throw new ArgumentException("No chromium options capability name specified", nameof(options)); } + /// + /// Initializes a new instance of the class using the specified command executor and options. + /// + /// The to use for executing commands. + /// The to be used with the ChromiumDriver. + /// Whether to automatically start the session. + /// If is . + /// If the Chromium options capability name is . + protected ChromiumDriver(ICommandExecutor commandExecutor, ChromiumOptions options, bool autoStartSession) + : base(commandExecutor, ConvertOptionsToCapabilities(options), autoStartSession) + { + this.optionsCapabilityName = options.CapabilityName ?? throw new ArgumentException("No chromium options capability name specified", nameof(options)); + } + /// /// Gets the dictionary of custom Chromium commands registered with the driver. /// @@ -144,7 +158,15 @@ await GenerateDriverServiceCommandExecutorAsync(service, options, commandTimeout .GetAwaiter().GetResult(); } - private static async Task GenerateDriverServiceCommandExecutorAsync(DriverService service, DriverOptions options, TimeSpan commandTimeout) + /// + /// Asynchronously generates a driver service command executor. + /// + /// The to use. + /// The to be used with the driver. + /// The maximum amount of time to wait for each command. + /// A task that represents the asynchronous operation. The task result contains the . + /// If or are . + protected static async Task GenerateDriverServiceCommandExecutorAsync(DriverService service, DriverOptions options, TimeSpan commandTimeout) { if (service is null) {