From b7d429a31056ef81d520204f9d251b80e23b21f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alaksiej=20Miale=C5=A1ka?= Date: Wed, 24 Jun 2020 12:31:13 +0200 Subject: [PATCH] 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 --- .../Elements/CachedElementStateProvider.cs | 7 ++-- .../Browser/CachedElementTests.cs | 37 +++++++++++++------ .../WindowsApp/CachedElementTests.cs | 16 ++++++++ .../WindowsApp/Locators/CalculatorWindow.cs | 2 + 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/CachedElementStateProvider.cs b/Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/CachedElementStateProvider.cs index df821f4..5890c9a 100644 --- a/Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/CachedElementStateProvider.cs +++ b/Aquality.Selenium.Core/src/Aquality.Selenium.Core/Elements/CachedElementStateProvider.cs @@ -24,15 +24,16 @@ public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, protected virtual IList HandledExceptions => new List { typeof(StaleElementReferenceException), typeof(NoSuchElementException) }; - protected virtual bool TryInvokeFunction(Func func) + protected virtual bool TryInvokeFunction(Func func, IList exceptionsToHandle = null) { + var handledExceptions = exceptionsToHandle ?? HandledExceptions; try { return func(elementCacheHandler.GetElement(TimeSpan.Zero, ElementState.ExistsInAnyState)); } catch (Exception e) { - if (HandledExceptions.Any(type => type.IsAssignableFrom(e.GetType()))) + if (handledExceptions.Any(type => type.IsAssignableFrom(e.GetType()))) { return false; } @@ -46,7 +47,7 @@ protected virtual bool TryInvokeFunction(Func func) public virtual bool IsClickable => TryInvokeFunction(element => element.Displayed && element.Enabled); - public virtual bool IsEnabled => TryInvokeFunction(element => element.Enabled); + public virtual bool IsEnabled => TryInvokeFunction(element => element.Enabled, new[] { typeof(StaleElementReferenceException) }); public virtual void WaitForClickable(TimeSpan? timeout = null) { diff --git a/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/Browser/CachedElementTests.cs b/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/Browser/CachedElementTests.cs index 392f3fd..25200b8 100644 --- a/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/Browser/CachedElementTests.cs +++ b/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/Browser/CachedElementTests.cs @@ -40,6 +40,14 @@ private static readonly Func[] StateFunctionsTrueWh state => !state.WaitForNotEnabled(TimeSpan.Zero), }; + private static readonly Func[] StateFunctionsThrowNoSuchElementException + = new Func[] + { + state => state.IsEnabled, + state => state.WaitForEnabled(TimeSpan.Zero), + state => !state.WaitForNotEnabled(TimeSpan.Zero), + }; + private IConditionalWait ConditionalWait => AqualityServices.ServiceProvider.GetRequiredService(); [SetUp] @@ -108,30 +116,35 @@ public void Should_RefreshElement_WhenItIsStale() } [Test] - [Ignore("Tests should be updated: find out more stable example")] - public void Should_ReturnCorrectState_False_WhenWindowIsRefreshed([ValueSource(nameof(StateFunctionsFalseWhenElementStale))] Func stateCondition) + public void Should_ThrowNoSuchElementException_ForAbsentElement([ValueSource(nameof(StateFunctionsThrowNoSuchElementException))] Func stateCondition) + { + var label = new Label(By.Name("Absent element"), "Absent element", ElementState.Displayed); + Assert.Throws(() => stateCondition.Invoke(label.State)); + } + + [Test] + public void Should_ReturnCorrectState_False_WhenWindowIsReopened([ValueSource(nameof(StateFunctionsFalseWhenElementStale))] Func stateCondition) { - AssertStateConditionAfterRefresh(stateCondition, expectedValue: false); + AssertStateConditionAfterReopen(stateCondition, expectedValue: false); } [Test] - [Ignore("Tests should be updated: find out more stable example")] - public void Should_ReturnCorrectState_True_WhenWindowIsRefreshed([ValueSource(nameof(StateFunctionsTrueWhenElementStaleWhichRetriveElement))] Func stateCondition) + public void Should_ReturnCorrectState_True_WhenWindowIsReopened([ValueSource(nameof(StateFunctionsTrueWhenElementStaleWhichRetriveElement))] Func stateCondition) { - AssertStateConditionAfterRefresh(stateCondition, expectedValue: true); + AssertStateConditionAfterReopen(stateCondition, expectedValue: true); } - private void AssertStateConditionAfterRefresh(Func stateCondition, bool expectedValue) + private void AssertStateConditionAfterReopen(Func stateCondition, bool expectedValue) { OpenDynamicContent(); var testElement = new Label(ContentLoc, "Example", ElementState.ExistsInAnyState); testElement.State.WaitForClickable(); - new Label(RemoveButtonLoc, "Remove", ElementState.Displayed).Click(); - ConditionalWait.WaitForTrue(() => testElement.Cache.IsStale, message: "Element should be stale when it disappeared."); - AqualityServices.Application.Driver.Navigate().Refresh(); - Assert.IsTrue(testElement.Cache.IsStale, "Element should remain stale after the page refresh."); + AqualityServices.Application.Quit(); + StartLoading(); + ConditionalWait.WaitForTrue(() => testElement.Cache.IsStale, message: "Element should be stale after page is closed."); + OpenDynamicContent(); Assert.AreEqual(expectedValue, stateCondition(testElement.State), - "Element state condition is not expected after refreshing the window"); + "Element state condition is not expected after reopening the window"); } [TearDown] diff --git a/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/CachedElementTests.cs b/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/CachedElementTests.cs index 8607fad..e6b541c 100644 --- a/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/CachedElementTests.cs +++ b/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/CachedElementTests.cs @@ -3,6 +3,7 @@ using Aquality.Selenium.Core.Tests.Applications.WindowsApp.Locators; using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; +using OpenQA.Selenium; using System; namespace Aquality.Selenium.Core.Tests.Applications.WindowsApp @@ -33,6 +34,14 @@ private static readonly Func[] StateFunctionsTrueWh state => !state.WaitForNotEnabled(TimeSpan.Zero), }; + private static readonly Func[] StateFunctionsThrowNoSuchElementException + = new Func[] + { + state => state.IsEnabled, + state => state.WaitForEnabled(TimeSpan.Zero), + state => !state.WaitForNotEnabled(TimeSpan.Zero), + }; + [SetUp] public void SetUp() { @@ -85,6 +94,13 @@ public void Should_ReturnNewElement_WhenWindowIsReopened() Assert.AreNotEqual(initialElement, resultElement, errorMessage); } + [Test] + public void Should_ThrowNoSuchElementException_ForAbsentElement([ValueSource(nameof(StateFunctionsThrowNoSuchElementException))] Func stateCondition) + { + var button = Factory.GetButton(CalculatorWindow.AbsentElement, "Absent element"); + Assert.Throws(() => stateCondition.Invoke(button.State)); + } + [Test] public void Should_ReturnCorrectState_WhenWindowIsClosed([ValueSource(nameof(StateFunctionsFalseWhenElementStale))] Func stateCondition) { diff --git a/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/Locators/CalculatorWindow.cs b/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/Locators/CalculatorWindow.cs index b100353..a45dc5c 100644 --- a/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/Locators/CalculatorWindow.cs +++ b/Aquality.Selenium.Core/tests/Aquality.Selenium.Core.Tests/Applications/WindowsApp/Locators/CalculatorWindow.cs @@ -20,5 +20,7 @@ public static class CalculatorWindow public static By ResultsLabel => MobileBy.AccessibilityId("48"); public static By EmptyButton => By.XPath("//*[@AutomationId='7']"); + + public static By AbsentElement => By.Name("Absent element"); } }