diff --git a/dotnet/src/support/Events/EventFiringWebDriver.cs b/dotnet/src/support/Events/EventFiringWebDriver.cs index ac428cf2830b5..77a3b5f3b50a5 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 { @@ -845,12 +846,24 @@ public EventFiringNavigation(EventFiringWebDriver driver) /// Move the browser back /// public void Back() + { + Task.Run(async delegate + { + await 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 +874,27 @@ public void Back() } /// - /// Move the browser forward + /// Move a single "item" forward in the browser's history. /// public void Forward() + { + Task.Run(async delegate + { + await 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 +905,34 @@ 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(async delegate + { + await 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 +943,52 @@ 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(async delegate + { + await 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(async delegate + { + await 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/Navigator.cs b/dotnet/src/webdriver/Navigator.cs index f8f6047e91f5a..48683a03c9fe1 100644 --- a/dotnet/src/webdriver/Navigator.cs +++ b/dotnet/src/webdriver/Navigator.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace OpenQA.Selenium { @@ -38,26 +39,63 @@ public Navigator(WebDriver driver) } /// - /// 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(async delegate + { + await 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() + { + 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(async delegate + { + await 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() + { + await this.driver.InternalExecuteAsync(DriverCommand.GoForward, null).ConfigureAwait(false); } /// - /// 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(async delegate + { + await 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) { @@ -68,31 +106,55 @@ public void GoToUrl(string url) { { "url", url } }; - this.driver.InternalExecute(DriverCommand.Get, parameters); - + 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(async delegate + { + await 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() + { + Task.Run(async delegate + { + await this.RefreshAsync(); + }).GetAwaiter().GetResult(); + } + + /// + /// Reload the current page as an asynchronous task. + /// + /// A task object representing the asynchronous operation. + public async Task RefreshAsync() { // driver.SwitchTo().DefaultContent(); - this.driver.InternalExecute(DriverCommand.Refresh, null); + 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/WebDriver.cs b/dotnet/src/webdriver/WebDriver.cs index 3de43e25c452f..d063e8f5905ea 100644 --- a/dotnet/src/webdriver/WebDriver.cs +++ b/dotnet/src/webdriver/WebDriver.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; +using System.Threading.Tasks; namespace OpenQA.Selenium { @@ -556,7 +557,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 +584,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 +592,7 @@ protected virtual Response Execute(string driverCommandToExecute, Dictionary 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. } @@ -90,5 +91,73 @@ public void ShouldRefreshPage() Assert.AreEqual("What's for dinner?", changedDiv.Text); } + [Test] + [NeedsFreshDriver(IsCreatedBeforeTest = true)] + public Task ShouldNotHaveProblemNavigatingWithNoPagesBrowsedAsync() + { + var navigation = driver.Navigate(); + Assert.DoesNotThrowAsync(async () => await navigation.BackAsync()); + Assert.DoesNotThrowAsync(async () => await navigation.ForwardAsync()); + return Task.CompletedTask; + } + + [Test] + public async Task ShouldGoBackAndForwardAsync() + { + INavigation navigation = driver.Navigate(); + + await navigation.GoToUrlAsync(macbethPage); + await navigation.GoToUrlAsync(simpleTestPage); + + await navigation.BackAsync(); + Assert.AreEqual(macbethTitle, driver.Title); + + await navigation.ForwardAsync(); + Assert.AreEqual(simpleTestTitle, driver.Title); + } + + [Test] + public void ShouldAcceptInvalidUrlsUsingUrisAsync() + { + INavigation navigation = driver.Navigate(); + Assert.That(async () => await navigation.GoToUrlAsync((Uri)null), Throws.InstanceOf()); + } + + [Test] + public async Task ShouldGoToUrlUsingStringAsync() + { + var navigation = driver.Navigate(); + + await navigation.GoToUrlAsync(macbethPage); + Assert.AreEqual(macbethTitle, driver.Title); + + await navigation.GoToUrlAsync(simpleTestPage); + Assert.AreEqual(simpleTestTitle, driver.Title); + } + + [Test] + public void ShouldGoToUrlUsingUriAsync() + { + var navigation = driver.Navigate(); + + navigation.GoToUrlAsync(new Uri(macbethPage)); + Assert.AreEqual(driver.Title, macbethTitle); + navigation.GoToUrl(new Uri(simpleTestPage)); + Assert.AreEqual(simpleTestTitle, driver.Title); + } + + [Test] + public async Task ShouldRefreshPageAsync() + { + await driver.Navigate().GoToUrlAsync(javascriptPage); + IWebElement changedDiv = driver.FindElement(By.Id("dynamo")); + driver.FindElement(By.Id("updatediv")).Click(); + + Assert.AreEqual("Fish and chips!", changedDiv.Text); + await driver.Navigate().RefreshAsync(); + + changedDiv = driver.FindElement(By.Id("dynamo")); + Assert.AreEqual("What's for dinner?", changedDiv.Text); + } } } 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]