diff --git a/src/main/java/aquality/selenium/core/application/AqualityModule.java b/src/main/java/aquality/selenium/core/applications/AqualityModule.java similarity index 77% rename from src/main/java/aquality/selenium/core/application/AqualityModule.java rename to src/main/java/aquality/selenium/core/applications/AqualityModule.java index c2f67d8..7bb3f17 100644 --- a/src/main/java/aquality/selenium/core/application/AqualityModule.java +++ b/src/main/java/aquality/selenium/core/applications/AqualityModule.java @@ -1,11 +1,15 @@ -package aquality.selenium.core.application; +package aquality.selenium.core.applications; import aquality.selenium.core.configurations.*; -import aquality.selenium.core.localization.*; +import aquality.selenium.core.localization.ILocalizationManager; +import aquality.selenium.core.localization.ILocalizationModule; +import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.logging.Logger; import aquality.selenium.core.utilities.IElementActionRetrier; import aquality.selenium.core.utilities.ISettingsFile; import aquality.selenium.core.utilities.IUtilitiesModule; +import aquality.selenium.core.waitings.IConditionalWait; +import aquality.selenium.core.waitings.IWaitingsModule; import com.google.inject.AbstractModule; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -14,7 +18,7 @@ * Describes all dependencies which is registered for the project. */ public class AqualityModule extends AbstractModule - implements ILocalizationModule, IUtilitiesModule { + implements ILocalizationModule, IUtilitiesModule, IWaitingsModule { private final Provider applicationProvider; @@ -37,5 +41,6 @@ protected void configure() { bind(IElementActionRetrier.class).to(getElementActionRetrierImplementation()).in(Singleton.class); bind(ILocalizationManager.class).to(getLocalizationManagerImplementation()).in(Singleton.class); bind(ILocalizedLogger.class).to(getLocalizedLoggerImplementation()).in(Singleton.class); + bind(IConditionalWait.class).to(getConditionalWaitImplementation()); } } diff --git a/src/main/java/aquality/selenium/core/application/AqualityServices.java b/src/main/java/aquality/selenium/core/applications/AqualityServices.java similarity index 97% rename from src/main/java/aquality/selenium/core/application/AqualityServices.java rename to src/main/java/aquality/selenium/core/applications/AqualityServices.java index 4f8d98c..03572d2 100644 --- a/src/main/java/aquality/selenium/core/application/AqualityServices.java +++ b/src/main/java/aquality/selenium/core/applications/AqualityServices.java @@ -1,4 +1,4 @@ -package aquality.selenium.core.application; +package aquality.selenium.core.applications; import com.google.inject.Guice; import com.google.inject.Injector; diff --git a/src/main/java/aquality/selenium/core/application/IApplication.java b/src/main/java/aquality/selenium/core/applications/IApplication.java similarity index 93% rename from src/main/java/aquality/selenium/core/application/IApplication.java rename to src/main/java/aquality/selenium/core/applications/IApplication.java index 19b3cf9..6b61943 100644 --- a/src/main/java/aquality/selenium/core/application/IApplication.java +++ b/src/main/java/aquality/selenium/core/applications/IApplication.java @@ -1,4 +1,4 @@ -package aquality.selenium.core.application; +package aquality.selenium.core.applications; import org.openqa.selenium.remote.RemoteWebDriver; diff --git a/src/main/java/aquality/selenium/core/localization/ILocalizationManager.java b/src/main/java/aquality/selenium/core/localization/ILocalizationManager.java index 776d3e0..3a98272 100644 --- a/src/main/java/aquality/selenium/core/localization/ILocalizationManager.java +++ b/src/main/java/aquality/selenium/core/localization/ILocalizationManager.java @@ -6,6 +6,7 @@ public interface ILocalizationManager { /** * Gets localized message from resources by its key. + * * @param messageKey Key in resource file. * @param args Arguments, which will be provided to template of localized message. * @return Localized message. diff --git a/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java b/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java new file mode 100644 index 0000000..e5a2f0c --- /dev/null +++ b/src/main/java/aquality/selenium/core/waitings/ConditionalWait.java @@ -0,0 +1,112 @@ +package aquality.selenium.core.waitings; + +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.configurations.ITimeoutConfiguration; +import com.google.common.base.Strings; +import com.google.inject.Inject; +import com.google.inject.Provider; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BooleanSupplier; + +public class ConditionalWait implements IConditionalWait { + + private final Provider applicationProvider; + private final ITimeoutConfiguration timeoutConfiguration; + + @Inject + public ConditionalWait(Provider applicationProvider, ITimeoutConfiguration timeoutConfiguration) { + this.applicationProvider = applicationProvider; + this.timeoutConfiguration = timeoutConfiguration; + } + + @Override + public boolean waitFor(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message, Collection> exceptionsToIgnore) { + try { + waitForTrue(condition, timeoutInSeconds, pollingIntervalInMilliseconds, message, exceptionsToIgnore); + return true; + } catch (TimeoutException e) { + return false; + } + } + + @Override + public void waitForTrue(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message, Collection> exceptionsToIgnore) throws TimeoutException { + BooleanSupplier supplier = Optional.ofNullable(condition).orElseThrow(() -> new IllegalArgumentException("Condition cannot be null")); + Long timeout = resolveConditionTimeout(timeoutInSeconds); + Long pollingInterval = resolvePollingInterval(pollingIntervalInMilliseconds); + String exMessage = resolveMessage(message); + double startTime = getCurrentTime(); + while (true) { + if (isConditionSatisfied(supplier, exceptionsToIgnore)) { + return; + } + + double currentTime = getCurrentTime(); + if ((currentTime - startTime) > timeout) { + String exceptionMessage = String.format("Timed out after %1$s seconds during wait for condition '%2$s'", timeout, exMessage); + throw new TimeoutException(exceptionMessage); + } + + try { + Thread.sleep(pollingInterval); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + @Override + public T waitFor(ExpectedCondition condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message, Collection> exceptionsToIgnore) { + IApplication app = applicationProvider.get(); + app.setImplicitWaitTimeout(0, TimeUnit.SECONDS); + Long timeout = resolveConditionTimeout(timeoutInSeconds); + Long pollingInterval = resolvePollingInterval(pollingIntervalInMilliseconds); + String exMessage = resolveMessage(message); + WebDriverWait wait = new WebDriverWait(app.getDriver(), timeout); + wait.pollingEvery(Duration.ofMillis(pollingInterval)); + wait.withMessage(exMessage); + wait.ignoreAll(exceptionsToIgnore == null ? Collections.singleton(StaleElementReferenceException.class) : exceptionsToIgnore); + try { + return wait.until(condition); + } finally { + app.setImplicitWaitTimeout(timeoutConfiguration.getImplicit(), TimeUnit.SECONDS); + } + } + + private double getCurrentTime() { + return System.nanoTime() / Math.pow(10, 9); + } + + private boolean isConditionSatisfied(BooleanSupplier condition, Collection> exceptionsToIgnore) { + try { + return condition.getAsBoolean(); + } catch (Exception e) { + if (exceptionsToIgnore == null || !exceptionsToIgnore.contains(e.getClass())) { + throw e; + } + + return false; + } + } + + private Long resolveConditionTimeout(Long timeout) { + return Optional.ofNullable(timeout).orElse(timeoutConfiguration.getCommand()); + } + + private Long resolvePollingInterval(Long timeout) { + return Optional.ofNullable(timeout).orElse(timeoutConfiguration.getPollingInterval()); + } + + private String resolveMessage(String message) { + return Strings.isNullOrEmpty(message) ? "" : message; + } +} diff --git a/src/main/java/aquality/selenium/core/waitings/IConditionalWait.java b/src/main/java/aquality/selenium/core/waitings/IConditionalWait.java new file mode 100644 index 0000000..5d84385 --- /dev/null +++ b/src/main/java/aquality/selenium/core/waitings/IConditionalWait.java @@ -0,0 +1,321 @@ +package aquality.selenium.core.waitings; + +import org.openqa.selenium.support.ui.ExpectedCondition; + +import java.util.Collection; +import java.util.concurrent.TimeoutException; +import java.util.function.BooleanSupplier; +import java.util.function.Function; + +/** + * Utility used to wait for some condition. + */ +public interface IConditionalWait { + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition) { + return waitFor(condition, null, null, null, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @param message Part of error message in case of Timeout exception + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition, String message) { + return waitFor(condition, null, null, message, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @param exceptionsToIgnore Exceptions to ignore + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition, Collection> exceptionsToIgnore) { + return waitFor(condition, null, null, null, exceptionsToIgnore); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @param message Part of error message in case of Timeout exception + * @param exceptionsToIgnore Exceptions to ignore + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition, String message, Collection> exceptionsToIgnore) { + return waitFor(condition, null, null, message, exceptionsToIgnore); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds) { + return waitFor(condition, timeoutInSeconds, pollingIntervalInMilliseconds, null, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @param message Part of error message in case of Timeout exception + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message) { + return waitFor(condition, timeoutInSeconds, pollingIntervalInMilliseconds, message, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @param exceptionsToIgnore Exceptions to ignore + * @return true if the condition has been met during the timeout + */ + default boolean waitFor(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, Collection> exceptionsToIgnore) { + return waitFor(condition, timeoutInSeconds, pollingIntervalInMilliseconds, null, exceptionsToIgnore); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @param message Part of error message in case of Timeout exception + * @param exceptionsToIgnore Exceptions to ignore + * @return true if the condition has been met during the timeout + */ + boolean waitFor(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message, Collection> exceptionsToIgnore); + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition) throws TimeoutException { + waitForTrue(condition, null, null, null, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @param message Part of error message in case of Timeout exception + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition, String message) throws TimeoutException { + waitForTrue(condition, null, null, message, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @param exceptionsToIgnore Exceptions to ignore + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition, Collection> exceptionsToIgnore) throws TimeoutException { + waitForTrue(condition, null, null, null, exceptionsToIgnore); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * Default values for timeouts used from configuration settings file + * + * @param condition Condition with boolean result (predicate) + * @param message Part of error message in case of Timeout exception + * @param exceptionsToIgnore Exceptions to ignore + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition, String message, Collection> exceptionsToIgnore) throws TimeoutException { + waitForTrue(condition, null, null, message, exceptionsToIgnore); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds) throws TimeoutException { + waitForTrue(condition, timeoutInSeconds, pollingIntervalInMilliseconds, null, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @param message Part of error message in case of Timeout exception + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message) throws TimeoutException { + waitForTrue(condition, timeoutInSeconds, pollingIntervalInMilliseconds, message, null); + } + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @param exceptionsToIgnore Exceptions to ignore + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + default void waitForTrue(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, Collection> exceptionsToIgnore) throws TimeoutException { + waitForTrue(condition, timeoutInSeconds, pollingIntervalInMilliseconds, null, exceptionsToIgnore); + } + + + /** + * Wait for some condition within timeout. Method does not use WebDriverWait + * + * @param condition Condition with boolean result (predicate) + * @param timeoutInSeconds Condition timeout + * @param pollingIntervalInMilliseconds Condition check interval + * @param message Part of error message in case of Timeout exception + * @param exceptionsToIgnore Exceptions to ignore + * @throws TimeoutException will be thrown in case if timeout is over but condition was not met + */ + void waitForTrue(BooleanSupplier condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message, Collection> exceptionsToIgnore) throws TimeoutException; + + /** + * Waits for function will be true or return some except false. + * Default timeout condition from settings is using. + * StaleElementReferenceException will be handled by default + * + * @param condition Function for waiting {@link Function} + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occurred + */ + default T waitFor(ExpectedCondition condition) { + return waitFor(condition, null, null, null, null); + } + + /** + * Waits for function will be true or return some except false. + * Default timeout condition from settings is using. + * StaleElementReferenceException will be handled by default + * + * @param condition Function for waiting {@link Function} + * @param message the message that will be added to an error in case if the condition is not matched during the timeout + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occurred + */ + default T waitFor(ExpectedCondition condition, String message) { + return waitFor(condition, null, null, message, null); + } + + /** + * Waits for function will be true or return some except false. + * Default timeout condition from settings is using. + * + * @param condition Function for waiting {@link Function} + * @param exceptionsToIgnore list of exceptions that should be ignored during waiting + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occurred + */ + default T waitFor(ExpectedCondition condition, Collection> exceptionsToIgnore) { + return waitFor(condition, null, null, null, exceptionsToIgnore); + } + + /** + * Waits for function will be true or return some except false. + * Default timeout condition from settings is using. + * + * @param condition Function for waiting {@link Function} + * @param message the message that will be added to an error in case if the condition is not matched during the timeout + * @param exceptionsToIgnore list of exceptions that should be ignored during waiting + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occurred + */ + default T waitFor(ExpectedCondition condition, String message, Collection> exceptionsToIgnore) { + return waitFor(condition, null, null, message, exceptionsToIgnore); + } + + /** + * Waits for function will be true or return some except false. + * StaleElementReferenceException will be handled by default + * + * @param condition Function for waiting {@link Function} + * @param timeOutInSeconds Time-out in seconds + * @param pollingIntervalInMilliseconds interval in milliseconds between checks whether condition match + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occured + */ + default T waitFor(ExpectedCondition condition, Long timeOutInSeconds, Long pollingIntervalInMilliseconds) { + return waitFor(condition, timeOutInSeconds, pollingIntervalInMilliseconds, null, null); + } + + /** + * Waits for function will be true or return some except false. + * StaleElementReferenceException will be handled by default + * + * @param condition Function for waiting {@link Function} + * @param timeoutInSeconds Time-out in seconds + * @param pollingIntervalInMilliseconds interval in milliseconds between checks whether condition match + * @param message the message that will be added to an error in case if the condition is not matched during the timeout + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occured + */ + default T waitFor(ExpectedCondition condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message) { + return waitFor(condition, timeoutInSeconds, pollingIntervalInMilliseconds, message, null); + } + + /** + * Waits for function will be true or return some except false. + * + * @param condition Function for waiting {@link Function} + * @param timeoutInSeconds Time-out in seconds + * @param pollingIntervalInMilliseconds interval in milliseconds between checks whether condition match + * @param exceptionsToIgnore list of exceptions that should be ignored during waiting + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occured + */ + default T waitFor(ExpectedCondition condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, Collection> exceptionsToIgnore) { + return waitFor(condition, timeoutInSeconds, pollingIntervalInMilliseconds, null, exceptionsToIgnore); + } + + /** + * Waits for function will be true or return some except false. + * + * @param condition Function for waiting {@link Function} + * @param timeoutInSeconds Time-out in seconds + * @param pollingIntervalInMilliseconds interval in milliseconds between checks whether condition match + * @param message the message that will be added to an error in case if the condition is not matched during the timeout + * @param exceptionsToIgnore list of exceptions that should be ignored during waiting + * @param Type of object which is waiting + * @return Object which waiting for or null - is exceptions occured + */ + T waitFor(ExpectedCondition condition, Long timeoutInSeconds, Long pollingIntervalInMilliseconds, String message, Collection> exceptionsToIgnore); +} diff --git a/src/main/java/aquality/selenium/core/waitings/IWaitingsModule.java b/src/main/java/aquality/selenium/core/waitings/IWaitingsModule.java new file mode 100644 index 0000000..c73bdb0 --- /dev/null +++ b/src/main/java/aquality/selenium/core/waitings/IWaitingsModule.java @@ -0,0 +1,14 @@ +package aquality.selenium.core.waitings; + +/** + * Provides implementations for waitings module. + */ +public interface IWaitingsModule { + + /** + * @return implementation of {@link IConditionalWait}. + */ + default Class getConditionalWaitImplementation() { + return ConditionalWait.class; + } +} diff --git a/src/test/java/tests/application/AqualityServicesTests.java b/src/test/java/tests/application/AqualityServicesTests.java index 1c9604b..68b5bb6 100644 --- a/src/test/java/tests/application/AqualityServicesTests.java +++ b/src/test/java/tests/application/AqualityServicesTests.java @@ -1,6 +1,6 @@ package tests.application; -import aquality.selenium.core.application.AqualityModule; +import aquality.selenium.core.applications.AqualityModule; import aquality.selenium.core.logging.Logger; import com.google.inject.ConfigurationException; import com.google.inject.Injector; diff --git a/src/test/java/tests/application/CustomAqualityServices.java b/src/test/java/tests/application/CustomAqualityServices.java index 1c64e42..b34842f 100644 --- a/src/test/java/tests/application/CustomAqualityServices.java +++ b/src/test/java/tests/application/CustomAqualityServices.java @@ -1,7 +1,7 @@ package tests.application; -import aquality.selenium.core.application.AqualityModule; -import aquality.selenium.core.application.AqualityServices; +import aquality.selenium.core.applications.AqualityModule; +import aquality.selenium.core.applications.AqualityServices; import com.google.inject.Injector; import tests.application.browser.ChromeApplication; diff --git a/src/test/java/tests/application/IApplicationTests.java b/src/test/java/tests/application/IApplicationTests.java index de8f90a..9eef6e8 100644 --- a/src/test/java/tests/application/IApplicationTests.java +++ b/src/test/java/tests/application/IApplicationTests.java @@ -1,6 +1,6 @@ package tests.application; -import aquality.selenium.core.application.IApplication; +import aquality.selenium.core.applications.IApplication; import com.google.inject.Injector; import org.testng.Assert; import org.testng.annotations.AfterMethod; diff --git a/src/test/java/tests/application/TestModule.java b/src/test/java/tests/application/TestModule.java index 7f92ef4..a345669 100644 --- a/src/test/java/tests/application/TestModule.java +++ b/src/test/java/tests/application/TestModule.java @@ -1,6 +1,6 @@ package tests.application; -import aquality.selenium.core.application.AqualityModule; +import aquality.selenium.core.applications.AqualityModule; import com.google.inject.Provider; import tests.application.browser.ChromeApplication; diff --git a/src/test/java/tests/application/browser/AqualityServices.java b/src/test/java/tests/application/browser/AqualityServices.java index cf2d6f0..9029834 100644 --- a/src/test/java/tests/application/browser/AqualityServices.java +++ b/src/test/java/tests/application/browser/AqualityServices.java @@ -3,7 +3,7 @@ import com.google.inject.Injector; import io.github.bonigarcia.wdm.WebDriverManager; -public class AqualityServices extends aquality.selenium.core.application.AqualityServices { +public class AqualityServices extends aquality.selenium.core.applications.AqualityServices { private static final ThreadLocal INSTANCE_CONTAINER = ThreadLocal.withInitial(AqualityServices::new); private AqualityServices() { diff --git a/src/test/java/tests/application/browser/BrowserTests.java b/src/test/java/tests/application/browser/BrowserTests.java index ed3e356..0b731c0 100644 --- a/src/test/java/tests/application/browser/BrowserTests.java +++ b/src/test/java/tests/application/browser/BrowserTests.java @@ -1,6 +1,6 @@ package tests.application.browser; -import aquality.selenium.core.application.IApplication; +import aquality.selenium.core.applications.IApplication; import com.google.inject.Injector; import tests.application.IApplicationTests; diff --git a/src/test/java/tests/application/browser/ChromeApplication.java b/src/test/java/tests/application/browser/ChromeApplication.java index dd4103f..17d71c0 100644 --- a/src/test/java/tests/application/browser/ChromeApplication.java +++ b/src/test/java/tests/application/browser/ChromeApplication.java @@ -1,6 +1,6 @@ package tests.application.browser; -import aquality.selenium.core.application.IApplication; +import aquality.selenium.core.applications.IApplication; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.remote.RemoteWebDriver; diff --git a/src/test/java/tests/application/windowsApp/ApplicationTests.java b/src/test/java/tests/application/windowsApp/ApplicationTests.java index c5c8f9a..478a754 100644 --- a/src/test/java/tests/application/windowsApp/ApplicationTests.java +++ b/src/test/java/tests/application/windowsApp/ApplicationTests.java @@ -1,6 +1,6 @@ package tests.application.windowsApp; -import aquality.selenium.core.application.IApplication; +import aquality.selenium.core.applications.IApplication; import com.google.inject.Injector; import tests.application.IApplicationTests; diff --git a/src/test/java/tests/application/windowsApp/AqualityServices.java b/src/test/java/tests/application/windowsApp/AqualityServices.java index 20d7ee0..1bffd51 100644 --- a/src/test/java/tests/application/windowsApp/AqualityServices.java +++ b/src/test/java/tests/application/windowsApp/AqualityServices.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.net.URL; -public class AqualityServices extends aquality.selenium.core.application.AqualityServices { +public class AqualityServices extends aquality.selenium.core.applications.AqualityServices { private static final ThreadLocal INSTANCE_CONTAINER = ThreadLocal.withInitial(AqualityServices::new); private static final String APP_PATH = "./src/test/resources/apps/Day Maxi Calc.exe"; private static final String DEFAULT_SERVICE_URL = "http://127.0.0.1:4723/"; diff --git a/src/test/java/tests/application/windowsApp/WindowsApplication.java b/src/test/java/tests/application/windowsApp/WindowsApplication.java index ac3a162..84b38d2 100644 --- a/src/test/java/tests/application/windowsApp/WindowsApplication.java +++ b/src/test/java/tests/application/windowsApp/WindowsApplication.java @@ -1,6 +1,6 @@ package tests.application.windowsApp; -import aquality.selenium.core.application.IApplication; +import aquality.selenium.core.applications.IApplication; import io.appium.java_client.windows.WindowsDriver; import io.appium.java_client.windows.WindowsElement; import org.openqa.selenium.remote.DesiredCapabilities; diff --git a/src/test/java/tests/configurations/EnvConfigurationTests.java b/src/test/java/tests/configurations/EnvConfigurationTests.java index 1fadfdb..cab602d 100644 --- a/src/test/java/tests/configurations/EnvConfigurationTests.java +++ b/src/test/java/tests/configurations/EnvConfigurationTests.java @@ -1,6 +1,6 @@ package tests.configurations; -import aquality.selenium.core.application.AqualityModule; +import aquality.selenium.core.applications.AqualityModule; import aquality.selenium.core.configurations.*; import aquality.selenium.core.utilities.ISettingsFile; import org.testng.Assert; diff --git a/src/test/java/tests/configurations/ProfileConfigurationTests.java b/src/test/java/tests/configurations/ProfileConfigurationTests.java index 44122d3..4b81c44 100644 --- a/src/test/java/tests/configurations/ProfileConfigurationTests.java +++ b/src/test/java/tests/configurations/ProfileConfigurationTests.java @@ -1,6 +1,6 @@ package tests.configurations; -import aquality.selenium.core.application.AqualityModule; +import aquality.selenium.core.applications.AqualityModule; import aquality.selenium.core.configurations.LoggerConfiguration; import aquality.selenium.core.utilities.ISettingsFile; import org.testng.annotations.BeforeMethod; diff --git a/src/test/java/tests/utilities/CustomSettingsFileTests.java b/src/test/java/tests/utilities/CustomSettingsFileTests.java index 943100a..abe2013 100644 --- a/src/test/java/tests/utilities/CustomSettingsFileTests.java +++ b/src/test/java/tests/utilities/CustomSettingsFileTests.java @@ -1,6 +1,6 @@ package tests.utilities; -import aquality.selenium.core.application.AqualityModule; +import aquality.selenium.core.applications.AqualityModule; import aquality.selenium.core.utilities.ISettingsFile; import com.google.inject.Provider; import org.testng.Assert; diff --git a/src/test/java/tests/utilities/SettingsFileTests.java b/src/test/java/tests/utilities/SettingsFileTests.java index f1fcef0..15397b6 100644 --- a/src/test/java/tests/utilities/SettingsFileTests.java +++ b/src/test/java/tests/utilities/SettingsFileTests.java @@ -1,6 +1,6 @@ package tests.utilities; -import aquality.selenium.core.application.AqualityModule; +import aquality.selenium.core.applications.AqualityModule; import aquality.selenium.core.utilities.ISettingsFile; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; diff --git a/src/test/java/tests/waitings/BaseConditionalWaitTest.java b/src/test/java/tests/waitings/BaseConditionalWaitTest.java new file mode 100644 index 0000000..9076700 --- /dev/null +++ b/src/test/java/tests/waitings/BaseConditionalWaitTest.java @@ -0,0 +1,43 @@ +package tests.waitings; + +import aquality.selenium.core.applications.IApplication; +import aquality.selenium.core.configurations.ITimeoutConfiguration; +import aquality.selenium.core.waitings.ConditionalWait; +import com.google.inject.Injector; +import com.google.inject.Provider; +import org.testng.annotations.AfterMethod; +import tests.application.browser.AqualityServices; +import utils.Timer; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; + +class BaseConditionalWaitTest { + static final long waitForTimeoutCondition = 10; + static final long waitForTimeoutPolling = 150; + static final long accuracy = 3; + static final Collection> ignoredExceptions = Collections.singleton(IllegalStateException.class); + ThreadLocal timer = ThreadLocal.withInitial(Timer::new); + private Injector serviceProvider = AqualityServices.getServiceProvider(); + protected Provider application = serviceProvider.getProvider(IApplication.class); + ITimeoutConfiguration timeoutConfiguration = serviceProvider.getInstance(ITimeoutConfiguration.class); + ConditionalWait conditionalWait = new ConditionalWait(application, timeoutConfiguration); + + @AfterMethod + public void stopTimer(){ + timer.get().stop(); + } + + BooleanSupplier throwNewException(AtomicBoolean atomicBoolean) { + return () -> { + if (atomicBoolean.get()) { + atomicBoolean.set(false); + throw new IllegalStateException(""); + } + + return true; + }; + } +} diff --git a/src/test/java/tests/waitings/WaitForObjectTests.java b/src/test/java/tests/waitings/WaitForObjectTests.java new file mode 100644 index 0000000..c5d38c0 --- /dev/null +++ b/src/test/java/tests/waitings/WaitForObjectTests.java @@ -0,0 +1,149 @@ +package tests.waitings; + +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +public class WaitForObjectTests extends BaseConditionalWaitTest { + + private static final String RESULT_STRING = "result"; + + @BeforeMethod + public void startApp() { + application.get().getDriver(); + } + + @AfterMethod + public void quit() { + if (application.get().isStarted()) { + application.get().getDriver().quit(); + } + } + + @DataProvider(name = "failWaitForAction", parallel = true) + public Object[][] failWaitForAction() { + return getDataProvider((app) -> false); + } + + @Test(dataProvider = "failWaitForAction") + public void testShouldThrowTimeoutExceptionIfConditionIsNotMetAndTimeoutIsOver(Callable failedAction, long timeout) throws Exception { + timer.get().start(); + try { + failedAction.call(); + } catch (TimeoutException e) { + double duration = timer.get().stop(); + long interval = 2 * timeout + accuracy; + assertTrue(duration >= timeout && duration < interval, + String.format("Duration '%s' should be between '%s' and '%s' (timeout and (2*timeout + accuracy)) when condition is not satisfied. ", + duration, timeout, interval)); + } + } + + @DataProvider(name = "successWaitForAction", parallel = true) + public Object[][] successWaitForAction() { + return getDataProvider((app) -> RESULT_STRING); + } + + @Test(dataProvider = "successWaitForAction") + public void testShouldReturnAnObjectIfConditionIsMetAndTimeoutIsNotOver(Callable successAction, long timeout) throws Exception { + timer.get().start(); + String result = successAction.call(); + double duration = timer.get().stop(); + assertTrue(duration <= timeout, + String.format("Duration '%s' should be less than accuracyTimeout '%s'", + duration, timeout)); + assertEquals(result, RESULT_STRING, "Method should return correct object"); + } + + @DataProvider(name = "throwWaitForAction", parallel = true) + public Object[][] throwWaitForAction() { + return getDataProvider((app) -> { + throw new IllegalArgumentException("I am exception"); + }); + } + + @Test(dataProvider = "throwWaitForAction") + public void testShouldThrowException(Callable throwAction, long timeout) throws Exception { + try { + timer.get().start(); + throwAction.call(); + } catch (IllegalArgumentException e) { + double duration = timer.get().stop(); + assertTrue(duration <= timeout, + String.format("Duration '%s' should be less than accuracyTimeout '%s'", + duration, timeout)); + assertEquals(e.getMessage(), "I am exception", "It should be custom exception"); + } + } + + @Test + public void testShouldIgnoreExceptionForWaitingWithoutCustomParameters() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithExceptions = () -> conditionalWait.waitFor((driver) -> throwNewException(atomicBoolean).getAsBoolean(), ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithExceptions, timeoutConfiguration.getCondition()); + } + + @Test + public void testShouldIgnoreExceptionForWaitingWithDefaultTimeout() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithMessageAndExceptions = () -> conditionalWait.waitFor((driver) -> throwNewException(atomicBoolean).getAsBoolean(), "Condition should be true", ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithMessageAndExceptions, timeoutConfiguration.getCondition()); + } + + @Test + public void testShouldIgnoreExceptionWaitingWithCustomTimeoutAndExceptions() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithAllParameters = () -> conditionalWait.waitFor((driver) -> throwNewException(atomicBoolean).getAsBoolean(), waitForTimeoutCondition, waitForTimeoutPolling, ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithAllParameters, waitForTimeoutCondition); + } + + @Test + public void testShouldIgnoreExceptionWaitingWithCustomTimeout() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithAllParameters = () -> conditionalWait.waitFor((driver) -> throwNewException(atomicBoolean).getAsBoolean(), waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true", ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithAllParameters, waitForTimeoutCondition); + } + + private void checkWaitForMethodForPassedCondition(BooleanSupplier waitAction, long timeout) { + long accuracyTimeout = timeout + accuracy; + timer.get().start(); + boolean result = waitAction.getAsBoolean(); + double duration = timer.get().stop(); + assertTrue(result, "waitFor should return true when condition is satisfied."); + assertTrue(duration <= accuracyTimeout, + String.format("Duration '%s' should be less than accuracyTimeout '%s'", + duration, accuracyTimeout)); + } + + private Object[][] getDataProvider(ExpectedCondition action) { + Callable onlyAction = () -> conditionalWait.waitFor(action); + Callable actionWithMessage = () -> conditionalWait.waitFor(action, "Condition should be true"); + Callable actionWithExceptions = () -> conditionalWait.waitFor(action, Collections.emptyList()); + Callable actionWithMessageAndExceptions = () -> conditionalWait.waitFor(action, "Condition should be true", Collections.emptyList()); + Callable actionWithCustomTimeouts = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling); + Callable actionWithCustomTimeoutsAndMessage = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true"); + Callable actionWithCustomTimeoutsAndExceptions = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling, Collections.emptyList()); + Callable actionWithAllParameters = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true", Collections.emptyList()); + return new Object[][]{ + {onlyAction, timeoutConfiguration.getCondition()}, + {actionWithMessage, timeoutConfiguration.getCondition()}, + {actionWithExceptions, timeoutConfiguration.getCondition()}, + {actionWithMessageAndExceptions, timeoutConfiguration.getCondition()}, + {actionWithCustomTimeouts, waitForTimeoutCondition}, + {actionWithCustomTimeoutsAndMessage, waitForTimeoutCondition}, + {actionWithCustomTimeoutsAndExceptions, waitForTimeoutCondition}, + {actionWithAllParameters, waitForTimeoutCondition}, + }; + } +} diff --git a/src/test/java/tests/waitings/WaitForTests.java b/src/test/java/tests/waitings/WaitForTests.java new file mode 100644 index 0000000..b8ded4d --- /dev/null +++ b/src/test/java/tests/waitings/WaitForTests.java @@ -0,0 +1,120 @@ +package tests.waitings; + +import org.openqa.selenium.StaleElementReferenceException; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class WaitForTests extends BaseConditionalWaitTest { + + @DataProvider(name = "falseWaitForAction", parallel = true) + public Object[][] falseWaitForAction() { + return getDataProvider(() -> false); + } + + @Test(dataProvider = "falseWaitForAction") + public void testFalseShouldBeReturnedIfConditionIsNotMetAndTimeoutIsOver(BooleanSupplier waitAction, long timeout) { + Date startTime = new Date(); + boolean result = waitAction.getAsBoolean(); + long duration = (new Date().getTime() - startTime.getTime()) / 1000; + long interval = 2 * timeout + accuracy; + assertFalse(result, "waitFor should return false when condition is not satisfied."); + assertTrue(duration >= timeout && duration < interval, + String.format("Duration '%s' should be between '%s' and '%s' (timeout and (2*timeout + accuracy)) when condition is not satisfied. ", + duration, timeout, interval)); + } + + @DataProvider(name = "trueWaitForAction", parallel = true) + public Object[][] trueWaitForAction() { + return getDataProvider(() -> true); + } + + @Test(dataProvider = "trueWaitForAction") + public void testTrueShouldBeReturnedIfConditionIsMetAndTimeoutIsNotOver(BooleanSupplier waitAction, long timeout) { + checkWaitForMethodForPassedCondition(waitAction, timeout); + } + + @Test + public void testShouldIgnoreExceptionForWaitingWithoutCustomParameters() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithExceptions = () -> conditionalWait.waitFor(throwNewException(atomicBoolean), ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithExceptions, timeoutConfiguration.getCondition()); + } + + @Test + public void testShouldIgnoreExceptionForWaitingWithDefaultTimeout() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithMessageAndExceptions = () -> conditionalWait.waitFor(throwNewException(atomicBoolean), "Condition should be true", ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithMessageAndExceptions, timeoutConfiguration.getCondition()); + } + + @Test + public void testShouldIgnoreExceptionWaitingWithCustomTimeoutAndException() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithAllParameters = () -> conditionalWait.waitFor(throwNewException(atomicBoolean), waitForTimeoutCondition, waitForTimeoutPolling, ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithAllParameters, waitForTimeoutCondition); + } + + @Test + public void testShouldIgnoreExceptionWaitingWithCustomTimeout() { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + BooleanSupplier actionWithAllParameters = () -> conditionalWait.waitFor(throwNewException(atomicBoolean), waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true", ignoredExceptions); + checkWaitForMethodForPassedCondition(actionWithAllParameters, waitForTimeoutCondition); + } + + private void checkWaitForMethodForPassedCondition(BooleanSupplier waitAction, long timeout) { + long accuracyTimeout = timeout + accuracy; + timer.get().start(); + boolean result = waitAction.getAsBoolean(); + double duration = timer.get().stop(); + assertTrue(result, "waitFor should return true when condition is satisfied."); + assertTrue(duration <= timeout, + String.format("Duration '%s' should be less than accuracyTimeout '%s'", + duration, accuracyTimeout)); + } + + @DataProvider(name = "throwExceptionAction", parallel = true) + public Object[][] throwExceptionAction() { + BooleanSupplier throwEx = () -> { + throw new StaleElementReferenceException(""); + }; + return getDataProvider(throwEx); + } + + @Test(dataProvider = "throwExceptionAction", expectedExceptions = StaleElementReferenceException.class) + public void testShouldThrowException(BooleanSupplier waitAction, long timeout) { + waitAction.getAsBoolean(); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNullCannotBePassedAsCondition() { + conditionalWait.waitFor((BooleanSupplier) null, "Condition should not be null"); + } + + private Object[][] getDataProvider(BooleanSupplier action) { + BooleanSupplier onlyAction = () -> conditionalWait.waitFor(action); + BooleanSupplier actionWithMessage = () -> conditionalWait.waitFor(action, "Condition should be true"); + BooleanSupplier actionWithExceptions = () -> conditionalWait.waitFor(action, Collections.emptyList()); + BooleanSupplier actionWithMessageAndExceptions = () -> conditionalWait.waitFor(action, "Condition should be true", Collections.emptyList()); + BooleanSupplier actionWithCustomTimeouts = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling); + BooleanSupplier actionWithCustomTimeoutsAndMessage = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true"); + BooleanSupplier actionWithCustomTimeoutsAndExceptions = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling, Collections.emptyList()); + BooleanSupplier actionWithAllParameters = () -> conditionalWait.waitFor(action, waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true", Collections.emptyList()); + return new Object[][]{ + {onlyAction, timeoutConfiguration.getCondition()}, + {actionWithMessage, timeoutConfiguration.getCondition()}, + {actionWithExceptions, timeoutConfiguration.getCondition()}, + {actionWithMessageAndExceptions, timeoutConfiguration.getCondition()}, + {actionWithCustomTimeouts, waitForTimeoutCondition}, + {actionWithCustomTimeoutsAndMessage, waitForTimeoutCondition}, + {actionWithCustomTimeoutsAndExceptions, waitForTimeoutCondition}, + {actionWithAllParameters, waitForTimeoutCondition}, + }; + } +} diff --git a/src/test/java/tests/waitings/WaitForTrueTests.java b/src/test/java/tests/waitings/WaitForTrueTests.java new file mode 100644 index 0000000..f3bf3f0 --- /dev/null +++ b/src/test/java/tests/waitings/WaitForTrueTests.java @@ -0,0 +1,172 @@ +package tests.waitings; + +import org.openqa.selenium.StaleElementReferenceException; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BooleanSupplier; + +import static org.testng.Assert.assertTrue; + +public class WaitForTrueTests extends BaseConditionalWaitTest { + + @DataProvider(name = "falseWaitForTrueAction", parallel = true) + public Object[][] falseWaitForAction() { + return getDataProvider(() -> false); + } + + @Test(dataProvider = "falseWaitForTrueAction") + public void testTimeoutExceptionShouldBeThrownIfConditionIsMetAndTimeoutIsOver(Callable waitForTrueAction, long timeout) throws Exception { + timer.get().start(); + try { + waitForTrueAction.call(); + } catch (TimeoutException e) { + double duration = timer.get().stop(); + long interval = 2 * timeout + accuracy; + assertTrue(duration >= timeout && duration < interval, + String.format("Duration '%s' should be between '%s' and '%s' (timeout and (2*timeout + accuracy)) when condition is not satisfied.", + duration, timeout, interval)); + } + } + + @DataProvider(name = "successWaitForAction", parallel = true) + public Object[][] successWaitForAction() { + return getDataProvider(() -> true); + } + + @Test(dataProvider = "successWaitForAction") + public void testTimeoutExceptionShouldNotBeThrownIfConditionIsMetAndTimeoutIsNotOver(Callable waitForTrueAction, long timeout) throws Exception { + timer.get().start(); + waitForTrueAction.call(); + double duration = timer.get().stop(); + assertTrue(duration < timeout, + String.format("Duration '%s' should be less than timeout '%s' when condition is satisfied.", + duration, timeout)); + } + + @DataProvider(name = "throwExceptionAction", parallel = true) + public Object[][] throwExceptionAction() { + BooleanSupplier throwEx = () -> { + throw new StaleElementReferenceException(""); + }; + return getDataProvider(throwEx); + } + + @Test(dataProvider = "throwExceptionAction", expectedExceptions = StaleElementReferenceException.class) + public void testCustomExceptionShouldBeThrown(Callable waitForTrueAction, long timeout) throws Exception { + timer.get().start(); + waitForTrueAction.call(); + double duration = timer.get().stop(); + assertTrue(duration < timeout, + String.format("Duration '%s' should be less than timeout '%s' when condition is satisfied.", + duration, timeout)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNullCannotBePassedAsCondition() throws TimeoutException { + conditionalWait.waitForTrue(null, "Condition should not be null"); + } + + @Test + public void testCustomExceptionShouldBeIgnoredWithoutCustomParameters() throws Exception { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + checkExceptionIsIgnored(() -> { + conditionalWait.waitForTrue(throwNewException(atomicBoolean), ignoredExceptions); + return true; + }, timeoutConfiguration.getCondition()); + } + + @Test + public void testCustomExceptionShouldBeIgnoredWithDefaultTimeout() throws Exception { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + checkExceptionIsIgnored(() -> { + conditionalWait.waitForTrue(throwNewException(atomicBoolean), "Condition should be true", ignoredExceptions); + return true; + }, timeoutConfiguration.getCondition()); + } + + @Test + public void testCustomExceptionShouldBeIgnoredWithCustomTimeoutAndException() throws Exception { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + checkExceptionIsIgnored(() -> { + conditionalWait.waitForTrue(throwNewException(atomicBoolean), waitForTimeoutCondition, waitForTimeoutPolling, ignoredExceptions); + return true; + }, waitForTimeoutCondition); + } + + @Test + public void testCustomExceptionShouldBeIgnoredWithCustomTimeout() throws Exception { + AtomicBoolean atomicBoolean = new AtomicBoolean(true); + checkExceptionIsIgnored(() -> { + conditionalWait.waitForTrue(throwNewException(atomicBoolean), waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true", ignoredExceptions); + return true; + }, waitForTimeoutCondition); + } + + private void checkExceptionIsIgnored(Callable waitForTrueAction, long timeout) throws Exception { + timer.get().start(); + waitForTrueAction.call(); + double duration = timer.get().stop(); + assertTrue(duration < timeout, + String.format("Duration '%s' should be less than timeout '%s' when condition is satisfied.", + duration, timeout)); + } + + private Object[][] getDataProvider(BooleanSupplier action) { + + Callable onlyAction = () -> { + conditionalWait.waitForTrue(action); + return true; + }; + + Callable actionWithMessage = () -> { + conditionalWait.waitForTrue(action, "Condition should be true"); + return true; + }; + + Callable actionWithExceptions = () -> { + conditionalWait.waitForTrue(action, Collections.emptyList()); + return true; + }; + + Callable actionWithMessageAndExceptions = () -> { + conditionalWait.waitFor(action, "Condition should be true", Collections.emptyList()); + return true; + }; + + Callable actionWithCustomTimeouts = () -> { + conditionalWait.waitForTrue(action, waitForTimeoutCondition, waitForTimeoutPolling); + return true; + }; + + Callable actionWithCustomTimeoutsAndMessage = () -> { + conditionalWait.waitForTrue(action, waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true"); + return true; + }; + + Callable actionWithCustomTimeoutsAndException = () -> { + conditionalWait.waitForTrue(action, waitForTimeoutCondition, waitForTimeoutPolling, Collections.emptyList()); + return true; + }; + + Callable actionWithAllParameters = () -> { + conditionalWait.waitForTrue(action, waitForTimeoutCondition, waitForTimeoutPolling, "Condition should be true", Collections.emptyList()); + return true; + }; + + return new Object[][]{ + {onlyAction, timeoutConfiguration.getCondition()}, + {actionWithMessage, timeoutConfiguration.getCondition()}, + {actionWithExceptions, timeoutConfiguration.getCondition()}, + {actionWithMessageAndExceptions, timeoutConfiguration.getCondition()}, + {actionWithCustomTimeouts, waitForTimeoutCondition}, + {actionWithCustomTimeoutsAndMessage, waitForTimeoutCondition}, + {actionWithCustomTimeoutsAndException, waitForTimeoutCondition}, + {actionWithAllParameters, waitForTimeoutCondition}, + }; + } +} diff --git a/src/test/java/utils/Timer.java b/src/test/java/utils/Timer.java new file mode 100644 index 0000000..01c5095 --- /dev/null +++ b/src/test/java/utils/Timer.java @@ -0,0 +1,19 @@ +package utils; + +public class Timer { + private double startTime; + + public void start() { + if (startTime == 0) { + startTime = getCurrentTimeInSeconds(); + } + } + + public double stop() { + return getCurrentTimeInSeconds() - startTime; + } + + private static double getCurrentTimeInSeconds() { + return System.nanoTime() / Math.pow(10, 9); + } +} \ No newline at end of file diff --git a/src/test/resources/TestSuite.xml b/src/test/resources/TestSuite.xml index 6a2875a..c686b8d 100644 --- a/src/test/resources/TestSuite.xml +++ b/src/test/resources/TestSuite.xml @@ -10,6 +10,9 @@ + + +