diff --git a/CSF.Screenplay.Selenium/Tasks/ClickAndWaitForDocumentReady.cs b/CSF.Screenplay.Selenium/Tasks/ClickAndWaitForDocumentReady.cs index 385f48f2..69aaf007 100644 --- a/CSF.Screenplay.Selenium/Tasks/ClickAndWaitForDocumentReady.cs +++ b/CSF.Screenplay.Selenium/Tasks/ClickAndWaitForDocumentReady.cs @@ -24,9 +24,6 @@ public class ClickAndWaitForDocumentReady : ISingleElementPerformable const string COMPLETE_READY_STATE = "complete"; static readonly NamedScriptWithResult getReadyState = Scripts.GetTheDocumentReadyState; - static readonly TimeSpan - pollingInterval = TimeSpan.FromMilliseconds(100), - stalenessTimeout = TimeSpan.FromMilliseconds(500); readonly TimeSpan waitTimeout; @@ -39,11 +36,10 @@ public async ValueTask PerformAsAsync(ICanPerform actor, IWebDriver webDriver, L { await actor.PerformAsync(ClickOn(element.Value), cancellationToken); await actor.PerformAsync(WaitUntil(ElementIsStale(element.Value.WebElement)) - .ForAtMost(stalenessTimeout) - .WithPollingInterval(pollingInterval) + .ForAtMost(waitTimeout) .Named($"{element.Value.Name} is no longer on the page"), cancellationToken); - await actor.PerformAsync(WaitUntil(PageIsReady).ForAtMost(waitTimeout).Named("the page is ready").WithPollingInterval(pollingInterval), + await actor.PerformAsync(WaitUntil(PageIsReady).ForAtMost(waitTimeout).Named("the page is ready"), cancellationToken); } diff --git a/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs b/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs index 4453e94f..f877f04c 100644 --- a/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs +++ b/Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs @@ -5,9 +5,9 @@ public class DelayedOpeningController : Controller { [HttpGet, Route("DelayedOpening")] - public async Task Index() + public async Task Index(int delaySeconds = 2) { - await Task.Delay(TimeSpan.FromSeconds(2)); - return View(); + await Task.Delay(TimeSpan.FromSeconds(delaySeconds)); + return View(delaySeconds); } } \ No newline at end of file diff --git a/Tests/CSF.Screenplay.Selenium.TestWebapp/Views/DelayedOpening/Index.cshtml b/Tests/CSF.Screenplay.Selenium.TestWebapp/Views/DelayedOpening/Index.cshtml index d7710b8f..6123f15e 100644 --- a/Tests/CSF.Screenplay.Selenium.TestWebapp/Views/DelayedOpening/Index.cshtml +++ b/Tests/CSF.Screenplay.Selenium.TestWebapp/Views/DelayedOpening/Index.cshtml @@ -4,7 +4,7 @@

Delayed navigation landing

- This is the page which takes 2 seconds to load. + This is the page which takes @Model second(s) to load.

You're finally here!

diff --git a/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/DelayedNavigation.html b/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/DelayedNavigation.html index c878b7c1..f3606a38 100644 --- a/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/DelayedNavigation.html +++ b/Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/DelayedNavigation.html @@ -6,6 +6,14 @@

Delayed navigation

Click the link below to trigger navigation to a page which takes a few moments before it becomes available.

- Click me + + Click me + \ No newline at end of file diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Actions/WaitTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Actions/WaitTests.cs index db9b43b6..7f5cc899 100644 --- a/Tests/CSF.Screenplay.Selenium.Tests/Actions/WaitTests.cs +++ b/Tests/CSF.Screenplay.Selenium.Tests/Actions/WaitTests.cs @@ -2,6 +2,8 @@ using System; using CSF.Screenplay.Performables; using CSF.Screenplay.Selenium.Elements; +using OpenQA.Selenium; +using OpenQA.Selenium.Remote; using static CSF.Screenplay.PerformanceStarter; using static CSF.Screenplay.Selenium.PerformableBuilder; @@ -18,21 +20,28 @@ static readonly ITarget static readonly NamedUri testPage = new NamedUri("WaitTests.html", "the test page"); + static int GetDelayMilliseconds(Actor actor) + => actor.GetAbility().WebDriver.Unproxy() is RemoteWebDriver ? 5000 : 2000; + + static int GetSufficientWaitMilliseconds(Actor actor) + => actor.GetAbility().WebDriver.Unproxy() is RemoteWebDriver ? 9000 : 4000; + + static int GetInsufficientWaitMilliseconds(Actor actor) => 500; + [Test, Screenplay] public async Task WaitingForSufficientTimeShouldSucceed(IStage stage) { var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 250ms has elapsed")) - .ForAtMost(TimeSpan.FromMilliseconds(500)) - .WithPollingInterval(TimeSpan.FromMilliseconds(150)) + await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")) + .ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster))) ); var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText()); - Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed")); + Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")); } [Test, Screenplay] @@ -41,16 +50,15 @@ public async Task WaitingForSufficientTimeWithIgnoredExceptionsShouldSucceed(ISt var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text("250")) - .ForAtMost(TimeSpan.FromMilliseconds(500)) - .WithPollingInterval(TimeSpan.FromMilliseconds(50)) + await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text(GetDelayMilliseconds(webster).ToString())) + .ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster))) .IgnoringTheseExceptionTypes(typeof(TargetNotFoundException)) ); var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText()); - Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed")); + Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")); } [Test, Screenplay] @@ -59,16 +67,15 @@ public async Task WaitingForSufficientTimeWithoutIgnoredExceptionsShouldSucceed( var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text("250")) - .ForAtMost(TimeSpan.FromMilliseconds(500)) - .WithPollingInterval(TimeSpan.FromMilliseconds(50)) + await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text(GetDelayMilliseconds(webster).ToString())) + .ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster))) // No ignored exceptions specified here, but the default behaviour will ignore TargetNotFoundException ); var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText()); - Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed")); + Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")); } [Test, Screenplay] @@ -77,11 +84,10 @@ public async Task WaitingForSufficientTimeWithEmptyIgnoredExceptionsShouldThrow( var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - Assert.That(async () => await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text("250")) - .ForAtMost(TimeSpan.FromMilliseconds(500)) - .WithPollingInterval(TimeSpan.FromMilliseconds(50)) + Assert.That(async () => await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text(GetDelayMilliseconds(webster).ToString())) + .ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster))) .IgnoringTheseExceptionTypes() // Explicitly empty ), Throws.InstanceOf()); } @@ -92,40 +98,41 @@ public async Task WaitingForInsufficientTimeShouldThrow(IStage stage) var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("2000").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 2000ms has elapsed")).ForAtMost(TimeSpan.FromMilliseconds(500))), - Throws.InstanceOf().And.InnerException.TypeOf()); + Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")) + .ForAtMost(TimeSpan.FromMilliseconds(GetInsufficientWaitMilliseconds(webster)))), + Throws.InstanceOf().And.InnerException.TypeOf()); } [Test, Screenplay] public async Task WaitingForSufficientTimeUsingDefaultWaitAbilityShouldSucceed(IStage stage) { var webster = stage.Spotlight(); - webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(500))); + webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster)))); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 250ms has elapsed"))); + await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"))); var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText()); - Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed")); + Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")); } [Test, Screenplay] public async Task WaitingForInsufficientTimeUsingDefaultWaitAbilityShouldThrow(IStage stage) { var webster = stage.Spotlight(); - webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(100))); + webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(GetInsufficientWaitMilliseconds(webster)))); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("2000").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 2000ms has elapsed"))), - Throws.InstanceOf().And.InnerException.TypeOf()); + Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"))), + Throws.InstanceOf().And.InnerException.TypeOf()); } [Test, Screenplay] @@ -134,12 +141,12 @@ public async Task WaitingForSufficientTimeWithoutAPredicateShouldSucceed(IStage var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(300))); + await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster)))); var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText()); - Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed")); + Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")); } [Test, Screenplay] @@ -148,11 +155,11 @@ public async Task WaitingForInsufficientTimeWithoutAPredicateShouldYieldIncorrec var webster = stage.Spotlight(); await Given(webster).WasAbleTo(OpenTheUrl(testPage)); - await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer)); + await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer)); await When(webster).AttemptsTo(ClickOn(clickableButton)); - await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(10))); + await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(GetInsufficientWaitMilliseconds(webster)))); var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText()); - Assert.That(contents, Is.Not.EqualTo("Clicked, and 250ms has elapsed")); + Assert.That(contents, Is.Not.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")); } } \ No newline at end of file diff --git a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/ClickAndWaitForDocumentReadyTests.cs b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/ClickAndWaitForDocumentReadyTests.cs index 907afe03..f11f401f 100644 --- a/Tests/CSF.Screenplay.Selenium.Tests/Tasks/ClickAndWaitForDocumentReadyTests.cs +++ b/Tests/CSF.Screenplay.Selenium.Tests/Tasks/ClickAndWaitForDocumentReadyTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using CSF.Screenplay.Performables; using CSF.Screenplay.Selenium.Elements; using OpenQA.Selenium; @@ -15,6 +16,8 @@ static readonly ITarget link = new ElementId("clickable"), displayText = new ElementId("textContent"); + static readonly string[] ignoredBrowsers = ["chrome", "MicrosoftEdge"]; + [Test, Screenplay] public async Task PerformAsAsyncShouldWaitSoItCanGetTheAppropriateContent(IStage stage) { @@ -33,12 +36,12 @@ public async Task PerformAsAsyncShouldThrowIfWeDontWaitLongEnough(IStage stage) var webster = stage.Spotlight(); var ability = webster.GetAbility(); - if(ability.DriverOptions.BrowserName == "chrome") - Assert.Pass("This test cannot meaningfully be run on a Chrome browser, because it always waits for the page load; treating as an implicit pass"); + if(ignoredBrowsers.Contains(ability.DriverOptions.BrowserName)) + Assert.Pass("This test cannot meaningfully be run on a Chrome or Edge browser, because they always wait for the page load. Treating this test as an implicit pass."); await Given(webster).WasAbleTo(OpenTheUrl(startPage)); - Assert.That(async () => await When(webster).AttemptsTo(ClickOn(link).AndWaitForANewPageToLoad(TimeSpan.FromMilliseconds(200))), + Assert.That(async () => await When(webster).AttemptsTo(ClickOn(link).AndWaitForANewPageToLoad(TimeSpan.FromMilliseconds(100))), Throws.InstanceOf().And.InnerException.InstanceOf()); } } \ No newline at end of file