Skip to content

Commit

Permalink
Refactored wait logic to use distinct values for implicit waits and w…
Browse files Browse the repository at this point in the history
…ait-for waits.
  • Loading branch information
wakaleo committed Mar 17, 2015
1 parent 0fa63e2 commit e7235f7
Show file tree
Hide file tree
Showing 43 changed files with 651 additions and 301 deletions.
15 changes: 10 additions & 5 deletions core/src/main/java/net/serenitybdd/core/pages/PageObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public abstract class PageObject {

private PageUrls pageUrls;

private net.serenitybdd.core.pages.SystemClock clock;
private net.serenitybdd.core.time.SystemClock clock;

private Duration waitForTimeout;
private Duration waitForElementTimeout;
Expand Down Expand Up @@ -120,7 +120,7 @@ private enum OpenMode {

protected PageObject() {
this.webdriverClock = new SystemClock();
this.clock = Injectors.getInjector().getInstance(net.serenitybdd.core.pages.SystemClock.class);
this.clock = Injectors.getInjector().getInstance(net.serenitybdd.core.time.SystemClock.class);
this.environmentVariables = Injectors.getInjector().getProvider(EnvironmentVariables.class).get();
this.sleeper = Sleeper.SYSTEM_SLEEPER;
setupPageUrls();
Expand Down Expand Up @@ -246,7 +246,7 @@ protected RenderedPageObjectView getRenderedView() {
return renderedView;
}

protected net.serenitybdd.core.pages.SystemClock getClock() {
protected net.serenitybdd.core.time.SystemClock getClock() {
return clock;
}

Expand Down Expand Up @@ -863,7 +863,9 @@ protected JavascriptExecutorFacade getJavascriptExecutorFacade() {
* Provides a fluent API for querying web elements.
*/
public <T extends net.serenitybdd.core.pages.WebElementFacade> T element(WebElement webElement) {
return net.serenitybdd.core.pages.WebElementFacadeImpl.wrapWebElement(driver, webElement, getImplicitWaitTimeout().in(MILLISECONDS));
return net.serenitybdd.core.pages.WebElementFacadeImpl.wrapWebElement(driver, webElement,
getImplicitWaitTimeout().in(MILLISECONDS),
getWaitForTimeout().in(MILLISECONDS));
}

public <T extends net.serenitybdd.core.pages.WebElementFacade> T $(WebElement webElement) {
Expand All @@ -879,7 +881,10 @@ public <T extends net.serenitybdd.core.pages.WebElementFacade> T element(WebElem
*/
public <T extends net.serenitybdd.core.pages.WebElementFacade> T element(By bySelector) {
WebElement webElement = getDriver().findElement(bySelector);
return net.serenitybdd.core.pages.WebElementFacadeImpl.wrapWebElement(driver, webElement, getImplicitWaitTimeout().in(MILLISECONDS));
return net.serenitybdd.core.pages.WebElementFacadeImpl.wrapWebElement(driver,
webElement,
getImplicitWaitTimeout().in(MILLISECONDS),
getWaitForTimeout().in(MILLISECONDS));
}

public <T extends net.serenitybdd.core.pages.WebElementFacade> T find(By selector) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ public List<WebElementFacade> thenFindAll(

public List<WebElementFacade> thenFindAll(By selector);

public long getTimeoutInMilliseconds();
public long getImplicitTimeoutInMilliseconds();

public <T extends WebElementFacade> T withTimeoutOf(int timeout,
TimeUnit unit);
public <T extends WebElementFacade> T withTimeoutOf(int timeout, TimeUnit unit);

/**
* Convenience method to chain method calls more fluently.
Expand Down
146 changes: 103 additions & 43 deletions core/src/main/java/net/serenitybdd/core/pages/WebElementFacadeImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import io.appium.java_client.FindsByAccessibilityId;
import io.appium.java_client.FindsByAndroidUIAutomator;
import io.appium.java_client.FindsByIosUIAutomation;
import net.serenitybdd.core.time.InternalSystemClock;
import net.serenitybdd.core.time.Stopwatch;
import net.thucydides.core.ThucydidesSystemProperty;
import net.thucydides.core.annotations.locators.MethodTiming;
import net.thucydides.core.annotations.locators.WithConfigurableTimeout;
import net.thucydides.core.guice.Injectors;
import net.thucydides.core.pages.jquery.JQueryEnabledPage;
Expand All @@ -33,7 +36,6 @@
import java.util.List;
import java.util.concurrent.TimeUnit;

import static ch.lambdaj.Lambda.by;
import static ch.lambdaj.Lambda.convert;
import static net.serenitybdd.core.pages.Selectors.isXPath;

Expand All @@ -45,7 +47,8 @@ public class WebElementFacadeImpl implements WebElementFacade, net.thucydides.co

private final WebElement webElement;
private final WebDriver driver;
private final long timeoutInMilliseconds;
private final long implicitTimeoutInMilliseconds;
private final long waitForTimeoutInMilliseconds;
private static final int WAIT_FOR_ELEMENT_PAUSE_LENGTH = 250;
private final Sleeper sleeper;
private final Clock webdriverClock;
Expand All @@ -58,49 +61,89 @@ public class WebElementFacadeImpl implements WebElementFacade, net.thucydides.co

private static final Logger LOGGER = LoggerFactory.getLogger(WebElementFacadeImpl.class);

protected WebElementFacadeImpl(final WebDriver driver,
final ElementLocator locator,
final WebElement webElement,
final long timeoutInMilliseconds) {
private final static long UNDEFINED_TIMEOUT = -1;

public WebElementFacadeImpl(final WebDriver driver,
final ElementLocator locator,
final WebElement webElement,
final long implicitTimeoutInMilliseconds,
final long waitForTimeoutInMilliseconds) {
this.webElement = webElement;
this.driver = driver;
this.timeoutInMilliseconds = timeoutInMilliseconds;
this.locator = locator;
this.webdriverClock = new org.openqa.selenium.support.ui.SystemClock();
this.webdriverClock = new org.openqa.selenium.support.ui.SystemClock();
this.sleeper = Sleeper.SYSTEM_SLEEPER;
this.javascriptExecutorFacade = new JavascriptExecutorFacade(driver);
this.environmentVariables = Injectors.getInjector().getProvider(EnvironmentVariables.class).get();
this.implicitTimeoutInMilliseconds = implicitTimeoutInMilliseconds;
this.waitForTimeoutInMilliseconds = (waitForTimeoutInMilliseconds >= 0) ? waitForTimeoutInMilliseconds : defaultWaitForTimeout();
}

public WebElementFacadeImpl(final WebDriver driver,
final ElementLocator locator,
final WebElement webElement,
final long implicitTimeoutInMilliseconds) {
this(driver, locator, webElement, implicitTimeoutInMilliseconds, implicitTimeoutInMilliseconds);
}


private long defaultWaitForTimeout() {
return ThucydidesSystemProperty.WEBDRIVER_WAIT_FOR_TIMEOUT.integerFrom(environmentVariables,
(int) DefaultTimeouts.DEFAULT_WAIT_FOR_TIMEOUT.in(TimeUnit.MILLISECONDS));
}

private WebElementFacadeImpl copy() {
return new WebElementFacadeImpl(driver, locator, webElement, timeoutInMilliseconds);
return new WebElementFacadeImpl(driver, locator, webElement, implicitTimeoutInMilliseconds, waitForTimeoutInMilliseconds);
}

public WebElementFacadeImpl(final WebDriver driver,
final ElementLocator locator,
final long timeoutInMilliseconds) {
this(driver, locator, null, timeoutInMilliseconds);
final long implicitTimeoutInMilliseconds) {
this(driver, locator, null, implicitTimeoutInMilliseconds, implicitTimeoutInMilliseconds);
}

public WebElementFacadeImpl(final WebDriver driver,
final ElementLocator locator,
final long implicitTimeoutInMilliseconds,
final long waitForTimeoutInMilliseconds) {
this(driver, locator, null, implicitTimeoutInMilliseconds, waitForTimeoutInMilliseconds);
}

public static <T extends WebElementFacade> T wrapWebElement(final WebDriver driver,
final WebElement element,
final long timeoutInMilliseconds,
final long waitForTimeoutInMilliseconds) {
return (T) new WebElementFacadeImpl(driver, null, element, timeoutInMilliseconds, waitForTimeoutInMilliseconds);

}

public static <T extends WebElementFacade> T wrapWebElement(final WebDriver driver,
final WebElement element,
final long timeoutInMilliseconds) {
return (T) new WebElementFacadeImpl(driver, null, element, timeoutInMilliseconds);
final WebElement element,
final long timeout) {
return (T) new WebElementFacadeImpl(driver, null, element, timeout, timeout);

}

protected WebElement getElement() {
if (resolvedElement() != null) {
return resolvedElement();
}
if (locator == null) {
return null;
}
resolvedELement = getLocator().findElement();
if (resolvedELement == null) {
throw new ElementNotVisibleException(locator.toString());
WebElement result;

try {
Stopwatch.SYSTEM.start();
if (resolvedElement() != null) {
result = resolvedElement();
} else if (locator == null) {
result = null;
} else {
resolvedELement = getLocator().findElement();
if (resolvedELement == null) {
throw new ElementNotVisibleException(locator.toString());
}
result = resolvedELement;
}
} catch (Throwable t) {
throw t;
}
return resolvedELement;
return result;
}

private WebElement resolvedElement() {
Expand Down Expand Up @@ -133,15 +176,15 @@ public <T extends WebElementFacade> T findBy(String xpathOrCssSelector) {
.cssSelector(xpathOrCssSelector)));
}

return wrapWebElement(driver, nestedElement, timeoutInMilliseconds());
return wrapWebElement(driver, nestedElement, timeoutInMilliseconds(), waitForTimeoutInMilliseconds);
}


public long timeoutInMilliseconds() {
if (driver instanceof WebDriverFacade) {
return ((WebDriverFacade) driver).getCurrentImplicitTimeout().in(TimeUnit.MILLISECONDS);
} else {
return timeoutInMilliseconds;
return implicitTimeoutInMilliseconds;
}

}
Expand All @@ -162,7 +205,7 @@ public List<WebElementFacade> thenFindAll(String xpathOrCssSelector) {
private List<WebElementFacade> webElementFacadesFrom(List<WebElement> nestedElements) {
List<WebElementFacade> results = Lists.newArrayList();
for (WebElement element : nestedElements) {
results.add(wrapWebElement(driver, element, timeoutInMilliseconds()));
results.add(wrapWebElement(driver, element, timeoutInMilliseconds(), waitForTimeoutInMilliseconds));
}
return results;
}
Expand All @@ -171,7 +214,7 @@ private List<WebElementFacade> webElementFacadesFrom(List<WebElement> nestedElem
public WebElementFacade findBy(By selector) {
logIfVerbose("findBy " + selector);
WebElement nestedElement = getElement().findElement(selector);
return wrapWebElement(driver, nestedElement, timeoutInMilliseconds());
return wrapWebElement(driver, nestedElement, timeoutInMilliseconds(), waitForTimeoutInMilliseconds);
}

@Override
Expand Down Expand Up @@ -227,8 +270,8 @@ public List<WebElement> findElementsByIosUIAutomation(String using) {
}

@Override
public long getTimeoutInMilliseconds() {
return timeoutInMilliseconds;
public long getImplicitTimeoutInMilliseconds() {
return implicitTimeoutInMilliseconds;
}

public Duration getImplicitTimeout() {
Expand All @@ -238,8 +281,7 @@ public Duration getImplicitTimeout() {

@Override
public WebElementFacade withTimeoutOf(int timeout, TimeUnit unit) {
return wrapWebElement(driver, getElement(),
TimeUnit.MILLISECONDS.convert(timeout, unit));
return wrapWebElement(driver, getElement(), implicitTimeoutInMilliseconds, TimeUnit.MILLISECONDS.convert(timeout, unit));
}

/**
Expand All @@ -251,7 +293,8 @@ public WebElementFacade withTimeoutOf(int timeout, TimeUnit unit) {
public boolean isVisible() {

try {
return (getElement() != null) && (getElement().isDisplayed());
WebElement element = getElement();
return (element != null) && (element.isDisplayed());
} catch (ElementNotVisibleException e) {
return false;
} catch (NoSuchElementException e) {
Expand Down Expand Up @@ -359,15 +402,17 @@ public boolean hasFocus() {
*/
@Override
public boolean containsText(final String value) {
return ((getElement() != null) && (getElement().getText().contains(value)));
WebElement element = getElement();
return ((element != null) && (element.getText().contains(value)));
}

/**
* Does this element exactly match given text?
*/
@Override
public boolean containsOnlyText(final String value) {
return ((getElement() != null) && (getElement().getText().equals(value)));
WebElement element = getElement();
return ((element != null) && (element.getText().equals(value)));
}

/**
Expand All @@ -389,11 +434,19 @@ public List<String> getSelectOptions() {

public ElementLocator getLocator() {
if ((locator instanceof WithConfigurableTimeout) && (driver instanceof ConfigurableTimeouts)) {
((WithConfigurableTimeout) locator).setTimeOutInSeconds((int) getImplicitTimeout().in(TimeUnit.SECONDS));
((WithConfigurableTimeout) locator).setTimeOutInSeconds((int) getLocatorTimeout());
}
return locator;
}

private long getLocatorTimeout() {
if (StepEventBus.getEventBus().aStepInTheCurrentTestHasFailed() || (MethodTiming.forThisThread().isInQuickMethod())) {
return 0;
} else {
return TimeUnit.SECONDS.convert(implicitTimeoutInMilliseconds, TimeUnit.MILLISECONDS);
}
}

@Override
public void setImplicitTimeout(Duration implicitTimeout) {
if (driver instanceof ConfigurableTimeouts) {
Expand Down Expand Up @@ -616,11 +669,18 @@ public boolean isPresent() {
}

try {
return (getElement() != null) && (getElement().isDisplayed() || !getElement().isDisplayed());
} catch (NoSuchElementException e) {
if (e.getCause().getMessage().contains("Element is not usable")) {
return true;
WebElement element = getElement();

if (element == null) {
return false;
}
element.isDisplayed();
return true;
} catch (ElementNotVisibleException e) {
return true;
} catch (NotFoundException e) {
return false;
} catch (ElementNotFoundAfterTimeoutError timeoutError) {
return false;
}
}
Expand Down Expand Up @@ -681,6 +741,7 @@ private void throwNoSuchElementExceptionWithCauseIfPresent(final Throwable timeo
String finalMessage = (StringUtils.isNotEmpty(timeoutMessage)) ? timeoutMessage : defaultMessage;
throw new NoSuchElementException(finalMessage, timeout);
}

private void throwShouldBeVisibleErrorWithCauseIfPresent(final Throwable timeout, final String defaultMessage) {
String timeoutMessage = (timeout.getCause() != null) ? timeout.getCause().getMessage() : timeout.getMessage();
String finalMessage = (StringUtils.isNotEmpty(timeoutMessage)) ? timeoutMessage : defaultMessage;
Expand Down Expand Up @@ -765,7 +826,7 @@ public Boolean apply(WebDriver driver) {
@Override
public Wait<WebDriver> waitForCondition() {
return new FluentWait<>(driver, webdriverClock, sleeper)
.withTimeout(timeoutInMilliseconds, TimeUnit.MILLISECONDS)
.withTimeout(waitForTimeoutInMilliseconds, TimeUnit.MILLISECONDS)
.pollingEvery(WAIT_FOR_ELEMENT_PAUSE_LENGTH, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
}
Expand Down Expand Up @@ -911,9 +972,8 @@ private boolean isMobileDriver() {
public void clear() {

if (!isMobileDriver()) {
getElement().sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.DELETE);
getElement().sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.DELETE);
}

getElement().clear();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public WidgetObjectImpl(PageObject page, ElementLocator locator, WebElement webE
}

public WidgetObjectImpl(PageObject page, ElementLocator locator, long timeoutInMilliseconds) {
this(page, locator, (WebElement) null, timeoutInMilliseconds);
this(page, locator, null, timeoutInMilliseconds);
}

public PageObject getPage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package net.serenitybdd.core.pages;
package net.serenitybdd.core.time;

import org.joda.time.DateTime;
import org.slf4j.Logger;
Expand Down
21 changes: 21 additions & 0 deletions core/src/main/java/net/serenitybdd/core/time/Stopwatch.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.serenitybdd.core.time;

/**
* Created by john on 14/03/15.
*/
public class Stopwatch {

public static Stopwatch SYSTEM = new Stopwatch();
long counter = 0;

public void start() {
counter = System.currentTimeMillis();
}

public long stop() {
long result = System.currentTimeMillis() - counter;
counter = 0;
return result;
}

}
Loading

0 comments on commit e7235f7

Please sign in to comment.