From d13de44d752be7f2714189bc6220e1d9186e371c Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 1 Dec 2024 21:15:51 -0500 Subject: [PATCH 01/13] [dotnet] Add nullability annotations to `SessionId` --- dotnet/src/webdriver/SessionId.cs | 69 +++++++++++++++++++++++------- dotnet/src/webdriver/WebDriver.cs | 2 +- dotnet/test/common/CommandTests.cs | 2 +- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index 7cf90da7f69d1..6197693ace724 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -17,29 +17,53 @@ // under the License. // +using System; +using System.Diagnostics.CodeAnalysis; + +#nullable enable + namespace OpenQA.Selenium { /// /// Provides a mechanism for maintaining a session for a test /// - public class SessionId + public class SessionId : IEquatable, IEquatable { - private string sessionOpaqueKey; + private readonly string? sessionOpaqueKey; /// /// Initializes a new instance of the class /// /// Key for the session in use - public SessionId(string opaqueKey) + [Obsolete("Call SessionId.Create")] + public SessionId(string? opaqueKey) { this.sessionOpaqueKey = opaqueKey; } + /// + /// Creates a new instance, or if is . + /// + /// The session ID value. + /// A , or . + [return: NotNullIfNotNull(nameof(opaqueKey))] + public static SessionId? Create(string? opaqueKey) + { + if (opaqueKey is null) + { + return null; + } + +#pragma warning disable CS0618 // Type or member is obsolete + return new SessionId(opaqueKey); +#pragma warning restore CS0618 // Type or member is obsolete + } + /// /// Get the value of the key /// /// The key in use - public override string ToString() + public override string? ToString() { return this.sessionOpaqueKey; } @@ -50,24 +74,37 @@ public override string ToString() /// The hash code of the key public override int GetHashCode() { - return this.sessionOpaqueKey.GetHashCode(); + return this.sessionOpaqueKey?.GetHashCode() ?? 0; } /// - /// Compares two Sessions + /// Indicates whether the current session ID value is the same as . /// - /// Session to compare - /// True if they are equal or False if they are not - public override bool Equals(object obj) + /// The session to compare to. + /// if the values are equal; otherwise, . + public override bool Equals(object? obj) { - bool objectsAreEqual = false; - SessionId other = obj as SessionId; - if (other != null) - { - objectsAreEqual = this.sessionOpaqueKey.Equals(other.sessionOpaqueKey); - } + return Equals(obj as SessionId); + } + + /// + /// Indicates whether the current session ID value is the same as . + /// + /// A value to compare with this session ID. + /// if the current session ID is equal to the parameter; otherwise, . + public bool Equals(SessionId? other) + { + return other is not null && Equals(other.sessionOpaqueKey); + } - return objectsAreEqual; + /// + /// Indicates whether the current session ID value is the same as . + /// + /// A value to compare with this session ID> + /// if the current session ID is equal to the parameter; otherwise, . + public bool Equals(string? other) + { + return string.Equals(this.sessionOpaqueKey, other, StringComparison.Ordinal); } } } diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 9bcca8b28f8b3..70c222805806b 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -679,7 +679,7 @@ protected void StartSession(ICapabilities capabilities) ReturnedCapabilities returnedCapabilities = new ReturnedCapabilities(rawCapabilities); this.capabilities = returnedCapabilities; - this.sessionId = new SessionId(response.SessionId); + this.sessionId = SessionId.Create(response.SessionId); } /// diff --git a/dotnet/test/common/CommandTests.cs b/dotnet/test/common/CommandTests.cs index f0480c034efd3..f9570ec76e5e7 100644 --- a/dotnet/test/common/CommandTests.cs +++ b/dotnet/test/common/CommandTests.cs @@ -33,7 +33,7 @@ public void CommandSerializesAnonymousType() ["arg"] = new { param1 = true, param2 = false }, }; - var command = new Command(new SessionId("session"), "test command", parameters); + var command = new Command(SessionId.Create("session"), "test command", parameters); Assert.That(command.ParametersAsJsonString, Is.EqualTo("""{"arg":{"param1":true,"param2":false}}""")); } From 1fd78635fd4b7b5c746f15e040ce00dc56242520 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 1 Dec 2024 21:27:08 -0500 Subject: [PATCH 02/13] Add `==` and `!=` operators to `SessionId` --- dotnet/src/webdriver/SessionId.cs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index 6197693ace724..e25a09fafb70f 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -106,5 +106,32 @@ public bool Equals(string? other) { return string.Equals(this.sessionOpaqueKey, other, StringComparison.Ordinal); } + + /// + /// Compares the two values to determine equality. + /// + /// The value to compare with . + /// The value to compare with . + /// if is equal to ; otherwise, . + public static bool operator ==(SessionId? left, SessionId? right) + { + if (left is null) + { + return right is null; + } + + return left.Equals(right); + } + + /// + /// Compares the two values to determine inequality. + /// + /// The value to compare with . + /// The value to compare with . + /// if is not equal to ; otherwise, . + public static bool operator !=(SessionId? left, SessionId? right) + { + return !(left == right); + } } } From eb84ccd67f1b88137c4a08a34773dd1637e57f10 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Sun, 1 Dec 2024 23:13:42 -0500 Subject: [PATCH 03/13] Align `SessionId` value to ordinal string rules --- dotnet/src/webdriver/SessionId.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index e25a09fafb70f..a03ed93791c86 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -74,7 +74,12 @@ public SessionId(string? opaqueKey) /// The hash code of the key public override int GetHashCode() { - return this.sessionOpaqueKey?.GetHashCode() ?? 0; + if (this.sessionOpaqueKey is { } key) + { + return StringComparer.Ordinal.GetHashCode(key); + } + + return 0; } /// From 0010255268b77e2d88e9dc04a95f949bd46b2143 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 6 Dec 2024 13:55:10 -0500 Subject: [PATCH 04/13] Throw on `new SessionId(null)` --- dotnet/src/webdriver/SessionId.cs | 33 ++++-------------------------- dotnet/src/webdriver/WebDriver.cs | 2 +- dotnet/test/common/CommandTests.cs | 2 +- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index a03ed93791c86..6c5e3c99d723e 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -18,7 +18,6 @@ // using System; -using System.Diagnostics.CodeAnalysis; #nullable enable @@ -29,34 +28,15 @@ namespace OpenQA.Selenium /// public class SessionId : IEquatable, IEquatable { - private readonly string? sessionOpaqueKey; + private readonly string sessionOpaqueKey; /// /// Initializes a new instance of the class /// /// Key for the session in use - [Obsolete("Call SessionId.Create")] - public SessionId(string? opaqueKey) + public SessionId(string opaqueKey) { - this.sessionOpaqueKey = opaqueKey; - } - - /// - /// Creates a new instance, or if is . - /// - /// The session ID value. - /// A , or . - [return: NotNullIfNotNull(nameof(opaqueKey))] - public static SessionId? Create(string? opaqueKey) - { - if (opaqueKey is null) - { - return null; - } - -#pragma warning disable CS0618 // Type or member is obsolete - return new SessionId(opaqueKey); -#pragma warning restore CS0618 // Type or member is obsolete + this.sessionOpaqueKey = opaqueKey ?? throw new ArgumentNullException(nameof(opaqueKey)); } /// @@ -74,12 +54,7 @@ public SessionId(string? opaqueKey) /// The hash code of the key public override int GetHashCode() { - if (this.sessionOpaqueKey is { } key) - { - return StringComparer.Ordinal.GetHashCode(key); - } - - return 0; + return StringComparer.Ordinal.GetHashCode(this.sessionOpaqueKey); } /// diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 70c222805806b..3a062efa5d9da 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -679,7 +679,7 @@ protected void StartSession(ICapabilities capabilities) ReturnedCapabilities returnedCapabilities = new ReturnedCapabilities(rawCapabilities); this.capabilities = returnedCapabilities; - this.sessionId = SessionId.Create(response.SessionId); + this.sessionId = response.SessionId is null ? null : new SessionId(response.SessionId); } /// diff --git a/dotnet/test/common/CommandTests.cs b/dotnet/test/common/CommandTests.cs index f9570ec76e5e7..f0480c034efd3 100644 --- a/dotnet/test/common/CommandTests.cs +++ b/dotnet/test/common/CommandTests.cs @@ -33,7 +33,7 @@ public void CommandSerializesAnonymousType() ["arg"] = new { param1 = true, param2 = false }, }; - var command = new Command(SessionId.Create("session"), "test command", parameters); + var command = new Command(new SessionId("session"), "test command", parameters); Assert.That(command.ParametersAsJsonString, Is.EqualTo("""{"arg":{"param1":true,"param2":false}}""")); } From 9544bad748875e22339a382b1a6e93440a57d3a0 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 6 Dec 2024 13:57:48 -0500 Subject: [PATCH 05/13] Add argument null exception XMLDoc --- dotnet/src/webdriver/SessionId.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index 6c5e3c99d723e..51080ff22cb8a 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -34,6 +34,7 @@ public class SessionId : IEquatable, IEquatable /// Initializes a new instance of the class /// /// Key for the session in use + /// If is . public SessionId(string opaqueKey) { this.sessionOpaqueKey = opaqueKey ?? throw new ArgumentNullException(nameof(opaqueKey)); From 955740d351ba6263568156d2214f68f32f407bb0 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 6 Dec 2024 14:06:18 -0500 Subject: [PATCH 06/13] `SessionId.ToString()` cannot return `null` --- dotnet/src/webdriver/SessionId.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index 51080ff22cb8a..e3bc1483acf57 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -44,7 +44,7 @@ public SessionId(string opaqueKey) /// Get the value of the key /// /// The key in use - public override string? ToString() + public override string ToString() { return this.sessionOpaqueKey; } From a05f5874ee012a603de5a27926f3b165134b9836 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 6 Dec 2024 15:50:41 -0500 Subject: [PATCH 07/13] Simplify `SessionId` --- dotnet/src/webdriver/SessionId.cs | 51 ++----------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index e3bc1483acf57..426b6adf47051 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -26,7 +26,7 @@ namespace OpenQA.Selenium /// /// Provides a mechanism for maintaining a session for a test /// - public class SessionId : IEquatable, IEquatable + public class SessionId { private readonly string sessionOpaqueKey; @@ -65,54 +65,7 @@ public override int GetHashCode() /// if the values are equal; otherwise, . public override bool Equals(object? obj) { - return Equals(obj as SessionId); - } - - /// - /// Indicates whether the current session ID value is the same as . - /// - /// A value to compare with this session ID. - /// if the current session ID is equal to the parameter; otherwise, . - public bool Equals(SessionId? other) - { - return other is not null && Equals(other.sessionOpaqueKey); - } - - /// - /// Indicates whether the current session ID value is the same as . - /// - /// A value to compare with this session ID> - /// if the current session ID is equal to the parameter; otherwise, . - public bool Equals(string? other) - { - return string.Equals(this.sessionOpaqueKey, other, StringComparison.Ordinal); - } - - /// - /// Compares the two values to determine equality. - /// - /// The value to compare with . - /// The value to compare with . - /// if is equal to ; otherwise, . - public static bool operator ==(SessionId? left, SessionId? right) - { - if (left is null) - { - return right is null; - } - - return left.Equals(right); - } - - /// - /// Compares the two values to determine inequality. - /// - /// The value to compare with . - /// The value to compare with . - /// if is not equal to ; otherwise, . - public static bool operator !=(SessionId? left, SessionId? right) - { - return !(left == right); + return obj is SessionId otherSession && this.sessionOpaqueKey.Equals(otherSession.sessionOpaqueKey, StringComparison.Ordinal); } } } From 3738de898e4599e84f682ffe140dcdb15d784f50 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Fri, 6 Dec 2024 16:33:52 -0500 Subject: [PATCH 08/13] Remove unnecessary code --- dotnet/src/webdriver/SessionId.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/webdriver/SessionId.cs b/dotnet/src/webdriver/SessionId.cs index 426b6adf47051..8f605d4731775 100644 --- a/dotnet/src/webdriver/SessionId.cs +++ b/dotnet/src/webdriver/SessionId.cs @@ -55,7 +55,7 @@ public override string ToString() /// The hash code of the key public override int GetHashCode() { - return StringComparer.Ordinal.GetHashCode(this.sessionOpaqueKey); + return this.sessionOpaqueKey.GetHashCode(); } /// @@ -65,7 +65,7 @@ public override int GetHashCode() /// if the values are equal; otherwise, . public override bool Equals(object? obj) { - return obj is SessionId otherSession && this.sessionOpaqueKey.Equals(otherSession.sessionOpaqueKey, StringComparison.Ordinal); + return obj is SessionId otherSession && this.sessionOpaqueKey.Equals(otherSession.sessionOpaqueKey); } } } From a9cc9d6cc90e4e3a603de604e5c503b5c4381ab4 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 11 Dec 2024 19:19:20 -0500 Subject: [PATCH 09/13] Throw on missing sessionId from StartSession --- dotnet/src/webdriver/WebDriver.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 3a062efa5d9da..50483d1446368 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -679,7 +679,9 @@ protected void StartSession(ICapabilities capabilities) ReturnedCapabilities returnedCapabilities = new ReturnedCapabilities(rawCapabilities); this.capabilities = returnedCapabilities; - this.sessionId = response.SessionId is null ? null : new SessionId(response.SessionId); + + string sessionId = response.SessionId ?? throw new WebDriverException("StartSession did not return a sessionId"); + this.sessionId = new SessionId(sessionId); } /// From 7810cf31771dd26289fd04e053eda6e65ea33f17 Mon Sep 17 00:00:00 2001 From: Michael Render Date: Wed, 11 Dec 2024 20:24:10 -0500 Subject: [PATCH 10/13] Handle nullability for session Id in `WebDriver` --- dotnet/src/webdriver/WebDriver.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 50483d1446368..ae20f11e0c57e 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -24,6 +24,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Threading.Tasks; @@ -44,7 +45,7 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds private IFileDetector fileDetector = new DefaultFileDetector(); private NetworkManager network; private WebElementFactory elementFactory; - private SessionId sessionId; + private List registeredCommands = new List(); /// @@ -191,10 +192,7 @@ public bool IsActionExecutor /// /// Gets the for the current session of this driver. /// - public SessionId SessionId - { - get { return this.sessionId; } - } + public SessionId SessionId { get; private set; } /// /// Gets or sets the responsible for detecting @@ -612,7 +610,7 @@ protected virtual Response Execute(string driverCommandToExecute, /// A containing information about the success or failure of the command and any data returned by the command. protected virtual async Task ExecuteAsync(string driverCommandToExecute, Dictionary parameters) { - Command commandToExecute = new Command(this.sessionId, driverCommandToExecute, parameters); + Command commandToExecute = new Command(this.SessionId, driverCommandToExecute, parameters); Response commandResponse; @@ -641,6 +639,7 @@ protected virtual async Task ExecuteAsync(string driverCommandToExecut /// Starts a session with the driver /// /// Capabilities of the browser + [MemberNotNull(nameof(SessionId))] protected void StartSession(ICapabilities capabilities) { Dictionary parameters = new Dictionary(); @@ -681,7 +680,7 @@ protected void StartSession(ICapabilities capabilities) this.capabilities = returnedCapabilities; string sessionId = response.SessionId ?? throw new WebDriverException("StartSession did not return a sessionId"); - this.sessionId = new SessionId(sessionId); + this.SessionId = new SessionId(sessionId); } /// @@ -725,7 +724,7 @@ protected virtual void Dispose(bool disposing) { try { - if (this.sessionId is not null) + if (this.SessionId is not null) { this.Execute(DriverCommand.Quit, null); } @@ -741,7 +740,7 @@ protected virtual void Dispose(bool disposing) } finally { - this.sessionId = null; + this.SessionId = null; } this.executor.Dispose(); } From b0ff5b44ad6cbefa8d1e94cd1996143cd6538fbf Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 20 Dec 2024 02:06:55 +0300 Subject: [PATCH 11/13] The remote end did not respond with ID of a session when it was required --- dotnet/src/webdriver/WebDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index ae20f11e0c57e..c63e31946e455 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -679,7 +679,7 @@ protected void StartSession(ICapabilities capabilities) ReturnedCapabilities returnedCapabilities = new ReturnedCapabilities(rawCapabilities); this.capabilities = returnedCapabilities; - string sessionId = response.SessionId ?? throw new WebDriverException("StartSession did not return a sessionId"); + string sessionId = response.SessionId ?? throw new WebDriverException("The remote end did not respond with ID of a session when it was required."); this.SessionId = new SessionId(sessionId); } From 3888f98820c31f4e8568e33cf0bd5994b1edaa34 Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 20 Dec 2024 02:08:00 +0300 Subject: [PATCH 12/13] Don't apply this for property --- dotnet/src/webdriver/WebDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index c63e31946e455..8b9c12c56ce07 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -610,7 +610,7 @@ protected virtual Response Execute(string driverCommandToExecute, /// A containing information about the success or failure of the command and any data returned by the command. protected virtual async Task ExecuteAsync(string driverCommandToExecute, Dictionary parameters) { - Command commandToExecute = new Command(this.SessionId, driverCommandToExecute, parameters); + Command commandToExecute = new Command(SessionId, driverCommandToExecute, parameters); Response commandResponse; From c4b0b068471a93b7bb4103fc6d7de51a6b84c19f Mon Sep 17 00:00:00 2001 From: Nikolay Borisenko <22616990+nvborisenko@users.noreply.github.com> Date: Fri, 20 Dec 2024 02:16:42 +0300 Subject: [PATCH 13/13] Even better missing session id exception --- dotnet/src/webdriver/WebDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 8b9c12c56ce07..672aa83b5b6e6 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -679,7 +679,7 @@ protected void StartSession(ICapabilities capabilities) ReturnedCapabilities returnedCapabilities = new ReturnedCapabilities(rawCapabilities); this.capabilities = returnedCapabilities; - string sessionId = response.SessionId ?? throw new WebDriverException("The remote end did not respond with ID of a session when it was required."); + string sessionId = response.SessionId ?? throw new WebDriverException($"The remote end did not respond with ID of a session when it was required. {response.Value}"); this.SessionId = new SessionId(sessionId); }