[Infrastructure] Standarize E2E test asserts and increase wait time (#9080)

* Standarize E2E test asserts and increase wait time
This commit is contained in:
Javier Calvarro Nelson 2019-04-05 09:00:11 +02:00 committed by GitHub
parent 23c88a14bf
commit 437134f1da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 135 deletions

View File

@ -75,7 +75,7 @@ namespace Templates.Test.Helpers
if (driver.Title.Contains("Certificate error", StringComparison.OrdinalIgnoreCase)) if (driver.Title.Contains("Certificate error", StringComparison.OrdinalIgnoreCase))
{ {
_output.WriteLine("Page contains certificate error. Attempting to get around this..."); _output.WriteLine("Page contains certificate error. Attempting to get around this...");
driver.Click(By.Id("moreInformationDropdownSpan")); driver.FindElement(By.Id("moreInformationDropdownSpan")).Click();
var continueLink = driver.FindElement(By.Id("invalidcert_continue")); var continueLink = driver.FindElement(By.Id("invalidcert_continue"));
if (continueLink != null) if (continueLink != null)
{ {

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.E2ETesting;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace Templates.Test.Helpers namespace Templates.Test.Helpers
@ -27,6 +28,13 @@ namespace Templates.Test.Helpers
DiagnosticsMessageSink = diagnosticsMessageSink; DiagnosticsMessageSink = diagnosticsMessageSink;
} }
static ProjectFactoryFixture()
{
// There is no good place to put this, so this is the best one.
// This sets the defualt timeout for all the Selenium test assertions.
WaitAssert.DefaultTimeout = TimeSpan.FromSeconds(30);
}
public async Task<Project> GetOrCreateProject(string projectKey, ITestOutputHelper output) public async Task<Project> GetOrCreateProject(string projectKey, ITestOutputHelper output)
{ {
await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output); await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output);

View File

@ -1,94 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using OpenQA.Selenium;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.UI;
using System;
using System.Linq;
namespace Templates.Test.Helpers
{
public static class WebDriverExtensions
{
// Maximum time any action performed by WebDriver will wait before failing.
// Any action will have to be completed in at most 10 seconds.
// Providing a smaller value won't improve the speed of the tests in any
// significant way and will make them more prone to fail on slower drivers.
internal const int DefaultMaxWaitTimeInSeconds = 10;
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, 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, ISearchContext searchContext, string cssSelector, int timeoutSeconds)
{
return FindElement(driver, searchContext, By.CssSelector(cssSelector), 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));
}
public static void WaitForUrl(this IWebDriver browser, string expectedUrl)
{
new WebDriverWait(browser, TimeSpan.FromSeconds(DefaultMaxWaitTimeInSeconds))
.Until(driver => driver.Url.Contains(expectedUrl, StringComparison.OrdinalIgnoreCase));
}
public static void WaitForElement(this IWebDriver browser, string expectedElementCss)
{
new WebDriverWait(browser, TimeSpan.FromSeconds(DefaultMaxWaitTimeInSeconds))
.Until(driver => driver.FindElements(By.CssSelector(expectedElementCss)).Count > 0);
}
public static void WaitForText(this IWebDriver browser, string cssSelector, string expectedText)
{
new WebDriverWait(browser, TimeSpan.FromSeconds(DefaultMaxWaitTimeInSeconds))
.Until(driver => {
try
{
var matchingElement = driver.FindElements(By.CssSelector(cssSelector)).FirstOrDefault();
return matchingElement?.Text == expectedText;
}
catch (Exception) // We can get a "stale element" exception if the DOM mutates while we're holding a reference to its element
{
return false;
}
});
}
}
}

View File

@ -75,35 +75,31 @@ namespace Templates.Test
// Give components.server enough time to load so that it can replace // Give components.server enough time to load so that it can replace
// the prerendered content before we start making assertions. // the prerendered content before we start making assertions.
Thread.Sleep(5000); Thread.Sleep(5000);
Browser.WaitForElement("ul"); Browser.Exists(By.TagName("ul"));
// <title> element gets project ID injected into it during template execution // <title> element gets project ID injected into it during template execution
Assert.Contains(Project.ProjectGuid, Browser.Title); Browser.Equal(Project.ProjectName.Trim(), () => Browser.Title.Trim());
// Initially displays the home page // Initially displays the home page
Assert.Equal("Hello, world!", Browser.GetText("h1")); Browser.Equal("Hello, world!", () => Browser.FindElement(By.TagName("h1")).Text);
// Can navigate to the counter page // Can navigate to the counter page
Browser.Click(By.PartialLinkText("Counter")); Browser.FindElement(By.PartialLinkText("Counter")).Click();
Browser.WaitForUrl("counter"); Browser.Contains("counter", () => Browser.Url);
Browser.WaitForText("h1", "Counter"); Browser.Equal("Counter", () => Browser.FindElement(By.TagName("h1")).Text);
// Clicking the counter button works // Clicking the counter button works
var counterComponent = Browser.FindElement("h1").Parent(); Browser.Equal("Current count: 0", () => Browser.FindElement(By.CssSelector("h1 + p")).Text);
var counterDisplay = Browser.FindElement("h1 + p"); Browser.FindElement(By.CssSelector("p+button")).Click();
Assert.Equal("Current count: 0", counterDisplay.Text); Browser.Equal("Current count: 1", () => Browser.FindElement(By.CssSelector("h1 + p")).Text);
Browser.Click(counterComponent, "button");
Browser.Equal("Current count: 1", () => Browser.FindElement("h1+p").Text);
// Can navigate to the 'fetch data' page // Can navigate to the 'fetch data' page
Browser.Click(By.PartialLinkText("Fetch data")); Browser.FindElement(By.PartialLinkText("Fetch data")).Click();
Browser.WaitForUrl("fetchdata"); Browser.Contains("fetchdata", () => Browser.Url);
Browser.WaitForText("h1", "Weather forecast"); Browser.Equal("Weather forecast", () => Browser.FindElement(By.TagName("h1")).Text);
// Asynchronously loads and displays the table of weather forecasts // Asynchronously loads and displays the table of weather forecasts
var fetchDataComponent = Browser.FindElement("h1").Parent(); Browser.Exists(By.CssSelector("table>tbody>tr"));
Browser.WaitForElement("table>tbody>tr"); Browser.Equal(5, () => Browser.FindElements(By.CssSelector("p+table>tbody>tr")).Count);
var table = Browser.FindElement(fetchDataComponent, "table", timeoutSeconds: 5);
Assert.Equal(5, table.FindElements(By.CssSelector("tbody tr")).Count);
} }
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.E2ETesting; using Microsoft.AspNetCore.E2ETesting;
@ -168,37 +169,34 @@ namespace Templates.Test.SpaTemplateTest
private void TestBasicNavigation(bool visitFetchData) private void TestBasicNavigation(bool visitFetchData)
{ {
Browser.WaitForElement("ul"); Browser.Exists(By.TagName("ul"));
// <title> element gets project ID injected into it during template execution // <title> element gets project ID injected into it during template execution
Assert.Contains(Project.ProjectGuid, Browser.Title); Browser.Contains(Project.ProjectGuid, () => Browser.Title);
// Initially displays the home page // Initially displays the home page
Assert.Equal("Hello, world!", Browser.GetText("h1")); Browser.Equal("Hello, world!", () => Browser.FindElement(By.TagName("h1")).Text);
// Can navigate to the counter page // Can navigate to the counter page
Browser.Click(By.PartialLinkText("Counter")); Browser.FindElement(By.PartialLinkText("Counter")).Click();
Browser.WaitForUrl("counter"); Browser.Contains("counter", () => Browser.Url);
Assert.Equal("Counter", Browser.GetText("h1")); Browser.Equal("Counter", () => Browser.FindElement(By.TagName("h1")).Text);
// Clicking the counter button works // Clicking the counter button works
var counterComponent = Browser.FindElement("h1").Parent(); Browser.Equal("0", () => Browser.FindElement(By.CssSelector("p>strong")).Text);
Assert.Equal("0", counterComponent.GetText("strong")); Browser.FindElement(By.CssSelector("p+button")).Click();
Browser.Click(counterComponent, "button"); Browser.Equal("1", () => Browser.FindElement(By.CssSelector("p>strong")).Text);
Assert.Equal("1", counterComponent.GetText("strong"));
if (visitFetchData) if (visitFetchData)
{ {
// Can navigate to the 'fetch data' page // Can navigate to the 'fetch data' page
Browser.Click(By.PartialLinkText("Fetch data")); Browser.FindElement(By.PartialLinkText("Fetch data")).Click();
Browser.WaitForUrl("fetch-data"); Browser.Contains("fetch-data", () => Browser.Url);
Assert.Equal("Weather forecast", Browser.GetText("h1")); Browser.Equal("Weather forecast", () => Browser.FindElement(By.TagName("h1")).Text);
// Asynchronously loads and displays the table of weather forecasts // Asynchronously loads and displays the table of weather forecasts
var fetchDataComponent = Browser.FindElement("h1").Parent(); Browser.Exists(By.CssSelector("table>tbody>tr"));
Browser.WaitForElement("table>tbody>tr"); Browser.Equal(5, () => Browser.FindElements(By.CssSelector("p+table>tbody>tr")).Count);
var table = Browser.FindElement(fetchDataComponent, "table", timeoutSeconds: 5);
Assert.Equal(5, table.FindElements(By.CssSelector("tbody tr")).Count);
} }
} }

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Runtime.ExceptionServices;
using OpenQA.Selenium; using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI; using OpenQA.Selenium.Support.UI;
using Xunit; using Xunit;
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.E2ETesting
public static class WaitAssert public static class WaitAssert
{ {
private readonly static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3); public static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3);
public static void Equal<T>(this IWebDriver driver, T expected, Func<T> actual) public static void Equal<T>(this IWebDriver driver, T expected, Func<T> actual)
=> WaitAssertCore(driver, () => Assert.Equal(expected, actual())); => WaitAssertCore(driver, () => Assert.Equal(expected, actual()));
@ -41,6 +41,9 @@ namespace Microsoft.AspNetCore.E2ETesting
public static void Single(this IWebDriver driver, Func<IEnumerable> actualValues) public static void Single(this IWebDriver driver, Func<IEnumerable> actualValues)
=> WaitAssertCore(driver, () => Assert.Single(actualValues())); => WaitAssertCore(driver, () => Assert.Single(actualValues()));
public static void Exists(this IWebDriver driver, By finder)
=> WaitAssertCore(driver, () => Assert.NotEmpty(driver.FindElements(finder)));
private static void WaitAssertCore(IWebDriver driver, Action assertion, TimeSpan timeout = default) private static void WaitAssertCore(IWebDriver driver, Action assertion, TimeSpan timeout = default)
{ {
if (timeout == default) if (timeout == default)
@ -48,6 +51,7 @@ namespace Microsoft.AspNetCore.E2ETesting
timeout = DefaultTimeout; timeout = DefaultTimeout;
} }
Exception lastException = null;
try try
{ {
new WebDriverWait(driver, timeout).Until(_ => new WebDriverWait(driver, timeout).Until(_ =>
@ -57,16 +61,24 @@ namespace Microsoft.AspNetCore.E2ETesting
assertion(); assertion();
return true; return true;
} }
catch catch(Exception e)
{ {
lastException = e;
return false; return false;
} }
}); });
} }
catch (WebDriverTimeoutException) catch (WebDriverTimeoutException)
{ {
// Instead of reporting it as a timeout, report the Xunit exception if (lastException != null)
assertion(); {
ExceptionDispatchInfo.Capture(lastException).Throw();
}
else
{
// Instead of reporting it as a timeout, report the Xunit exception
assertion();
}
} }
} }
} }