Skip to content

Commit c4c1d33

Browse files
authored
Merge pull request #282 from csf-dev/craigfowler/issue273
Resolve #273 - Fix "wait" tests with remote web driver
2 parents d3a9b15 + a01b9c2 commit c4c1d33

6 files changed

Lines changed: 64 additions & 50 deletions

File tree

CSF.Screenplay.Selenium/Tasks/ClickAndWaitForDocumentReady.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ public class ClickAndWaitForDocumentReady : ISingleElementPerformable
2424
const string COMPLETE_READY_STATE = "complete";
2525

2626
static readonly NamedScriptWithResult<string> getReadyState = Scripts.GetTheDocumentReadyState;
27-
static readonly TimeSpan
28-
pollingInterval = TimeSpan.FromMilliseconds(100),
29-
stalenessTimeout = TimeSpan.FromMilliseconds(500);
3027

3128
readonly TimeSpan waitTimeout;
3229

@@ -39,11 +36,10 @@ public async ValueTask PerformAsAsync(ICanPerform actor, IWebDriver webDriver, L
3936
{
4037
await actor.PerformAsync(ClickOn(element.Value), cancellationToken);
4138
await actor.PerformAsync(WaitUntil(ElementIsStale(element.Value.WebElement))
42-
.ForAtMost(stalenessTimeout)
43-
.WithPollingInterval(pollingInterval)
39+
.ForAtMost(waitTimeout)
4440
.Named($"{element.Value.Name} is no longer on the page"),
4541
cancellationToken);
46-
await actor.PerformAsync(WaitUntil(PageIsReady).ForAtMost(waitTimeout).Named("the page is ready").WithPollingInterval(pollingInterval),
42+
await actor.PerformAsync(WaitUntil(PageIsReady).ForAtMost(waitTimeout).Named("the page is ready"),
4743
cancellationToken);
4844
}
4945

Tests/CSF.Screenplay.Selenium.TestWebapp/Controllers/DelayedOpeningController.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
public class DelayedOpeningController : Controller
66
{
77
[HttpGet, Route("DelayedOpening")]
8-
public async Task<ViewResult> Index()
8+
public async Task<ViewResult> Index(int delaySeconds = 2)
99
{
10-
await Task.Delay(TimeSpan.FromSeconds(2));
11-
return View();
10+
await Task.Delay(TimeSpan.FromSeconds(delaySeconds));
11+
return View(delaySeconds);
1212
}
1313
}

Tests/CSF.Screenplay.Selenium.TestWebapp/Views/DelayedOpening/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<body>
55
<h1>Delayed navigation landing</h1>
66
<p>
7-
This is the page which takes 2 seconds to load.
7+
This is the page which takes @Model second(s) to load.
88
</p>
99
<p id="textContent">You're finally here!</p>
1010
</body>

Tests/CSF.Screenplay.Selenium.TestWebapp/wwwroot/DelayedNavigation.html

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ <h1>Delayed navigation</h1>
66
<p>
77
Click the link below to trigger navigation to a page which takes a few moments before it becomes available.
88
</p>
9-
<a href="/DelayedOpening" id="clickable">Click me</a>
9+
<label for="delaySeconds">Delay loading by (seconds)</label><input type="number" id="delaySeconds" value="2">
10+
<a href="/DelayedOpening?delaySeconds=2" id="clickable">Click me</a>
1011
</body>
12+
<script>
13+
window.onload = function() {
14+
document.getElementById("delaySeconds").addEventListener('change', (ev) => {
15+
document.getElementById("clickable").setAttribute("href", "/DelayedOpening?delaySeconds=" + ev.currentTarget.value);
16+
});
17+
}
18+
</script>
1119
</html>

Tests/CSF.Screenplay.Selenium.Tests/Actions/WaitTests.cs

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System;
33
using CSF.Screenplay.Performables;
44
using CSF.Screenplay.Selenium.Elements;
5+
using OpenQA.Selenium;
6+
using OpenQA.Selenium.Remote;
57
using static CSF.Screenplay.PerformanceStarter;
68
using static CSF.Screenplay.Selenium.PerformableBuilder;
79

@@ -18,21 +20,28 @@ static readonly ITarget
1820

1921
static readonly NamedUri testPage = new NamedUri("WaitTests.html", "the test page");
2022

23+
static int GetDelayMilliseconds(Actor actor)
24+
=> actor.GetAbility<BrowseTheWeb>().WebDriver.Unproxy() is RemoteWebDriver ? 5000 : 2000;
25+
26+
static int GetSufficientWaitMilliseconds(Actor actor)
27+
=> actor.GetAbility<BrowseTheWeb>().WebDriver.Unproxy() is RemoteWebDriver ? 9000 : 4000;
28+
29+
static int GetInsufficientWaitMilliseconds(Actor actor) => 500;
30+
2131
[Test, Screenplay]
2232
public async Task WaitingForSufficientTimeShouldSucceed(IStage stage)
2333
{
2434
var webster = stage.Spotlight<Webster>();
2535

2636
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
27-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
37+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
2838
await When(webster).AttemptsTo(ClickOn(clickableButton));
29-
await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 250ms has elapsed"))
30-
.ForAtMost(TimeSpan.FromMilliseconds(500))
31-
.WithPollingInterval(TimeSpan.FromMilliseconds(150))
39+
await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"))
40+
.ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster)))
3241
);
3342
var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
3443

