Last active
August 29, 2015 13:59
-
-
Save anthavio/10846398 to your computer and use it in GitHub Desktop.
SeleniumWaitBuilder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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