From 0e5d7ef1d4b9b40e701876a2a22291dd21b0358d Mon Sep 17 00:00:00 2001 From: Pranav K Date: Mon, 28 Sep 2020 17:07:22 -0700 Subject: [PATCH] Attempt to read the logs from the browser (#26289) * Attempt to read the logs from the browser Seleniunm currently does not return log messages (See https://github.com/SeleniumHQ/selenium/issues/8229). This making figuring out blazor WASM test failures a lot harder. This change adds some JS to the test apps to read the console output. * WIP --- .../HostedInAspNet.Client/wwwroot/index.html | 1 + .../wwwroot/seleniumworkaround.js | 26 +++++++++++++++++++ .../wwwroot/index.html | 2 ++ .../wwwroot/seleniumworkaround.js | 26 +++++++++++++++++++ .../ServerExecutionTests/ServerAuthTest.cs | 4 +-- ...verInteropTestDefaultExceptionsBehavior.cs | 3 +-- .../E2ETest/Tests/BinaryHttpClientTest.cs | 6 +---- .../E2ETest/Tests/BootResourceCachingTest.cs | 4 +-- .../E2ETest/Tests/ClientSideHostingTest.cs | 5 ++-- .../test/E2ETest/Tests/HostedInAspNetTest.cs | 4 +-- .../test/E2ETest/Tests/HttpClientTest.cs | 8 ++---- .../test/E2ETest/Tests/InteropTest.cs | 3 +-- .../test/E2ETest/Tests/RoutingTest.cs | 5 +--- .../test/E2ETest/Tests/StandaloneAppTest.cs | 7 +++-- .../E2ETest/Tests/WebAssemblyLazyLoadTest.cs | 10 +++---- .../BasicTestApp/wwwroot/index.html | 1 + .../wwwroot/js/seleniumworkaround.js | 26 +++++++++++++++++++ .../BrowserAssertFailedException.cs | 4 +-- src/Shared/E2ETesting/WaitAssert.cs | 15 ++++++++++- 19 files changed, 118 insertions(+), 42 deletions(-) create mode 100644 src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/seleniumworkaround.js create mode 100644 src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/seleniumworkaround.js create mode 100644 src/Components/test/testassets/BasicTestApp/wwwroot/js/seleniumworkaround.js diff --git a/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/index.html b/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/index.html index 643b68bb6b..b1ef16997e 100644 --- a/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/index.html +++ b/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/index.html @@ -6,6 +6,7 @@ Loading... + diff --git a/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/seleniumworkaround.js b/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/seleniumworkaround.js new file mode 100644 index 0000000000..645add5e4d --- /dev/null +++ b/src/Components/WebAssembly/testassets/HostedInAspNet.Client/wwwroot/seleniumworkaround.js @@ -0,0 +1,26 @@ +(function () { + // Note: there are multiple copies of this file throughout the repo. If you're editing it, please look for + // other seleniumworkaround.js files and keep them all in sync. + const logs = []; + + const defaultLog = console.log; + console.log = function () { + defaultLog.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + const defaultError = console.error; + console.error = function () { + defaultError.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + const defaultWarn = console.warn; + console.warn = function () { + defaultWarn.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + window.getBrowserLogs = () => logs; +})(); + diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/index.html b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/index.html index 3f43ecd868..d153fa6d5a 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/index.html +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/index.html @@ -17,6 +17,8 @@ Reload 🗙 + + diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/seleniumworkaround.js b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/seleniumworkaround.js new file mode 100644 index 0000000000..645add5e4d --- /dev/null +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Client/wwwroot/seleniumworkaround.js @@ -0,0 +1,26 @@ +(function () { + // Note: there are multiple copies of this file throughout the repo. If you're editing it, please look for + // other seleniumworkaround.js files and keep them all in sync. + const logs = []; + + const defaultLog = console.log; + console.log = function () { + defaultLog.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + const defaultError = console.error; + console.error = function () { + defaultError.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + const defaultWarn = console.warn; + console.warn = function () { + defaultWarn.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + window.getBrowserLogs = () => logs; +})(); + diff --git a/src/Components/test/E2ETest/ServerExecutionTests/ServerAuthTest.cs b/src/Components/test/E2ETest/ServerExecutionTests/ServerAuthTest.cs index 86c6b3d958..146edddb84 100644 --- a/src/Components/test/E2ETest/ServerExecutionTests/ServerAuthTest.cs +++ b/src/Components/test/E2ETest/ServerExecutionTests/ServerAuthTest.cs @@ -62,8 +62,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests ((IJavaScriptExecutor)Browser).ExecuteScript("Blazor._internal.forceCloseConnection()"); // Wait until the reconnection dialog has been shown but is now hidden - new WebDriverWait(Browser, TimeSpan.FromSeconds(10)) - .Until(driver => driver.FindElement(By.Id("components-reconnect-modal"))?.GetCssValue("display") == "none"); + var reconnectModel = Browser.Exists(By.Id("components-reconnect-modal")); + Browser.Equal("none", () => reconnectModel.GetCssValue("display")); } } } diff --git a/src/Components/test/E2ETest/ServerExecutionTests/ServerInteropTestDefaultExceptionsBehavior.cs b/src/Components/test/E2ETest/ServerExecutionTests/ServerInteropTestDefaultExceptionsBehavior.cs index 71810d072a..47f7c3fe60 100644 --- a/src/Components/test/E2ETest/ServerExecutionTests/ServerInteropTestDefaultExceptionsBehavior.cs +++ b/src/Components/test/E2ETest/ServerExecutionTests/ServerInteropTestDefaultExceptionsBehavior.cs @@ -48,8 +48,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests var interopButton = Browser.FindElement(By.Id("btn-interop")); interopButton.Click(); - var wait = new WebDriverWait(Browser, TimeSpan.FromSeconds(10)) - .Until(d => d.FindElement(By.Id("done-with-interop"))); + Browser.Exists(By.Id("done-with-interop")); foreach (var expectedValue in expectedValues) { diff --git a/src/Components/test/E2ETest/Tests/BinaryHttpClientTest.cs b/src/Components/test/E2ETest/Tests/BinaryHttpClientTest.cs index 0a316bbbca..624a024bf2 100644 --- a/src/Components/test/E2ETest/Tests/BinaryHttpClientTest.cs +++ b/src/Components/test/E2ETest/Tests/BinaryHttpClientTest.cs @@ -2,13 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using BasicTestApp; using BasicTestApp.HttpClientTest; using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; using Microsoft.AspNetCore.E2ETesting; using Microsoft.AspNetCore.Testing; using OpenQA.Selenium; -using OpenQA.Selenium.Support.UI; using TestServer; using Xunit; using Xunit.Abstractions; @@ -61,9 +59,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests _appElement.FindElement(By.Id("send-request")).Click(); - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.Id("response-status")) != null); - _responseStatus = _appElement.FindElement(By.Id("response-status")); + _responseStatus = Browser.Exists(By.Id("response-status")); _responseStatusText = _appElement.FindElement(By.Id("response-status-text")); _testOutcome = _appElement.FindElement(By.Id("test-outcome")); } diff --git a/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs b/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs index 889f96a1b2..7cda8292c2 100644 --- a/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs +++ b/src/Components/test/E2ETest/Tests/BootResourceCachingTest.cs @@ -148,8 +148,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests private void WaitUntilLoaded() { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.TagName("h1")).Text == "Hello, world!"); + var element = Browser.Exists(By.TagName("h1")); + Browser.Equal("Hello, world!", () => element.Text); } } } diff --git a/src/Components/test/E2ETest/Tests/ClientSideHostingTest.cs b/src/Components/test/E2ETest/Tests/ClientSideHostingTest.cs index a45e536323..63bc9a7ea3 100644 --- a/src/Components/test/E2ETest/Tests/ClientSideHostingTest.cs +++ b/src/Components/test/E2ETest/Tests/ClientSideHostingTest.cs @@ -61,11 +61,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests Assert.NotNull(Browser.FindElement(By.Id("test-selector"))); } - private void WaitUntilLoaded() { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.TagName("app")).Text != "Loading..."); + var app = Browser.Exists(By.TagName("app")); + Browser.NotEqual("Loading...", () => app.Text); } } } diff --git a/src/Components/test/E2ETest/Tests/HostedInAspNetTest.cs b/src/Components/test/E2ETest/Tests/HostedInAspNetTest.cs index 6765362ee0..dda8109fe6 100644 --- a/src/Components/test/E2ETest/Tests/HostedInAspNetTest.cs +++ b/src/Components/test/E2ETest/Tests/HostedInAspNetTest.cs @@ -48,8 +48,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests private void WaitUntilLoaded() { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.TagName("app")).Text != "Loading..."); + var app = Browser.Exists(By.TagName("app")); + Browser.NotEqual("Loading...", () => app.Text); } } } diff --git a/src/Components/test/E2ETest/Tests/HttpClientTest.cs b/src/Components/test/E2ETest/Tests/HttpClientTest.cs index c0ed775938..739d08b7d4 100644 --- a/src/Components/test/E2ETest/Tests/HttpClientTest.cs +++ b/src/Components/test/E2ETest/Tests/HttpClientTest.cs @@ -134,9 +134,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests string WaitAndGetResponseText() { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.Id("response-text")) != null); - return app.FindElement(By.Id("response-text")).Text; + return Browser.Exists(By.Id("response-text")).Text; } } @@ -150,9 +148,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests _appElement.FindElement(By.Id("send-request")).Click(); - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.Id("response-status")) != null); - _responseStatus = _appElement.FindElement(By.Id("response-status")); + _responseStatus = Browser.Exists(By.Id("response-status")); _responseBody = _appElement.FindElement(By.Id("response-body")); _responseHeaders = _appElement.FindElement(By.Id("response-headers")); } diff --git a/src/Components/test/E2ETest/Tests/InteropTest.cs b/src/Components/test/E2ETest/Tests/InteropTest.cs index ef763452a9..0fb093d989 100644 --- a/src/Components/test/E2ETest/Tests/InteropTest.cs +++ b/src/Components/test/E2ETest/Tests/InteropTest.cs @@ -135,8 +135,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests var interopButton = Browser.FindElement(By.Id("btn-interop")); interopButton.Click(); - var wait = new WebDriverWait(Browser, TimeSpan.FromSeconds(10)) - .Until(d => d.FindElement(By.Id("done-with-interop"))); + Browser.Exists(By.Id("done-with-interop")); foreach (var expectedValue in expectedValues) { diff --git a/src/Components/test/E2ETest/Tests/RoutingTest.cs b/src/Components/test/E2ETest/Tests/RoutingTest.cs index d1642864db..f449df459e 100644 --- a/src/Components/test/E2ETest/Tests/RoutingTest.cs +++ b/src/Components/test/E2ETest/Tests/RoutingTest.cs @@ -546,10 +546,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests SetUrlViaPushState("/LongPage1"); - new WebDriverWait(Browser, TimeSpan.FromSeconds(2)).Until( - driver => driver.FindElement(By.Id("loading-banner")) != null); - - Assert.True(app.FindElement(By.Id("loading-banner")) != null); + Browser.Exists(By.Id("loading-banner")); } [Fact] diff --git a/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs b/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs index 4cc1f57cff..f97889f44f 100644 --- a/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs +++ b/src/Components/test/E2ETest/Tests/StandaloneAppTest.cs @@ -96,8 +96,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests // Wait until loaded var tableSelector = By.CssSelector("table.table"); - new WebDriverWait(Browser, TimeSpan.FromSeconds(10)).Until( - driver => driver.FindElement(tableSelector) != null); + Browser.Exists(tableSelector); // Check the table is displayed correctly var rows = Browser.FindElements(By.CssSelector("table.table tbody tr")); @@ -111,8 +110,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests private void WaitUntilLoaded() { - new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until( - driver => driver.FindElement(By.TagName("app")).Text != "Loading..."); + var app = Browser.Exists(By.TagName("app")); + Browser.NotEqual("Loading...", () => app.Text); } public void Dispose() diff --git a/src/Components/test/E2ETest/Tests/WebAssemblyLazyLoadTest.cs b/src/Components/test/E2ETest/Tests/WebAssemblyLazyLoadTest.cs index 65d17697e5..d75aa2e02b 100644 --- a/src/Components/test/E2ETest/Tests/WebAssemblyLazyLoadTest.cs +++ b/src/Components/test/E2ETest/Tests/WebAssemblyLazyLoadTest.cs @@ -66,13 +66,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests { // Navigate to a page with lazy loaded assemblies for the first time SetUrlViaPushState("/WithLazyAssembly"); - var app = Browser.MountTestComponent(); + Browser.MountTestComponent(); // Wait for the page to finish loading - new WebDriverWait(Browser, TimeSpan.FromSeconds(2)).Until( - driver => driver.FindElement(By.Id("use-package-button")) != null); - - var button = app.FindElement(By.Id("use-package-button")); + var button = Browser.Exists(By.Id("use-package-button")); // We should have requested the DLL Assert.True(HasLoadedAssembly("Newtonsoft.Json.dll")); @@ -98,8 +95,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests SetUrlViaPushState("/WithLazyLoadedRoutes"); // Wait for the page to finish loading - new WebDriverWait(Browser, TimeSpan.FromSeconds(2)).Until( - driver => driver.FindElement(By.Id("lazy-load-msg")) != null); + Browser.Exists(By.Id("lazy-load-msg")); // Now the assembly has been loaded Assert.True(HasLoadedAssembly("LazyTestContentPackage.dll")); diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/index.html b/src/Components/test/testassets/BasicTestApp/wwwroot/index.html index ed8bf6a89f..b5c1e33d8f 100644 --- a/src/Components/test/testassets/BasicTestApp/wwwroot/index.html +++ b/src/Components/test/testassets/BasicTestApp/wwwroot/index.html @@ -26,6 +26,7 @@ + diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/js/seleniumworkaround.js b/src/Components/test/testassets/BasicTestApp/wwwroot/js/seleniumworkaround.js new file mode 100644 index 0000000000..645add5e4d --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/wwwroot/js/seleniumworkaround.js @@ -0,0 +1,26 @@ +(function () { + // Note: there are multiple copies of this file throughout the repo. If you're editing it, please look for + // other seleniumworkaround.js files and keep them all in sync. + const logs = []; + + const defaultLog = console.log; + console.log = function () { + defaultLog.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + const defaultError = console.error; + console.error = function () { + defaultError.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + const defaultWarn = console.warn; + console.warn = function () { + defaultWarn.apply(console, arguments); + logs.push(Array.from(arguments).join(' ')); + } + + window.getBrowserLogs = () => logs; +})(); + diff --git a/src/Shared/E2ETesting/BrowserAssertFailedException.cs b/src/Shared/E2ETesting/BrowserAssertFailedException.cs index 08d04a912d..7d9af5d052 100644 --- a/src/Shared/E2ETesting/BrowserAssertFailedException.cs +++ b/src/Shared/E2ETesting/BrowserAssertFailedException.cs @@ -14,12 +14,12 @@ namespace OpenQA.Selenium // case. public class BrowserAssertFailedException : XunitException { - public BrowserAssertFailedException(IReadOnlyList logs, Exception innerException, string screenShotPath, string innerHTML) + public BrowserAssertFailedException(IReadOnlyCollection logs, Exception innerException, string screenShotPath, string innerHTML) : base(BuildMessage(innerException, logs, screenShotPath, innerHTML), innerException) { } - private static string BuildMessage(Exception exception, IReadOnlyList logs, string screenShotPath, string innerHTML) + private static string BuildMessage(Exception exception, IReadOnlyCollection logs, string screenShotPath, string innerHTML) { var builder = new StringBuilder(); builder.AppendLine(exception.ToString()); diff --git a/src/Shared/E2ETesting/WaitAssert.cs b/src/Shared/E2ETesting/WaitAssert.cs index 144715253d..eb936d0ccf 100644 --- a/src/Shared/E2ETesting/WaitAssert.cs +++ b/src/Shared/E2ETesting/WaitAssert.cs @@ -5,8 +5,11 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.ExceptionServices; using OpenQA.Selenium; +using OpenQA.Selenium.DevTools.Page; +using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.UI; using Xunit; @@ -117,7 +120,17 @@ namespace Microsoft.AspNetCore.E2ETesting var fileId = $"{Guid.NewGuid():N}.png"; var screenShotPath = Path.Combine(Path.GetFullPath(E2ETestOptions.Instance.ScreenShotsPath), fileId); - var errors = driver.GetBrowserLogs(LogLevel.All); + var errors = driver.GetBrowserLogs(LogLevel.All).Select(c => c.ToString()).ToList(); + if (errors.Count == 0) + { + // Workaround for selenium bug https://github.com/SeleniumHQ/selenium/issues/8229. Getting log does + // not work. However some of our test apps provide a mechnanism to read the logs. Try that. + + var logs = (IReadOnlyCollection)((IJavaScriptExecutor)driver).ExecuteScript( + "return window.getBrowserLogs && window.getBrowserLogs() || []"); + + errors = logs.Select(l => l.ToString()).ToList(); + } TakeScreenShot(driver, screenShotPath); var exceptionInfo = lastException != null ? ExceptionDispatchInfo.Capture(lastException) :