35-
Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed"));
44+
Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"));
3645
}
3746

3847
[Test, Screenplay]
@@ -41,16 +50,15 @@ public async Task WaitingForSufficientTimeWithIgnoredExceptionsShouldSucceed(ISt
4150
var webster = stage.Spotlight<Webster>();
4251

4352
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
44-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
53+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
4554
await When(webster).AttemptsTo(ClickOn(clickableButton));
46-
await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text("250"))
47-
.ForAtMost(TimeSpan.FromMilliseconds(500))
48-
.WithPollingInterval(TimeSpan.FromMilliseconds(50))
55+
await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text(GetDelayMilliseconds(webster).ToString()))
56+
.ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster)))
4957
.IgnoringTheseExceptionTypes(typeof(TargetNotFoundException))
5058
);
5159
var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
5260

53-
Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed"));
61+
Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"));
5462
}
5563

5664
[Test, Screenplay]
@@ -59,16 +67,15 @@ public async Task WaitingForSufficientTimeWithoutIgnoredExceptionsShouldSucceed(
5967
var webster = stage.Spotlight<Webster>();
6068

6169
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
62-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
70+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
6371
await When(webster).AttemptsTo(ClickOn(clickableButton));
64-
await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text("250"))
65-
.ForAtMost(TimeSpan.FromMilliseconds(500))
66-
.WithPollingInterval(TimeSpan.FromMilliseconds(50))
72+
await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text(GetDelayMilliseconds(webster).ToString()))
73+
.ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster)))
6774
// No ignored exceptions specified here, but the default behaviour will ignore TargetNotFoundException
6875
);
6976
var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
7077

71-
Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed"));
78+
Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"));
7279
}
7380

7481
[Test, Screenplay]
@@ -77,11 +84,10 @@ public async Task WaitingForSufficientTimeWithEmptyIgnoredExceptionsShouldThrow(
7784
var webster = stage.Spotlight<Webster>();
7885

7986
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
80-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
87+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
8188
await When(webster).AttemptsTo(ClickOn(clickableButton));
82-
Assert.That(async () => await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text("250"))
83-
.ForAtMost(TimeSpan.FromMilliseconds(500))
84-
.WithPollingInterval(TimeSpan.FromMilliseconds(50))
89+
Assert.That(async () => await Then(webster).Should(WaitUntil(displayTextSpan.Has().Text(GetDelayMilliseconds(webster).ToString()))
90+
.ForAtMost(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster)))
8591
.IgnoringTheseExceptionTypes() // Explicitly empty
8692
), Throws.InstanceOf<PerformableException>());
8793
}
@@ -92,40 +98,41 @@ public async Task WaitingForInsufficientTimeShouldThrow(IStage stage)
9298
var webster = stage.Spotlight<Webster>();
9399

94100
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
95-
await Given(webster).WasAbleTo(EnterTheText("2000").Into(delayTimer));
101+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
96102
await When(webster).AttemptsTo(ClickOn(clickableButton));
97103

98-
Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 2000ms has elapsed")).ForAtMost(TimeSpan.FromMilliseconds(500))),
99-
Throws.InstanceOf<PerformableException>().And.InnerException.TypeOf<OpenQA.Selenium.WebDriverTimeoutException>());
104+
Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"))
105+
.ForAtMost(TimeSpan.FromMilliseconds(GetInsufficientWaitMilliseconds(webster)))),
106+
Throws.InstanceOf<PerformableException>().And.InnerException.TypeOf<WebDriverTimeoutException>());
100107
}
101108

102109
[Test, Screenplay]
103110
public async Task WaitingForSufficientTimeUsingDefaultWaitAbilityShouldSucceed(IStage stage)
104111
{
105112
var webster = stage.Spotlight<Webster>();
106-
webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(500)));
113+
webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster))));
107114

108115
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
109-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
116+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
110117
await When(webster).AttemptsTo(ClickOn(clickableButton));
111-
await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 250ms has elapsed")));
118+
await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed")));
112119
var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
113120

