Skip to content

Commit 181524f

Browse files
authored
Merge pull request #474 from Lombiq/issue/OSOE-1057
OSOE-1057: Checking for the Chrome pseudo-StaleElementReferenceException everywhere too
2 parents 91dc517 + 367da11 commit 181524f

File tree

5 files changed

+56
-48
lines changed

5 files changed

+56
-48
lines changed

Lombiq.Tests.UI/Extensions/NavigationWebElementExtensions.cs

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,49 +29,50 @@ public static Task ClickReliablyAsync(this IWebElement element, UITestContext co
2929
element,
3030
async () =>
3131
{
32-
try
33-
{
34-
await context.Configuration.Events.BeforeClick
35-
.InvokeAsync<ClickEventHandler>(eventHandler => eventHandler(context, element));
32+
await context.Configuration.Events.BeforeClick
33+
.InvokeAsync<ClickEventHandler>(eventHandler => eventHandler(context, element));
3634

37-
// When the button is under some overhanging UI element, the MoveToElement sometimes fails with the
38-
// "move target out of bounds" exception message. And while the UI is changing, wrongly timed clicks
39-
// can fail with StaleElementReferenceExceptions.
40-
// In these cases it should be retried.
41-
var notFound = true;
42-
for (var i = 1; notFound && i <= maxTries; i++)
35+
// When the button is under some overhanging UI element, the MoveToElement sometimes fails with the
36+
// "move target out of bounds" exception message. And while the UI is changing, wrongly timed clicks can
37+
// fail with StaleElementReferenceExceptions. In these cases it should be retried.
38+
var notFound = true;
39+
for (var i = 1; notFound && i <= maxTries; i++)
40+
{
41+
try
4342
{
44-
try
45-
{
46-
context.Driver.Perform(actions => actions.MoveToElement(element).Click());
47-
notFound = false;
48-
}
49-
catch (WebDriverException ex) when (i < maxTries && ex.Message.Contains("move target out of bounds"))
43+
context.Driver.Perform(actions => actions.MoveToElement(element).Click());
44+
notFound = false;
45+
}
46+
catch (WebDriverException ex) when (i < maxTries)
47+
{
48+
switch (ex.Message)
5049
{
51-
context.Configuration.TestOutputHelper.WriteLineTimestampedAndDebug(
52-
"\"move target out of bounds\" exception, retrying the click.");
50+
case string message when message.Contains("move target out of bounds"):
51+
context.Configuration.TestOutputHelper.WriteLineTimestampedAndDebug(
52+
"\"move target out of bounds\" exception, retrying the click.");
53+
break;
5354

54-
await Task.Delay(RetrySettings.Interval, context.Configuration.TestCancellationToken);
55-
}
56-
catch (StaleElementReferenceException) when (i < maxTries)
57-
{
58-
context.Configuration.TestOutputHelper.WriteLineTimestampedAndDebug(
59-
"Stale element reference exception, retrying the click.");
55+
case string message when ex.IsStateElementLikeException():
56+
context.Configuration.TestOutputHelper.WriteLineTimestampedAndDebug(
57+
"Stale element exception with the message \"{0}\", retrying the click.",
58+
message);
59+
break;
60+
61+
case string message when message.ContainsOrdinalIgnoreCase(
62+
"javascript error: Failed to execute 'elementsFromPoint' on 'Document': The provided double value is non-finite."):
63+
throw new NotSupportedException(
64+
"For this element use the standard Click() method.");
6065

61-
await Task.Delay(RetrySettings.Interval, context.Configuration.TestCancellationToken);
66+
default:
67+
throw;
6268
}
63-
}
6469

65-
await context.Configuration.Events.AfterClick
66-
.InvokeAsync<ClickEventHandler>(eventHandler => eventHandler(context, element));
67-
}
68-
catch (WebDriverException ex)
69-
when (ex.Message.ContainsOrdinalIgnoreCase(
70-
"javascript error: Failed to execute 'elementsFromPoint' on 'Document': The provided double value is non-finite."))
71-
{
72-
throw new NotSupportedException(
73-
"For this element use the standard Click() method.");
70+
await Task.Delay(RetrySettings.Interval, context.Configuration.TestCancellationToken);
71+
}
7472
}
73+
74+
await context.Configuration.Events.AfterClick
75+
.InvokeAsync<ClickEventHandler>(eventHandler => eventHandler(context, element));
7576
});
7677

