Skip to content

Commit

Permalink
Merge pull request #21 from riganti/waitfor-fixes
Browse files Browse the repository at this point in the history
Fix glitches in WaitFor: NRE, monotonic clock, exception after success
  • Loading branch information
quigamdev authored Sep 14, 2024
2 parents 680fa34 + 2466734 commit 6741d78
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 61 deletions.
52 changes: 21 additions & 31 deletions src/Core/Riganti.Selenium.Core/BrowserWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -589,34 +589,28 @@ public IBrowserWrapper WaitFor(Func<bool> condition, int timeout, string failure
{
throw new NullReferenceException("Condition cannot be null.");
}
var now = DateTime.UtcNow;
var stopwatch = Stopwatch.StartNew();

bool isConditionMet = false;
Exception ex = null;
do
while (true)
{
try
{
isConditionMet = condition();
if (condition())
return this;
}
catch (StaleElementReferenceException)
catch (StaleElementReferenceException) when (ignoreCertainException)
{
if (!ignoreCertainException)
throw;
}
catch (InvalidElementStateException)
catch (InvalidElementStateException) when (ignoreCertainException)
{
if (!ignoreCertainException)
throw;
}

if (DateTime.UtcNow.Subtract(now).TotalMilliseconds > timeout)
if (stopwatch.ElapsedMilliseconds > timeout)
{
throw new WaitBlockException(failureMessage);
throw new WaitBlockException(failureMessage ?? "Condition returned false after timeout expired.");
}
Wait(checkInterval);
} while (!isConditionMet);
return this;
}
}
/// <inheritdoc />

Expand Down Expand Up @@ -654,32 +648,28 @@ public IBrowserWrapper WaitFor(Action action, int timeout, int checkInterval = 3
{
throw new ArgumentNullException(nameof(action));
}
var now = DateTime.UtcNow;
var stopwatch = Stopwatch.StartNew();

Exception exceptionThrown = null;
do
while (true)
{
try
{
action();
exceptionThrown = null;
return this;
}
catch (Exception ex)
catch (Exception exceptionThrown)
{
exceptionThrown = ex;
}

if (DateTime.UtcNow.Subtract(now).TotalMilliseconds > timeout)
{
if (failureMessage != null)
if (stopwatch.ElapsedMilliseconds > timeout)
{
throw new WaitBlockException(failureMessage, exceptionThrown);
if (failureMessage != null)
{
throw new WaitBlockException(failureMessage, exceptionThrown);
}
throw;
}
throw exceptionThrown;
}
Wait(checkInterval);
} while (exceptionThrown != null);
return this;
}
}

public IElementWrapper WaitFor(Func<IBrowserWrapper, IElementWrapper> selector, WaitForOptions options = null)
Expand Down Expand Up @@ -788,4 +778,4 @@ protected IBrowserWrapper EvaluateBrowserCheck<TException>(IValidator<IBrowserWr
return this;
}
}
}
}
29 changes: 13 additions & 16 deletions src/Core/Riganti.Selenium.Core/ElementWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Riganti.Selenium.Core.Abstractions;
using Riganti.Selenium.Core.Abstractions.Exceptions;
using Riganti.Selenium.Core.Api;
using System.Diagnostics;

namespace Riganti.Selenium.Core
{
Expand Down Expand Up @@ -560,34 +561,30 @@ public IElementWrapper WaitFor(Func<IElementWrapper, bool> condition, int maxTim
{
throw new NullReferenceException("Condition cannot be null.");
}
var now = DateTime.UtcNow;

bool isConditionMet = false;
do
var stopwatch = Stopwatch.StartNew();

while (true)
{
try
{
isConditionMet = condition(this);
if (condition(this))
return this;
}
catch (StaleElementReferenceException)
catch (StaleElementReferenceException) when (ignoreCertainException)
{
if (!ignoreCertainException)
throw;
}
catch (InvalidElementStateException)
catch (InvalidElementStateException) when (ignoreCertainException)
{
if (!ignoreCertainException)
throw;
}

if (DateTime.UtcNow.Subtract(now).TotalMilliseconds > maxTimeout)
if (stopwatch.ElapsedMilliseconds > maxTimeout)
{
throw new WaitBlockException(failureMessage);
throw new WaitBlockException(failureMessage ?? "Condition returned false after timeout expired.");
}
Wait(checkInterval);
} while (!isConditionMet);

return this;
Wait(checkInterval);
}
}

public IElementWrapper WaitFor(Action<IElementWrapper> checkExpression, int maxTimeout, string failureMessage, int checkInterval = 30)
Expand Down Expand Up @@ -730,4 +727,4 @@ public bool HasCssClass(string cssClass)
return attr.Split(' ').Any(s => string.Equals(cssClass, s, StringComparison.OrdinalIgnoreCase));
}
}
}
}
29 changes: 15 additions & 14 deletions src/Core/Riganti.Selenium.Core/WaitForExecutor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Riganti.Selenium.Core.Abstractions.Exceptions;
using System.Threading;
using System.Diagnostics;

namespace Riganti.Selenium.Core
{
Expand All @@ -12,26 +13,25 @@ public void WaitFor(Action condition, int timeout, string failureMessage, int ch
{
throw new NullReferenceException("Condition cannot be null.");
}
var now = DateTime.UtcNow;
var stopwatch = Stopwatch.StartNew();

bool isConditionMet = false;
do
while (true)
{
try
{
condition();
isConditionMet = true;
return;
}
catch (TestExceptionBase ex)
{
if (DateTime.UtcNow.Subtract(now).TotalMilliseconds > timeout)
if (stopwatch.ElapsedMilliseconds > timeout)
{
if (throwOriginal) throw;
else throw new WaitBlockException(failureMessage ?? ex.Message, ex);
}
}
Thread.Sleep(checkInterval);
} while (!isConditionMet);
}
}
public void WaitFor(Func<bool> condition, WaitForOptions options = null)
{
Expand All @@ -40,29 +40,30 @@ public void WaitFor(Func<bool> condition, WaitForOptions options = null)
{
throw new NullReferenceException("Condition cannot be null.");
}
var now = DateTime.UtcNow;
var stopwatch = Stopwatch.StartNew();

bool isConditionMet = false;
do
while (true)
{
try
{
isConditionMet = condition();
if (condition())
return;
}
catch (TestExceptionBase ex)
{
if (DateTime.UtcNow.Subtract(now).TotalMilliseconds > options.Timeout)
if (stopwatch.ElapsedMilliseconds > options.Timeout)
{
if (options.ThrowOriginalException) throw;
else throw new WaitBlockException(options.FailureMessage ?? ex.Message, ex);
}
}
if (DateTime.UtcNow.Subtract(now).TotalMilliseconds > options.Timeout)
if (stopwatch.ElapsedMilliseconds > options.Timeout)
{
throw new WaitBlockException(options.FailureMessage);
throw new WaitBlockException(options.FailureMessage ?? "Condition returned false after timeout expired.");
}

Thread.Sleep(options.CheckInterval);
} while (!isConditionMet);
}
}

/// <summary>
Expand Down

0 comments on commit 6741d78

Please sign in to comment.