From 12a8e29288e2862ac3b94d79eac5bdbde4185ded Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 5 Sep 2017 16:19:04 +0100 Subject: [PATCH] Add basic browser automation tests for SPA templates (Windows only) --- test/Templates.Test/Helpers/AspNetProcess.cs | 20 ++++-- .../Helpers/WebDriverExtensions.cs | 70 +++++++++++++++++++ test/Templates.Test/SpaTemplateTest.cs | 37 +++++++++- test/Templates.Test/Templates.Test.csproj | 4 ++ 4 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 test/Templates.Test/Helpers/WebDriverExtensions.cs diff --git a/test/Templates.Test/Helpers/AspNetProcess.cs b/test/Templates.Test/Helpers/AspNetProcess.cs index 705f009ef8..44127dad19 100644 --- a/test/Templates.Test/Helpers/AspNetProcess.cs +++ b/test/Templates.Test/Helpers/AspNetProcess.cs @@ -1,10 +1,12 @@ -using System; +using OpenQA.Selenium; +using OpenQA.Selenium.Edge; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Threading; +using System.Reflection; using Xunit; namespace Templates.Test.Helpers @@ -53,13 +55,13 @@ namespace Templates.Test.Helpers _listeningUri = new Uri(listeningUrlString, UriKind.Absolute); } - internal void AssertOk(string requestUrl) + public void AssertOk(string requestUrl) => AssertStatusCode(requestUrl, HttpStatusCode.OK); - internal void AssertNotFound(string requestUrl) + public void AssertNotFound(string requestUrl) => AssertStatusCode(requestUrl, HttpStatusCode.NotFound); - internal void AssertStatusCode(string requestUrl, HttpStatusCode statusCode) + public void AssertStatusCode(string requestUrl, HttpStatusCode statusCode) { var request = new HttpRequestMessage( HttpMethod.Get, @@ -69,6 +71,14 @@ namespace Templates.Test.Helpers Assert.Equal(statusCode, response.StatusCode); } + public IWebDriver VisitInBrowser() + { + var driver = new EdgeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1); + driver.Navigate().GoToUrl(_listeningUri); + return driver; + } + public void Dispose() { _httpClient.Dispose(); diff --git a/test/Templates.Test/Helpers/WebDriverExtensions.cs b/test/Templates.Test/Helpers/WebDriverExtensions.cs new file mode 100644 index 0000000000..04ec47b1ed --- /dev/null +++ b/test/Templates.Test/Helpers/WebDriverExtensions.cs @@ -0,0 +1,70 @@ +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; +using OpenQA.Selenium.Support.UI; +using System; + +namespace Templates.Test.Helpers +{ + public static class WebDriverExtensions + { + public static string GetText(this ISearchContext driver, string cssSelector) + { + return driver.FindElement(By.CssSelector(cssSelector)).Text; + } + + public static void Click(this IWebDriver driver, By by) + { + Click(driver, null, by); + } + + public static void Click(this IWebDriver driver, ISearchContext searchContext, By by) + { + // This elaborate way of clicking is a workaround for https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/5238133/ + new Actions(driver) + .MoveToElement((searchContext ?? driver).FindElement(by)) + .Click() + .Perform(); + } + + public static void Click(this IWebDriver driver, string cssSelector) + { + Click(driver, null, cssSelector); + } + + public static void Click(this IWebDriver driver, ISearchContext searchContext, string cssSelector) + { + Click(driver, searchContext, By.CssSelector(cssSelector)); + } + + public static IWebElement FindElement(this ISearchContext searchContext, string cssSelector) + { + return searchContext.FindElement(By.CssSelector(cssSelector)); + } + + public static IWebElement Parent(this IWebElement webElement) + { + return webElement.FindElement(By.XPath("..")); + } + + public static IWebElement FindElement(this IWebDriver driver, string cssSelector, int timeoutSeconds) + { + return FindElement(driver, null, cssSelector, timeoutSeconds); + } + + public static IWebElement FindElement(this IWebDriver driver, ISearchContext searchContext, string cssSelector, int timeoutSeconds) + { + return FindElement(driver, searchContext, By.CssSelector(cssSelector), timeoutSeconds); + } + + public static IWebElement FindElement(this IWebDriver driver, By by, int timeoutSeconds) + { + return FindElement(driver, null, by, timeoutSeconds); + } + + public static IWebElement FindElement(this IWebDriver driver, ISearchContext searchContext, By by, int timeoutSeconds) + { + return new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds)) + .Until(drv => searchContext.FindElement(by)); + } + } +} diff --git a/test/Templates.Test/SpaTemplateTest.cs b/test/Templates.Test/SpaTemplateTest.cs index 9246961723..e35305b456 100644 --- a/test/Templates.Test/SpaTemplateTest.cs +++ b/test/Templates.Test/SpaTemplateTest.cs @@ -1,4 +1,6 @@ -using Xunit; +using OpenQA.Selenium; +using Templates.Test.Helpers; +using Xunit; namespace Templates.Test { @@ -22,7 +24,40 @@ namespace Templates.Test using (var aspNetProcess = StartAspNetProcess(targetFrameworkOverride)) { aspNetProcess.AssertOk("/"); + + using (var browser = aspNetProcess.VisitInBrowser()) + { + TestBasicNavigation(browser); + } } } + + private void TestBasicNavigation(IWebDriver browser) + { + // element gets project ID injected into it during template execution + Assert.Contains(ProjectName, browser.Title); + + // Initially displays the home page + Assert.Equal("Hello, world!", browser.GetText("h1")); + + // Can navigate to the counter page + browser.Click(By.PartialLinkText("Counter")); + Assert.Equal("Counter", browser.GetText("h1")); + + // Clicking the counter button works + var counterComponent = browser.FindElement("h1").Parent(); + Assert.Equal("0", counterComponent.GetText("strong")); + browser.Click(counterComponent, "button"); + Assert.Equal("1", counterComponent.GetText("strong")); + + // Can navigate to the 'fetch data' page + browser.Click(By.PartialLinkText("Fetch data")); + Assert.Equal("Weather forecast", browser.GetText("h1")); + + // Loads and displays the table of weather forecasts + var fetchDataComponent = browser.FindElement("h1").Parent(); + var table = browser.FindElement(fetchDataComponent, "table", 5); + Assert.Equal(5, table.FindElements(By.CssSelector("tbody tr")).Count); + } } } diff --git a/test/Templates.Test/Templates.Test.csproj b/test/Templates.Test/Templates.Test.csproj index 6366d9d7b6..c3464e99a8 100644 --- a/test/Templates.Test/Templates.Test.csproj +++ b/test/Templates.Test/Templates.Test.csproj @@ -5,8 +5,12 @@ </PropertyGroup> <ItemGroup> + <PackageReference Include="System.Security.Permissions" /> <PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" /> + <PackageReference Include="Selenium.WebDriver.MicrosoftDriver" Version="15.15063.0" NoWarn="KRB4002" /> + <PackageReference Include="Selenium.Support" Version="3.4.0" NoWarn="NU1701" /> + <PackageReference Include="Selenium.WebDriver" Version="3.4.0" NoWarn="NU1701" /> <PackageReference Include="xunit" /> <PackageReference Include="xunit.analyzers" /> <PackageReference Include="xunit.runner.visualstudio" />