7778
/// <inheritdoc cref="ClickReliablyUntilNavigationHasOccurredAsync(IWebElement, UITestContext, TimeSpan?, TimeSpan?)"/>
@@ -122,7 +123,7 @@ public static Task ClickReliablyUntilUrlChangeAsync(
122123
{
123124
await element.ClickReliablyAsync(context);
124125
}
125-
catch (StaleElementReferenceException)
126+
catch (WebDriverException ex) when (ex.IsStateElementLikeException())
126127
{
127128
// If navigation happened while retrying the click, the element will become stale, but that's normal.
128129
}

Lombiq.Tests.UI/Extensions/ReliabilityUITestContextExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public static T RetrieveWithRetriesIfStaleOrFail<T>(
215215
{
216216
result = process();
217217
}
218-
catch (StaleElementReferenceException)
218+
catch (WebDriverException ex) when (ex.IsStateElementLikeException())
219219
{
220220
return false;
221221
}
@@ -257,7 +257,7 @@ public static void DoWithRetriesIfStaleOrFail(
257257
{
258258
process();
259259
}
260-
catch (StaleElementReferenceException)
260+
catch (WebDriverException ex) when (ex.IsStateElementLikeException())
261261
{
262262
return false;
263263
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace OpenQA.Selenium;
2+
3+
public static class WebDriverExceptionExtensions
4+
{
5+
/// <summary>
6+
/// Checks if the exception is one that's thrown when trying to access an element that's stale.
7+
/// </summary>
8+
public static bool IsStateElementLikeException(this WebDriverException exception) =>
9+
exception is StaleElementReferenceException ||
10+
// This is the same as StaleElementReferenceException but for some reason ChromeDriver randomly throws this
11+
// instead. Also see:
12+
// https://stackoverflow.com/questions/76250688/webdriverexception-unhandled-inspector-error-no-node-with-given-id-found-at-a.
13+
(exception is UnknownErrorException ex && ex.Message.Contains("Node with given id does not belong to the document"));
14+
}

Lombiq.Tests.UI/Helpers/ReliabilityHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public static class ReliabilityHelper
1818
// exception below.
1919
return await innerProcess();
2020
}
21-
catch (StaleElementReferenceException)
21+
catch (WebDriverException ex) when (ex.IsStateElementLikeException())
2222
{
2323
// When navigating away this exception will be thrown for all old element references. Not nice to use
2424
// exceptions but there doesn't seem to be a better way to do this.
@@ -34,7 +34,7 @@ public static class ReliabilityHelper
3434
// exception below.
3535
return await innerProcess();
3636
}
37-
catch (StaleElementReferenceException)
37+
catch (WebDriverException ex) when (ex.IsStateElementLikeException())
3838
{
3939
return true;
4040
}

Lombiq.Tests.UI/Models/PageNavigationState.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,10 @@ public bool CheckIfNavigationHasOccurred()
3333
// will always return false.
3434
return _root.Size.Width < 0;
3535
}
36-
catch (StaleElementReferenceException)
36+
catch (WebDriverException ex) when (ex.IsStateElementLikeException())
3737
{
3838
return true;
3939
}
40-
catch (UnknownErrorException ex) when (ex.Message.Contains("Node with given id does not belong to the document"))
41-
{
42-
// This is the same as StaleElementReferenceException but for some reason ChromeDriver randomly throws this
43-
// instead. Also see:
44-
// https://stackoverflow.com/questions/76250688/webdriverexception-unhandled-inspector-error-no-node-with-given-id-found-at-a.
45-
return true;
46-
}
4740
}
4841

4942
public void Wait(TimeSpan? timeout = null, TimeSpan? interval = null) =>

0 commit comments

Comments
 (0)