diff --git a/dotnet/src/support/Events/EventFiringWebDriver.cs b/dotnet/src/support/Events/EventFiringWebDriver.cs
index ac428cf2830b5..d866404c6ccdf 100644
--- a/dotnet/src/support/Events/EventFiringWebDriver.cs
+++ b/dotnet/src/support/Events/EventFiringWebDriver.cs
@@ -20,6 +20,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
+using System.Threading.Tasks;
namespace OpenQA.Selenium.Support.Events
{
@@ -332,6 +333,17 @@ public INavigation Navigate()
return new EventFiringNavigation(this);
}
+ ///
+ /// Provides access to WebDriverBiDi events in script and log domains.
+ ///
+ /// An object allowing the user to access
+ /// WebDriverBiDi events.
+ [Obsolete("This method is in beta and may change in future releases.")]
+ public IScript Script()
+ {
+ throw new NotImplementedException();
+ }
+
///
/// Instructs the driver to send future commands to a different frame or window.
///
@@ -845,12 +857,21 @@ public EventFiringNavigation(EventFiringWebDriver driver)
/// Move the browser back
///
public void Back()
+ {
+ Task.Run(this.BackAsync).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Move the browser back as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation
+ public async Task BackAsync()
{
try
{
WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver);
this.parentDriver.OnNavigatingBack(e);
- this.wrappedNavigation.Back();
+ await this.wrappedNavigation.BackAsync().ConfigureAwait(false);
this.parentDriver.OnNavigatedBack(e);
}
catch (Exception ex)
@@ -861,15 +882,24 @@ public void Back()
}
///
- /// Move the browser forward
+ /// Move a single "item" forward in the browser's history.
///
public void Forward()
+ {
+ Task.Run(this.ForwardAsync).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Move a single "item" forward in the browser's history as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ public async Task ForwardAsync()
{
try
{
WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver);
this.parentDriver.OnNavigatingForward(e);
- this.wrappedNavigation.Forward();
+ await this.wrappedNavigation.ForwardAsync().ConfigureAwait(false);
this.parentDriver.OnNavigatedForward(e);
}
catch (Exception ex)
@@ -880,16 +910,31 @@ public void Forward()
}
///
- /// Navigate to a url for your test
+ /// Navigate to a url.
///
/// String of where you want the browser to go to
public void GoToUrl(string url)
{
+ Task.Run(() => this.GoToUrlAsync(url)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Navigate to a url as an asynchronous task.
+ ///
+ /// String of where you want the browser to go.
+ /// A task object representing the asynchronous operation.
+ public async Task GoToUrlAsync(string url)
+ {
+ if (url == null)
+ {
+ throw new ArgumentNullException(nameof(url), "url cannot be null");
+ }
+
try
{
WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver, url);
this.parentDriver.OnNavigating(e);
- this.wrappedNavigation.GoToUrl(url);
+ await this.wrappedNavigation.GoToUrlAsync(url).ConfigureAwait(false);
this.parentDriver.OnNavigated(e);
}
catch (Exception ex)
@@ -900,38 +945,46 @@ public void GoToUrl(string url)
}
///
- /// Navigate to a url for your test
+ /// Navigate to a url.
///
/// Uri object of where you want the browser to go to
public void GoToUrl(Uri url)
+ {
+ Task.Run(() => this.GoToUrlAsync(url)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Navigate to a url as an asynchronous task.
+ ///
+ /// Uri object of where you want the browser to go.
+ /// A task object representing the asynchronous operation.
+ public async Task GoToUrlAsync(Uri url)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url), "url cannot be null");
}
- try
- {
- WebDriverNavigationEventArgs e = new WebDriverNavigationEventArgs(this.parentDriver, url.ToString());
- this.parentDriver.OnNavigating(e);
- this.wrappedNavigation.GoToUrl(url);
- this.parentDriver.OnNavigated(e);
- }
- catch (Exception ex)
- {
- this.parentDriver.OnException(new WebDriverExceptionEventArgs(this.parentDriver, ex));
- throw;
- }
+ await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false);
}
///
- /// Refresh the browser
+ /// Reload the current page.
///
public void Refresh()
+ {
+ Task.Run(this.RefreshAsync).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Reload the current page as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ public async Task RefreshAsync()
{
try
{
- this.wrappedNavigation.Refresh();
+ await this.wrappedNavigation.RefreshAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
diff --git a/dotnet/src/webdriver/ICommandExecutor.cs b/dotnet/src/webdriver/ICommandExecutor.cs
index 68cfe200391f4..3bcfb520166d3 100644
--- a/dotnet/src/webdriver/ICommandExecutor.cs
+++ b/dotnet/src/webdriver/ICommandExecutor.cs
@@ -17,6 +17,7 @@
//
using System;
+using System.Threading.Tasks;
namespace OpenQA.Selenium
{
@@ -39,5 +40,13 @@ public interface ICommandExecutor : IDisposable
/// The command you wish to execute
/// A response from the browser
Response Execute(Command commandToExecute);
+
+
+ ///
+ /// Executes a command as an asynchronous task.
+ ///
+ /// The command you wish to execute
+ /// A task object representing the asynchronous operation
+ Task ExecuteAsync(Command commandToExecute);
}
}
diff --git a/dotnet/src/webdriver/INavigation.cs b/dotnet/src/webdriver/INavigation.cs
index bff75d4743e6b..a55b4dfda12bf 100644
--- a/dotnet/src/webdriver/INavigation.cs
+++ b/dotnet/src/webdriver/INavigation.cs
@@ -17,6 +17,7 @@
//
using System;
+using System.Threading.Tasks;
namespace OpenQA.Selenium
{
@@ -31,12 +32,24 @@ public interface INavigation
///
void Back();
+ ///
+ /// Move back a single entry in the browser's history as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ Task BackAsync();
+
///
/// Move a single "item" forward in the browser's history.
///
/// Does nothing if we are on the latest page viewed.
void Forward();
+ ///
+ /// Move a single "item" forward in the browser's history as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ Task ForwardAsync();
+
///
/// Load a new web page in the current browser window.
///
@@ -52,6 +65,13 @@ public interface INavigation
///
void GoToUrl(string url);
+ ///
+ /// Navigate to a url as an asynchronous task.
+ ///
+ /// String of where you want the browser to go.
+ /// A task object representing the asynchronous operation.
+ Task GoToUrlAsync(string url);
+
///
/// Load a new web page in the current browser window.
///
@@ -67,9 +87,22 @@ public interface INavigation
///
void GoToUrl(Uri url);
+ ///
+ /// Navigate to a url as an asynchronous task.
+ ///
+ /// Uri object of where you want the browser to go.
+ /// A task object representing the asynchronous operation.
+ Task GoToUrlAsync(Uri url);
+
///
/// Refreshes the current page.
///
void Refresh();
+
+ ///
+ /// Reload the current page as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ Task RefreshAsync();
}
}
diff --git a/dotnet/src/webdriver/IScript.cs b/dotnet/src/webdriver/IScript.cs
new file mode 100644
index 0000000000000..a3b77aa23ef2f
--- /dev/null
+++ b/dotnet/src/webdriver/IScript.cs
@@ -0,0 +1,57 @@
+//
+// 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.
+//
+
+using System;
+using System.Threading.Tasks;
+using WebDriverBiDi.Log;
+
+namespace OpenQA.Selenium
+{
+ ///
+ /// Defines an interface providing access to WebDriverBiDi events in script and log domains.
+ ///
+ [Obsolete("This class is in beta and may change in future releases.")]
+ public interface IScript
+ {
+ ///
+ /// Add and remove handlers for console messages.
+ ///
+ [Obsolete("This event is in beta and may change in future releases.")]
+ event EventHandler ConsoleMessageHandler;
+
+ ///
+ /// Add and remove handlers for console messages.
+ ///
+ [Obsolete("This event is in beta and may change in future releases.")]
+ event EventHandler JavaScriptErrorHandler;
+
+ ///
+ /// Asynchronously starts monitoring for console and JavaScript log entries.
+ ///
+ /// A task object representing the asynchronous operation.
+ [Obsolete("This task is in beta and may change in future releases.")]
+ Task StartMonitoringLogEntries();
+
+ ///
+ /// Asynchronously stops monitoring for console and JavaScript log entries.
+ ///
+ /// A task object representing the asynchronous operation.
+ [Obsolete("This task is in beta and may change in future releases.")]
+ Task StopMonitoringLogEntries();
+ }
+}
diff --git a/dotnet/src/webdriver/IWebDriver.cs b/dotnet/src/webdriver/IWebDriver.cs
index 1f8d85a912691..f68cd72d1405a 100644
--- a/dotnet/src/webdriver/IWebDriver.cs
+++ b/dotnet/src/webdriver/IWebDriver.cs
@@ -115,6 +115,14 @@ public interface IWebDriver : ISearchContext, IDisposable
/// the browser's history and to navigate to a given URL.
INavigation Navigate();
+ ///
+ /// Provides access to WebDriverBiDi events for Script and Logs.
+ ///
+ /// An object allowing the user to access
+ /// WebDriverBiDi events.
+ [Obsolete("This method is in beta and may change in future releases.")]
+ IScript Script();
+
///
/// Instructs the driver to send future commands to a different frame or window.
///
diff --git a/dotnet/src/webdriver/Navigator.cs b/dotnet/src/webdriver/Navigator.cs
index f8f6047e91f5a..f61a5f4983f9b 100644
--- a/dotnet/src/webdriver/Navigator.cs
+++ b/dotnet/src/webdriver/Navigator.cs
@@ -18,6 +18,8 @@
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
+using WebDriverBiDi.BrowsingContext;
namespace OpenQA.Selenium
{
@@ -27,6 +29,14 @@ namespace OpenQA.Selenium
internal class Navigator : INavigation
{
private WebDriver driver;
+ private string browsingContextId;
+ private static readonly Dictionary PageLoadStrategyMapper = new()
+ {
+ {"normal", ReadinessState.Complete},
+ {"eager", ReadinessState.Interactive},
+ {"none", ReadinessState.None}
+ };
+ private ReadinessState readinessState;
///
/// Initializes a new instance of the class
@@ -35,64 +45,157 @@ internal class Navigator : INavigation
public Navigator(WebDriver driver)
{
this.driver = driver;
+ // TODO: store the value of the current window's context id on the driver object
+ this.browsingContextId = driver.CurrentWindowHandle;
+
+ string strategyCap = driver.Capabilities.GetCapability("pageLoadStrategy") as string;
+ this.readinessState = strategyCap == null ? ReadinessState.Complete : PageLoadStrategyMapper[strategyCap];
}
///
- /// Move the browser back
+ /// Move back a single entry in the browser's history.
///
public void Back()
{
- this.driver.InternalExecute(DriverCommand.GoBack, null);
+ Task.Run(this.BackAsync).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Move back a single entry in the browser's history as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ public async Task BackAsync()
+ {
+ if (this.driver.BiDiDriver != null)
+ {
+ var traverseHistoryCommandParameters =
+ new TraverseHistoryCommandParameters(this.browsingContextId, -1);
+ await this.driver.BiDiDriver.BrowsingContext.TraverseHistoryAsync(traverseHistoryCommandParameters)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ await this.driver.InternalExecuteAsync(DriverCommand.GoBack, null).ConfigureAwait(false);
+ }
}
///
- /// Move the browser forward
+ /// Move a single "item" forward in the browser's history.
///
public void Forward()
{
- this.driver.InternalExecute(DriverCommand.GoForward, null);
+ Task.Run(this.ForwardAsync).GetAwaiter().GetResult();
}
///
- /// Navigate to a url for your test
+ /// Move a single "item" forward in the browser's history as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ public async Task ForwardAsync()
+ {
+ if (this.driver.BiDiDriver != null)
+ {
+ var traverseHistoryCommandParameters =
+ new TraverseHistoryCommandParameters(this.browsingContextId, 1);
+ await this.driver.BiDiDriver.BrowsingContext.TraverseHistoryAsync(traverseHistoryCommandParameters)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ await this.driver.InternalExecuteAsync(DriverCommand.GoForward, null).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Navigate to a url.
///
/// String of where you want the browser to go to
public void GoToUrl(string url)
+ {
+ Task.Run(() => this.GoToUrlAsync(url)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Navigate to a url as an asynchronous task.
+ ///
+ /// String of where you want the browser to go.
+ /// A task object representing the asynchronous operation.
+ public async Task GoToUrlAsync(string url)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url), "URL cannot be null.");
}
- Dictionary parameters = new Dictionary
+ if (this.driver.BiDiDriver != null)
{
- { "url", url }
- };
- this.driver.InternalExecute(DriverCommand.Get, parameters);
-
+ NavigateCommandParameters navigateCommandParameters = new NavigateCommandParameters(this.browsingContextId, url)
+ {
+ Wait = this.readinessState
+ };
+ await driver.BiDiDriver.BrowsingContext.NavigateAsync(navigateCommandParameters).ConfigureAwait(false);
+ }
+ else
+ {
+ Dictionary parameters = new Dictionary
+ {
+ { "url", url }
+ };
+ await this.driver.InternalExecuteAsync(DriverCommand.Get, parameters).ConfigureAwait(false);
+ }
}
///
- /// Navigate to a url for your test
+ /// Navigate to a url.
///
- /// Uri object of where you want the browser to go to
+ /// Uri object of where you want the browser to go.
public void GoToUrl(Uri url)
+ {
+ Task.Run(() => this.GoToUrlAsync(url)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Navigate to a url as an asynchronous task.
+ ///
+ /// Uri object of where you want the browser to go.
+ /// A task object representing the asynchronous operation.
+ public async Task GoToUrlAsync(Uri url)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url), "URL cannot be null.");
}
- this.GoToUrl(url.ToString());
+ await this.GoToUrlAsync(url.ToString()).ConfigureAwait(false);
}
///
- /// Refresh the browser
+ /// Reload the current page.
///
public void Refresh()
{
- // driver.SwitchTo().DefaultContent();
- this.driver.InternalExecute(DriverCommand.Refresh, null);
+ Task.Run(this.RefreshAsync).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Reload the current page as an asynchronous task.
+ ///
+ /// A task object representing the asynchronous operation.
+ public async Task RefreshAsync()
+ {
+ if (this.driver.BiDiDriver != null)
+ {
+ var reloadCommandParameters =
+ new ReloadCommandParameters(this.browsingContextId)
+ {
+ Wait = this.readinessState
+ };
+ await this.driver.BiDiDriver.BrowsingContext.ReloadAsync(reloadCommandParameters).ConfigureAwait(false);
+ }
+ else
+ {
+ await this.driver.InternalExecuteAsync(DriverCommand.Refresh, null).ConfigureAwait(false);
+ }
}
}
}
diff --git a/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs b/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs
index 4b0fcb27ed7fc..1e486ad96f98d 100644
--- a/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs
+++ b/dotnet/src/webdriver/Remote/DriverServiceCommandExecutor.cs
@@ -17,6 +17,7 @@
//
using System;
+using System.Threading.Tasks;
namespace OpenQA.Selenium.Remote
{
@@ -92,6 +93,16 @@ public HttpCommandExecutor HttpExecutor
/// The command you wish to execute
/// A response from the browser
public Response Execute(Command commandToExecute)
+ {
+ return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Executes a command as an asynchronous task.
+ ///
+ /// The command you wish to execute
+ /// A task object representing the asynchronous operation
+ public async Task ExecuteAsync(Command commandToExecute)
{
if (commandToExecute == null)
{
@@ -108,7 +119,7 @@ public Response Execute(Command commandToExecute)
// command, so that we can get the finally block.
try
{
- toReturn = this.internalExecutor.Execute(commandToExecute);
+ toReturn = await this.internalExecutor.ExecuteAsync(commandToExecute).ConfigureAwait(false);
}
finally
{
diff --git a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs
index 3dbb5e4efd179..1872e418a7888 100644
--- a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs
+++ b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs
@@ -158,6 +158,16 @@ public bool TryAddCommand(string commandName, CommandInfo info)
/// The command you wish to execute
/// A response from the browser
public virtual Response Execute(Command commandToExecute)
+ {
+ return Task.Run(() => this.ExecuteAsync(commandToExecute)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Executes a command as an asynchronous task.
+ ///
+ /// The command you wish to execute
+ /// A task object representing the asynchronous operation
+ public virtual async Task ExecuteAsync(Command commandToExecute)
{
if (commandToExecute == null)
{
@@ -184,7 +194,7 @@ public virtual Response Execute(Command commandToExecute)
HttpResponseInfo responseInfo = null;
try
{
- responseInfo = Task.Run(async () => await this.MakeHttpRequest(requestInfo)).GetAwaiter().GetResult();
+ responseInfo = await this.MakeHttpRequest(requestInfo).ConfigureAwait(false);
}
catch (HttpRequestException ex)
{
diff --git a/dotnet/src/webdriver/Script.cs b/dotnet/src/webdriver/Script.cs
new file mode 100644
index 0000000000000..e96c86a2c1041
--- /dev/null
+++ b/dotnet/src/webdriver/Script.cs
@@ -0,0 +1,99 @@
+//
+// 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.
+//
+
+using System;
+using System.Threading.Tasks;
+using WebDriverBiDi;
+using WebDriverBiDi.Log;
+using WebDriverBiDi.Session;
+
+namespace OpenQA.Selenium
+{
+ ///
+ /// Provides a mechanism to access WebDriverBiDi events in script and log domains.
+ ///
+ [Obsolete("This class is in beta and may change in future releases.")]
+ internal class Script : IScript
+ {
+ private readonly BiDiDriver biDiDriver;
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// Driver in use
+ [Obsolete("This class is in beta and may change in future releases.")]
+ public Script(WebDriver driver)
+ {
+ this.biDiDriver = driver.BiDiDriver;
+ this.biDiDriver.Log.EntryAdded += OnEntryAdded;
+ }
+
+ ///
+ /// Add and remove handlers for console messages.
+ ///
+ [Obsolete("This event is in beta and may change in future releases.")]
+ public event EventHandler ConsoleMessageHandler;
+
+ ///
+ /// Add and remove handlers for console messages.
+ ///
+ [Obsolete("This event is in beta and may change in future releases.")]
+ public event EventHandler JavaScriptErrorHandler;
+
+ ///
+ /// Asynchronously starts monitoring for console and JavaScript log entries.
+ ///
+ /// A task object representing the asynchronous operation.
+ [Obsolete("This task is in beta and may change in future releases.")]
+ public async Task StartMonitoringLogEntries()
+ {
+ SubscribeCommandParameters subscribe = new();
+ subscribe.Events.Add("log.entryAdded");
+ await biDiDriver.Session.SubscribeAsync(subscribe).ConfigureAwait(false);
+ }
+
+ ///
+ /// Asynchronously stops monitoring for all console and JavaScript log entries.
+ ///
+ /// A task object representing the asynchronous operation.
+ [Obsolete("This task is in beta and may change in future releases.")]
+ public async Task StopMonitoringLogEntries()
+ {
+ UnsubscribeCommandParameters unsubscribe = new();
+ unsubscribe.Events.Remove("log.entryAdded");
+ await biDiDriver.Session.UnsubscribeAsync(unsubscribe).ConfigureAwait(false);
+ }
+ ///
+ /// Handles the EntryAdded event raised by the sender.
+ ///
+ /// The object that raised the event.
+ /// The event arguments containing information about the added entry.
+ private void OnEntryAdded(object? sender, EntryAddedEventArgs eventArgs)
+ {
+ if (eventArgs.Type == "javascript")
+ {
+ JavaScriptErrorHandler?.Invoke(this, eventArgs);
+
+ }
+ else
+ {
+ ConsoleMessageHandler?.Invoke(this, eventArgs);
+ }
+ }
+ }
+}
diff --git a/dotnet/src/webdriver/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs
index 3de43e25c452f..5f2dcee1f8cc4 100644
--- a/dotnet/src/webdriver/WebDriver.cs
+++ b/dotnet/src/webdriver/WebDriver.cs
@@ -24,6 +24,8 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
+using System.Threading.Tasks;
+using WebDriverBiDi;
namespace OpenQA.Selenium
{
@@ -45,6 +47,7 @@ public class WebDriver : IWebDriver, ISearchContext, IJavaScriptExecutor, IFinds
private SessionId sessionId;
private String authenticatorId;
private List registeredCommands = new List();
+ private BiDiDriver biDiDriver;
///
/// Initializes a new instance of the class.
@@ -188,6 +191,14 @@ public SessionId SessionId
get { return this.sessionId; }
}
+ ///
+ /// Gets the for the current session of this driver.
+ ///
+ internal BiDiDriver BiDiDriver
+ {
+ get { return this.biDiDriver; }
+ }
+
///
/// Gets or sets the responsible for detecting
/// sequences of keystrokes representing file paths and names.
@@ -444,6 +455,17 @@ public INavigation Navigate()
return new Navigator(this);
}
+ ///
+ /// Provides access to WebDriverBiDi events in script and log domains.
+ ///
+ /// An object allowing the user to access
+ /// WebDriverBiDi events.
+ [Obsolete("This method is in beta and may change in future releases.")]
+ public IScript Script()
+ {
+ return new Script(this);
+ }
+
///
/// Executes a command with this driver.
///
@@ -556,7 +578,25 @@ internal ReadOnlyCollection GetElementsFromResponse(Response respon
/// WebDriver Response
internal Response InternalExecute(string driverCommandToExecute, Dictionary parameters)
{
- return this.Execute(driverCommandToExecute, parameters);
+ return Task.Run(() => this.InternalExecuteAsync(driverCommandToExecute, parameters)).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Executes commands with the driver asynchronously
+ ///
+ /// Command that needs executing
+ /// Parameters needed for the command
+ /// A task object representing the asynchronous operation
+ internal Task InternalExecuteAsync(string driverCommandToExecute,
+ Dictionary parameters)
+ {
+ return this.ExecuteAsync(driverCommandToExecute, parameters);
+ }
+
+ internal Response Execute(string driverCommandToExecute,
+ Dictionary parameters)
+ {
+ return Task.Run(() => this.ExecuteAsync(driverCommandToExecute, parameters)).GetAwaiter().GetResult();
}
///
@@ -565,7 +605,7 @@ internal Response InternalExecute(string driverCommandToExecute, DictionaryA value representing the command to execute.
/// A containing the names and values of the parameters of the command.
/// A containing information about the success or failure of the command and any data returned by the command.
- protected virtual Response Execute(string driverCommandToExecute, Dictionary parameters)
+ protected virtual async Task ExecuteAsync(string driverCommandToExecute, Dictionary parameters)
{
Command commandToExecute = new Command(this.sessionId, driverCommandToExecute, parameters);
@@ -573,7 +613,7 @@ protected virtual Response Execute(string driverCommandToExecute, Dictionary this.biDiDriver.StartAsync(webSocketUrl)).GetAwaiter().GetResult();
+ }
}
///
diff --git a/dotnet/src/webdriver/WebDriver.csproj b/dotnet/src/webdriver/WebDriver.csproj
index 354284b328755..31a76f5ee4123 100644
--- a/dotnet/src/webdriver/WebDriver.csproj
+++ b/dotnet/src/webdriver/WebDriver.csproj
@@ -53,6 +53,7 @@
+
diff --git a/dotnet/test/common/BUILD.bazel b/dotnet/test/common/BUILD.bazel
index a2ca040cbe9ce..2968ef73294e2 100644
--- a/dotnet/test/common/BUILD.bazel
+++ b/dotnet/test/common/BUILD.bazel
@@ -69,10 +69,13 @@ csharp_library(
dotnet_nunit_test_suite(
name = "AllTests",
size = "large",
- srcs = glob([
- "**/*Test.cs",
- "**/*Tests.cs",
- ]) + [
+ srcs = glob(
+ [
+ "**/*Test.cs",
+ "**/*Tests.cs",
+ ],
+ exclude = ["test/common/BiDi/*.cs"],
+ ) + [
":assembly-fixtures",
],
out = "WebDriver.Common.Tests",
@@ -100,3 +103,39 @@ dotnet_nunit_test_suite(
framework("nuget", "NUnit"),
],
)
+
+dotnet_bidi_test_suite(
+ name = "BiDiTests",
+ size = "large",
+ srcs = glob([
+ "**/BiDi/*Test.cs",
+ ]) + [
+ ":assembly-fixtures",
+ ],
+ out = "WebDriver.Common.Tests",
+ browsers = [
+ # The first browser in this list is assumed to be the one that should
+ # be used by default.
+ "firefox",
+ "edge",
+ "chrome",
+ ],
+ data = [
+ ":test-data",
+ ],
+ target_frameworks = ["net7.0"],
+ targeting_packs = [
+ framework("nuget", "Microsoft.NETCore.App.Ref"),
+ ],
+ deps = [
+ ":fixtures",
+ "//dotnet/src/webdriver",
+ framework("nuget", "BenderProxy"),
+ framework("nuget", "Newtonsoft.Json"),
+ framework("nuget", "NUnit"),
+ framework("nuget", "WebDriverBiDi"),
+ ],
+ env = {
+ "WEBDRIVER_BIDI": "true",
+ }
+)
diff --git a/dotnet/test/common/BiDi/LogsTest.cs b/dotnet/test/common/BiDi/LogsTest.cs
new file mode 100644
index 0000000000000..ee9a784b5e527
--- /dev/null
+++ b/dotnet/test/common/BiDi/LogsTest.cs
@@ -0,0 +1,146 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using OpenQA.Selenium.Environment;
+using WebDriverBiDi.Log;
+
+namespace OpenQA.Selenium
+{
+ [TestFixture]
+ public class LogsTest : DriverTestFixture
+ {
+ [TearDown]
+ public void TearDownMethod()
+ {
+ driver.Script().StopMonitoringLogEntries();
+ }
+
+ [Test]
+ public async Task CanListenToConsoleLog()
+ {
+ EntryAddedEventArgs eventArgs = null;
+ driver.Script().ConsoleMessageHandler += (sender, args) => { eventArgs = args; };
+ driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("bidi/logEntryAdded.html");
+
+ await driver.Script().StartMonitoringLogEntries();
+ driver.FindElement(By.Id("consoleLog")).Click();
+
+ WaitFor(() => eventArgs != null, "Log messages are empty'");
+
+ Assert.That(eventArgs.Text, Is.EqualTo("Hello, world!"));
+ Assert.That(eventArgs.Type, Is.EqualTo("console"));
+ Assert.That(eventArgs.Arguments.Count, Is.EqualTo(1));
+ Assert.That(eventArgs.Arguments[0].Type, Is.EqualTo("string"));
+ Assert.That(eventArgs.Method, Is.EqualTo("log"));
+ Assert.That(eventArgs.Level, Is.EqualTo(WebDriverBiDi.Log.LogLevel.Info));
+ }
+
+ [Test]
+ public async Task CanFilterConsoleLogs()
+ {
+ EntryAddedEventArgs eventArgs = null;
+ driver.Script().ConsoleMessageHandler += (sender, args) =>
+ {
+ if (args.Level == WebDriverBiDi.Log.LogLevel.Error)
+ {
+ eventArgs = args;
+ }
+ };
+ driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("bidi/logEntryAdded.html");
+
+ await driver.Script().StartMonitoringLogEntries();
+ driver.FindElement(By.Id("consoleLog")).Click();
+ driver.FindElement(By.Id("consoleError")).Click();
+
+ WaitFor(() => eventArgs != null, "Log messages are empty'");
+
+ Assert.That(eventArgs.Text, Is.EqualTo("I am console error"));
+ Assert.That(eventArgs.Type, Is.EqualTo("console"));
+ Assert.That(eventArgs.Arguments.Count, Is.EqualTo(1));
+ Assert.That(eventArgs.Arguments[0].Type, Is.EqualTo("string"));
+ Assert.That(eventArgs.Method, Is.EqualTo("error"));
+ Assert.That(eventArgs.Level, Is.EqualTo(WebDriverBiDi.Log.LogLevel.Error));
+ }
+
+ [Test]
+ public async Task CanListenToJavaScriptLog()
+ {
+ EntryAddedEventArgs eventArgs = null;
+ driver.Script().JavaScriptErrorHandler += (sender, args) =>
+ {
+ eventArgs = args;
+ };
+ driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("bidi/logEntryAdded.html");
+
+ await driver.Script().StartMonitoringLogEntries();
+ driver.FindElement(By.Id("jsException")).Click();
+
+ WaitFor(() => eventArgs != null, "Log messages are empty'");
+
+ Assert.That(eventArgs.Text, Is.EqualTo("Error: Not working"));
+ Assert.That(eventArgs.Type, Is.EqualTo("javascript"));
+ Assert.That(eventArgs.Level, Is.EqualTo(WebDriverBiDi.Log.LogLevel.Error));
+ }
+
+ [Test]
+ public async Task CanRetrieveStacktraceForALog()
+ {
+ EntryAddedEventArgs eventArgs = null;
+ driver.Script().JavaScriptErrorHandler += (sender, args) =>
+ {
+ eventArgs = args;
+ };
+ driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("bidi/logEntryAdded.html");
+
+ await driver.Script().StartMonitoringLogEntries();
+ driver.FindElement(By.Id("logWithStacktrace")).Click();
+
+ WaitFor(() => eventArgs != null, "Log messages are empty'");
+
+ Assert.That(eventArgs.Text, Is.EqualTo("Error: Not working"));
+ Assert.That(eventArgs.Type, Is.EqualTo("javascript"));
+ Assert.That(eventArgs.Level, Is.EqualTo(WebDriverBiDi.Log.LogLevel.Error));
+ Assert.That(eventArgs.StackTrace.CallFrames.Count, Is.GreaterThanOrEqualTo(3));
+ }
+
+ [Test]
+ public async Task CanListenToLogsWithMultipleConsumers()
+ {
+ List eventArgs = new List();
+ driver.Script().ConsoleMessageHandler += (sender, args) =>
+ {
+ eventArgs.Add(args);
+ };
+
+ driver.Script().ConsoleMessageHandler += (sender, args) =>
+ {
+ eventArgs.Add(args);
+ };
+
+ driver.Script().JavaScriptErrorHandler += (sender, args) =>
+ {
+ eventArgs.Add(args);
+ };
+
+ driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("bidi/logEntryAdded.html");
+
+ await driver.Script().StartMonitoringLogEntries();
+ driver.FindElement(By.Id("consoleLog")).Click();
+ driver.FindElement(By.Id("jsException")).Click();
+
+ WaitFor(() => eventArgs.Count == 3, "Log messages are empty'");
+
+ EntryAddedEventArgs event1 = eventArgs[0];
+ EntryAddedEventArgs event2 = eventArgs[1];
+
+ Assert.That(event1.Text, Is.EqualTo("Hello, world!"));
+ Assert.That(event1.Type, Is.EqualTo("console"));
+ Assert.That(event1.Method, Is.EqualTo("log"));
+ Assert.That(event1.Level, Is.EqualTo(WebDriverBiDi.Log.LogLevel.Info));
+ Assert.That(event2.Text, Is.EqualTo("Hello, world!"));
+ Assert.That(event2.Type, Is.EqualTo("console"));
+ Assert.That(event2.Method, Is.EqualTo("log"));
+ Assert.That(event2.Level, Is.EqualTo(WebDriverBiDi.Log.LogLevel.Info));
+ }
+ }
+}
diff --git a/dotnet/test/common/Environment/DriverFactory.cs b/dotnet/test/common/Environment/DriverFactory.cs
index 448dfc61d1a98..6139b3322ea2f 100644
--- a/dotnet/test/common/Environment/DriverFactory.cs
+++ b/dotnet/test/common/Environment/DriverFactory.cs
@@ -67,7 +67,6 @@ public IWebDriver CreateDriverWithOptions(Type driverType, DriverOptions driverO
{
browser = Browser.Chrome;
options = GetDriverOptions(driverType, driverOptions);
- options.UseWebSocketUrl = true;
var chromeOptions = (ChromeOptions)options;
chromeOptions.AddArguments("--no-sandbox", "--disable-dev-shm-usage");
@@ -185,6 +184,11 @@ protected void OnDriverLaunching(DriverService service, DriverOptions options)
options.ImplicitWaitTimeout = overriddenOptions.ImplicitWaitTimeout;
}
+ if (System.Environment.GetEnvironmentVariable("WEBDRIVER_BIDI") == "true")
+ {
+ options.UseWebSocketUrl = true;
+ }
+
return options;
}
diff --git a/dotnet/test/common/NavigationTest.cs b/dotnet/test/common/NavigationTest.cs
index ee40894aa047c..482c1a28b2ee6 100644
--- a/dotnet/test/common/NavigationTest.cs
+++ b/dotnet/test/common/NavigationTest.cs
@@ -1,5 +1,6 @@
using NUnit.Framework;
using System;
+using WebDriverBiDi;
namespace OpenQA.Selenium
{
@@ -12,10 +13,20 @@ public class NavigationTest : DriverTestFixture
[NeedsFreshDriver(IsCreatedBeforeTest = true)]
public void ShouldNotHaveProblemNavigatingWithNoPagesBrowsed()
{
- INavigation navigation;
- navigation = driver.Navigate();
- navigation.Back();
- navigation.Forward();
+ INavigation navigation = driver.Navigate();
+
+ if (((WebDriver)driver).Capabilities.HasCapability("webSocketUrl"))
+ {
+ var ex1 = Assert.Throws(() => navigation.Back());
+ Assert.True(ex1!.Message.Contains("no such history entry"));
+ var ex2 = Assert.Throws(() => navigation.Forward());
+ Assert.True(ex2!.Message.Contains("no such history entry"));
+ }
+ else
+ {
+ navigation.Back();
+ navigation.Forward();
+ }
}
[Test]
@@ -40,7 +51,7 @@ public void ShouldAcceptInvalidUrlsUsingUris()
INavigation navigation;
navigation = driver.Navigate();
Assert.That(() => navigation.GoToUrl((Uri)null), Throws.InstanceOf());
- // new Uri("") and new Uri("isidsji30342??éåµñ©æ")
+ // new Uri("") and new Uri("isidsji30342??éåµñ©æ")
// throw an exception, so we needn't worry about them.
}
@@ -89,6 +100,5 @@ public void ShouldRefreshPage()
changedDiv = driver.FindElement(By.Id("dynamo"));
Assert.AreEqual("What's for dinner?", changedDiv.Text);
}
-
}
}
diff --git a/dotnet/test/common/StubDriver.cs b/dotnet/test/common/StubDriver.cs
index dd981d9339a70..489d532a7c7c2 100644
--- a/dotnet/test/common/StubDriver.cs
+++ b/dotnet/test/common/StubDriver.cs
@@ -58,6 +58,11 @@ public INavigation Navigate()
throw new NotImplementedException();
}
+ public IScript Script()
+ {
+ throw new NotImplementedException();
+ }
+
public ITargetLocator SwitchTo()
{
throw new NotImplementedException();
diff --git a/dotnet/test/support/Events/EventFiringWebDriverTest.cs b/dotnet/test/support/Events/EventFiringWebDriverTest.cs
index 972be725adfa9..3d59f056f3ee8 100644
--- a/dotnet/test/support/Events/EventFiringWebDriverTest.cs
+++ b/dotnet/test/support/Events/EventFiringWebDriverTest.cs
@@ -58,12 +58,15 @@ Navigated back
Navigating forward
Navigated forward
";
+ string normalizedExpectedLog = expectedLog.Replace("\r\n", "\n").Replace("\r", "\n");
mockDriver.VerifySet(x => x.Url = "http://www.get.com", Times.Once);
mockDriver.Verify(x => x.Navigate(), Times.Exactly(3));
- mockNavigation.Verify(x => x.GoToUrl("http://www.navigate-to.com"), Times.Once);
- mockNavigation.Verify(x => x.Back(), Times.Once);
- mockNavigation.Verify(x => x.Forward(), Times.Once);
- Assert.AreEqual(expectedLog, log.ToString());
+ mockNavigation.Verify(x => x.GoToUrlAsync("http://www.navigate-to.com"), Times.Once);
+ mockNavigation.Verify(x => x.BackAsync(), Times.Once);
+ mockNavigation.Verify(x => x.ForwardAsync(), Times.Once);
+
+ string normalizedActualLog = log.ToString().Replace("\r\n", "\n").Replace("\r", "\n");
+ Assert.AreEqual(normalizedExpectedLog, normalizedActualLog);
}
[Test]
diff --git a/third_party/dotnet/nuget/packages/webdriverbidi.0.0.1.nupkg b/third_party/dotnet/nuget/packages/webdriverbidi.0.0.1.nupkg
new file mode 100644
index 0000000000000..99afa6c504946
Binary files /dev/null and b/third_party/dotnet/nuget/packages/webdriverbidi.0.0.1.nupkg differ