Skip to content

Commit b7d429a

Browse files
authored
Fixed CachedElementStateProvider behaviour: IsEnabled and related functions should throw NoSuchElementException (#77)
* Fixed CachedElementStateProvider behaviour: IsEnabled and related functions should throw NoSuchElementException when the element is absent. Fixes #57, #62
1 parent e2adab1 commit b7d429a

File tree

4 files changed

+47
-15
lines changed

4 files changed

+47
-15
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@ public CachedElementStateProvider(By locator, IConditionalWait conditionalWait,
2424

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

27-
protected virtual bool TryInvokeFunction(Func<IWebElement, bool> func)
27+
protected virtual bool TryInvokeFunction(Func<IWebElement, bool> func, IList<Type> exceptionsToHandle = null)
2828
{
29+
var handledExceptions = exceptionsToHandle ?? HandledExceptions;
2930
try
3031
{
3132
return func(elementCacheHandler.GetElement(TimeSpan.Zero, ElementState.ExistsInAnyState));
3233
}
3334
catch (Exception e)
3435
{
35-
if (HandledExceptions.Any(type => type.IsAssignableFrom(e.GetType())))
36+
if (handledExceptions.Any(type => type.IsAssignableFrom(e.GetType())))
3637
{
3738
return false;
3839
}
@@ -46,7 +47,7 @@ protected virtual bool TryInvokeFunction(Func<IWebElement, bool> func)
4647

4748
public virtual bool IsClickable => TryInvokeFunction(element => element.Displayed && element.Enabled);
4849

49-
public virtual bool IsEnabled => TryInvokeFunction(element => element.Enabled);
50+
public virtual bool IsEnabled => TryInvokeFunction(element => element.Enabled, new[] { typeof(StaleElementReferenceException) });
5051

5152
public virtual void WaitForClickable(TimeSpan? timeout = null)
5253
{

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

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ private static readonly Func<IElementStateProvider, bool>[] StateFunctionsTrueWh
4040
state => !state.WaitForNotEnabled(TimeSpan.Zero),
4141
};
4242

43+
private static readonly Func<IElementStateProvider, bool>[] StateFunctionsThrowNoSuchElementException
44+
= new Func<IElementStateProvider, bool>[]
45+
{
46+
state => state.IsEnabled,
47+
state => state.WaitForEnabled(TimeSpan.Zero),
48+
state => !state.WaitForNotEnabled(TimeSpan.Zero),
49+
};
50+
4351
private IConditionalWait ConditionalWait => AqualityServices.ServiceProvider.GetRequiredService<IConditionalWait>();
4452

4553
[SetUp]
@@ -108,30 +116,35 @@ public void Should_RefreshElement_WhenItIsStale()
108116
}
109117

110118
[Test]
111-
[Ignore("Tests should be updated: find out more stable example")]
112-
public void Should_ReturnCorrectState_False_WhenWindowIsRefreshed([ValueSource(nameof(StateFunctionsFalseWhenElementStale))] Func<IElementStateProvider, bool> stateCondition)
119+
public void Should_ThrowNoSuchElementException_ForAbsentElement([ValueSource(nameof(StateFunctionsThrowNoSuchElementException))] Func<IElementStateProvider, bool> stateCondition)
120+
{
121+
var label = new Label(By.Name("Absent element"), "Absent element", ElementState.Displayed);
122+
Assert.Throws<NoSuchElementException>(() => stateCondition.Invoke(label.State));
123+
}
124+
125+
[Test]
126+
public void Should_ReturnCorrectState_False_WhenWindowIsReopened([ValueSource(nameof(StateFunctionsFalseWhenElementStale))] Func<IElementStateProvider, bool> stateCondition)
113127
{
114-
AssertStateConditionAfterRefresh(stateCondition, expectedValue: false);
128+
AssertStateConditionAfterReopen(stateCondition, expectedValue: false);
115129
}
116130

117131
[Test]
118-
[Ignore("Tests should be updated: find out more stable example")]
119-
public void Should_ReturnCorrectState_True_WhenWindowIsRefreshed([ValueSource(nameof(StateFunctionsTrueWhenElementStaleWhichRetriveElement))] Func<IElementStateProvider, bool> stateCondition)
132+
public void Should_ReturnCorrectState_True_WhenWindowIsReopened([ValueSource(nameof(StateFunctionsTrueWhenElementStaleWhichRetriveElement))] Func<IElementStateProvider, bool> stateCondition)
120133
{
121-
AssertStateConditionAfterRefresh(stateCondition, expectedValue: true);
134+
AssertStateConditionAfterReopen(stateCondition, expectedValue: true);
122135
}
123136

124-
private void AssertStateConditionAfterRefresh(Func<IElementStateProvider, bool> stateCondition, bool expectedValue)
137+
private void AssertStateConditionAfterReopen(Func<IElementStateProvider, bool> stateCondition, bool expectedValue)
125138
{
126139
OpenDynamicContent();
127140
var testElement = new Label(ContentLoc, "Example", ElementState.ExistsInAnyState);
128141
testElement.State.WaitForClickable();
129-
new Label(RemoveButtonLoc, "Remove", ElementState.Displayed).Click();
130-
ConditionalWait.WaitForTrue(() => testElement.Cache.IsStale, message: "Element should be stale when it disappeared.");
131-
AqualityServices.Application.Driver.Navigate().Refresh();
132-
Assert.IsTrue(testElement.Cache.IsStale, "Element should remain stale after the page refresh.");
142+
AqualityServices.Application.Quit();
143+
StartLoading();
144+
ConditionalWait.WaitForTrue(() => testElement.Cache.IsStale, message: "Element should be stale after page is closed.");
145+
OpenDynamicContent();
133146
Assert.AreEqual(expectedValue, stateCondition(testElement.State),
134-
"Element state condition is not expected after refreshing the window");
147+
"Element state condition is not expected after reopening the window");
135148
}
136149

137150
[TearDown]

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Aquality.Selenium.Core.Tests.Applications.WindowsApp.Locators;
44
using Microsoft.Extensions.DependencyInjection;
55
using NUnit.Framework;
6+
using OpenQA.Selenium;
67
using System;
78

89
namespace Aquality.Selenium.Core.Tests.Applications.WindowsApp
@@ -33,6 +34,14 @@ private static readonly Func<IElementStateProvider, bool>[] StateFunctionsTrueWh
3334
state => !state.WaitForNotEnabled(TimeSpan.Zero),
3435
};
3536

37+
private static readonly Func<IElementStateProvider, bool>[] StateFunctionsThrowNoSuchElementException
38+
= new Func<IElementStateProvider, bool>[]
39+
{
40+
state => state.IsEnabled,
41+
state => state.WaitForEnabled(TimeSpan.Zero),
42+
state => !state.WaitForNotEnabled(TimeSpan.Zero),
43+
};
44+
3645
[SetUp]
3746
public void SetUp()
3847
{
@@ -85,6 +94,13 @@ public void Should_ReturnNewElement_WhenWindowIsReopened()
8594
Assert.AreNotEqual(initialElement, resultElement, errorMessage);
8695
}
8796

97+
[Test]
98+
public void Should_ThrowNoSuchElementException_ForAbsentElement([ValueSource(nameof(StateFunctionsThrowNoSuchElementException))] Func<IElementStateProvider, bool> stateCondition)
99+
{
100+
var button = Factory.GetButton(CalculatorWindow.AbsentElement, "Absent element");
101+
Assert.Throws<NoSuchElementException>(() => stateCondition.Invoke(button.State));
102+
}
103+
88104
[Test]
89105
public void Should_ReturnCorrectState_WhenWindowIsClosed([ValueSource(nameof(StateFunctionsFalseWhenElementStale))] Func<IElementStateProvider, bool> stateCondition)
90106
{

Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/Locators/CalculatorWindow.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ public static class CalculatorWindow
2020
public static By ResultsLabel => MobileBy.AccessibilityId("48");
2121

2222
public static By EmptyButton => By.XPath("//*[@AutomationId='7']");
23+
24+
public static By AbsentElement => By.Name("Absent element");
2325
}
2426
}

0 commit comments

Comments
 (0)