Skip to content

Commit fbde33b

Browse files
authored
Fix CachedElementStateProvider issue (#51): return state regardless from constructor ElementState
* Fix CachedElementStateProvider to return state regardless from constructor ElementState * Corrected condition in test Should_ReturnCorrectState_True_WhenWindowIsRefreshed
1 parent 413ff3e commit fbde33b

File tree

5 files changed

+90
-39
lines changed

5 files changed

+90
-39
lines changed

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Aquality.Selenium.Core.xml

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/CachedElementStateProvider.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,27 @@ public CachedElementStateProvider(By locator, ConditionalWait conditionalWait, I
2121
this.locator = locator;
2222
}
2323

24-
protected virtual IList<Type> ExceptionsHandledByDefault => new List<Type> { typeof(StaleElementReferenceException) };
24+
protected virtual IList<Type> HandledExceptions => new List<Type> { typeof(StaleElementReferenceException), typeof(NoSuchElementException) };
2525

26-
protected virtual bool TryInvokeFunction(Func<IWebElement, bool> func, IList<Type> exceptionsToHandle = null)
26+
protected virtual bool TryInvokeFunction(Func<IWebElement, bool> func)
2727
{
28-
var handledExceptions = exceptionsToHandle ?? ExceptionsHandledByDefault;
2928
try
3029
{
31-
return func(elementCacheHandler.GetElement(TimeSpan.Zero));
30+
return func(elementCacheHandler.GetElement(TimeSpan.Zero, ElementState.ExistsInAnyState));
3231
}
3332
catch (Exception e)
3433
{
35-
if (handledExceptions.Any(type => type.IsAssignableFrom(e.GetType())))
34+
if (HandledExceptions.Any(type => type.IsAssignableFrom(e.GetType())))
3635
{
3736
return false;
3837
}
3938
throw;
4039
}
4140
}
4241

43-
public virtual bool IsDisplayed => !elementCacheHandler.IsStale
44-
&& TryInvokeFunction(element => element.Displayed, new List<Type> { typeof(StaleElementReferenceException), typeof(NoSuchElementException) });
42+
public virtual bool IsDisplayed => !elementCacheHandler.IsStale && TryInvokeFunction(element => element.Displayed);
4543

46-
public virtual bool IsExist => !elementCacheHandler.IsStale
47-
&& TryInvokeFunction(element => true, new List<Type> { typeof(NoSuchElementException) });
44+
public virtual bool IsExist => !elementCacheHandler.IsStale && TryInvokeFunction(element => true);
4845

4946
public virtual bool IsClickable => TryInvokeFunction(element => element.Displayed && element.Enabled);
5047

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/ElementCacheHandler.cs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,33 @@ public ElementCacheHandler(By locator, ElementState state, IElementFinder finder
2020
elementFinder = finder;
2121
}
2222

23-
public bool IsStale => remoteElement != null && IsRefreshNeeded;
23+
public bool IsStale => remoteElement != null && IsRefreshNeeded();
2424

25-
public bool IsRefreshNeeded
25+
public bool IsRefreshNeeded(ElementState? customState = null)
2626
{
27-
get
27+
if (remoteElement == null)
2828
{
29-
if (remoteElement == null)
30-
{
31-
return true;
32-
}
33-
try
34-
{
35-
var isDisplayed = remoteElement.Displayed;
36-
// no refresh needed if the property is available
37-
return false;
38-
}
39-
catch
40-
{
41-
return true;
42-
}
29+
return true;
30+
}
31+
try
32+
{
33+
var isDisplayed = remoteElement.Displayed;
34+
// refresh is needed only if the property is not match to expected element state
35+
return (customState ?? state) == ElementState.Displayed && !isDisplayed;
36+
}
37+
catch
38+
{
39+
// refresh is needed if the property is not available
40+
return true;
4341
}
4442
}
4543

46-
public RemoteWebElement GetElement(TimeSpan? timeout = null)
44+
public RemoteWebElement GetElement(TimeSpan? timeout = null, ElementState? customState = null)
4745
{
4846

49-
if (IsRefreshNeeded)
47+
if (IsRefreshNeeded(customState))
5048
{
51-
remoteElement = (RemoteWebElement)elementFinder.FindElement(locator, state, timeout);
49+
remoteElement = (RemoteWebElement)elementFinder.FindElement(locator, customState ?? state, timeout);
5250
}
5351

5452
return remoteElement;

Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/Interfaces/IElementCacheHandler.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ public interface IElementCacheHandler
1111
/// <summary>
1212
/// Determines is the cached element refresh needed.
1313
/// </summary>
14-
bool IsRefreshNeeded { get; }
14+
/// <param name="customState">Custom element's existance state used for search.</param>
15+
/// <returns></returns>
16+
bool IsRefreshNeeded(ElementState? customState = null);
1517

1618
/// <summary>
1719
/// Determines is the element stale.
@@ -21,8 +23,9 @@ public interface IElementCacheHandler
2123
/// <summary>
2224
/// Allows to get cached element.
2325
/// </summary>
24-
/// <param name="timeout">Timeout used to retrive the element when <see cref="IsRefreshNeeded"/> is true.</param>
26+
/// <param name="timeout">Timeout used to retrive the element when <see cref="IsRefreshNeeded(ElementState?)"/> is true.</param>
27+
/// <param name="customState">Custom element's existance state used for search.</param>
2528
/// <returns>Cached element.</returns>
26-
RemoteWebElement GetElement(TimeSpan? timeout = null);
29+
RemoteWebElement GetElement(TimeSpan? timeout = null, ElementState? customState = null);
2730
}
2831
}

Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/Browser/CachedElementTests.cs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ public class CachedElementTests : TestWithBrowser
1313
{
1414
private static readonly By RemoveButtonLoc = By.XPath("//button[.='Remove']");
1515
private static readonly By ContentLoc = By.Id("checkbox");
16+
private static readonly By StartLoc = By.XPath("//*[@id='start']//button");
17+
private static readonly By LoadingLoc = By.Id("loading");
1618
private static readonly Uri DynamicContentUrl = new Uri($"{TestSite}/dynamic_controls");
19+
private static readonly Uri DynamicLoadingUrl = new Uri($"{TestSite}/dynamic_loading/1");
1720
private const string ElementCacheVariableName = "elementCache.isEnabled";
1821

1922
private static readonly Func<IElementStateProvider, bool>[] StateFunctionsFalseWhenElementStale
@@ -40,12 +43,60 @@ private static readonly Func<IElementStateProvider, bool>[] StateFunctionsTrueWh
4043
public new void SetUp()
4144
{
4245
Environment.SetEnvironmentVariable(ElementCacheVariableName, true.ToString());
46+
}
47+
48+
private void StartLoading()
49+
{
50+
AqualityServices.Application.Driver.Navigate().GoToUrl(DynamicLoadingUrl);
51+
new Label(StartLoc, "start", ElementState.Displayed).Click();
52+
}
53+
54+
private void OpenDynamicContent()
55+
{
4356
AqualityServices.Application.Driver.Navigate().GoToUrl(DynamicContentUrl);
4457
}
4558

59+
private void WaitForLoading(Label loader)
60+
{
61+
Assume.That(loader.State.WaitForDisplayed(), "Loader should be displayed in the beginning");
62+
Assume.That(loader.State.WaitForNotDisplayed(), "Loader should not be displayed in the end");
63+
}
64+
65+
[Test]
66+
public void Should_ReturnFalse_AtWaitForDisplayed_WhenElementIsNotDisplayed()
67+
{
68+
var loader = new Label(LoadingLoc, "loader", ElementState.Displayed);
69+
StartLoading();
70+
WaitForLoading(loader);
71+
Assert.IsFalse(loader.State.WaitForDisplayed(TimeSpan.Zero), nameof(Should_ReturnFalse_AtWaitForDisplayed_WhenElementIsNotDisplayed));
72+
}
73+
74+
[Test]
75+
public void Should_ReturnTrue_AtWaitForExist_WhenElementIsNotDisplayed()
76+
{
77+
var loader = new Label(LoadingLoc, "loader", ElementState.Displayed);
78+
StartLoading();
79+
WaitForLoading(loader);
80+
Assert.IsTrue(loader.State.WaitForExist(TimeSpan.Zero), nameof(Should_ReturnTrue_AtWaitForExist_WhenElementIsNotDisplayed));
81+
}
82+
83+
[Test]
84+
public void Should_BeStale_WhenBecameInvisible()
85+
{
86+
StartLoading();
87+
var loader = new Label(LoadingLoc, "loader", ElementState.Displayed);
88+
Assume.That(loader.State.WaitForDisplayed(), "Loader should be displayed in the beginning");
89+
Assert.IsTrue(AqualityServices.ServiceProvider.GetRequiredService<ConditionalWait>().WaitFor(
90+
() => loader.Cache.IsStale), "Loader should become invisible and be treated as stale");
91+
Assert.IsFalse(loader.State.IsDisplayed, "Invisible loader should be not displayed");
92+
Assert.IsFalse(loader.State.IsExist, "Loader that was displayed previously and become invisible should be treated as disappeared");
93+
Assert.IsTrue(loader.State.WaitForExist(TimeSpan.Zero), "When waiting for existance, we should get an actual element's state");
94+
}
95+
4696
[Test]
4797
public void Should_RefreshElement_WhenItIsStale()
4898
{
99+
OpenDynamicContent();
49100
var example = new Label(ContentLoc, "Example", ElementState.Displayed);
50101
example.GetElement();
51102
var exToString = example.GetElement().ToString();
@@ -68,13 +119,12 @@ public void Should_ReturnCorrectState_True_WhenWindowIsRefreshed([ValueSource(na
68119

69120
private void AssertStateConditionAfterRefresh(Func<IElementStateProvider, bool> stateCondition, bool expectedValue)
70121
{
122+
OpenDynamicContent();
71123
var testElement = new Label(ContentLoc, "Example", ElementState.ExistsInAnyState);
72124
testElement.State.WaitForClickable();
73125
new Label(RemoveButtonLoc, "Remove", ElementState.Displayed).Click();
74-
AqualityServices.ServiceProvider.GetRequiredService<ConditionalWait>().WaitFor(driver =>
75-
{
76-
return testElement.Cache.IsStale;
77-
}, message: "Element should be stale when it disappeared.");
126+
AqualityServices.ServiceProvider.GetRequiredService<ConditionalWait>().WaitForTrue(
127+
() => testElement.Cache.IsStale, message: "Element should be stale when it disappeared.");
78128
AqualityServices.Application.Driver.Navigate().Refresh();
79129
Assert.IsTrue(testElement.Cache.IsStale, "Element should remain stale after the page refresh.");
80130
Assert.AreEqual(expectedValue, stateCondition(testElement.State),

0 commit comments

Comments
 (0)