From 4048dcc08589ae2c5c58ed778be6fbbeb3530d51 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Wed, 5 Feb 2020 17:38:03 +0300 Subject: [PATCH 01/11] implement the default ElementStateProvider --- .../core/elements/ElementStateProvider.java | 104 +++++++++++ .../interfaces/IElementStateProvider.java | 169 ++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 src/main/java/aquality/selenium/core/elements/ElementStateProvider.java create mode 100644 src/main/java/aquality/selenium/core/elements/interfaces/IElementStateProvider.java diff --git a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java new file mode 100644 index 0000000..6444730 --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java @@ -0,0 +1,104 @@ +package aquality.selenium.core.elements; + +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import java.util.function.Predicate; + +public class ElementStateProvider implements IElementStateProvider { + + private static final long ZERO_TIMEOUT = 0L; + private final By locator; + private final IConditionalWait conditionalWait; + private final IElementFinder elementFinder; + + ElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { + this.locator = locator; + this.conditionalWait = conditionalWait; + this.elementFinder = elementFinder; + } + + @Override + public boolean isClickable() { + return waitForClickable(ZERO_TIMEOUT, true); + } + + @Override + public void waitForClickable(Long timeout) { + waitForClickable(timeout, false); + } + + private boolean waitForClickable(Long timeout, boolean catchTimeoutException) { + DesiredState desiredState = + new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "CLICKABLE"); + desiredState = catchTimeoutException ? desiredState.withCatchingTimeoutException() : desiredState; + return isElementInDesiredCondition(desiredState, timeout); + } + + private boolean isElementInDesiredCondition(DesiredState elementStateCondition, Long timeout) { + return !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty(); + } + + @Override + public boolean isDisplayed() { + return waitForDisplayed(ZERO_TIMEOUT); + } + + @Override + public boolean waitForDisplayed(Long timeout) { + return isAnyElementFound(timeout, ElementState.DISPLAYED); + } + + private boolean isAnyElementFound(Long timeout, ElementState state) { + return !elementFinder.findElements(locator, state, timeout).isEmpty(); + } + + @Override + public boolean waitForNotDisplayed(Long timeout) { + return conditionalWait.waitFor(() -> !isDisplayed(), timeout, null); + } + + @Override + public boolean isExist() { + return waitForExist(ZERO_TIMEOUT); + } + + @Override + public boolean waitForExist(Long timeout) { + return isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE); + } + + @Override + public boolean waitForNotExist(Long timeout) { + return conditionalWait.waitFor(() -> !isExist(), timeout, null); + } + + @Override + public boolean isEnabled() { + return waitForEnabled(ZERO_TIMEOUT); + } + + @Override + public boolean waitForEnabled(Long timeout) { + return isElementInDesiredCondition(this::isElementEnabled, "ENABLED", timeout); + } + + protected boolean isElementEnabled(WebElement element) { + return element.isEnabled(); + } + + private boolean isElementInDesiredCondition(Predicate condition, String stateName, Long timeout) { + DesiredState desiredState = new DesiredState(condition, stateName) + .withCatchingTimeoutException() + .withThrowingNoSuchElementException(); + return isElementInDesiredCondition(desiredState, timeout); + } + + @Override + public boolean waitForNotEnabled(Long timeout) { + return isElementInDesiredCondition(this::isElementEnabled, "NOT ENABLED", timeout); + } +} diff --git a/src/main/java/aquality/selenium/core/elements/interfaces/IElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/interfaces/IElementStateProvider.java new file mode 100644 index 0000000..980a1ff --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/interfaces/IElementStateProvider.java @@ -0,0 +1,169 @@ +package aquality.selenium.core.elements.interfaces; + + +/** + * Provides ability to define of element's state (whether it is displayed, exist or not) + * Also provides respective positive and negative waiting functions + */ +public interface IElementStateProvider { + + /** + * Is an element clickable on the form. + * + * @return true if element clickable, false otherwise + */ + boolean isClickable(); + + /** + * Waits for is element clickable on the form. + * + * @param timeout Timeout for waiting + * @throws org.openqa.selenium.TimeoutException when timeout exceeded and element is not clickable. + */ + void waitForClickable(Long timeout); + + /** + * Waits for is element clickable on the form. + * Uses condition timeout from settings file for waiting + * + * @throws org.openqa.selenium.TimeoutException when timeout exceeded and element is not clickable. + */ + default void waitForClickable() { + waitForClickable(null); + } + + /** + * Is an element displayed on the form. + * + * @return true if element displayed, false otherwise + */ + boolean isDisplayed(); + + /** + * Waits for is element displayed on the form. + * + * @param timeout Timeout for waiting + * @return true if element displayed after waiting, false otherwise + */ + boolean waitForDisplayed(Long timeout); + + /** + * Waits for is element displayed on the form. + * Uses condition timeout from settings file for waiting + * + * @return true if element displayed after waiting, false otherwise + */ + default boolean waitForDisplayed() { + return waitForDisplayed(null); + } + + /** + * Waits for is element displayed on the form. + * + * @param timeout Timeout for waiting + * @return true if element displayed after waiting, false otherwise + */ + boolean waitForNotDisplayed(Long timeout); + + + /** + * Waits for is element displayed on the form. + * Uses condition timeout from settings file for waiting + * + * @return true if element displayed after waiting, false otherwise + */ + default boolean waitForNotDisplayed() { + return waitForNotDisplayed(null); + } + + /** + * Is an element exist in DOM (without visibility check) + * + * @return true if element exist, false otherwise + */ + boolean isExist(); + + /** + * Waits until element is exist in DOM (without visibility check). + * + * @param timeout Timeout for waiting + * @return true if element exist after waiting, false otherwise + */ + boolean waitForExist(Long timeout); + + + /** + * Waits until element is exist in DOM (without visibility check). + * Uses condition timeout from settings file for waiting + * + * @return true if element exist after waiting, false otherwise + */ + default boolean waitForExist() { + return waitForExist(null); + } + + /** + * Waits until element does not exist in DOM (without visibility check). + * + * @return true if element does not exist after waiting, false otherwise + */ + boolean waitForNotExist(Long timeout); + + /** + * Waits until element does not exist in DOM (without visibility check). + * Uses condition timeout from settings file for waiting + * + * @return true if element does not exist after waiting, false otherwise + */ + default boolean waitForNotExist() { + return waitForNotExist(null); + } + + /** + * Check that the element is enabled + * + * @return true if enabled + * @throws org.openqa.selenium.NoSuchElementException when timeout exceeded and element not found. + */ + boolean isEnabled(); + + /** + * Check that the element is enabled + * + * @param timeout Timeout for waiting + * @return true if enabled + * @throws org.openqa.selenium.NoSuchElementException when timeout exceeded and element not found. + */ + boolean waitForEnabled(Long timeout); + + + /** + * Check that the element is enabled (performed by a class member) + * Uses condition timeout from settings file for waiting + * + * @return true if enabled + * @throws org.openqa.selenium.NoSuchElementException when timeout exceeded and element not found. + */ + default boolean waitForEnabled() { + return waitForEnabled(null); + } + + /** + * Waits until element does not enabled in DOM + * + * @return true if element does not enabled after waiting, false otherwise + * @throws org.openqa.selenium.NoSuchElementException when timeout exceeded and element not found. + */ + boolean waitForNotEnabled(Long timeout); + + /** + * Waits until element does not enabled in DOM + * Uses condition timeout from settings file for waiting + * + * @return true if element does not enabled after waiting, false otherwise + * @throws org.openqa.selenium.NoSuchElementException when timeout exceeded and element not found. + */ + default boolean waitForNotEnabled() { + return waitForNotEnabled(null); + } +} \ No newline at end of file From 8e81fe770aa5c436a6bae37ae0ab3fc4d2b5f600 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Thu, 6 Feb 2020 13:33:27 +0300 Subject: [PATCH 02/11] Add tests and fixed ElementStateProvider and ConditionalWait --- .../core/elements/ElementStateProvider.java | 4 +- .../core/waitings/ConditionalWait.java | 2 +- .../application/browser/AqualityServices.java | 4 + .../elements/ElementStateProviderTests.java | 228 ++++++++++++++++++ .../java/theinternet/DynamicControlsForm.java | 25 ++ .../java/theinternet/DynamicLoadingForm.java | 32 +++ .../java/theinternet/TheInternetPage.java | 22 ++ src/test/java/utils/TimeUtil.java | 16 ++ src/test/resources/TestSuite.xml | 1 + 9 files changed, 331 insertions(+), 3 deletions(-) create mode 100644 src/test/java/tests/elements/ElementStateProviderTests.java create mode 100644 src/test/java/theinternet/DynamicControlsForm.java create mode 100644 src/test/java/theinternet/DynamicLoadingForm.java create mode 100644 src/test/java/theinternet/TheInternetPage.java create mode 100644 src/test/java/utils/TimeUtil.java diff --git a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java index 6444730..9ee1a67 100644 --- a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java @@ -15,7 +15,7 @@ public class ElementStateProvider implements IElementStateProvider { private final IConditionalWait conditionalWait; private final IElementFinder elementFinder; - ElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { + public ElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { this.locator = locator; this.conditionalWait = conditionalWait; this.elementFinder = elementFinder; @@ -99,6 +99,6 @@ private boolean isElementInDesiredCondition(Predicate condition, Str @Override public boolean waitForNotEnabled(Long timeout) { - return isElementInDesiredCondition(this::isElementEnabled, "NOT ENABLED", timeout); + return isElementInDesiredCondition(element -> !isElementEnabled(element), "NOT ENABLED", timeout); } } diff --git a/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java b/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java index e5a2f0c..79cc46b 100644 --- a/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java +++ b/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java @@ -99,7 +99,7 @@ private boolean isConditionSatisfied(BooleanSupplier condition, Collection T get(Class type) { + return getServiceProvider().getInstance(type); + } } diff --git a/src/test/java/tests/elements/ElementStateProviderTests.java b/src/test/java/tests/elements/ElementStateProviderTests.java new file mode 100644 index 0000000..d62cafe --- /dev/null +++ b/src/test/java/tests/elements/ElementStateProviderTests.java @@ -0,0 +1,228 @@ +package tests.elements; + +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.configurations.ITimeoutConfiguration; +import aquality.selenium.core.elements.ElementStateProvider; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.NoSuchElementException; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import tests.application.browser.AqualityServices; +import theinternet.DynamicControlsForm; +import theinternet.DynamicLoadingForm; +import theinternet.TheInternetPage; + +import static utils.TimeUtil.calculateDuration; +import static utils.TimeUtil.getCurrentTime; + +public class ElementStateProviderTests { + private static final double OPERATION_TIME = 5; + private static final long CUSTOM_WAIT_TIME = 2L; + private static final Dimension DEFAULT_SIZE = new Dimension(1024, 768); + private static final By absentElementLocator = By.xpath("//div[@class='not exist element']"); + + private IApplication getBrowser() { + return AqualityServices.getApplication(); + } + + private boolean isApplicationStarted() { + return AqualityServices.isApplicationStarted(); + } + + private void navigate(TheInternetPage page) { + getBrowser().getDriver().navigate().to(page.getAddress()); + } + + @BeforeMethod + protected void beforeMethod() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + getBrowser().getDriver().manage().window().setSize(DEFAULT_SIZE); + } + + @AfterMethod + private void cleanUp () { + if (isApplicationStarted()) { + getBrowser().getDriver().quit(); + } + } + + private ElementStateProvider state(By locator) { + return new ElementStateProvider(locator, + AqualityServices.get(IConditionalWait.class), + AqualityServices.get(IElementFinder.class)); + } + + private ElementStateProvider inputState() { + return state(DynamicControlsForm.getInputTextboxLocator()); + } + + private ElementStateProvider checkboxState() { + return state(DynamicControlsForm.getCheckboxLocator()); + } + + private ElementStateProvider loaderState() { + return state(DynamicLoadingForm.getLoadingLabelLocator()); + } + + private ElementStateProvider startButtonState() { + return state(DynamicLoadingForm.getStartButtonLocator()); + } + + private ElementStateProvider absentElementState() { + return state(absentElementLocator); + } + + private void click(By locator) { + locator.findElement(getBrowser().getDriver()).click(); + } + + private void clickEnable() { + click(DynamicControlsForm.getEnableButtonLocator()); + } + + private void clickRemove() { + click(DynamicControlsForm.getRemoveButtonLocator()); + } + + private void clickStart() { + click(DynamicLoadingForm.getStartButtonLocator()); + } + + private long getConditionTimeout() { + return AqualityServices.get(ITimeoutConfiguration.class).getCondition(); + } + + @Test + public void testElementShouldWaitForEnabledWithCustomTimeout() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + long waitTime = CUSTOM_WAIT_TIME; + + long startTime = getCurrentTime(); + boolean isEnabled = inputState().waitForEnabled(waitTime); + double duration = calculateDuration(startTime); + + Assert.assertFalse(isEnabled); + Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + } + + @Test + public void testElementShouldWaitForEnabledWithDefaultTimeout() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + long waitTime = getConditionTimeout(); + + long startTime = getCurrentTime(); + boolean isEnabled = inputState().waitForEnabled(); + double duration = calculateDuration(startTime); + + Assert.assertFalse(isEnabled); + Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + } + + @Test + public void testElementShouldWaitForNotEnabledWithCustomTimeout() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + long waitTime = CUSTOM_WAIT_TIME; + + clickEnable(); + inputState().waitForEnabled(); + + long startTime = getCurrentTime(); + boolean isDisabled = inputState().waitForNotEnabled(waitTime); + double duration = calculateDuration(startTime); + + Assert.assertFalse(isDisabled); + Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + } + + @Test(expectedExceptions = NoSuchElementException.class) + public void testNoSuchShouldBeThrownForWaitEnabledIfElementNotFound(){ + absentElementState().waitForEnabled(CUSTOM_WAIT_TIME); + } + + @Test(expectedExceptions = NoSuchElementException.class) + public void testNoSuchShouldBeThrownForWaitNotEnabledIfElementNotFound(){ + absentElementState().waitForNotEnabled(CUSTOM_WAIT_TIME); + } + + @Test + public void testElementShouldWaitForNotEnabledWithDefaultTimeout() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + long waitTime = getConditionTimeout(); + + + clickEnable(); + inputState().waitForEnabled(); + + long startTime = getCurrentTime(); + boolean isDisabled = inputState().waitForNotEnabled(); + double duration = calculateDuration(startTime); + + Assert.assertFalse(isDisabled); + Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + } + + @Test + public void testTextBoxEnabled() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + clickEnable(); + inputState().waitForEnabled(); + Assert.assertTrue(inputState().isEnabled()); + } + + @Test + public void testWaitInvisibility() { + navigate(TheInternetPage.DYNAMIC_LOADING); + + startButtonState().waitForClickable(); + clickStart(); + + loaderState().waitForDisplayed(DynamicLoadingForm.getSmallTimeout()); + + boolean status = loaderState().waitForNotDisplayed(DynamicLoadingForm.getSmallTimeout()); + Assert.assertFalse(status); + + status = loaderState().waitForNotDisplayed(); + Assert.assertTrue(status); + } + + @Test + public void testWaitForExist(){ + navigate(TheInternetPage.DYNAMIC_LOADING); + boolean status = loaderState().waitForExist(DynamicLoadingForm.getTimeout()); + + Assert.assertFalse(status); + Assert.assertTrue(startButtonState().waitForExist()); + } + + @Test + public void testShouldBePossibleToWaitElementNotExistsCustom(){ + navigate(TheInternetPage.DYNAMIC_CONTROLS); + long waitTime = CUSTOM_WAIT_TIME; + clickRemove(); + long startTime = getCurrentTime(); + boolean isMissed = checkboxState().waitForNotExist(waitTime); + double duration = calculateDuration(startTime); + + Assert.assertFalse(isMissed); + Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + } + + @Test + public void testShouldBePossibleToWaitElementNotExists(){ + navigate(TheInternetPage.DYNAMIC_CONTROLS); + long waitTime = getConditionTimeout(); + clickRemove(); + + long startTime = getCurrentTime(); + boolean isMissed = checkboxState().waitForNotExist(); + double duration = calculateDuration(startTime); + + Assert.assertTrue(isMissed); + Assert.assertTrue(duration < waitTime); + } +} diff --git a/src/test/java/theinternet/DynamicControlsForm.java b/src/test/java/theinternet/DynamicControlsForm.java new file mode 100644 index 0000000..54608ea --- /dev/null +++ b/src/test/java/theinternet/DynamicControlsForm.java @@ -0,0 +1,25 @@ +package theinternet; + +import org.openqa.selenium.By; + +public class DynamicControlsForm { + + private DynamicControlsForm(){ + } + + public static By getEnableButtonLocator() { + return By.xpath("//button[contains(@onclick, 'swapInput()')][contains(.,'Enable')]"); + } + + public static By getInputTextboxLocator() { + return By.xpath("//input[@type='text']"); + } + + public static By getCheckboxLocator() { + return By.xpath("//div[@id='checkbox']"); + } + + public static By getRemoveButtonLocator() { + return By.xpath("//button[contains(@onclick, 'swapCheckbox()')]"); + } +} diff --git a/src/test/java/theinternet/DynamicLoadingForm.java b/src/test/java/theinternet/DynamicLoadingForm.java new file mode 100644 index 0000000..19c6ee7 --- /dev/null +++ b/src/test/java/theinternet/DynamicLoadingForm.java @@ -0,0 +1,32 @@ +package theinternet; + +import org.openqa.selenium.By; + +public class DynamicLoadingForm { + + private static final long DEFAULT_LOADING_TIMEOUT = 5L; + private static final long SMALL_LOADING_TIMEOUT = 2L; + + private DynamicLoadingForm(){ + } + + public static By getLoadingLabelLocator() { + return By.id("loading"); + } + + public static By getStartButtonLocator() { + return By.xpath("//div[@id='start']/button"); + } + + public static long getTimeout() { + return DEFAULT_LOADING_TIMEOUT; + } + + public static long getSmallTimeout() { + return SMALL_LOADING_TIMEOUT; + } + + public static By getLblFinish() { + return By.id("finish"); + } +} diff --git a/src/test/java/theinternet/TheInternetPage.java b/src/test/java/theinternet/TheInternetPage.java new file mode 100644 index 0000000..b3080d4 --- /dev/null +++ b/src/test/java/theinternet/TheInternetPage.java @@ -0,0 +1,22 @@ +package theinternet; + +public enum TheInternetPage { + DYNAMIC_CONTROLS, + DYNAMIC_LOADING("dynamic_loading/1"); + + private static final String BASE_URL = "http://the-internet.herokuapp.com/"; + + private final String postfix; + + TheInternetPage() { + this.postfix = name().toLowerCase(); + } + + TheInternetPage(String postfix) { + this.postfix = postfix; + } + + public String getAddress() { + return BASE_URL.concat(postfix); + } +} diff --git a/src/test/java/utils/TimeUtil.java b/src/test/java/utils/TimeUtil.java new file mode 100644 index 0000000..825adea --- /dev/null +++ b/src/test/java/utils/TimeUtil.java @@ -0,0 +1,16 @@ +package utils; + +public class TimeUtil { + + public static long getCurrentTime(){ + return System.nanoTime(); + } + + public static double getCurrentTimeInSeconds(){ + return System.nanoTime()/Math.pow(10,9); + } + + public static double calculateDuration(long startTimeNanoSec){ + return (getCurrentTime() - startTimeNanoSec)/Math.pow(10,9); + } +} diff --git a/src/test/resources/TestSuite.xml b/src/test/resources/TestSuite.xml index cacebc9..c51be7e 100644 --- a/src/test/resources/TestSuite.xml +++ b/src/test/resources/TestSuite.xml @@ -13,6 +13,7 @@ + From 442237665aa38306765ecbf3ee898621acac6e8c Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Thu, 6 Feb 2020 15:31:45 +0300 Subject: [PATCH 03/11] refactored ElementStateProviderTests --- .../browser/ITheInternetPageTest.java | 36 ++++ .../elements/ElementStateProviderTests.java | 166 +++++------------- src/test/java/theinternet/BaseForm.java | 26 +++ .../java/theinternet/DynamicControlsForm.java | 30 ++-- .../java/theinternet/DynamicLoadingForm.java | 32 ++-- 5 files changed, 142 insertions(+), 148 deletions(-) create mode 100644 src/test/java/tests/application/browser/ITheInternetPageTest.java create mode 100644 src/test/java/theinternet/BaseForm.java diff --git a/src/test/java/tests/application/browser/ITheInternetPageTest.java b/src/test/java/tests/application/browser/ITheInternetPageTest.java new file mode 100644 index 0000000..97f7447 --- /dev/null +++ b/src/test/java/tests/application/browser/ITheInternetPageTest.java @@ -0,0 +1,36 @@ +package tests.application.browser; + +import aquality.selenium.core.applications.IApplication; +import org.openqa.selenium.Dimension; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import theinternet.TheInternetPage; + +public interface ITheInternetPageTest { + Dimension DEFAULT_SIZE = new Dimension(1024, 768); + + default IApplication getBrowser() { + return AqualityServices.getApplication(); + } + + default boolean isApplicationStarted() { + return AqualityServices.isApplicationStarted(); + } + + default void navigate(TheInternetPage page) { + getBrowser().getDriver().navigate().to(page.getAddress()); + } + + @BeforeMethod + default void beforeMethod() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + getBrowser().getDriver().manage().window().setSize(DEFAULT_SIZE); + } + + @AfterMethod + default void cleanUp () { + if (isApplicationStarted()) { + getBrowser().getDriver().quit(); + } + } +} diff --git a/src/test/java/tests/elements/ElementStateProviderTests.java b/src/test/java/tests/elements/ElementStateProviderTests.java index d62cafe..251be28 100644 --- a/src/test/java/tests/elements/ElementStateProviderTests.java +++ b/src/test/java/tests/elements/ElementStateProviderTests.java @@ -1,55 +1,31 @@ package tests.elements; -import aquality.selenium.core.applications.IApplication; import aquality.selenium.core.configurations.ITimeoutConfiguration; import aquality.selenium.core.elements.ElementStateProvider; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; -import org.openqa.selenium.Dimension; import org.openqa.selenium.NoSuchElementException; import org.testng.Assert; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import tests.application.browser.AqualityServices; +import tests.application.browser.ITheInternetPageTest; import theinternet.DynamicControlsForm; import theinternet.DynamicLoadingForm; import theinternet.TheInternetPage; +import java.util.function.Predicate; + import static utils.TimeUtil.calculateDuration; import static utils.TimeUtil.getCurrentTime; -public class ElementStateProviderTests { +public class ElementStateProviderTests implements ITheInternetPageTest { private static final double OPERATION_TIME = 5; private static final long CUSTOM_WAIT_TIME = 2L; - private static final Dimension DEFAULT_SIZE = new Dimension(1024, 768); private static final By absentElementLocator = By.xpath("//div[@class='not exist element']"); - - private IApplication getBrowser() { - return AqualityServices.getApplication(); - } - - private boolean isApplicationStarted() { - return AqualityServices.isApplicationStarted(); - } - - private void navigate(TheInternetPage page) { - getBrowser().getDriver().navigate().to(page.getAddress()); - } - - @BeforeMethod - protected void beforeMethod() { - navigate(TheInternetPage.DYNAMIC_CONTROLS); - getBrowser().getDriver().manage().window().setSize(DEFAULT_SIZE); - } - - @AfterMethod - private void cleanUp () { - if (isApplicationStarted()) { - getBrowser().getDriver().quit(); - } - } + private final DynamicControlsForm dynamicControlsForm = new DynamicControlsForm(this::getBrowser, this::state); + private final DynamicLoadingForm dynamicLoadingForm = new DynamicLoadingForm(this::getBrowser, this::state); private ElementStateProvider state(By locator) { return new ElementStateProvider(locator, @@ -57,169 +33,107 @@ private ElementStateProvider state(By locator) { AqualityServices.get(IElementFinder.class)); } - private ElementStateProvider inputState() { - return state(DynamicControlsForm.getInputTextboxLocator()); - } - - private ElementStateProvider checkboxState() { - return state(DynamicControlsForm.getCheckboxLocator()); - } - - private ElementStateProvider loaderState() { - return state(DynamicLoadingForm.getLoadingLabelLocator()); - } - - private ElementStateProvider startButtonState() { - return state(DynamicLoadingForm.getStartButtonLocator()); - } - - private ElementStateProvider absentElementState() { - return state(absentElementLocator); - } - - private void click(By locator) { - locator.findElement(getBrowser().getDriver()).click(); - } - - private void clickEnable() { - click(DynamicControlsForm.getEnableButtonLocator()); - } - - private void clickRemove() { - click(DynamicControlsForm.getRemoveButtonLocator()); - } - - private void clickStart() { - click(DynamicLoadingForm.getStartButtonLocator()); - } - private long getConditionTimeout() { return AqualityServices.get(ITimeoutConfiguration.class).getCondition(); } @Test public void testElementShouldWaitForEnabledWithCustomTimeout() { - navigate(TheInternetPage.DYNAMIC_CONTROLS); long waitTime = CUSTOM_WAIT_TIME; - - long startTime = getCurrentTime(); - boolean isEnabled = inputState().waitForEnabled(waitTime); - double duration = calculateDuration(startTime); - - Assert.assertFalse(isEnabled); - Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + checkWaitingTimeForInputState(waitTime, state -> state.waitForEnabled(waitTime)); } @Test public void testElementShouldWaitForEnabledWithDefaultTimeout() { - navigate(TheInternetPage.DYNAMIC_CONTROLS); long waitTime = getConditionTimeout(); - - long startTime = getCurrentTime(); - boolean isEnabled = inputState().waitForEnabled(); - double duration = calculateDuration(startTime); - - Assert.assertFalse(isEnabled); - Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + checkWaitingTimeForInputState(waitTime, IElementStateProvider::waitForEnabled); } @Test public void testElementShouldWaitForNotEnabledWithCustomTimeout() { - navigate(TheInternetPage.DYNAMIC_CONTROLS); long waitTime = CUSTOM_WAIT_TIME; + dynamicControlsForm.clickEnable(); + dynamicControlsForm.inputState().waitForEnabled(); - clickEnable(); - inputState().waitForEnabled(); + checkWaitingTimeForInputState(waitTime, state -> state.waitForNotEnabled(waitTime)); + } + private void checkWaitingTimeForInputState(Long waitTime, Predicate notExpectedCondition) { long startTime = getCurrentTime(); - boolean isDisabled = inputState().waitForNotEnabled(waitTime); + boolean isConditionSatisfied = notExpectedCondition.test(dynamicControlsForm.inputState()); double duration = calculateDuration(startTime); - Assert.assertFalse(isDisabled); + Assert.assertFalse(isConditionSatisfied); Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); } @Test(expectedExceptions = NoSuchElementException.class) - public void testNoSuchShouldBeThrownForWaitEnabledIfElementNotFound(){ - absentElementState().waitForEnabled(CUSTOM_WAIT_TIME); + public void testNoSuchShouldBeThrownForWaitEnabledIfElementNotFound() { + state(absentElementLocator).waitForEnabled(CUSTOM_WAIT_TIME); } @Test(expectedExceptions = NoSuchElementException.class) - public void testNoSuchShouldBeThrownForWaitNotEnabledIfElementNotFound(){ - absentElementState().waitForNotEnabled(CUSTOM_WAIT_TIME); + public void testNoSuchShouldBeThrownForWaitNotEnabledIfElementNotFound() { + state(absentElementLocator).waitForNotEnabled(CUSTOM_WAIT_TIME); } @Test public void testElementShouldWaitForNotEnabledWithDefaultTimeout() { - navigate(TheInternetPage.DYNAMIC_CONTROLS); long waitTime = getConditionTimeout(); + dynamicControlsForm.clickEnable(); + dynamicControlsForm.inputState().waitForEnabled(); - clickEnable(); - inputState().waitForEnabled(); - - long startTime = getCurrentTime(); - boolean isDisabled = inputState().waitForNotEnabled(); - double duration = calculateDuration(startTime); - - Assert.assertFalse(isDisabled); - Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + checkWaitingTimeForInputState(waitTime, IElementStateProvider::waitForNotEnabled); } @Test public void testTextBoxEnabled() { - navigate(TheInternetPage.DYNAMIC_CONTROLS); - clickEnable(); - inputState().waitForEnabled(); - Assert.assertTrue(inputState().isEnabled()); + dynamicControlsForm.clickEnable(); + dynamicControlsForm.inputState().waitForEnabled(); + Assert.assertTrue(dynamicControlsForm.inputState().isEnabled()); } @Test public void testWaitInvisibility() { navigate(TheInternetPage.DYNAMIC_LOADING); - startButtonState().waitForClickable(); - clickStart(); + dynamicLoadingForm.startButtonState().waitForClickable(); + dynamicLoadingForm.clickStart(); - loaderState().waitForDisplayed(DynamicLoadingForm.getSmallTimeout()); + dynamicLoadingForm.loaderState().waitForDisplayed(dynamicLoadingForm.getSmallTimeout()); - boolean status = loaderState().waitForNotDisplayed(DynamicLoadingForm.getSmallTimeout()); + boolean status = dynamicLoadingForm.loaderState().waitForNotDisplayed(dynamicLoadingForm.getSmallTimeout()); Assert.assertFalse(status); - status = loaderState().waitForNotDisplayed(); + status = dynamicLoadingForm.loaderState().waitForNotDisplayed(); Assert.assertTrue(status); } @Test - public void testWaitForExist(){ + public void testWaitForExist() { navigate(TheInternetPage.DYNAMIC_LOADING); - boolean status = loaderState().waitForExist(DynamicLoadingForm.getTimeout()); + boolean status = dynamicLoadingForm.loaderState().waitForExist(dynamicLoadingForm.getTimeout()); Assert.assertFalse(status); - Assert.assertTrue(startButtonState().waitForExist()); + Assert.assertTrue(dynamicLoadingForm.startButtonState().waitForExist()); } @Test - public void testShouldBePossibleToWaitElementNotExistsCustom(){ - navigate(TheInternetPage.DYNAMIC_CONTROLS); + public void testShouldBePossibleToWaitElementNotExistsCustomTime() { long waitTime = CUSTOM_WAIT_TIME; - clickRemove(); - long startTime = getCurrentTime(); - boolean isMissed = checkboxState().waitForNotExist(waitTime); - double duration = calculateDuration(startTime); + dynamicControlsForm.clickRemove(); - Assert.assertFalse(isMissed); - Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + checkWaitingTimeForInputState(waitTime, inputState -> dynamicControlsForm.checkboxState().waitForNotExist(waitTime)); } @Test - public void testShouldBePossibleToWaitElementNotExists(){ - navigate(TheInternetPage.DYNAMIC_CONTROLS); + public void testShouldBePossibleToWaitElementNotExists() { long waitTime = getConditionTimeout(); - clickRemove(); + dynamicControlsForm.clickRemove(); long startTime = getCurrentTime(); - boolean isMissed = checkboxState().waitForNotExist(); + boolean isMissed = dynamicControlsForm.checkboxState().waitForNotExist(); double duration = calculateDuration(startTime); Assert.assertTrue(isMissed); diff --git a/src/test/java/theinternet/BaseForm.java b/src/test/java/theinternet/BaseForm.java new file mode 100644 index 0000000..4fc6983 --- /dev/null +++ b/src/test/java/theinternet/BaseForm.java @@ -0,0 +1,26 @@ +package theinternet; + +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import org.openqa.selenium.By; +import java.util.function.Function; +import java.util.function.Supplier; + +abstract class BaseForm { + + private final Supplier appSupplier; + private final Function stateProviderFunction; + + BaseForm(Supplier appSupplier, Function stateProviderFunction) { + this.appSupplier = appSupplier; + this.stateProviderFunction = stateProviderFunction; + } + + void click(By locator) { + locator.findElement(appSupplier.get().getDriver()).click(); + } + + IElementStateProvider state(By locator) { + return stateProviderFunction.apply(locator); + } +} diff --git a/src/test/java/theinternet/DynamicControlsForm.java b/src/test/java/theinternet/DynamicControlsForm.java index 54608ea..92105e3 100644 --- a/src/test/java/theinternet/DynamicControlsForm.java +++ b/src/test/java/theinternet/DynamicControlsForm.java @@ -1,25 +1,35 @@ package theinternet; +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; import org.openqa.selenium.By; -public class DynamicControlsForm { +import java.util.function.Function; +import java.util.function.Supplier; - private DynamicControlsForm(){ +public class DynamicControlsForm extends BaseForm { + private static final By ENABLE_BUTTON_LOCATOR = By.xpath("//button[contains(@onclick, 'swapInput()')][contains(.,'Enable')]"); + private static final By REMOVE_BUTTON_LOCATOR = By.xpath("//button[contains(@onclick, 'swapCheckbox()')]"); + private static final By INPUT_TEXTBOX_LOCATOR = By.xpath("//input[@type='text']"); + private static final By CHECKBOX_LOCATOR = By.xpath("//div[@id='checkbox']"); + + public DynamicControlsForm(Supplier appSupplier, Function stateProviderFunction) { + super(appSupplier, stateProviderFunction); } - public static By getEnableButtonLocator() { - return By.xpath("//button[contains(@onclick, 'swapInput()')][contains(.,'Enable')]"); + public void clickEnable() { + click(ENABLE_BUTTON_LOCATOR); } - public static By getInputTextboxLocator() { - return By.xpath("//input[@type='text']"); + public void clickRemove() { + click(REMOVE_BUTTON_LOCATOR); } - public static By getCheckboxLocator() { - return By.xpath("//div[@id='checkbox']"); + public IElementStateProvider inputState() { + return state(INPUT_TEXTBOX_LOCATOR); } - public static By getRemoveButtonLocator() { - return By.xpath("//button[contains(@onclick, 'swapCheckbox()')]"); + public IElementStateProvider checkboxState() { + return state(CHECKBOX_LOCATOR); } } diff --git a/src/test/java/theinternet/DynamicLoadingForm.java b/src/test/java/theinternet/DynamicLoadingForm.java index 19c6ee7..3120786 100644 --- a/src/test/java/theinternet/DynamicLoadingForm.java +++ b/src/test/java/theinternet/DynamicLoadingForm.java @@ -1,32 +1,40 @@ package theinternet; +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; import org.openqa.selenium.By; -public class DynamicLoadingForm { +import java.util.function.Function; +import java.util.function.Supplier; + +public class DynamicLoadingForm extends BaseForm { private static final long DEFAULT_LOADING_TIMEOUT = 5L; private static final long SMALL_LOADING_TIMEOUT = 2L; + private static final By LOADING_LABEL_LOCATOR = By.id("loading"); + private static final By START_BUTTON_LOCATOR = By.xpath("//div[@id='start']/button"); - private DynamicLoadingForm(){ + public DynamicLoadingForm(Supplier appSupplier, Function stateProviderFunction) { + super(appSupplier, stateProviderFunction); } - public static By getLoadingLabelLocator() { - return By.id("loading"); + public long getTimeout() { + return DEFAULT_LOADING_TIMEOUT; } - public static By getStartButtonLocator() { - return By.xpath("//div[@id='start']/button"); + public long getSmallTimeout() { + return SMALL_LOADING_TIMEOUT; } - public static long getTimeout() { - return DEFAULT_LOADING_TIMEOUT; + public void clickStart() { + click(START_BUTTON_LOCATOR); } - public static long getSmallTimeout() { - return SMALL_LOADING_TIMEOUT; + public IElementStateProvider loaderState() { + return state(LOADING_LABEL_LOCATOR); } - public static By getLblFinish() { - return By.id("finish"); + public IElementStateProvider startButtonState() { + return state(START_BUTTON_LOCATOR); } } From ce27a42e99f3c5d6ead9736a299ea7ee29b5442d Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Thu, 6 Feb 2020 17:13:31 +0300 Subject: [PATCH 04/11] implement ElementCacheHandler and CachedElementStateProvider --- .../elements/CachedElementStateProvider.java | 119 ++++++++++++++++++ .../core/elements/ElementCacheHandler.java | 55 ++++++++ .../interfaces/IElementCacheHandler.java | 79 ++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java create mode 100644 src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java create mode 100644 src/main/java/aquality/selenium/core/elements/interfaces/IElementCacheHandler.java diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java new file mode 100644 index 0000000..4d74ebf --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -0,0 +1,119 @@ +package aquality.selenium.core.elements; + +import aquality.selenium.core.elements.interfaces.IElementCacheHandler; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.logging.Logger; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.WebElement; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; + +/** + * Provides functions to retrive the state for cached element. + */ +public class CachedElementStateProvider implements IElementStateProvider { + + private static final long ZERO_TIMEOUT = 0L; + private final By locator; + private final IConditionalWait conditionalWait; + private final IElementCacheHandler elementCacheHandler; + + public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler) { + this.locator = locator; + this.conditionalWait = conditionalWait; + this.elementCacheHandler = elementCacheHandler; + } + + protected List> getHandledExceptions() { + return Arrays.asList(StaleElementReferenceException.class, NoSuchElementException.class); + } + + protected boolean tryInvokeFunction(Predicate predicate) { + try { + return predicate.test(elementCacheHandler.getElement(ZERO_TIMEOUT, ElementState.EXISTS_IN_ANY_STATE)); + } catch (Exception exception) { + if (getHandledExceptions().contains(exception.getClass())) { + return false; + } + throw exception; + } + } + + protected boolean waitForCondition(BooleanSupplier condition, String conditionName, Long timeout) { + boolean result = conditionalWait.waitFor(condition, timeout, null); + if (!result) { + String timeoutString = timeout == null ? "" : String.format("of %1$s seconds", timeout); + String message = String.format( + "Element %1$s has not become %2$s after timeout %3$s", locator, conditionName, timeoutString); + Logger.getInstance().warn(message); + } + return result; + } + + @Override + public boolean isClickable() { + return tryInvokeFunction(element -> element.isDisplayed() && element.isEnabled()); + } + + @Override + public void waitForClickable(Long timeout) { + String errorMessage = String.format("Element %1$s has not become clickable after timeout.", locator); + try { + conditionalWait.waitForTrue(this::isClickable, timeout, null, errorMessage); + } catch (TimeoutException e) { + throw new org.openqa.selenium.TimeoutException(e.getMessage(), e); + } + } + + @Override + public boolean isDisplayed() { + return !elementCacheHandler.isStale() && tryInvokeFunction(WebElement::isDisplayed); + } + + @Override + public boolean waitForDisplayed(Long timeout) { + return waitForCondition(() -> tryInvokeFunction(WebElement::isDisplayed), "displayed", timeout); + } + + @Override + public boolean waitForNotDisplayed(Long timeout) { + return waitForCondition(() -> tryInvokeFunction(element -> !element.isDisplayed()), "invisible or absent", timeout); + } + + @Override + public boolean isExist() { + return !elementCacheHandler.isStale() && tryInvokeFunction(element -> true); + } + + @Override + public boolean waitForExist(Long timeout) { + return waitForCondition(() -> tryInvokeFunction(element -> true), "exist", timeout); + } + + @Override + public boolean waitForNotExist(Long timeout) { + return waitForCondition(() -> tryInvokeFunction(element -> !element.isDisplayed()), "absent", timeout); + } + + @Override + public boolean isEnabled() { + return tryInvokeFunction(WebElement::isEnabled); + } + + @Override + public boolean waitForEnabled(Long timeout) { + return waitForCondition(this::isEnabled, "enabled", timeout); + } + + @Override + public boolean waitForNotEnabled(Long timeout) { + return waitForCondition(() -> tryInvokeFunction(element -> !element.isDisplayed()), "disabled", timeout); + } +} diff --git a/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java b/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java new file mode 100644 index 0000000..c2e3a2f --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java @@ -0,0 +1,55 @@ +package aquality.selenium.core.elements; + +import aquality.selenium.core.elements.interfaces.IElementCacheHandler; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import org.openqa.selenium.By; +import org.openqa.selenium.remote.RemoteWebElement; + +/** + * Implementation of {@link IElementCacheHandler}. + */ +public class ElementCacheHandler implements IElementCacheHandler { + + private final By locator; + private final ElementState state; + private final IElementFinder finder; + + private RemoteWebElement remoteElement; + + public ElementCacheHandler(By locator, ElementState state, IElementFinder finder) { + this.locator = locator; + this.state = state; + this.finder = finder; + } + + @Override + public boolean isRefreshNeeded(ElementState customState) { + if (!wasCached()) { + return true; + } + try { + boolean isDisplayed = remoteElement.isDisplayed(); + // refresh is needed only if the property is not match to expected element state + ElementState requiredState = customState == null ? state : customState; + return requiredState == ElementState.DISPLAYED && !isDisplayed; + } catch (Throwable e) { + // refresh is needed if the property is not available + return true; + } + } + + @Override + public boolean wasCached() { + return remoteElement != null; + } + + @Override + public RemoteWebElement getElement(Long timeout, ElementState customState) { + ElementState requiredState = customState == null ? state : customState; + if (isRefreshNeeded(requiredState)) { + remoteElement = (RemoteWebElement) finder.findElement(locator, requiredState, timeout); + } + + return remoteElement; + } +} diff --git a/src/main/java/aquality/selenium/core/elements/interfaces/IElementCacheHandler.java b/src/main/java/aquality/selenium/core/elements/interfaces/IElementCacheHandler.java new file mode 100644 index 0000000..0239153 --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/interfaces/IElementCacheHandler.java @@ -0,0 +1,79 @@ +package aquality.selenium.core.elements.interfaces; + +import aquality.selenium.core.elements.ElementState; +import org.openqa.selenium.remote.RemoteWebElement; + +/** + * Allows to use cached element. + */ +public interface IElementCacheHandler { + + /** + * Determines is the cached element refresh needed. + * + * @return true if the refresh is needed (element wasn't found previously or is stale), false otherwise. + */ + default boolean isRefreshNeeded() { + return isRefreshNeeded(null); + } + + /** + * Determines is the cached element refresh needed. + * + * @param customState custom element's existance state used for search. + * @return true if the refresh is needed (element wasn't found previously or is stale), false otherwise. + */ + boolean isRefreshNeeded(ElementState customState); + + /** + * Determines is the element stale. + * @return true if the element was found previously and is currently stale, false otherwise. + */ + default boolean isStale() { + return wasCached() && isRefreshNeeded(); + } + + /** + * Determines was the element cached previously. + * @return true if the element was found and cached previously, false otherwise. + */ + boolean wasCached(); + + /** + * Allows to get cached element. + * + * @param timeout timeout used to retrive the element when {@see isRefreshNeeded()} is true. + * @param customState custom element's existance state used for search. + * @return cached element. + */ + RemoteWebElement getElement(Long timeout, ElementState customState); + + /** + * Allows to get cached element. + * + * @param timeout timeout used to retrive the element when {@see isRefreshNeeded()} is true. + * @return cached element. + */ + default RemoteWebElement getElement(Long timeout) { + return getElement(timeout, null); + } + + /** + * Allows to get cached element. + * + * @param customState custom element's existance state used for search. + * @return cached element. + */ + default RemoteWebElement getElement(ElementState customState) { + return getElement(null, customState); + } + + /** + * Allows to get cached element. + * + * @return cached element. + */ + default RemoteWebElement getElement() { + return getElement(null, null); + } +} From 24d2134b5d9b4dfa9ff59d08e3ea269d5a064a29 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Thu, 6 Feb 2020 18:18:43 +0300 Subject: [PATCH 05/11] corrected CachedElementStateProvider, add tests for it --- .../elements/CachedElementStateProvider.java | 20 ++- .../CachedElementStateProviderTests.java | 21 +++ .../elements/ElementStateProviderTests.java | 130 +--------------- .../IWebElementStateProviderTests.java | 141 ++++++++++++++++++ src/test/resources/TestSuite.xml | 1 + 5 files changed, 181 insertions(+), 132 deletions(-) create mode 100644 src/test/java/tests/elements/CachedElementStateProviderTests.java create mode 100644 src/test/java/tests/elements/IWebElementStateProviderTests.java diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java index 4d74ebf..f3910db 100644 --- a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -10,6 +10,7 @@ import org.openqa.selenium.WebElement; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeoutException; import java.util.function.BooleanSupplier; @@ -36,10 +37,14 @@ protected List> getHandledExceptions() { } protected boolean tryInvokeFunction(Predicate predicate) { + return tryInvokeFunction(predicate, getHandledExceptions()); + } + + protected boolean tryInvokeFunction(Predicate predicate, List> handledExceptions) { try { return predicate.test(elementCacheHandler.getElement(ZERO_TIMEOUT, ElementState.EXISTS_IN_ANY_STATE)); } catch (Exception exception) { - if (getHandledExceptions().contains(exception.getClass())) { + if (handledExceptions.contains(exception.getClass())) { return false; } throw exception; @@ -51,7 +56,7 @@ protected boolean waitForCondition(BooleanSupplier condition, String conditionNa if (!result) { String timeoutString = timeout == null ? "" : String.format("of %1$s seconds", timeout); String message = String.format( - "Element %1$s has not become %2$s after timeout %3$s", locator, conditionName, timeoutString); + "Element %1$s has not become %2$s after timeout %3$s", locator, conditionName.toUpperCase(), timeoutString); Logger.getInstance().warn(message); } return result; @@ -99,12 +104,17 @@ public boolean waitForExist(Long timeout) { @Override public boolean waitForNotExist(Long timeout) { - return waitForCondition(() -> tryInvokeFunction(element -> !element.isDisplayed()), "absent", timeout); + return waitForCondition(() -> !isExist(), "absent", timeout); } @Override public boolean isEnabled() { - return tryInvokeFunction(WebElement::isEnabled); + return tryInvokeFunction(this::isElementEnabled, Collections.singletonList(StaleElementReferenceException.class)); + } + + // todo: move to base class/expected conditions/Desired states + protected boolean isElementEnabled(WebElement element) { + return element.isEnabled(); } @Override @@ -114,6 +124,6 @@ public boolean waitForEnabled(Long timeout) { @Override public boolean waitForNotEnabled(Long timeout) { - return waitForCondition(() -> tryInvokeFunction(element -> !element.isDisplayed()), "disabled", timeout); + return waitForCondition(() -> !isEnabled(), "disabled", timeout); } } diff --git a/src/test/java/tests/elements/CachedElementStateProviderTests.java b/src/test/java/tests/elements/CachedElementStateProviderTests.java new file mode 100644 index 0000000..11a8959 --- /dev/null +++ b/src/test/java/tests/elements/CachedElementStateProviderTests.java @@ -0,0 +1,21 @@ +package tests.elements; + +import aquality.selenium.core.elements.CachedElementStateProvider; +import aquality.selenium.core.elements.ElementCacheHandler; +import aquality.selenium.core.elements.ElementState; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import tests.application.browser.AqualityServices; + +public class CachedElementStateProviderTests implements IWebElementStateProviderTests { + + @Override + public IElementStateProvider state(By locator) { + return new CachedElementStateProvider(locator, + AqualityServices.get(IConditionalWait.class), + new ElementCacheHandler(locator, ElementState.EXISTS_IN_ANY_STATE, AqualityServices.get(IElementFinder.class))); + } + +} diff --git a/src/test/java/tests/elements/ElementStateProviderTests.java b/src/test/java/tests/elements/ElementStateProviderTests.java index 251be28..bbf06e5 100644 --- a/src/test/java/tests/elements/ElementStateProviderTests.java +++ b/src/test/java/tests/elements/ElementStateProviderTests.java @@ -1,142 +1,18 @@ package tests.elements; -import aquality.selenium.core.configurations.ITimeoutConfiguration; import aquality.selenium.core.elements.ElementStateProvider; import aquality.selenium.core.elements.interfaces.IElementFinder; import aquality.selenium.core.elements.interfaces.IElementStateProvider; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; -import org.testng.Assert; -import org.testng.annotations.Test; import tests.application.browser.AqualityServices; -import tests.application.browser.ITheInternetPageTest; -import theinternet.DynamicControlsForm; -import theinternet.DynamicLoadingForm; -import theinternet.TheInternetPage; -import java.util.function.Predicate; +public class ElementStateProviderTests implements IWebElementStateProviderTests { -import static utils.TimeUtil.calculateDuration; -import static utils.TimeUtil.getCurrentTime; - -public class ElementStateProviderTests implements ITheInternetPageTest { - private static final double OPERATION_TIME = 5; - private static final long CUSTOM_WAIT_TIME = 2L; - private static final By absentElementLocator = By.xpath("//div[@class='not exist element']"); - private final DynamicControlsForm dynamicControlsForm = new DynamicControlsForm(this::getBrowser, this::state); - private final DynamicLoadingForm dynamicLoadingForm = new DynamicLoadingForm(this::getBrowser, this::state); - - private ElementStateProvider state(By locator) { + @Override + public IElementStateProvider state(By locator) { return new ElementStateProvider(locator, AqualityServices.get(IConditionalWait.class), AqualityServices.get(IElementFinder.class)); } - - private long getConditionTimeout() { - return AqualityServices.get(ITimeoutConfiguration.class).getCondition(); - } - - @Test - public void testElementShouldWaitForEnabledWithCustomTimeout() { - long waitTime = CUSTOM_WAIT_TIME; - checkWaitingTimeForInputState(waitTime, state -> state.waitForEnabled(waitTime)); - } - - @Test - public void testElementShouldWaitForEnabledWithDefaultTimeout() { - long waitTime = getConditionTimeout(); - checkWaitingTimeForInputState(waitTime, IElementStateProvider::waitForEnabled); - } - - @Test - public void testElementShouldWaitForNotEnabledWithCustomTimeout() { - long waitTime = CUSTOM_WAIT_TIME; - dynamicControlsForm.clickEnable(); - dynamicControlsForm.inputState().waitForEnabled(); - - checkWaitingTimeForInputState(waitTime, state -> state.waitForNotEnabled(waitTime)); - } - - private void checkWaitingTimeForInputState(Long waitTime, Predicate notExpectedCondition) { - long startTime = getCurrentTime(); - boolean isConditionSatisfied = notExpectedCondition.test(dynamicControlsForm.inputState()); - double duration = calculateDuration(startTime); - - Assert.assertFalse(isConditionSatisfied); - Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); - } - - @Test(expectedExceptions = NoSuchElementException.class) - public void testNoSuchShouldBeThrownForWaitEnabledIfElementNotFound() { - state(absentElementLocator).waitForEnabled(CUSTOM_WAIT_TIME); - } - - @Test(expectedExceptions = NoSuchElementException.class) - public void testNoSuchShouldBeThrownForWaitNotEnabledIfElementNotFound() { - state(absentElementLocator).waitForNotEnabled(CUSTOM_WAIT_TIME); - } - - @Test - public void testElementShouldWaitForNotEnabledWithDefaultTimeout() { - long waitTime = getConditionTimeout(); - - dynamicControlsForm.clickEnable(); - dynamicControlsForm.inputState().waitForEnabled(); - - checkWaitingTimeForInputState(waitTime, IElementStateProvider::waitForNotEnabled); - } - - @Test - public void testTextBoxEnabled() { - dynamicControlsForm.clickEnable(); - dynamicControlsForm.inputState().waitForEnabled(); - Assert.assertTrue(dynamicControlsForm.inputState().isEnabled()); - } - - @Test - public void testWaitInvisibility() { - navigate(TheInternetPage.DYNAMIC_LOADING); - - dynamicLoadingForm.startButtonState().waitForClickable(); - dynamicLoadingForm.clickStart(); - - dynamicLoadingForm.loaderState().waitForDisplayed(dynamicLoadingForm.getSmallTimeout()); - - boolean status = dynamicLoadingForm.loaderState().waitForNotDisplayed(dynamicLoadingForm.getSmallTimeout()); - Assert.assertFalse(status); - - status = dynamicLoadingForm.loaderState().waitForNotDisplayed(); - Assert.assertTrue(status); - } - - @Test - public void testWaitForExist() { - navigate(TheInternetPage.DYNAMIC_LOADING); - boolean status = dynamicLoadingForm.loaderState().waitForExist(dynamicLoadingForm.getTimeout()); - - Assert.assertFalse(status); - Assert.assertTrue(dynamicLoadingForm.startButtonState().waitForExist()); - } - - @Test - public void testShouldBePossibleToWaitElementNotExistsCustomTime() { - long waitTime = CUSTOM_WAIT_TIME; - dynamicControlsForm.clickRemove(); - - checkWaitingTimeForInputState(waitTime, inputState -> dynamicControlsForm.checkboxState().waitForNotExist(waitTime)); - } - - @Test - public void testShouldBePossibleToWaitElementNotExists() { - long waitTime = getConditionTimeout(); - dynamicControlsForm.clickRemove(); - - long startTime = getCurrentTime(); - boolean isMissed = dynamicControlsForm.checkboxState().waitForNotExist(); - double duration = calculateDuration(startTime); - - Assert.assertTrue(isMissed); - Assert.assertTrue(duration < waitTime); - } } diff --git a/src/test/java/tests/elements/IWebElementStateProviderTests.java b/src/test/java/tests/elements/IWebElementStateProviderTests.java new file mode 100644 index 0000000..a669965 --- /dev/null +++ b/src/test/java/tests/elements/IWebElementStateProviderTests.java @@ -0,0 +1,141 @@ +package tests.elements; + +import aquality.selenium.core.configurations.ITimeoutConfiguration; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.application.browser.AqualityServices; +import tests.application.browser.ITheInternetPageTest; +import theinternet.DynamicControlsForm; +import theinternet.DynamicLoadingForm; +import theinternet.TheInternetPage; + +import java.util.function.Predicate; + +import static utils.TimeUtil.calculateDuration; +import static utils.TimeUtil.getCurrentTime; + +public interface IWebElementStateProviderTests extends ITheInternetPageTest { + double OPERATION_TIME = 5; + long CUSTOM_WAIT_TIME = 2L; + By ABSENT_ELEMENT_LOCATOR = By.xpath("//div[@class='not exist element']"); + + default DynamicControlsForm getDynamicControlsForm() { + return new DynamicControlsForm(this::getBrowser, this::state); + } + + default DynamicLoadingForm getDynamicLoadingForm() { + return new DynamicLoadingForm(this::getBrowser, this::state); + } + + IElementStateProvider state(By locator); + + default long getConditionTimeout() { + return AqualityServices.get(ITimeoutConfiguration.class).getCondition(); + } + + default void checkWaitingTimeForInputState(Long waitTime, Predicate notExpectedCondition) { + long startTime = getCurrentTime(); + boolean isConditionSatisfied = notExpectedCondition.test(getDynamicControlsForm().inputState()); + double duration = calculateDuration(startTime); + + Assert.assertFalse(isConditionSatisfied); + Assert.assertTrue(duration >= waitTime && duration <= (waitTime + OPERATION_TIME)); + } + + @Test + default void testElementShouldWaitForEnabledWithCustomTimeout() { + long waitTime = ElementStateProviderTests.CUSTOM_WAIT_TIME; + checkWaitingTimeForInputState(waitTime, state -> state.waitForEnabled(waitTime)); + } + + @Test + default void testElementShouldWaitForEnabledWithDefaultTimeout() { + long waitTime = getConditionTimeout(); + checkWaitingTimeForInputState(waitTime, IElementStateProvider::waitForEnabled); + } + + @Test + default void testElementShouldWaitForNotEnabledWithCustomTimeout() { + long waitTime = ElementStateProviderTests.CUSTOM_WAIT_TIME; + getDynamicControlsForm().clickEnable(); + getDynamicControlsForm().inputState().waitForEnabled(); + + checkWaitingTimeForInputState(waitTime, state -> state.waitForNotEnabled(waitTime)); + } + + @Test(expectedExceptions = NoSuchElementException.class) + default void testNoSuchShouldBeThrownForWaitEnabledIfElementNotFound() { + state(ElementStateProviderTests.ABSENT_ELEMENT_LOCATOR).waitForEnabled(ElementStateProviderTests.CUSTOM_WAIT_TIME); + } + + @Test(expectedExceptions = NoSuchElementException.class) + default void testNoSuchShouldBeThrownForWaitNotEnabledIfElementNotFound() { + state(ElementStateProviderTests.ABSENT_ELEMENT_LOCATOR).waitForNotEnabled(ElementStateProviderTests.CUSTOM_WAIT_TIME); + } + + @Test + default void testElementShouldWaitForNotEnabledWithDefaultTimeout() { + long waitTime = getConditionTimeout(); + + getDynamicControlsForm().clickEnable(); + getDynamicControlsForm().inputState().waitForEnabled(); + + checkWaitingTimeForInputState(waitTime, IElementStateProvider::waitForNotEnabled); + } + + @Test + default void testTextBoxEnabled() { + getDynamicControlsForm().clickEnable(); + getDynamicControlsForm().inputState().waitForEnabled(); + Assert.assertTrue(getDynamicControlsForm().inputState().isEnabled()); + } + + @Test + default void testWaitInvisibility() { + navigate(TheInternetPage.DYNAMIC_LOADING); + + getDynamicLoadingForm().startButtonState().waitForClickable(); + getDynamicLoadingForm().clickStart(); + + getDynamicLoadingForm().loaderState().waitForDisplayed(getDynamicLoadingForm().getSmallTimeout()); + + boolean status = getDynamicLoadingForm().loaderState().waitForNotDisplayed(getDynamicLoadingForm().getSmallTimeout()); + Assert.assertFalse(status); + + status = getDynamicLoadingForm().loaderState().waitForNotDisplayed(); + Assert.assertTrue(status); + } + + @Test + default void testWaitForExist() { + navigate(TheInternetPage.DYNAMIC_LOADING); + boolean status = getDynamicLoadingForm().loaderState().waitForExist(getDynamicLoadingForm().getTimeout()); + + Assert.assertFalse(status); + Assert.assertTrue(getDynamicLoadingForm().startButtonState().waitForExist()); + } + + @Test + default void testShouldBePossibleToWaitElementNotExistsCustomTime() { + long waitTime = ElementStateProviderTests.CUSTOM_WAIT_TIME; + getDynamicControlsForm().clickRemove(); + + checkWaitingTimeForInputState(waitTime, inputState -> getDynamicControlsForm().checkboxState().waitForNotExist(waitTime)); + } + + @Test + default void testShouldBePossibleToWaitElementNotExists() { + long waitTime = getConditionTimeout(); + getDynamicControlsForm().clickRemove(); + + long startTime = getCurrentTime(); + boolean isMissed = getDynamicControlsForm().checkboxState().waitForNotExist(); + double duration = calculateDuration(startTime); + + Assert.assertTrue(isMissed); + Assert.assertTrue(duration < waitTime); + } +} diff --git a/src/test/resources/TestSuite.xml b/src/test/resources/TestSuite.xml index c51be7e..cbdb45d 100644 --- a/src/test/resources/TestSuite.xml +++ b/src/test/resources/TestSuite.xml @@ -14,6 +14,7 @@ + From 23f6f91468efb405919de49e2c7f7fdd15b56399 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Fri, 7 Feb 2020 10:51:23 +0300 Subject: [PATCH 06/11] refactored ElementStateProviders, moved common part to base class --- .../elements/CachedElementStateProvider.java | 26 ++--- .../elements/DefaultElementStateProvider.java | 89 +++++++++++++++++ .../core/elements/ElementStateProvider.java | 97 +++---------------- ... => DefaultElementStateProviderTests.java} | 6 +- .../IWebElementStateProviderTests.java | 10 +- src/test/resources/TestSuite.xml | 2 +- 6 files changed, 121 insertions(+), 109 deletions(-) create mode 100644 src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java rename src/test/java/tests/elements/{ElementStateProviderTests.java => DefaultElementStateProviderTests.java} (70%) diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java index f3910db..5bcc2e6 100644 --- a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -1,7 +1,6 @@ package aquality.selenium.core.elements; import aquality.selenium.core.elements.interfaces.IElementCacheHandler; -import aquality.selenium.core.elements.interfaces.IElementStateProvider; import aquality.selenium.core.logging.Logger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; @@ -19,16 +18,12 @@ /** * Provides functions to retrive the state for cached element. */ -public class CachedElementStateProvider implements IElementStateProvider { +public class CachedElementStateProvider extends ElementStateProvider { - private static final long ZERO_TIMEOUT = 0L; - private final By locator; - private final IConditionalWait conditionalWait; private final IElementCacheHandler elementCacheHandler; public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler) { - this.locator = locator; - this.conditionalWait = conditionalWait; + super(locator, conditionalWait); this.elementCacheHandler = elementCacheHandler; } @@ -64,7 +59,7 @@ protected boolean waitForCondition(BooleanSupplier condition, String conditionNa @Override public boolean isClickable() { - return tryInvokeFunction(element -> element.isDisplayed() && element.isEnabled()); + return tryInvokeFunction(elementClickable().getElementStateCondition()); } @Override @@ -84,7 +79,7 @@ public boolean isDisplayed() { @Override public boolean waitForDisplayed(Long timeout) { - return waitForCondition(() -> tryInvokeFunction(WebElement::isDisplayed), "displayed", timeout); + return waitForCondition(() -> tryInvokeFunction(WebElement::isDisplayed), ElementState.DISPLAYED.toString(), timeout); } @Override @@ -99,7 +94,7 @@ public boolean isExist() { @Override public boolean waitForExist(Long timeout) { - return waitForCondition(() -> tryInvokeFunction(element -> true), "exist", timeout); + return waitForCondition(() -> tryInvokeFunction(element -> true), ElementState.EXISTS_IN_ANY_STATE.toString(), timeout); } @Override @@ -109,21 +104,16 @@ public boolean waitForNotExist(Long timeout) { @Override public boolean isEnabled() { - return tryInvokeFunction(this::isElementEnabled, Collections.singletonList(StaleElementReferenceException.class)); - } - - // todo: move to base class/expected conditions/Desired states - protected boolean isElementEnabled(WebElement element) { - return element.isEnabled(); + return tryInvokeFunction(elementEnabled().getElementStateCondition(), Collections.singletonList(StaleElementReferenceException.class)); } @Override public boolean waitForEnabled(Long timeout) { - return waitForCondition(this::isEnabled, "enabled", timeout); + return waitForCondition(this::isEnabled, elementEnabled().getStateName(), timeout); } @Override public boolean waitForNotEnabled(Long timeout) { - return waitForCondition(() -> !isEnabled(), "disabled", timeout); + return waitForCondition(() -> !isEnabled(), elementNotEnabled().getStateName(), timeout); } } diff --git a/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java new file mode 100644 index 0000000..1e5304c --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java @@ -0,0 +1,89 @@ +package aquality.selenium.core.elements; + +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; + +public class DefaultElementStateProvider extends ElementStateProvider { + + private static final long ZERO_TIMEOUT = 0L; + private final By locator; + private final IConditionalWait conditionalWait; + private final IElementFinder elementFinder; + + public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { + super(locator, conditionalWait); + this.locator = locator; + this.conditionalWait = conditionalWait; + this.elementFinder = elementFinder; + } + + @Override + public boolean isClickable() { + return waitForIsClickable(ZERO_TIMEOUT, true); + } + + @Override + public void waitForClickable(Long timeout) { + waitForIsClickable(timeout, false); + } + + private boolean waitForIsClickable(Long timeout, boolean catchTimeoutException) { + DesiredState desiredState = elementClickable(); + desiredState = catchTimeoutException ? desiredState.withCatchingTimeoutException() : desiredState; + return isElementInDesiredCondition(desiredState, timeout); + } + + private boolean isElementInDesiredCondition(DesiredState elementStateCondition, Long timeout) { + return !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty(); + } + + @Override + public boolean isDisplayed() { + return waitForDisplayed(ZERO_TIMEOUT); + } + + @Override + public boolean waitForDisplayed(Long timeout) { + return isAnyElementFound(timeout, ElementState.DISPLAYED); + } + + private boolean isAnyElementFound(Long timeout, ElementState state) { + return !elementFinder.findElements(locator, state, timeout).isEmpty(); + } + + @Override + public boolean waitForNotDisplayed(Long timeout) { + return conditionalWait.waitFor(() -> !isDisplayed(), timeout, null); + } + + @Override + public boolean isExist() { + return waitForExist(ZERO_TIMEOUT); + } + + @Override + public boolean waitForExist(Long timeout) { + return isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE); + } + + @Override + public boolean waitForNotExist(Long timeout) { + return conditionalWait.waitFor(() -> !isExist(), timeout, null); + } + + @Override + public boolean isEnabled() { + return waitForEnabled(ZERO_TIMEOUT); + } + + @Override + public boolean waitForEnabled(Long timeout) { + return isElementInDesiredCondition(elementEnabled(), timeout); + } + + @Override + public boolean waitForNotEnabled(Long timeout) { + return isElementInDesiredCondition(elementNotEnabled(), timeout); + } +} diff --git a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java index 9ee1a67..7eaa44f 100644 --- a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java @@ -1,104 +1,37 @@ package aquality.selenium.core.elements; -import aquality.selenium.core.elements.interfaces.IElementFinder; import aquality.selenium.core.elements.interfaces.IElementStateProvider; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; -import java.util.function.Predicate; +public abstract class ElementStateProvider implements IElementStateProvider { + static final long ZERO_TIMEOUT = 0L; + protected final By locator; + protected final IConditionalWait conditionalWait; -public class ElementStateProvider implements IElementStateProvider { - - private static final long ZERO_TIMEOUT = 0L; - private final By locator; - private final IConditionalWait conditionalWait; - private final IElementFinder elementFinder; - - public ElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { + protected ElementStateProvider(By locator, IConditionalWait conditionalWait) { this.locator = locator; this.conditionalWait = conditionalWait; - this.elementFinder = elementFinder; - } - - @Override - public boolean isClickable() { - return waitForClickable(ZERO_TIMEOUT, true); - } - - @Override - public void waitForClickable(Long timeout) { - waitForClickable(timeout, false); - } - - private boolean waitForClickable(Long timeout, boolean catchTimeoutException) { - DesiredState desiredState = - new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "CLICKABLE"); - desiredState = catchTimeoutException ? desiredState.withCatchingTimeoutException() : desiredState; - return isElementInDesiredCondition(desiredState, timeout); - } - - private boolean isElementInDesiredCondition(DesiredState elementStateCondition, Long timeout) { - return !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty(); - } - - @Override - public boolean isDisplayed() { - return waitForDisplayed(ZERO_TIMEOUT); - } - - @Override - public boolean waitForDisplayed(Long timeout) { - return isAnyElementFound(timeout, ElementState.DISPLAYED); - } - - private boolean isAnyElementFound(Long timeout, ElementState state) { - return !elementFinder.findElements(locator, state, timeout).isEmpty(); - } - - @Override - public boolean waitForNotDisplayed(Long timeout) { - return conditionalWait.waitFor(() -> !isDisplayed(), timeout, null); - } - - @Override - public boolean isExist() { - return waitForExist(ZERO_TIMEOUT); - } - - @Override - public boolean waitForExist(Long timeout) { - return isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE); - } - - @Override - public boolean waitForNotExist(Long timeout) { - return conditionalWait.waitFor(() -> !isExist(), timeout, null); - } - - @Override - public boolean isEnabled() { - return waitForEnabled(ZERO_TIMEOUT); - } - - @Override - public boolean waitForEnabled(Long timeout) { - return isElementInDesiredCondition(this::isElementEnabled, "ENABLED", timeout); } protected boolean isElementEnabled(WebElement element) { return element.isEnabled(); } - private boolean isElementInDesiredCondition(Predicate condition, String stateName, Long timeout) { - DesiredState desiredState = new DesiredState(condition, stateName) + protected DesiredState elementEnabled() { + return new DesiredState(this::isElementEnabled, "ENABLED") + .withCatchingTimeoutException() + .withThrowingNoSuchElementException(); + } + + protected DesiredState elementNotEnabled() { + return new DesiredState(element -> !isElementEnabled(element), "NOT ENABLED") .withCatchingTimeoutException() .withThrowingNoSuchElementException(); - return isElementInDesiredCondition(desiredState, timeout); } - @Override - public boolean waitForNotEnabled(Long timeout) { - return isElementInDesiredCondition(element -> !isElementEnabled(element), "NOT ENABLED", timeout); + protected DesiredState elementClickable() { + return new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "CLICKABLE"); } } diff --git a/src/test/java/tests/elements/ElementStateProviderTests.java b/src/test/java/tests/elements/DefaultElementStateProviderTests.java similarity index 70% rename from src/test/java/tests/elements/ElementStateProviderTests.java rename to src/test/java/tests/elements/DefaultElementStateProviderTests.java index bbf06e5..2694c6d 100644 --- a/src/test/java/tests/elements/ElementStateProviderTests.java +++ b/src/test/java/tests/elements/DefaultElementStateProviderTests.java @@ -1,17 +1,17 @@ package tests.elements; -import aquality.selenium.core.elements.ElementStateProvider; +import aquality.selenium.core.elements.DefaultElementStateProvider; import aquality.selenium.core.elements.interfaces.IElementFinder; import aquality.selenium.core.elements.interfaces.IElementStateProvider; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; import tests.application.browser.AqualityServices; -public class ElementStateProviderTests implements IWebElementStateProviderTests { +public class DefaultElementStateProviderTests implements IWebElementStateProviderTests { @Override public IElementStateProvider state(By locator) { - return new ElementStateProvider(locator, + return new DefaultElementStateProvider(locator, AqualityServices.get(IConditionalWait.class), AqualityServices.get(IElementFinder.class)); } diff --git a/src/test/java/tests/elements/IWebElementStateProviderTests.java b/src/test/java/tests/elements/IWebElementStateProviderTests.java index a669965..cf2b078 100644 --- a/src/test/java/tests/elements/IWebElementStateProviderTests.java +++ b/src/test/java/tests/elements/IWebElementStateProviderTests.java @@ -47,7 +47,7 @@ default void checkWaitingTimeForInputState(Long waitTime, Predicate state.waitForEnabled(waitTime)); } @@ -59,7 +59,7 @@ default void testElementShouldWaitForEnabledWithDefaultTimeout() { @Test default void testElementShouldWaitForNotEnabledWithCustomTimeout() { - long waitTime = ElementStateProviderTests.CUSTOM_WAIT_TIME; + long waitTime = CUSTOM_WAIT_TIME; getDynamicControlsForm().clickEnable(); getDynamicControlsForm().inputState().waitForEnabled(); @@ -68,12 +68,12 @@ default void testElementShouldWaitForNotEnabledWithCustomTimeout() { @Test(expectedExceptions = NoSuchElementException.class) default void testNoSuchShouldBeThrownForWaitEnabledIfElementNotFound() { - state(ElementStateProviderTests.ABSENT_ELEMENT_LOCATOR).waitForEnabled(ElementStateProviderTests.CUSTOM_WAIT_TIME); + state(ABSENT_ELEMENT_LOCATOR).waitForEnabled(CUSTOM_WAIT_TIME); } @Test(expectedExceptions = NoSuchElementException.class) default void testNoSuchShouldBeThrownForWaitNotEnabledIfElementNotFound() { - state(ElementStateProviderTests.ABSENT_ELEMENT_LOCATOR).waitForNotEnabled(ElementStateProviderTests.CUSTOM_WAIT_TIME); + state(ABSENT_ELEMENT_LOCATOR).waitForNotEnabled(CUSTOM_WAIT_TIME); } @Test @@ -120,7 +120,7 @@ default void testWaitForExist() { @Test default void testShouldBePossibleToWaitElementNotExistsCustomTime() { - long waitTime = ElementStateProviderTests.CUSTOM_WAIT_TIME; + long waitTime = CUSTOM_WAIT_TIME; getDynamicControlsForm().clickRemove(); checkWaitingTimeForInputState(waitTime, inputState -> getDynamicControlsForm().checkboxState().waitForNotExist(waitTime)); diff --git a/src/test/resources/TestSuite.xml b/src/test/resources/TestSuite.xml index cbdb45d..33d2ab9 100644 --- a/src/test/resources/TestSuite.xml +++ b/src/test/resources/TestSuite.xml @@ -13,7 +13,7 @@ - + From 58e74f512f993195aff05ed85bf37d36cf4e1510 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Fri, 7 Feb 2020 10:57:24 +0300 Subject: [PATCH 07/11] refactored ElementStateProviders --- .../core/elements/CachedElementStateProvider.java | 7 +++++-- .../core/elements/DefaultElementStateProvider.java | 10 ++++------ .../selenium/core/elements/ElementStateProvider.java | 11 +++-------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java index 5bcc2e6..0367136 100644 --- a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -20,10 +20,13 @@ */ public class CachedElementStateProvider extends ElementStateProvider { + private final By locator; + private final IConditionalWait conditionalWait; private final IElementCacheHandler elementCacheHandler; public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler) { - super(locator, conditionalWait); + this.locator = locator; + this.conditionalWait = conditionalWait; this.elementCacheHandler = elementCacheHandler; } @@ -37,7 +40,7 @@ protected boolean tryInvokeFunction(Predicate predicate) { protected boolean tryInvokeFunction(Predicate predicate, List> handledExceptions) { try { - return predicate.test(elementCacheHandler.getElement(ZERO_TIMEOUT, ElementState.EXISTS_IN_ANY_STATE)); + return predicate.test(elementCacheHandler.getElement(getZeroTImeout(), ElementState.EXISTS_IN_ANY_STATE)); } catch (Exception exception) { if (handledExceptions.contains(exception.getClass())) { return false; diff --git a/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java index 1e5304c..f0cc8f6 100644 --- a/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java @@ -6,13 +6,11 @@ public class DefaultElementStateProvider extends ElementStateProvider { - private static final long ZERO_TIMEOUT = 0L; private final By locator; private final IConditionalWait conditionalWait; private final IElementFinder elementFinder; public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { - super(locator, conditionalWait); this.locator = locator; this.conditionalWait = conditionalWait; this.elementFinder = elementFinder; @@ -20,7 +18,7 @@ public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, @Override public boolean isClickable() { - return waitForIsClickable(ZERO_TIMEOUT, true); + return waitForIsClickable(getZeroTImeout(), true); } @Override @@ -40,7 +38,7 @@ private boolean isElementInDesiredCondition(DesiredState elementStateCondition, @Override public boolean isDisplayed() { - return waitForDisplayed(ZERO_TIMEOUT); + return waitForDisplayed(getZeroTImeout()); } @Override @@ -59,7 +57,7 @@ public boolean waitForNotDisplayed(Long timeout) { @Override public boolean isExist() { - return waitForExist(ZERO_TIMEOUT); + return waitForExist(getZeroTImeout()); } @Override @@ -74,7 +72,7 @@ public boolean waitForNotExist(Long timeout) { @Override public boolean isEnabled() { - return waitForEnabled(ZERO_TIMEOUT); + return waitForEnabled(getZeroTImeout()); } @Override diff --git a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java index 7eaa44f..d0976fe 100644 --- a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java @@ -1,18 +1,13 @@ package aquality.selenium.core.elements; import aquality.selenium.core.elements.interfaces.IElementStateProvider; -import aquality.selenium.core.waitings.IConditionalWait; -import org.openqa.selenium.By; import org.openqa.selenium.WebElement; public abstract class ElementStateProvider implements IElementStateProvider { - static final long ZERO_TIMEOUT = 0L; - protected final By locator; - protected final IConditionalWait conditionalWait; + private static final long ZERO_TIMEOUT = 0L; - protected ElementStateProvider(By locator, IConditionalWait conditionalWait) { - this.locator = locator; - this.conditionalWait = conditionalWait; + protected Long getZeroTImeout() { + return ZERO_TIMEOUT; } protected boolean isElementEnabled(WebElement element) { From a7b686e6436c39556483e549c88d59e1657a7d7e Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Fri, 7 Feb 2020 13:43:11 +0300 Subject: [PATCH 08/11] extended tests for Cached web & windows elements corrected CachedElementStateProvider and ElementFinder --- .../elements/CachedElementStateProvider.java | 2 +- .../selenium/core/elements/ElementFinder.java | 2 +- .../tests/application/ICachedElement.java | 41 +++++++ .../application/browser/CachedLabel.java | 49 +++++++++ .../application/windowsApp/CachedButton.java | 50 +++++++++ .../tests/elements/CachedWebElementTests.java | 100 ++++++++++++++++++ .../elements/CachedWindowsElementTests.java | 83 +++++++++++++++ .../tests/elements/ICachedElementTests.java | 34 ++++++ .../java/theinternet/DynamicControlsForm.java | 15 +++ .../java/theinternet/DynamicLoadingForm.java | 10 ++ src/test/resources/TestSuite.xml | 2 + 11 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 src/test/java/tests/application/ICachedElement.java create mode 100644 src/test/java/tests/application/browser/CachedLabel.java create mode 100644 src/test/java/tests/application/windowsApp/CachedButton.java create mode 100644 src/test/java/tests/elements/CachedWebElementTests.java create mode 100644 src/test/java/tests/elements/CachedWindowsElementTests.java create mode 100644 src/test/java/tests/elements/ICachedElementTests.java diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java index 0367136..6282534 100644 --- a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -87,7 +87,7 @@ public boolean waitForDisplayed(Long timeout) { @Override public boolean waitForNotDisplayed(Long timeout) { - return waitForCondition(() -> tryInvokeFunction(element -> !element.isDisplayed()), "invisible or absent", timeout); + return waitForCondition(() -> !isDisplayed(), "invisible or absent", timeout); } @Override diff --git a/src/main/java/aquality/selenium/core/elements/ElementFinder.java b/src/main/java/aquality/selenium/core/elements/ElementFinder.java index 73f7455..89455d0 100644 --- a/src/main/java/aquality/selenium/core/elements/ElementFinder.java +++ b/src/main/java/aquality/selenium/core/elements/ElementFinder.java @@ -70,7 +70,7 @@ protected void handleTimeoutException(TimeoutException exception, By locator, De localizedLogger.debug("loc.elements.were.found.but.not.in.state", locator, desiredState.getStateName()); } } else { - String combinedMessage = String.format("%1$s: %2$s", exception.getMessage(), message); + String combinedMessage = String.format("%1$s: %2$s", message, exception.getMessage()); if (desiredState.isThrowingNoSuchElementException() && !wasAnyElementFound) { throw new NoSuchElementException(combinedMessage); } diff --git a/src/test/java/tests/application/ICachedElement.java b/src/test/java/tests/application/ICachedElement.java new file mode 100644 index 0000000..dc3b138 --- /dev/null +++ b/src/test/java/tests/application/ICachedElement.java @@ -0,0 +1,41 @@ +package tests.application; + +import aquality.selenium.core.elements.CachedElementStateProvider; +import aquality.selenium.core.elements.ElementCacheHandler; +import aquality.selenium.core.elements.ElementState; +import aquality.selenium.core.elements.interfaces.IElementCacheHandler; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import org.openqa.selenium.remote.RemoteWebElement; + +public interface ICachedElement { + + By getLocator(); + ElementState getElementState(); + IElementCacheHandler getElementCacheHandler(); + void setElementCacheHandler(IElementCacheHandler elementCacheHandler); + IElementFinder getElementFinder(); + IConditionalWait getConditionalWait(); + + + default IElementCacheHandler cache() { + if (getElementCacheHandler() == null) { + setElementCacheHandler(new ElementCacheHandler(getLocator(), getElementState(), getElementFinder())); + } + return getElementCacheHandler(); + } + + default RemoteWebElement getElement() { + return cache().getElement(); + } + + default void click() { + getElement().click(); + } + + default IElementStateProvider state() { + return new CachedElementStateProvider(getLocator(), getConditionalWait(), cache()); + } +} diff --git a/src/test/java/tests/application/browser/CachedLabel.java b/src/test/java/tests/application/browser/CachedLabel.java new file mode 100644 index 0000000..d0b955a --- /dev/null +++ b/src/test/java/tests/application/browser/CachedLabel.java @@ -0,0 +1,49 @@ +package tests.application.browser; + +import aquality.selenium.core.elements.ElementState; +import aquality.selenium.core.elements.interfaces.IElementCacheHandler; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import tests.application.ICachedElement; + +public class CachedLabel implements ICachedElement { + private final By locator; + private final ElementState elementState; + private IElementCacheHandler elementCacheHandler; + + public CachedLabel(By locator, ElementState state) { + this.locator = locator; + this.elementState = state; + } + + @Override + public By getLocator() { + return locator; + } + + @Override + public ElementState getElementState() { + return elementState; + } + + @Override + public IElementCacheHandler getElementCacheHandler() { + return elementCacheHandler; + } + + @Override + public void setElementCacheHandler(IElementCacheHandler elementCacheHandler) { + this.elementCacheHandler = elementCacheHandler; + } + + @Override + public IElementFinder getElementFinder() { + return AqualityServices.get(IElementFinder.class); + } + + @Override + public IConditionalWait getConditionalWait() { + return AqualityServices.get(IConditionalWait.class); + } +} diff --git a/src/test/java/tests/application/windowsApp/CachedButton.java b/src/test/java/tests/application/windowsApp/CachedButton.java new file mode 100644 index 0000000..c9576d5 --- /dev/null +++ b/src/test/java/tests/application/windowsApp/CachedButton.java @@ -0,0 +1,50 @@ +package tests.application.windowsApp; + +import aquality.selenium.core.elements.ElementState; +import aquality.selenium.core.elements.interfaces.IElementCacheHandler; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.waitings.IConditionalWait; +import org.openqa.selenium.By; +import tests.application.ICachedElement; + +public class CachedButton implements ICachedElement { + private final By locator; + private final ElementState elementState; + private IElementCacheHandler elementCacheHandler; + + public CachedButton(By locator) { + this.locator = locator; + this.elementState = ElementState.EXISTS_IN_ANY_STATE; + } + + + @Override + public By getLocator() { + return locator; + } + + @Override + public ElementState getElementState() { + return elementState; + } + + @Override + public IElementCacheHandler getElementCacheHandler() { + return elementCacheHandler; + } + + @Override + public void setElementCacheHandler(IElementCacheHandler elementCacheHandler) { + this.elementCacheHandler = elementCacheHandler; + } + + @Override + public IElementFinder getElementFinder() { + return AqualityServices.get(IElementFinder.class); + } + + @Override + public IConditionalWait getConditionalWait() { + return AqualityServices.get(IConditionalWait.class); + } +} diff --git a/src/test/java/tests/elements/CachedWebElementTests.java b/src/test/java/tests/elements/CachedWebElementTests.java new file mode 100644 index 0000000..b9eccfa --- /dev/null +++ b/src/test/java/tests/elements/CachedWebElementTests.java @@ -0,0 +1,100 @@ +package tests.elements; + +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.waitings.IConditionalWait; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.application.browser.AqualityServices; +import tests.application.browser.CachedLabel; +import tests.application.browser.ITheInternetPageTest; +import theinternet.DynamicControlsForm; +import theinternet.DynamicLoadingForm; +import theinternet.TheInternetPage; + +import java.util.concurrent.TimeoutException; +import java.util.function.Predicate; + +public class CachedWebElementTests implements ITheInternetPageTest, ICachedElementTests { + + private void startLoading() { + navigate(TheInternetPage.DYNAMIC_LOADING); + DynamicLoadingForm.getStartLabel().click(); + } + + private void openDynamicControls() { + navigate(TheInternetPage.DYNAMIC_CONTROLS); + } + + private void waitForLoading(CachedLabel loader) { + Assert.assertTrue(loader.state().waitForDisplayed(), "Loader should be displayed in the beginning"); + Assert.assertTrue(loader.state().waitForNotDisplayed(), "Loader should not be displayed in the end"); + } + + private IConditionalWait conditionalWait() { + return AqualityServices.get(IConditionalWait.class); + } + + @Test + public void testShouldReturnFalseAtWaitForDisplayedWhenElementIsNotDisplayed() { + CachedLabel loader = DynamicLoadingForm.getLoadingLabel(); + startLoading(); + waitForLoading(loader); + Assert.assertFalse(loader.state().waitForDisplayed(ZERO_TIMEOUT)); + } + + @Test + public void testShouldReturnTrueAtWaitForExistWhenElementIsNotDisplayed() { + CachedLabel loader = DynamicLoadingForm.getLoadingLabel(); + startLoading(); + waitForLoading(loader); + Assert.assertTrue(loader.state().waitForExist(ZERO_TIMEOUT)); + } + + @Test + public void testShouldBeStaleWhenBecameInvisible() { + startLoading(); + CachedLabel loader = DynamicLoadingForm.getLoadingLabel(); + Assert.assertTrue(loader.state().waitForDisplayed(), "Loader should be displayed in the beginning"); + Assert.assertTrue(conditionalWait().waitFor(() -> loader.cache().isStale()), + "Loader should become invisible and be treated as stale"); + Assert.assertFalse(loader.state().isDisplayed(), "Invisible loader should be not displayed"); + Assert.assertFalse(loader.state().isExist(), + "Loader that was displayed previously and become invisible should be treated as disappeared"); + Assert.assertTrue(loader.state().waitForExist(ZERO_TIMEOUT), + "When waiting for existance, we should get an actual element's state"); + } + + @Test + public void testShouldRefreshElementWhenItIsStale() { + openDynamicControls(); + CachedLabel example = DynamicControlsForm.getContentLabel(); + String exampleToString = example.getElement().getId(); + getBrowser().getDriver().navigate().refresh(); + Assert.assertTrue(example.cache().isRefreshNeeded(), "refresh should be needed when refreshed the page"); + String newToString = example.getElement().getId(); + Assert.assertNotEquals(newToString, exampleToString, "new and old element's IDs should be different"); + } + + @Test(dataProvider = "stateFunctionsFalseWhenElementStale") + public void testShouldReturnCorrectStateFalseWhenWindowIsRefreshed(Predicate stateCondition) throws TimeoutException { + assertStateConditionAfterRefresh(stateCondition, false); + } + + @Test(dataProvider = "stateFunctionsTrueWhenElementStaleWhichRetriveElement") + public void testShouldReturnCorrectStateTrueWhenWindowIsRefreshed(Predicate stateCondition) throws TimeoutException { + assertStateConditionAfterRefresh(stateCondition, true); + } + + private void assertStateConditionAfterRefresh(Predicate stateCondition, boolean expectedValue) throws TimeoutException { + openDynamicControls(); + CachedLabel testElement = DynamicControlsForm.getCheckboxLabel(); + testElement.state().waitForClickable(); + DynamicControlsForm.getRemoveLabel().click(); + conditionalWait().waitForTrue( + () -> testElement.cache().isStale(), "Element should be stale when it disappeared."); + getBrowser().getDriver().navigate().refresh(); + Assert.assertTrue(testElement.cache().isStale(), "Element should remain stale after the page refresh."); + Assert.assertEquals(stateCondition.test(testElement.state()), expectedValue, + "Element state condition is not expected after refreshing the window"); + } +} diff --git a/src/test/java/tests/elements/CachedWindowsElementTests.java b/src/test/java/tests/elements/CachedWindowsElementTests.java new file mode 100644 index 0000000..28ac2fb --- /dev/null +++ b/src/test/java/tests/elements/CachedWindowsElementTests.java @@ -0,0 +1,83 @@ +package tests.elements; + +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.ITestWithApplication; +import tests.application.windowsApp.AqualityServices; +import tests.application.windowsApp.CachedButton; +import tests.application.windowsApp.CalculatorWindow; + +import java.util.function.Predicate; + +public class CachedWindowsElementTests implements ICachedElementTests, ITestWithApplication { + + @Override + public IApplication getApplication() { + return AqualityServices.getApplication(); + } + + @Override + public boolean isApplicationStarted() { + return AqualityServices.isApplicationStarted(); + } + + @Test + public void testShouldWorkWithCalculatorWithCachedElement() { + new CachedButton(CalculatorWindow.getOneButton()).click(); + new CachedButton(CalculatorWindow.getPlusButton()).click(); + new CachedButton(CalculatorWindow.getOneButton()).click(); + new CachedButton(CalculatorWindow.getEqualsButton()).click(); + String result = new CachedButton(CalculatorWindow.getResultsLabel()).getElement().getText(); + Assert.assertTrue(result.contains("2")); + } + + @Test + public void testShouldReturnSameElementAfterInteraction() { + CachedButton oneButton = new CachedButton(CalculatorWindow.getOneButton()); + String initialElement = oneButton.getElement().getId(); + oneButton.click(); + String resultElement = oneButton.getElement().getId(); + Assert.assertEquals(resultElement, initialElement, "Element should be the same after getting interaction"); + } + + @Test + public void testShouldReturnSameElementAfterGetState() { + CachedButton oneButton = new CachedButton(CalculatorWindow.getOneButton()); + String initialElement = oneButton.getElement().getId(); + oneButton.state().waitForClickable(); + String resultElement = oneButton.getElement().getId(); + Assert.assertEquals(resultElement, initialElement, "Element should be the same after getting state"); + } + + @Test + public void testShouldReturnNewElementWhenWindowIsReopened() { + CachedButton oneButton = new CachedButton(CalculatorWindow.getOneButton()); + String initialElement = oneButton.getElement().getId(); + getApplication().getDriver().quit(); + oneButton.state().waitForClickable(); + String resultElement = oneButton.getElement().getId(); + Assert.assertNotEquals(resultElement, initialElement, "Element is still the same after reopening the window"); + } + + @Test(dataProvider = "stateFunctionsFalseWhenElementStale") + public void testShouldReturnCorrectStateWhenWindowIsClosed(Predicate stateCondition) { + assertStateConditionAfterQuit(stateCondition, false, false); + } + + @Test(dataProvider = "stateFunctionsTrueWhenElementStaleWhichRetriveElement") + public void testShouldReturnCorrectStateAndReopenApplicationWhenWindowIsClosed(Predicate stateCondition) { + assertStateConditionAfterQuit(stateCondition, true, true); + } + + private void assertStateConditionAfterQuit(Predicate stateCondition, boolean expectedValue, boolean shouldAppRestart){ + CachedButton oneButton = new CachedButton(CalculatorWindow.getOneButton()); + oneButton.getElement(); + getApplication().getDriver().quit(); + Assert.assertEquals(stateCondition.test(oneButton.state()), expectedValue, + "Element state condition is not expected after closing the window"); + Assert.assertEquals(isApplicationStarted(), shouldAppRestart, + String.format("Window was %1$s reopened when retrived the element state.", shouldAppRestart ? "not" : "")); + } +} diff --git a/src/test/java/tests/elements/ICachedElementTests.java b/src/test/java/tests/elements/ICachedElementTests.java new file mode 100644 index 0000000..933cc76 --- /dev/null +++ b/src/test/java/tests/elements/ICachedElementTests.java @@ -0,0 +1,34 @@ +package tests.elements; + +import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import org.testng.annotations.DataProvider; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public interface ICachedElementTests { + long ZERO_TIMEOUT = 0L; + + @DataProvider + default Object[] stateFunctionsFalseWhenElementStale() { + List> stateFunctions = new ArrayList<>(); + stateFunctions.add(IElementStateProvider::isDisplayed); + stateFunctions.add(IElementStateProvider::isExist); + stateFunctions.add(state -> !state.waitForNotDisplayed(ZERO_TIMEOUT)); + stateFunctions.add(state -> !state.waitForNotExist(ZERO_TIMEOUT)); + return stateFunctions.toArray(); + } + + @DataProvider + default Object[] stateFunctionsTrueWhenElementStaleWhichRetriveElement() { + List> stateFunctions = new ArrayList<>(); + stateFunctions.add(IElementStateProvider::isEnabled); + stateFunctions.add(IElementStateProvider::isClickable); + stateFunctions.add(state -> state.waitForDisplayed(ZERO_TIMEOUT)); + stateFunctions.add(state -> state.waitForExist(ZERO_TIMEOUT)); + stateFunctions.add(state -> state.waitForEnabled(ZERO_TIMEOUT)); + stateFunctions.add(state -> !state.waitForNotEnabled(ZERO_TIMEOUT)); + return stateFunctions.toArray(); + } +} diff --git a/src/test/java/theinternet/DynamicControlsForm.java b/src/test/java/theinternet/DynamicControlsForm.java index 92105e3..7888519 100644 --- a/src/test/java/theinternet/DynamicControlsForm.java +++ b/src/test/java/theinternet/DynamicControlsForm.java @@ -1,8 +1,10 @@ package theinternet; import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementStateProvider; import org.openqa.selenium.By; +import tests.application.browser.CachedLabel; import java.util.function.Function; import java.util.function.Supplier; @@ -12,6 +14,7 @@ public class DynamicControlsForm extends BaseForm { private static final By REMOVE_BUTTON_LOCATOR = By.xpath("//button[contains(@onclick, 'swapCheckbox()')]"); private static final By INPUT_TEXTBOX_LOCATOR = By.xpath("//input[@type='text']"); private static final By CHECKBOX_LOCATOR = By.xpath("//div[@id='checkbox']"); + private static final By CONTENT_LOCATOR = By.id("content"); public DynamicControlsForm(Supplier appSupplier, Function stateProviderFunction) { super(appSupplier, stateProviderFunction); @@ -32,4 +35,16 @@ public IElementStateProvider inputState() { public IElementStateProvider checkboxState() { return state(CHECKBOX_LOCATOR); } + + public static CachedLabel getContentLabel() { + return new CachedLabel(CONTENT_LOCATOR, ElementState.DISPLAYED); + } + + public static CachedLabel getCheckboxLabel() { + return new CachedLabel(CHECKBOX_LOCATOR, ElementState.EXISTS_IN_ANY_STATE); + } + + public static CachedLabel getRemoveLabel() { + return new CachedLabel(REMOVE_BUTTON_LOCATOR, ElementState.EXISTS_IN_ANY_STATE); + } } diff --git a/src/test/java/theinternet/DynamicLoadingForm.java b/src/test/java/theinternet/DynamicLoadingForm.java index 3120786..8de7e90 100644 --- a/src/test/java/theinternet/DynamicLoadingForm.java +++ b/src/test/java/theinternet/DynamicLoadingForm.java @@ -1,8 +1,10 @@ package theinternet; import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementStateProvider; import org.openqa.selenium.By; +import tests.application.browser.CachedLabel; import java.util.function.Function; import java.util.function.Supplier; @@ -18,6 +20,14 @@ public DynamicLoadingForm(Supplier appSupplier, Function + @@ -26,6 +27,7 @@ + From 40b72b00d20dd813715bdacf4d7455183a05fbe8 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Fri, 7 Feb 2020 14:01:18 +0300 Subject: [PATCH 09/11] corrected the message in retrier tests --- src/test/java/tests/utilities/ElementActionRetrierTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/tests/utilities/ElementActionRetrierTests.java b/src/test/java/tests/utilities/ElementActionRetrierTests.java index 8ee0010..8f29c3e 100644 --- a/src/test/java/tests/utilities/ElementActionRetrierTests.java +++ b/src/test/java/tests/utilities/ElementActionRetrierTests.java @@ -82,7 +82,7 @@ private void checkRetrierShouldWaitPollingTimeBetweenMethodsCall(Runnable retryF retryFunction.run(); long duration = new Date().getTime() - startTime.getTime(); assertTrue(duration >= POLLING_INTERVAL, String.format("duration '%s' should be more than polling interval '%s'", duration, POLLING_INTERVAL)); - assertTrue(duration <= 2 * POLLING_INTERVAL, String.format("duration '%s' less than doubled polling interval '%s'", duration, POLLING_INTERVAL)); + assertTrue(duration <= 2 * POLLING_INTERVAL, String.format("duration '%s' should be less than doubled polling interval '%s'", duration, POLLING_INTERVAL)); } @Test(expectedExceptions = InvalidArgumentException.class) From 1d0a0bf9f619326d4a9a5410d3781b4d39dbc0e1 Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Fri, 7 Feb 2020 14:47:26 +0300 Subject: [PATCH 10/11] replace Throwable with Exception in catch block of ElementCacheHandler --- .../aquality/selenium/core/elements/ElementCacheHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java b/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java index c2e3a2f..caadf0b 100644 --- a/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java +++ b/src/main/java/aquality/selenium/core/elements/ElementCacheHandler.java @@ -32,7 +32,7 @@ public boolean isRefreshNeeded(ElementState customState) { // refresh is needed only if the property is not match to expected element state ElementState requiredState = customState == null ? state : customState; return requiredState == ElementState.DISPLAYED && !isDisplayed; - } catch (Throwable e) { + } catch (Exception e) { // refresh is needed if the property is not available return true; } From 0ae0334dfa00104eca11f5dd7431a8ceedbaa68f Mon Sep 17 00:00:00 2001 From: "Meleshko, Aleksey2" Date: Mon, 10 Feb 2020 19:15:40 +0300 Subject: [PATCH 11/11] add localization for logged message at CachedElementStateProvider --- .../core/elements/CachedElementStateProvider.java | 13 +++++++------ src/main/resources/localization/be.json | 3 ++- src/main/resources/localization/en.json | 3 ++- src/main/resources/localization/ru.json | 3 ++- src/test/java/tests/application/ICachedElement.java | 4 +++- .../java/tests/application/browser/CachedLabel.java | 6 ++++++ .../tests/application/windowsApp/CachedButton.java | 6 ++++++ .../elements/CachedElementStateProviderTests.java | 4 +++- .../localization/LocalizationManagerTests.java | 3 ++- 9 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java index 6282534..db0c56c 100644 --- a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -1,7 +1,7 @@ package aquality.selenium.core.elements; import aquality.selenium.core.elements.interfaces.IElementCacheHandler; -import aquality.selenium.core.logging.Logger; +import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -23,11 +23,13 @@ public class CachedElementStateProvider extends ElementStateProvider { private final By locator; private final IConditionalWait conditionalWait; private final IElementCacheHandler elementCacheHandler; + private final ILocalizedLogger localizedLogger; - public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler) { + public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler, ILocalizedLogger localizedLogger) { this.locator = locator; this.conditionalWait = conditionalWait; this.elementCacheHandler = elementCacheHandler; + this.localizedLogger = localizedLogger; } protected List> getHandledExceptions() { @@ -52,10 +54,8 @@ protected boolean tryInvokeFunction(Predicate predicate, List