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)
{
diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs
index 78c76f08ebadb..27583a06c10f2 100644
--- a/dotnet/src/webdriver/WebDriver.cs
+++ b/dotnet/src/webdriver/WebDriver.cs
@@ -43,11 +43,22 @@ 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.
protected WebDriver(ICommandExecutor executor, ICapabilities capabilities)
+ : this(executor, capabilities, true)
+ {
+ }
+
+ ///
+ /// 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. When , the session is started immediately; when , the session must be started manually.
+ 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;
}
}
@@ -88,7 +102,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 +194,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 +598,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 +622,12 @@ protected void StartSession(ICapabilities capabilities)
{
Dictionary matchCapabilities = this.GetCapabilitiesDictionary(capabilities);
- List