From 2337e756c6f96f75faa9f013e3093ce4a70dd3c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Thu, 8 Feb 2024 22:59:42 +0100 Subject: [PATCH 1/7] Adding ClickReliablyOnThenWaitForUrlChangeAsync() --- .../NavigationUITestContextExtensions.cs | 12 ++++++++++ .../NavigationWebElementExtensions.cs | 23 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs index 99b15cda7..150c68e0a 100644 --- a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs @@ -296,6 +296,18 @@ public static Task ClickReliablyOnUntilPageLeaveAsync( TimeSpan? interval = null) => context.Get(by).ClickReliablyUntilPageLeaveAsync(context, timeout, interval); + /// + /// A convenience method that merges and so the doesn't have to be passed twice. + /// + public static Task ClickReliablyOnThenWaitForUrlChangeAsync( + this UITestContext context, + By by, + TimeSpan? timeout = null, + TimeSpan? interval = null) => + context.Get(by).ClickReliablyThenWaitForUrlChangeAsync(context, timeout, interval); + /// /// Switches control to JS alert box, accepts it, and switches control back to main document or first frame. /// diff --git a/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs b/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs index 9a8fc7381..33c94ca24 100644 --- a/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs @@ -67,7 +67,8 @@ await context.Configuration.Events.AfterClick /// /// Repeatedly clicks an element until the browser leaves the page. If you're doing a Get() before then use instead. + /// cref="NavigationUITestContextExtensions.ClickReliablyOnUntilPageLeaveAsync(UITestContext, By, TimeSpan?, TimeSpan?)"/> + /// instead. /// public static Task ClickReliablyUntilPageLeaveAsync( this IWebElement element, @@ -82,4 +83,24 @@ public static Task ClickReliablyUntilPageLeaveAsync( }, timeout, interval); + + /// + /// Repeatedly clicks an element until the browser leaves the page. If you're doing a Get() before then use + /// instead. + /// + public static async Task ClickReliablyThenWaitForUrlChangeAsync( + this IWebElement element, + UITestContext context, + TimeSpan? timeout = null, + TimeSpan? interval = null) + { + var originalUri = context.GetCurrentUri(); + await element.ClickReliablyAsync(context); + + context.DoWithRetriesOrFail( + () => context.GetCurrentUri() != originalUri, + timeout, + interval); + } } From f6224e0c751520e9c9bdeff15bfee6abf59c2402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Fri, 9 Feb 2024 02:29:04 +0100 Subject: [PATCH 2/7] Adding ClickReliablyOnByLinkTextAsync() --- .../Extensions/NavigationUITestContextExtensions.cs | 8 ++++++++ .../OrchardCoreDashboardUITestContextExtensions.cs | 4 ++-- .../Extensions/TenantsUITestContextExtensions.cs | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs index 150c68e0a..01214d63c 100644 --- a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs @@ -284,6 +284,14 @@ private static async Task SetFieldDropdownByIndexAsync(UITestContext context, By public static Task ClickReliablyOnAsync(this UITestContext context, By by, int maxTries = 3) => context.Get(by).ClickReliablyAsync(context, maxTries); + /// + /// Reliably clicks on the lnk identified by the given text with . + /// + /// The maximum number of clicks attempted altogether, if retries are needed. + public static Task ClickReliablyOnByLinkTextAsync(this UITestContext context, string linkText, int maxTries = 3) => + context.Get(By.LinkText(linkText)).ClickReliablyAsync(context, maxTries); + /// /// A convenience method that merges and Date: Sun, 11 Feb 2024 20:20:37 +0100 Subject: [PATCH 3/7] ClickReliablyThenWaitForUrlChangeAsync is better to retry the click too, not just wait for the URL change, thus also renaming to ClickReliablyUntilUrlChangeAsync --- .../NavigationUITestContextExtensions.cs | 6 ++--- .../NavigationWebElementExtensions.cs | 27 ++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs index 01214d63c..b54afb0fd 100644 --- a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs @@ -306,15 +306,15 @@ public static Task ClickReliablyOnUntilPageLeaveAsync( /// /// A convenience method that merges and so the doesn't have to be passed twice. /// - public static Task ClickReliablyOnThenWaitForUrlChangeAsync( + public static Task ClickReliablyOnUntilUrlChangeAsync( this UITestContext context, By by, TimeSpan? timeout = null, TimeSpan? interval = null) => - context.Get(by).ClickReliablyThenWaitForUrlChangeAsync(context, timeout, interval); + context.Get(by).ClickReliablyUntilUrlChangeAsync(context, timeout, interval); /// /// Switches control to JS alert box, accepts it, and switches control back to main document or first frame. diff --git a/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs b/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs index 33c94ca24..6ec54ff52 100644 --- a/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs @@ -66,9 +66,11 @@ await context.Configuration.Events.AfterClick }); /// - /// Repeatedly clicks an element until the browser leaves the page. If you're doing a Get() before then use - /// instead. + /// Repeatedly clicks an element until the browser leaves the page. Note that unlike this doesn't just necessitate a URL change but also a page leave. If + /// you're doing a Get() before then use instead. /// public static Task ClickReliablyUntilPageLeaveAsync( this IWebElement element, @@ -85,21 +87,26 @@ public static Task ClickReliablyUntilPageLeaveAsync( interval); /// - /// Repeatedly clicks an element until the browser leaves the page. If you're doing a Get() before then use - /// instead. + /// Repeatedly clicks an element until the browser URL changes. Note that unlike this doesn't necessitate a page leave, but can include it. If you're + /// doing a Get() before then use instead. /// - public static async Task ClickReliablyThenWaitForUrlChangeAsync( + public static Task ClickReliablyUntilUrlChangeAsync( this IWebElement element, UITestContext context, TimeSpan? timeout = null, TimeSpan? interval = null) { var originalUri = context.GetCurrentUri(); - await element.ClickReliablyAsync(context); - context.DoWithRetriesOrFail( - () => context.GetCurrentUri() != originalUri, + return context.DoWithRetriesOrFailAsync( + async () => + { + await element.ClickReliablyAsync(context); + return context.GetCurrentUri() != originalUri; + }, timeout, interval); } From 7d8b14a1f9826de59dbf5a334f43c1ea8421545d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Sun, 11 Feb 2024 20:33:00 +0100 Subject: [PATCH 4/7] Spelling --- Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs index b54afb0fd..01acae2a2 100644 --- a/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/NavigationUITestContextExtensions.cs @@ -285,7 +285,7 @@ public static Task ClickReliablyOnAsync(this UITestContext context, By by, int m context.Get(by).ClickReliablyAsync(context, maxTries); /// - /// Reliably clicks on the lnk identified by the given text with . /// /// The maximum number of clicks attempted altogether, if retries are needed. From ca027d5a7cbdfd9a7c7477e4f4177fa86a35a0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Sun, 11 Feb 2024 21:04:57 +0100 Subject: [PATCH 5/7] Adding helping log messages to log section start and end --- Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs index 849fb0b32..43ce6a329 100644 --- a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs @@ -150,7 +150,9 @@ private static async Task ExecuteSectionAsync( // multiple sections are started in concurrent threads, the result will be incorrect. This shouldn't be too much // of an issue for now though since tests, while async, are single-threaded. context.Scope.AtataContext.Log.Start(section); + context.Scope.AtataContext.Log.Info("Log section {0} started.", section.Message); var result = await functionAsync(); + context.Scope.AtataContext.Log.Info("Log section {0} ended.", section.Message); context.Scope.AtataContext.Log.EndSection(); return result; } From d6d3849121d7625adb1e50725450dadecfee328a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Sun, 11 Feb 2024 21:21:26 +0100 Subject: [PATCH 6/7] Adding logging to log section retries due to StaleElementReferenceException --- .../Extensions/ExtendedLoggingExtensions.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs index 43ce6a329..32bfe12f2 100644 --- a/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs +++ b/Lombiq.Tests.UI/Extensions/ExtendedLoggingExtensions.cs @@ -105,6 +105,7 @@ private static void ExecuteSection(this UITestContext context, LogSection sectio } catch (StaleElementReferenceException) when (notLast) { + LogStaleElementReferenceExceptionRetry(context, i); Thread.Sleep(TimeSpan.FromSeconds(1)); } } @@ -122,6 +123,7 @@ private static TResult ExecuteSection(this UITestContext context, LogSe } catch (StaleElementReferenceException) when (notLast) { + LogStaleElementReferenceExceptionRetry(context, i); Thread.Sleep(TimeSpan.FromSeconds(1)); } } @@ -158,10 +160,18 @@ private static async Task ExecuteSectionAsync( } catch (StaleElementReferenceException) when (notLast) { + LogStaleElementReferenceExceptionRetry(context, i); await Task.Delay(TimeSpan.FromSeconds(1)); } } throw new InvalidOperationException("Impossible to reach."); } + + private static void LogStaleElementReferenceExceptionRetry(UITestContext context, int tryIndex) => + context.Scope.AtataContext.Log.Info( + "The operation in the log section failed with StaleElementReferenceException but will be retried. This " + + "is try number {0} out of {1}.", + tryIndex + 1, + StabilityRetryCount); } From 4c960038594305775977b7e14433fedc4c63480f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Leh=C3=B3czky?= Date: Mon, 12 Feb 2024 23:44:30 +0100 Subject: [PATCH 7/7] Restricting TestAdminPagesAsMonkeyRecursivelyShouldWorkWithAdminUser sample test so it doesn't take too much time --- Lombiq.Tests.UI.Samples/Tests/MonkeyTests.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Lombiq.Tests.UI.Samples/Tests/MonkeyTests.cs b/Lombiq.Tests.UI.Samples/Tests/MonkeyTests.cs index 896663c65..b8b7738ae 100644 --- a/Lombiq.Tests.UI.Samples/Tests/MonkeyTests.cs +++ b/Lombiq.Tests.UI.Samples/Tests/MonkeyTests.cs @@ -58,8 +58,17 @@ public Task TestCurrentPageAsMonkeyRecursivelyShouldWorkWithAnonymousUser() => public Task TestAdminPagesAsMonkeyRecursivelyShouldWorkWithAdminUser() => ExecuteTestAfterSetupAsync( context => + { // Monkey tests needn't all start from the homepage. This one starts from the Orchard admin dashboard. - context.TestAdminAsMonkeyRecursivelyAsync(CreateMonkeyTestingOptions()), + + var monkeyTestingOptions = CreateMonkeyTestingOptions(); + + // So we don't take too much time testing the whole Orchard admin, this sample restricts requests to + // "/Admin". But this is just this sample, you can unleash monkeys on the whole admin too! + monkeyTestingOptions.UrlFilters.Add(new MatchesRegexMonkeyTestingUrlFilter("/Admin$")); + + return context.TestAdminAsMonkeyRecursivelyAsync(monkeyTestingOptions); + }, configuration => configuration.AssertBrowserLog = logEntries => logEntries.ShouldNotContain( logEntry => IsValidAdminBrowserLogEntry(logEntry),