Add basic browser automation tests for SPA templates (Windows only)
This commit is contained in:
parent
95f2b8d375
commit
12a8e29288
|
|
@ -1,10 +1,12 @@
|
||||||
using System;
|
using OpenQA.Selenium;
|
||||||
|
using OpenQA.Selenium.Edge;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Reflection;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Templates.Test.Helpers
|
namespace Templates.Test.Helpers
|
||||||
|
|
@ -53,13 +55,13 @@ namespace Templates.Test.Helpers
|
||||||
_listeningUri = new Uri(listeningUrlString, UriKind.Absolute);
|
_listeningUri = new Uri(listeningUrlString, UriKind.Absolute);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AssertOk(string requestUrl)
|
public void AssertOk(string requestUrl)
|
||||||
=> AssertStatusCode(requestUrl, HttpStatusCode.OK);
|
=> AssertStatusCode(requestUrl, HttpStatusCode.OK);
|
||||||
|
|
||||||
internal void AssertNotFound(string requestUrl)
|
public void AssertNotFound(string requestUrl)
|
||||||
=> AssertStatusCode(requestUrl, HttpStatusCode.NotFound);
|
=> AssertStatusCode(requestUrl, HttpStatusCode.NotFound);
|
||||||
|
|
||||||
internal void AssertStatusCode(string requestUrl, HttpStatusCode statusCode)
|
public void AssertStatusCode(string requestUrl, HttpStatusCode statusCode)
|
||||||
{
|
{
|
||||||
var request = new HttpRequestMessage(
|
var request = new HttpRequestMessage(
|
||||||
HttpMethod.Get,
|
HttpMethod.Get,
|
||||||
|
|
@ -69,6 +71,14 @@ namespace Templates.Test.Helpers
|
||||||
Assert.Equal(statusCode, response.StatusCode);
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_httpClient.Dispose();
|
_httpClient.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
using Xunit;
|
using OpenQA.Selenium;
|
||||||
|
using Templates.Test.Helpers;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace Templates.Test
|
namespace Templates.Test
|
||||||
{
|
{
|
||||||
|
|
@ -22,7 +24,40 @@ namespace Templates.Test
|
||||||
using (var aspNetProcess = StartAspNetProcess(targetFrameworkOverride))
|
using (var aspNetProcess = StartAspNetProcess(targetFrameworkOverride))
|
||||||
{
|
{
|
||||||
aspNetProcess.AssertOk("/");
|
aspNetProcess.AssertOk("/");
|
||||||
|
|
||||||
|
using (var browser = aspNetProcess.VisitInBrowser())
|
||||||
|
{
|
||||||
|
TestBasicNavigation(browser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TestBasicNavigation(IWebDriver browser)
|
||||||
|
{
|
||||||
|
// <title> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,12 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Security.Permissions" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||||
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" />
|
<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" />
|
||||||
<PackageReference Include="xunit.analyzers" />
|
<PackageReference Include="xunit.analyzers" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" />
|
<PackageReference Include="xunit.runner.visualstudio" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue