package org.fluentlenium.core.wait;
import org.fluentlenium.core.FluentControl;
import org.fluentlenium.core.FluentPage;
import org.fluentlenium.core.conditions.FluentConditions;
import org.fluentlenium.core.conditions.FluentListConditions;
import org.fluentlenium.core.conditions.wait.WaitConditionProxy;
import org.fluentlenium.core.domain.FluentWebElement;
import org.fluentlenium.utils.SupplierOfInstance;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* A wait object wrapping default selenium {@link org.openqa.selenium.support.ui.FluentWait} object into a more
* complete API, allowing to wait for any condition to be verified.
*/
public class FluentWait
implements FluentWaitFunctional<FluentControl>, FluentWaitConditions<FluentWait>, FluentWaitConfiguration<FluentWait> {
private final org.openqa.selenium.support.ui.FluentWait<FluentControl> wait;
private final WebDriver driver;
private boolean useDefaultException;
private boolean messageDefined;
private boolean defaultExceptionsRegistered;
/**
* Creates a new fluent wait.
*
* @param control control interface
*/
public FluentWait(FluentControl control) {
wait = new org.openqa.selenium.support.ui.FluentWait<>(control);
wait.withTimeout(5, TimeUnit.SECONDS);
driver = control.getDriver();
useDefaultException = true;
}
@Override
public org.openqa.selenium.support.ui.FluentWait getWait() {
return wait;
}
@Override
public FluentWait atMost(long duration, TimeUnit unit) {
wait.withTimeout(duration, unit);
return this;
}
@Override
public FluentWait atMost(long duration) {
return atMost(duration, TimeUnit.MILLISECONDS);
}
@Override
public FluentWait pollingEvery(long duration, TimeUnit unit) {
wait.pollingEvery(duration, unit);
return this;
}
@Override
public FluentWait pollingEvery(long duration) {
return pollingEvery(duration, TimeUnit.MILLISECONDS);
}
@Override
public FluentWait ignoreAll(Collection<Class<? extends Throwable>> types) {
wait.ignoreAll(types);
return this;
}
@Override
public FluentWait ignoring(Class<? extends RuntimeException> exceptionType) {
wait.ignoring(exceptionType);
return this;
}
@Override
public FluentWait ignoring(Class<? extends RuntimeException> firstType, Class<? extends RuntimeException> secondType) {
wait.ignoring(firstType, secondType);
return this;
}
@Override
public FluentWait withMessage(String message) {
wait.withMessage(message);
messageDefined = true;
return this;
}
@Override
public FluentWait withMessage(Supplier<String> message) {
wait.withMessage(message::get);
messageDefined = true;
return this;
}
@Override
public FluentWait withNoDefaultsException() {
useDefaultException = false;
return this;
}
private void updateWaitWithDefaultExceptions() {
if (useDefaultException & !defaultExceptionsRegistered) {
defaultExceptionsRegistered = true;
wait.ignoring(NoSuchElementException.class);
wait.ignoring(StaleElementReferenceException.class);
}
}
/**
* Check if a message is defined.
*
* @return true if this fluent wait use a custom message, false otherwise
*/
public boolean hasMessageDefined() {
return messageDefined;
}
@Override
public void untilPredicate(Predicate<FluentControl> predicate) {
updateWaitWithDefaultExceptions();
wait.until(predicate::test);
}
@Override
public void until(Supplier<Boolean> booleanSupplier) {
updateWaitWithDefaultExceptions();
wait.until(new Function<Object, Boolean>() {
public Boolean apply(Object input) {
return booleanSupplier.get();
}
public String toString() {
return booleanSupplier.toString();
}
});
}
@Override
public <T> T until(Function<? super FluentControl, T> function) {
updateWaitWithDefaultExceptions();
return wait.until(new Function<Object, T>() {
@Override
public T apply(Object input) {
return function.apply((FluentControl) input);
}
@Override
public String toString() {
return function.toString();
}
});
}
@Override
public FluentConditions until(FluentWebElement element) {
updateWaitWithDefaultExceptions();
return WaitConditionProxy.element(this, "Element " + element.toString(), new SupplierOfInstance<>(element));
}
@Override
public FluentListConditions until(List<? extends FluentWebElement> elements) {
updateWaitWithDefaultExceptions();
return WaitConditionProxy.one(this, "Elements " + elements.toString(), () -> elements);
}
@Override
public FluentListConditions untilEach(List<? extends FluentWebElement> elements) {
updateWaitWithDefaultExceptions();
return WaitConditionProxy.each(this, "Elements " + elements.toString(), () -> elements);
}
@Override
public FluentConditions untilElement(Supplier<? extends FluentWebElement> element) {
updateWaitWithDefaultExceptions();
return WaitConditionProxy.element(this, "Element " + element, element);
}
@Override
public FluentListConditions untilElements(Supplier<? extends List<? extends FluentWebElement>> elements) {
updateWaitWithDefaultExceptions();
return WaitConditionProxy.one(this, "Elements " + elements, elements);
}
@Override
public FluentListConditions untilEachElements(Supplier<? extends List<? extends FluentWebElement>> elements) {
updateWaitWithDefaultExceptions();
return WaitConditionProxy.each(this, "Elements " + elements, elements);
}
@SuppressWarnings("unchecked")
@Override
public FluentWaitWindowConditions untilWindow(String windowName) {
return new FluentWaitWindowConditions(this, windowName);
}
@Override
public FluentWaitPageConditions untilPage() {
updateWaitWithDefaultExceptions();
return new FluentWaitPageConditions(this, driver);
}
@Override
public FluentWaitPageConditions untilPage(FluentPage page) {
updateWaitWithDefaultExceptions();
return new FluentWaitPageConditions(this, driver, page);
}
@Override
public FluentWait explicitlyFor(long amount, TimeUnit timeUnit) {
try {
timeUnit.sleep(amount);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return this;
}
@Override
public FluentWait explicitlyFor(long amount) {
return explicitlyFor(amount, TimeUnit.MILLISECONDS);
}
}