From c8108f77b2062d5852c301950bbb7c19515632d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El-Saig=20D=C3=A1vid?= Date: Mon, 14 Aug 2023 12:41:10 +0200 Subject: [PATCH 1/5] Set BinaryLocation. --- Lombiq.Tests.UI/Services/WebDriverFactory.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lombiq.Tests.UI/Services/WebDriverFactory.cs b/Lombiq.Tests.UI/Services/WebDriverFactory.cs index 0df9036cf..bb9661b0b 100644 --- a/Lombiq.Tests.UI/Services/WebDriverFactory.cs +++ b/Lombiq.Tests.UI/Services/WebDriverFactory.cs @@ -1,4 +1,5 @@ using Atata.WebDriverSetup; +using Lombiq.HelpfulLibraries.Cli.Helpers; using Lombiq.Tests.UI.Extensions; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; @@ -8,6 +9,8 @@ using OpenQA.Selenium.IE; using System; using System.IO; +using System.Linq; +using System.Runtime.InteropServices; namespace Lombiq.Tests.UI.Services; @@ -64,6 +67,15 @@ public static EdgeDriver CreateEdgeDriver(BrowserConfiguration configuration, Ti options.SetCommonChromiumOptions(configuration); + // While the Edge driver easily locates Edge in Windows, it struggles in Linux, where the different release + // channels have different executable names. This setting looks up the "microsoft-edge-stable" command and + // sets the full path as the browser's binary location. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + CliWrapHelper.WhichAsync("microsoft-edge-stable").GetAwaiter().GetResult().FirstOrDefault() is { } binaryLocation) + { + options.BinaryLocation = binaryLocation.FullName; + } + configuration.BrowserOptionsConfigurator?.Invoke(options); var service = EdgeDriverService.CreateDefaultService(); From bfb5a39a814d8963bce1e346aa78cf0c4fcf6cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El-Saig=20D=C3=A1vid?= Date: Mon, 14 Aug 2023 12:41:35 +0200 Subject: [PATCH 2/5] Enable edge. --- Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs b/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs index 2ff9f1ffc..8be3276cd 100644 --- a/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs +++ b/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs @@ -54,7 +54,7 @@ public Task VerifyBlogImage(Browser browser) => // is the different rendering of text on each platform, but it can occur between different Linux distributions too. // Here: https://pandasauce.org/post/linux-fonts/ you can find a good summary about this from 2019, but still valid // in 2022. - [Theory, Chrome] + [Theory, Edge] public Task VerifyNavbar(Browser browser) => ExecuteTestAfterSetupAsync( context => From 7c00b12327810e2fb2a6ab42651fa1b53ff749f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El-Saig=20D=C3=A1vid?= Date: Mon, 14 Aug 2023 12:47:53 +0200 Subject: [PATCH 3/5] Restore Chrome for VerifyNavbar. --- Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs b/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs index 8be3276cd..15062985f 100644 --- a/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs +++ b/Lombiq.Tests.UI.Samples/Tests/BasicVisualVerificationTests.cs @@ -54,7 +54,7 @@ public Task VerifyBlogImage(Browser browser) => // is the different rendering of text on each platform, but it can occur between different Linux distributions too. // Here: https://pandasauce.org/post/linux-fonts/ you can find a good summary about this from 2019, but still valid // in 2022. - [Theory, Edge] + [Theory, Chrome, Edge] public Task VerifyNavbar(Browser browser) => ExecuteTestAfterSetupAsync( context => From 05b3e02cb9e01f2fbce479d5e7a59768161d6291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Mon, 14 Aug 2023 17:34:01 +0200 Subject: [PATCH 4/5] Typos --- Lombiq.Tests.UI/Services/WebDriverFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI/Services/WebDriverFactory.cs b/Lombiq.Tests.UI/Services/WebDriverFactory.cs index bb9661b0b..627b41126 100644 --- a/Lombiq.Tests.UI/Services/WebDriverFactory.cs +++ b/Lombiq.Tests.UI/Services/WebDriverFactory.cs @@ -67,7 +67,7 @@ public static EdgeDriver CreateEdgeDriver(BrowserConfiguration configuration, Ti options.SetCommonChromiumOptions(configuration); - // While the Edge driver easily locates Edge in Windows, it struggles in Linux, where the different release + // While the Edge driver easily locates Edge on Windows, it struggles on Linux, where the different release // channels have different executable names. This setting looks up the "microsoft-edge-stable" command and // sets the full path as the browser's binary location. if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && From 6fc7461212561cb9bc58bde9aa94ff6bec653232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20El-Saig?= Date: Mon, 14 Aug 2023 18:49:54 +0200 Subject: [PATCH 5/5] Make setup async. --- Lombiq.Tests.UI/Services/AtataFactory.cs | 25 ++++++++--- .../Services/UITestExecutionSession.cs | 2 +- Lombiq.Tests.UI/Services/WebDriverFactory.cs | 44 ++++++++++++------- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Lombiq.Tests.UI/Services/AtataFactory.cs b/Lombiq.Tests.UI/Services/AtataFactory.cs index 854fd6e92..a94f8bc70 100644 --- a/Lombiq.Tests.UI/Services/AtataFactory.cs +++ b/Lombiq.Tests.UI/Services/AtataFactory.cs @@ -3,7 +3,9 @@ using Lombiq.HelpfulLibraries.Common.Utilities; using OpenQA.Selenium; using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Xunit.Abstractions; namespace Lombiq.Tests.UI.Services; @@ -16,7 +18,16 @@ public class AtataConfiguration public static class AtataFactory { + [Obsolete($"Please use {nameof(StartAtataScopeAsync)} instead.")] + [SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "Backwards compatibility.")] + [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Backwards compatibility.")] public static AtataScope StartAtataScope( + ITestOutputHelper testOutputHelper, + Uri baseUri, + OrchardCoreUITestExecutorConfiguration configuration) => + StartAtataScopeAsync(testOutputHelper, baseUri, configuration).GetAwaiter().GetResult(); + + public static async Task StartAtataScopeAsync( ITestOutputHelper testOutputHelper, Uri baseUri, OrchardCoreUITestExecutorConfiguration configuration) @@ -33,7 +44,7 @@ public static AtataScope StartAtataScope( var builder = AtataContext.Configure() // The drivers are disposed when disposing AtataScope. #pragma warning disable CA2000 // Dispose objects before losing scope - .UseDriver(CreateDriver(browserConfiguration, timeoutConfiguration, testOutputHelper)) + .UseDriver(await CreateDriverAsync(browserConfiguration, timeoutConfiguration, testOutputHelper)) #pragma warning restore CA2000 // Dispose objects before losing scope .UseBaseUrl(baseUri.ToString()) .UseCulture(browserConfiguration.AcceptLanguage.ToString()) @@ -56,12 +67,12 @@ public static void SetupShellCliCommandFactory() => .UseCmdForWindows() .UseForOtherOS(new BashShellCliCommandFactory("-login")); - private static IWebDriver CreateDriver( + private static async Task CreateDriverAsync( BrowserConfiguration browserConfiguration, TimeoutConfiguration timeoutConfiguration, ITestOutputHelper testOutputHelper) { - IWebDriver From(Func factory) + Task FromAsync(Func> factory) where T : IWebDriver => factory(browserConfiguration, timeoutConfiguration.PageLoadTimeout); @@ -82,10 +93,10 @@ IWebDriver From(Func factory) { return browserConfiguration.Browser switch { - Browser.Chrome => From(WebDriverFactory.CreateChromeDriver), - Browser.Edge => From(WebDriverFactory.CreateEdgeDriver), - Browser.Firefox => From(WebDriverFactory.CreateFirefoxDriver), - Browser.InternetExplorer => From(WebDriverFactory.CreateInternetExplorerDriver), + Browser.Chrome => await FromAsync(WebDriverFactory.CreateChromeDriverAsync), + Browser.Edge => await FromAsync(WebDriverFactory.CreateEdgeDriverAsync), + Browser.Firefox => await FromAsync(WebDriverFactory.CreateFirefoxDriverAsync), + Browser.InternetExplorer => await FromAsync(WebDriverFactory.CreateInternetExplorerDriverAsync), _ => throw new InvalidOperationException($"Unknown browser: {browserConfiguration.Browser}."), }; } diff --git a/Lombiq.Tests.UI/Services/UITestExecutionSession.cs b/Lombiq.Tests.UI/Services/UITestExecutionSession.cs index 0d280cf79..48199cdef 100644 --- a/Lombiq.Tests.UI/Services/UITestExecutionSession.cs +++ b/Lombiq.Tests.UI/Services/UITestExecutionSession.cs @@ -632,7 +632,7 @@ Task UITestingBeforeAppStartHandlerAsync(string contentRootPath, InstanceCommand _configuration.Events.AfterPageChange += TakeScreenshotIfEnabledAsync; } - var atataScope = AtataFactory.StartAtataScope(_testOutputHelper, uri, _configuration); + var atataScope = await AtataFactory.StartAtataScopeAsync(_testOutputHelper, uri, _configuration); return new UITestContext( contextId, diff --git a/Lombiq.Tests.UI/Services/WebDriverFactory.cs b/Lombiq.Tests.UI/Services/WebDriverFactory.cs index bb9661b0b..dd9b8795d 100644 --- a/Lombiq.Tests.UI/Services/WebDriverFactory.cs +++ b/Lombiq.Tests.UI/Services/WebDriverFactory.cs @@ -11,6 +11,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; namespace Lombiq.Tests.UI.Services; @@ -18,9 +19,9 @@ public static class WebDriverFactory { private static readonly object _setupLock = new(); - public static ChromeDriver CreateChromeDriver(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) + public static Task CreateChromeDriverAsync(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) { - ChromeDriver CreateDriverInner(ChromeDriverService service) + Task CreateDriverInnerAsync(ChromeDriverService service) { var chromeConfig = new ChromeConfiguration { Options = new ChromeOptions().SetCommonOptions() }; @@ -48,20 +49,22 @@ ChromeDriver CreateDriverInner(ChromeDriverService service) // Helps with misconfigured hosts. if (chromeConfig.Service.HostName == "localhost") chromeConfig.Service.HostName = "127.0.0.1"; - return new ChromeDriver(chromeConfig.Service, chromeConfig.Options, pageLoadTimeout).SetCommonTimeouts(pageLoadTimeout); + return Task.FromResult( + new ChromeDriver(chromeConfig.Service, chromeConfig.Options, pageLoadTimeout) + .SetCommonTimeouts(pageLoadTimeout)); } var chromeWebDriverPath = Environment.GetEnvironmentVariable("CHROMEWEBDRIVER"); // #spell-check-ignore-line if (chromeWebDriverPath is { } driverPath && Directory.Exists(driverPath)) { - return CreateDriverInner(ChromeDriverService.CreateDefaultService(driverPath)); + return CreateDriverInnerAsync(ChromeDriverService.CreateDefaultService(driverPath)); } - return CreateDriver(BrowserNames.Chrome, () => CreateDriverInner(service: null)); + return CreateDriverAsync(BrowserNames.Chrome, () => CreateDriverInnerAsync(service: null)); } - public static EdgeDriver CreateEdgeDriver(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) => - CreateDriver(BrowserNames.Edge, () => + public static Task CreateEdgeDriverAsync(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) => + CreateDriverAsync(BrowserNames.Edge, async () => { var options = new EdgeOptions().SetCommonOptions(); @@ -71,7 +74,7 @@ public static EdgeDriver CreateEdgeDriver(BrowserConfiguration configuration, Ti // channels have different executable names. This setting looks up the "microsoft-edge-stable" command and // sets the full path as the browser's binary location. if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && - CliWrapHelper.WhichAsync("microsoft-edge-stable").GetAwaiter().GetResult().FirstOrDefault() is { } binaryLocation) + (await CliWrapHelper.WhichAsync("microsoft-edge-stable"))?.FirstOrDefault() is { } binaryLocation) { options.BinaryLocation = binaryLocation.FullName; } @@ -84,7 +87,7 @@ public static EdgeDriver CreateEdgeDriver(BrowserConfiguration configuration, Ti return new EdgeDriver(service, options).SetCommonTimeouts(pageLoadTimeout); }); - public static FirefoxDriver CreateFirefoxDriver(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) + public static Task CreateFirefoxDriverAsync(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) { var options = new FirefoxOptions().SetCommonOptions(); @@ -101,11 +104,13 @@ public static FirefoxDriver CreateFirefoxDriver(BrowserConfiguration configurati configuration.BrowserOptionsConfigurator?.Invoke(options); - return CreateDriver(BrowserNames.Firefox, () => new FirefoxDriver(options).SetCommonTimeouts(pageLoadTimeout)); + return CreateDriverAsync( + BrowserNames.Firefox, + () => Task.FromResult(new FirefoxDriver(options).SetCommonTimeouts(pageLoadTimeout))); } - public static InternetExplorerDriver CreateInternetExplorerDriver(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) => - CreateDriver(BrowserNames.InternetExplorer, () => + public static Task CreateInternetExplorerDriverAsync(BrowserConfiguration configuration, TimeSpan pageLoadTimeout) => + CreateDriverAsync(BrowserNames.InternetExplorer, () => { var options = new InternetExplorerOptions().SetCommonOptions(); @@ -113,7 +118,7 @@ public static InternetExplorerDriver CreateInternetExplorerDriver(BrowserConfigu options.AcceptInsecureCertificates = false; configuration.BrowserOptionsConfigurator?.Invoke(options); - return new InternetExplorerDriver(options).SetCommonTimeouts(pageLoadTimeout); + return Task.FromResult(new InternetExplorerDriver(options).SetCommonTimeouts(pageLoadTimeout)); }); private static TDriverOptions SetCommonOptions(this TDriverOptions driverOptions) @@ -172,13 +177,13 @@ private static TDriver SetCommonTimeouts(this TDriver driver, TimeSpan return driver; } - private static TDriver CreateDriver(string browserName, Func driverFactory) + private static async Task CreateDriverAsync(string browserName, Func> driverFactory) where TDriver : IWebDriver { try { - lock (_setupLock) DriverSetup.AutoSetUp(browserName); - return driverFactory(); + AutoSetup(browserName); + return await driverFactory(); } catch (Exception ex) { @@ -189,6 +194,13 @@ private static TDriver CreateDriver(string browserName, Func d } } + // We don't use the async version of auto setup because it doesn't do any locking. In fact it's just the sync method + // passed to Task.Run() so it wouldn't benefit us anyway. + private static void AutoSetup(string browserName) + { + lock (_setupLock) DriverSetup.AutoSetUp(browserName); + } + private sealed class ChromeConfiguration { public ChromeOptions Options { get; init; }