114-
Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed"));
121+
Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"));
115122
}
116123

117124
[Test, Screenplay]
118125
public async Task WaitingForInsufficientTimeUsingDefaultWaitAbilityShouldThrow(IStage stage)
119126
{
120127
var webster = stage.Spotlight<Webster>();
121-
webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(100)));
128+
webster.IsAbleTo(new UseADefaultWaitTime(TimeSpan.FromMilliseconds(GetInsufficientWaitMilliseconds(webster))));
122129

123130
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
124-
await Given(webster).WasAbleTo(EnterTheText("2000").Into(delayTimer));
131+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
125132
await When(webster).AttemptsTo(ClickOn(clickableButton));
126133

127-
Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text("Clicked, and 2000ms has elapsed"))),
128-
Throws.InstanceOf<PerformableException>().And.InnerException.TypeOf<OpenQA.Selenium.WebDriverTimeoutException>());
134+
Assert.That(async () => await Then(webster).Should(WaitUntil(displayText.Has().Text($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"))),
135+
Throws.InstanceOf<PerformableException>().And.InnerException.TypeOf<WebDriverTimeoutException>());
129136
}
130137

131138
[Test, Screenplay]
@@ -134,12 +141,12 @@ public async Task WaitingForSufficientTimeWithoutAPredicateShouldSucceed(IStage
134141
var webster = stage.Spotlight<Webster>();
135142

136143
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
137-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
144+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
138145
await When(webster).AttemptsTo(ClickOn(clickableButton));
139-
await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(300)));
146+
await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(GetSufficientWaitMilliseconds(webster))));
140147
var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
141148

142-
Assert.That(contents, Is.EqualTo("Clicked, and 250ms has elapsed"));
149+
Assert.That(contents, Is.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"));
143150
}
144151

145152
[Test, Screenplay]
@@ -148,11 +155,11 @@ public async Task WaitingForInsufficientTimeWithoutAPredicateShouldYieldIncorrec
148155
var webster = stage.Spotlight<Webster>();
149156

150157
await Given(webster).WasAbleTo(OpenTheUrl(testPage));
151-
await Given(webster).WasAbleTo(EnterTheText("250").Into(delayTimer));
158+
await Given(webster).WasAbleTo(EnterTheText(GetDelayMilliseconds(webster).ToString()).Into(delayTimer));
152159
await When(webster).AttemptsTo(ClickOn(clickableButton));
153-
await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(10)));
160+
await Then(webster).Should(WaitFor(TimeSpan.FromMilliseconds(GetInsufficientWaitMilliseconds(webster))));
154161
var contents = await Then(webster).Should(ReadFromTheElement(displayText).TheText());
155162

156-
Assert.That(contents, Is.Not.EqualTo("Clicked, and 250ms has elapsed"));
163+
Assert.That(contents, Is.Not.EqualTo($"Clicked, and {GetDelayMilliseconds(webster)}ms has elapsed"));
157164
}
158165
}

Tests/CSF.Screenplay.Selenium.Tests/Tasks/ClickAndWaitForDocumentReadyTests.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using CSF.Screenplay.Performables;
34
using CSF.Screenplay.Selenium.Elements;
45
using OpenQA.Selenium;
@@ -15,6 +16,8 @@ static readonly ITarget
1516
link = new ElementId("clickable"),
1617
displayText = new ElementId("textContent");
1718

19+
static readonly string[] ignoredBrowsers = ["chrome", "MicrosoftEdge"];
20+
1821
[Test, Screenplay]
1922
public async Task PerformAsAsyncShouldWaitSoItCanGetTheAppropriateContent(IStage stage)
2023
{
@@ -33,12 +36,12 @@ public async Task PerformAsAsyncShouldThrowIfWeDontWaitLongEnough(IStage stage)
3336
var webster = stage.Spotlight<Webster>();
3437
var ability = webster.GetAbility<BrowseTheWeb>();
3538

36-
if(ability.DriverOptions.BrowserName == "chrome")
37-
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");
39+
if(ignoredBrowsers.Contains(ability.DriverOptions.BrowserName))
40+
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.");
3841

3942
await Given(webster).WasAbleTo(OpenTheUrl(startPage));
4043

41-
Assert.That(async () => await When(webster).AttemptsTo(ClickOn(link).AndWaitForANewPageToLoad(TimeSpan.FromMilliseconds(200))),
44+
Assert.That(async () => await When(webster).AttemptsTo(ClickOn(link).AndWaitForANewPageToLoad(TimeSpan.FromMilliseconds(100))),
4245
Throws.InstanceOf<PerformableException>().And.InnerException.InstanceOf<WebDriverException>());
4346
}
4447
}

0 commit comments

Comments
 (0)