Skip to content

Instantly share code, notes, and snippets.

@anthavio
Last active August 29, 2015 13:59
Show Gist options
  • Save anthavio/10846398 to your computer and use it in GitHub Desktop.
Save anthavio/10846398 to your computer and use it in GitHub Desktop.
SeleniumWaitBuilder
package com.nature.quickstep.test;
import java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
*
* @author martin.vanek
*
*/
public class SeleniumWaitBuilder {
public static WbBegin with(WebDriver driver) {
return new WaitBuilder(driver);
}
public interface WbBegin {
/**
* Positive condition outcome pass
*/
public WbCondition passOn();
/**
* Negative condition outcome kill
*/
public WbCondition failOn();
}
/**
* Terminal - execute conditional waiting
*/
public interface WbEnd {
public void seconds(int seconds);
}
public interface WbAnd {
public WbBegin and();
}
public interface WbNext extends WbAnd, WbCondition, WbEnd {
}
public interface WbCondition {
/**
* Wait for presence of element
*/
public WbNext element(By locator);
/**
* Wait for url becomes...
*
* public WaitTime url(StringConditionType type, String value);
*/
public StringCondition url();
public StringCondition title();
/**
* Wait for root tag become $lt;hmtl$gt; - use to skip redirects, because Selenium hides http status codes
*/
public WbNext html();
/**
* Generic condition method
*/
public WbEnd condition(ExpectedCondition<?> condition);
}
public interface StringCondition {
public WbNext equals(String value);
public WbNext contains(String value);
public WbNext endsWith(String value);
public WbNext matches(String value);
}
public static class WaitBuilder implements WbBegin, WbCondition, WbNext, WbEnd {
private final WebDriver driver;
private boolean positive;
private ConditionsList pass = new ConditionsList(true);
private ConditionsList fail = new ConditionsList(false);
public WaitBuilder(WebDriver driver) {
this.positive = true; //important
this.driver = driver;
}
@Override
public WbCondition passOn() {
this.positive = true;
return this;
}
@Override
public WbCondition failOn() {
this.positive = false;
return this;
}
@Override
public WbBegin and() {
return this;
}
public WbNext element(By locator) {
if (locator == null) {
throw new IllegalArgumentException("Null element locator");
}
ExpectedCondition<WebElement> condition = ExpectedConditions.presenceOfElementLocated(locator);
condition(condition);
return this;
}
public WbNext html() {
ExpectedCondition<WebElement> condition = ExpectedConditions.presenceOfElementLocated(By.xpath("/html"));
condition(condition);
return this;
}
public StringCondition url() {
return new WaitUrlImpl();
}
public StringCondition title() {
return new WaitTitleImpl();
}
@Override
public WbEnd condition(ExpectedCondition<?> condition) {
if (condition == null) {
throw new IllegalArgumentException("Null condition");
}
if (positive) {
pass.add(condition);
} else {
fail.add(condition);
}
return this;
}
@Override
public void seconds(int seconds) {
WebDriverWait wait = new WebDriverWait(driver, seconds);
if (pass.size() != 0) {
if (fail.size() != 0) {
wait.until(new DualityCollectionCondition(pass, fail));//true or ErrorConditionException or TimeoutException
} else {
wait.until(pass); //true or TimeoutException
}
} else {
if (fail.size() != 0) {
try {
wait.until(fail); //ErrorConditionException or TimeoutException
} catch (org.openqa.selenium.TimeoutException tx) {
//SUCCESS - Error condition did not happened so we can leave
}
} else {
throw new IllegalStateException("Neither pass nor fail conditions");
}
}
}
class WaitUrlImpl implements StringCondition {
@Override
public WbNext equals(String value) {
URLCondition condition = new URLCondition(value, StringConditionType.EQUALS);
condition(condition);
return WaitBuilder.this;
}
@Override
public WbNext contains(String value) {
URLCondition condition = new URLCondition(value, StringConditionType.CONTAINS);
condition(condition);
return WaitBuilder.this;
}
@Override
public WbNext endsWith(String value) {
URLCondition condition = new URLCondition(value, StringConditionType.ENDS_WITH);
condition(condition);
return WaitBuilder.this;
}
@Override
public WbNext matches(String value) {
URLCondition condition = new URLCondition(value, StringConditionType.MATCH_REGEX);
condition(condition);
return WaitBuilder.this;
}
}
class WaitTitleImpl implements StringCondition {
@Override
public WbNext equals(String value) {
TitleCondition condition = new TitleCondition(value, StringConditionType.EQUALS);
condition(condition);
return WaitBuilder.this;
}
@Override
public WbNext contains(String value) {
TitleCondition condition = new TitleCondition(value, StringConditionType.CONTAINS);
condition(condition);
return WaitBuilder.this;
}
@Override
public WbNext endsWith(String value) {
TitleCondition condition = new TitleCondition(value, StringConditionType.ENDS_WITH);
condition(condition);
return WaitBuilder.this;
}
@Override
public WbNext matches(String value) {
TitleCondition condition = new TitleCondition(value, StringConditionType.MATCH_REGEX);
condition(condition);
return WaitBuilder.this;
}
}
}
static class DualityCollectionCondition implements ExpectedCondition<Boolean> {
private ConditionsList passOn;
private ConditionsList failOn;
public DualityCollectionCondition(ConditionsList passOn, ConditionsList failOn) {
this.passOn = passOn;
this.failOn = failOn;
}
@Override
public Boolean apply(WebDriver driver) {
failOn.apply(driver); //false or ErrorConditionException
return passOn.apply(driver); //true or TimeoutException
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (passOn.conditions.size() == 1) {
sb.append(passOn.conditions.iterator().next());
} else {
sb.append(passOn.conditions);
}
if (failOn.conditions.size() != 0) {
if (sb.length() != 0) {
sb.append(" and");
}
sb.append(" not ");
if (failOn.conditions.size() == 1) {
sb.append(failOn.conditions.iterator().next());
} else {
sb.append(failOn.conditions);
}
}
return sb.toString();
}
}
static class ConditionsList implements ExpectedCondition<Boolean> {
private final boolean positive;
private List<ExpectedCondition<?>> conditions = new ArrayList<ExpectedCondition<?>>();
public ConditionsList(boolean positive) {
this.positive = positive;
}
public int size() {
return conditions.size();
}
public boolean isPositive() {
return positive;
}
@Override
public String toString() {
return "ConditionsList [positive=" + positive + ", conditions=" + conditions + "]";
}
public void add(ExpectedCondition<?> condition) {
if (condition == null) {
throw new IllegalArgumentException("Null condition");
}
conditions.add(condition);
}
@Override
public Boolean apply(WebDriver driver) {
if (positive) {
return applyPositive(driver);
} else {
return applyNegative(driver);
}
}
/**
* All conditions must be satisfied to return true
*/
private boolean applyPositive(WebDriver driver) {
for (ExpectedCondition<?> condition : conditions) {
Object apply = condition.apply(driver);
if (apply == null) {
return false; //expected element not returned
} else {
if (apply instanceof Boolean) {
if (!(Boolean) apply) {
return false; //well false is false
}
}
}
}
return true; //nobody said no -> go!
}
/**
* return false or throw ErrorConditionException
*/
private boolean applyNegative(WebDriver driver) {
for (ExpectedCondition<?> condition : conditions) {
Object result = condition.apply(driver);
if (result != null) {
if (result instanceof Boolean) {
if ((Boolean) result) {
throw new ErrorConditionException(condition);
} else {
return false; //keep checking
}
} else {
//anything else not null
throw new ErrorConditionException(result, condition);
}
} else {
return false;
}
}
return false; //nobody failed
}
}
public static class ErrorConditionException extends WebDriverException {
private static final long serialVersionUID = 1L;
private final ExpectedCondition<?> condition;
public ErrorConditionException(ExpectedCondition<?> condition) {
super(String.valueOf(condition));
this.condition = condition;
}
public ErrorConditionException(Object applyResult, ExpectedCondition<?> condition) {
super("Result: " + applyResult + " for: " + condition);
this.condition = condition;
}
public ExpectedCondition<?> getCondition() {
return condition;
}
}
public static enum StringConditionType {
EQUALS, CONTAINS, ENDS_WITH, MATCH_REGEX;
}
static abstract class AbstractStringCondition implements ExpectedCondition<Boolean> {
private final String name;
private final String expectedValue;
private final StringConditionType type;
private final boolean positive;
private transient String currentValue;
protected abstract String getCurrentValue(WebDriver driver);
public AbstractStringCondition(String name, String value, StringConditionType type) {
this(name, value, type, true);
}
public AbstractStringCondition(String name, String value, StringConditionType type, boolean positive) {
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Blank condition name " + name);
}
this.name = name;
if (value == null || value.length() == 0) {
throw new IllegalArgumentException("Blank condition value " + value);
}
this.expectedValue = value;
if (type == null) {
throw new IllegalArgumentException("Null condition type");
}
this.type = type;
this.positive = positive;
}
@Override
public Boolean apply(WebDriver driver) {
currentValue = getCurrentValue(driver);
if (currentValue == null) {
return false;
}
boolean result;
switch (type) {
case EQUALS:
result = currentValue.equals(expectedValue);
break;
case CONTAINS:
result = currentValue.contains(expectedValue);
break;
case ENDS_WITH:
result = currentValue.endsWith(currentValue);
break;
case MATCH_REGEX:
result = currentValue.matches(expectedValue);
break;
default:
throw new IllegalStateException("Unsupported type: " + type);
}
return positive ? result : !result;
}
@Override
public String toString() {
return String.format("%s \"%s\" %s \"%s\"", name, currentValue, type, expectedValue);
}
}
static class URLCondition extends AbstractStringCondition {
public URLCondition(String value, StringConditionType type, boolean positive) {
super("URL", value, type, positive);
}
public URLCondition(String value, StringConditionType type) {
super("URL", value, type, true);
}
@Override
protected String getCurrentValue(WebDriver driver) {
return driver.getCurrentUrl();
}
}
static class TitleCondition extends AbstractStringCondition {
public TitleCondition(String value, StringConditionType type, boolean positive) {
super("Title", value, type, positive);
}
public TitleCondition(String value, StringConditionType type) {
super("Title", value, type, true);
}
@Override
protected String getCurrentValue(WebDriver driver) {
return driver.getTitle();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment