package com.sugarcrm.candybean.automation.webdriver; import com.sugarcrm.candybean.automation.element.Hook; import com.sugarcrm.candybean.exceptions.CandybeanException; import org.openqa.selenium.*; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.sugarcrm.candybean.automation.element.Hook.getBy; /** * This is a list of available wait conditions for WebDriverPause. It is a mix of default Selenium * conditions and custom conditions that implements the interface ExpectedCondition * * @author Eric Tam [email protected] * @author Jason Mittertreiner */ public class WaitConditions { private WaitConditions() { // Utility class } /** * A helper method to create a WebDriverElement given Hook and element. Depending on the tag, it will * return either WebDriverSelector or WebDriverElement * * @param hook * @param element * @param driver * @return * @throws CandybeanException */ private static WebDriverElement createWebDriverElement(Hook hook, WebElement element, WebDriver driver) throws CandybeanException { if ("select".equals(element.getTagName())) { return new WebDriverSelector(hook, driver); } return new WebDriverElement(hook, 0, driver, element); } /** * A helper method to find the first matching element on the page * * @param hook The hook used to search for the element * @param driver WebDriver to search with * @return The element, if found * @throws CandybeanException If the element is not found */ private static WebElement findElement(Hook hook, WebDriver driver) throws CandybeanException { List<WebElement> elements = driver.findElements(getBy(hook)); if (elements.isEmpty()) throw new CandybeanException("No such elements found"); return elements.get(0); } /** * This returns the negated (logically opposite) condition. It waits until apply() returns null or false * e.g. Conditions.not(Conditions.visible(...)) * * @param condition the condition you would like to negate * @return The negated condition */ public static ExpectedCondition<Boolean> not(ExpectedCondition<?> condition) { return ExpectedConditions.not(condition); } /** * Wait until the element is present on the DOM AND visible * * @param hook * @return */ public static ExpectedCondition<WebDriverElement> visible(final Hook hook) { return new ExpectedCondition<WebDriverElement>() { @Override public WebDriverElement apply(WebDriver driver) { try { WebElement element = findElement(hook, driver); return element.isDisplayed() ? createWebDriverElement(hook, element, driver) : null; } catch (CandybeanException | StaleElementReferenceException e) { return null; } } @Override public String toString() { return "visibility of " + hook; } }; } /** * Wait until the element is present on the DOM AND visible * * @param wde * @return */ public static ExpectedCondition<WebDriverElement> visible(final WebDriverElement wde) { return new ExpectedCondition<WebDriverElement>() { @Override public WebDriverElement apply(WebDriver driver) { try { return wde.isDisplayed() ? wde : null; } catch (CandybeanException e) { return null; } } @Override public String toString() { return "visibility of " + wde; } }; } /** * Wait until the element is not present on the DOM OR invisible * This is not possible with ExpectedConditions.not() because no only * do we need to return when isDisplayed is false, we also need to * return true with isDisplayed throws an exception, which * ExpectedConditions.not does not do. * * @param hook The hook to search for the element * @return True, when the element is invisible or removed, otherwise false */ public static ExpectedCondition<Boolean> invisible(final Hook hook) { return new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { try { WebElement element = findElement(hook, driver); return !(element.isDisplayed()); } catch (CandybeanException | StaleElementReferenceException e) { return true; } } @Override public String toString() { return "invisibility of " + hook; } }; } /** * Wait until the element is not present on the DOM OR invisible * This is not possible with ExpectedConditions.not because no only * do we need to return when isDisplayed is false, we also need to * return true with isDisplayed throws an exception, which * ExpectedConditions.not does not do. * * @param wde The webdriver element to search for * @return True, when the element is invisible or removed, otherwise false */ public static ExpectedCondition<Boolean> invisible(final WebDriverElement wde) { return new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { try { return !wde.isDisplayed(); } catch (CandybeanException | StaleElementReferenceException e) { return true; } } @Override public String toString() { return "invisibility of " + wde; } }; } /** * Wait until the element is not present on the DOM OR invisible * * @param hook * @param text * @return * @throws CandybeanException */ public static ExpectedCondition<Boolean> invisibleWithText(Hook hook, String text) throws CandybeanException { return ExpectedConditions.invisibilityOfElementWithText(getBy(hook), text); } /** * Wait until the element is present on the DOM * * @param hook * @return */ public static ExpectedCondition<WebDriverElement> present(final Hook hook) { return new ExpectedCondition<WebDriverElement>() { @Override public WebDriverElement apply(WebDriver driver) { try { WebElement element = findElement(hook, driver); return createWebDriverElement(hook, element, driver); } catch (CandybeanException | StaleElementReferenceException e ) { return null; } } @Override public String toString() { return "presence of " + hook; } }; } /** * Wait until the element is clickable state (visible AND enabled) * * @param hook * @return * @throws CandybeanException */ public static ExpectedCondition<WebElement> clickable(Hook hook) throws CandybeanException { return ExpectedConditions.elementToBeClickable(getBy(hook)); } /** * Wait until the element is clickable state (visible AND enabled) * * @param wde * @return */ public static ExpectedCondition<WebElement> clickable(WebDriverElement wde) { return ExpectedConditions.elementToBeClickable(wde.we); } /** * Test if the x and y coordinates of the element are with in the width and height of the screen * * @param hook The hook used to find the element * @param isOnScreen If the element should be on screen or not * @return ExpectedCondition that tests to see if the element in on screen * @throws CandybeanException */ public static ExpectedCondition<WebDriverElement> onScreen(final Hook hook, final boolean isOnScreen) throws CandybeanException { return new ExpectedCondition<WebDriverElement>() { @Override public WebDriverElement apply(WebDriver driver) { try { WebDriverElement element = createWebDriverElement(hook,findElement(hook, driver),driver); return element.isOnScreen() == isOnScreen ? element : null; } catch (CandybeanException | StaleElementReferenceException e) { return null; } } @Override public String toString() { return "if " + hook + (isOnScreen ? "is on screen" : "is off screen"); } }; } /** * Wait until the element is selected * * @param hook * @return * @throws CandybeanException */ public static ExpectedCondition<Boolean> selected(Hook hook) throws CandybeanException { return ExpectedConditions.elementToBeSelected(getBy(hook)); } /** * Wait until the element is selected * * @param wde * @return */ public static ExpectedCondition<Boolean> selected(WebDriverElement wde) { return ExpectedConditions.elementToBeSelected(wde.we); } /** * Wait until the element is unselected * * @param hook * @return * @throws CandybeanException */ public static ExpectedCondition<Boolean> unselected(Hook hook) throws CandybeanException { return ExpectedConditions.elementSelectionStateToBe(getBy(hook), false); } /** * Wait until the element is unselected * * @param wde * @return */ public static ExpectedCondition<Boolean> unselected(WebDriverElement wde) { return ExpectedConditions.elementSelectionStateToBe(wde.we, false); } /** * Wait until the frame is available to switch (Polling on switchTo().frame(...) until No NoSuchFrameException) and * switch to this frame if no exception has occurred * * @param hook * @return * @throws CandybeanException */ public static ExpectedCondition<WebDriver> frameToBeAvailableAndSwitchToIt(Hook hook) throws CandybeanException { return ExpectedConditions.frameToBeAvailableAndSwitchToIt(getBy(hook)); } /** * Wait until the frame is available to switch (Polling on switchTo().frame(...) until No NoSuchFrameException) and * switch to this frame if no exception has occurred * * @param name * @return */ public static ExpectedCondition<WebDriver> frameToBeAvailableAndSwitchToIt(String name) { return ExpectedConditions.frameToBeAvailableAndSwitchToIt(name); } /** * Wait until the frame is available to switch (Polling on switchTo().frame(...) until No NoSuchFrameException) and * switch to this frame if no exception has occurred * * @param frameIndex used to find the frame using the index */ public static ExpectedCondition<WebDriver> frameToBeAvailableAndSwitchToIt(final int frameIndex) { return new ExpectedCondition<WebDriver>() { @Override public WebDriver apply(WebDriver driver) { try { return driver.switchTo().frame(frameIndex); } catch (NoSuchFrameException e) { return null; } } @Override public String toString() { return "frame to be available: " + frameIndex; } }; } /** * Wait until the frame is available to switch (Polling on switchTo().frame(...) until No NoSuchFrameException) and * switch to this frame if no exception has occurred * * @param wde used to find the frame using WebDriverElement */ public static ExpectedCondition<WebDriver> frameToBeAvailableAndSwitchToIt(final WebDriverElement wde) { return new ExpectedCondition<WebDriver>() { @Override public WebDriver apply(WebDriver driver) { try { return driver.switchTo().frame(wde.we); } catch (NoSuchFrameException e) { return null; } } @Override public String toString() { return "frame to be available: " + wde.toString(); } }; } /** * Wait until the element is not present on the DOM * * @param wde * @return */ public static ExpectedCondition<Boolean> staleness(WebDriverElement wde) { return ExpectedConditions.stalenessOf(wde.we); } /** * Wait until the expected text presents on the element * * @param hook * @param text * @return * @throws CandybeanException */ public static ExpectedCondition<Boolean> textIsPresent(Hook hook, String text) throws CandybeanException { return ExpectedConditions.textToBePresentInElementLocated(getBy(hook), text); } /** * Wait until the expected text presents on the element * * @param wde * @param text * @return */ public static ExpectedCondition<Boolean> textIsPresent(WebDriverElement wde, String text) { return ExpectedConditions.textToBePresentInElement(wde.we, text); } /** * Wait until the expected text presents on the element's value attribute * * @param hook * @param text * @return * @throws CandybeanException */ public static ExpectedCondition<Boolean> textIsPresentInValueAttribute(Hook hook, String text) throws CandybeanException { return ExpectedConditions.textToBePresentInElementValue(getBy(hook), text); } /** * Wait until the expected text presents on the element's value attribute * * @param wde * @param text * @return */ public static ExpectedCondition<Boolean> textIsPresentInValue(WebDriverElement wde, String text) { return ExpectedConditions.textToBePresentInElementValue(wde.we, text); } /** * Wait until the window is available to switch (Polling on switchTo().window(...) until no * NoSuchWindowException) and switch to this window if no exception has occurred * * @param nameOrHandle The name or handle of the window to switch to. * @return a WebDriver instance focused on the specified window */ public static ExpectedCondition<WebDriver> windowToBeAvailableAndSwitchToIt(final String nameOrHandle) { return new ExpectedCondition<WebDriver>() { @Override public WebDriver apply(WebDriver driver) { try { return driver.switchTo().window(nameOrHandle); } catch (NoSuchWindowException e) { System.out.println("No such window."); return null; } } @Override public String toString(){ return "window to be available: " + nameOrHandle; } }; } /** * Wait for the specified number of windows to exist, e.g. when launching a new one to ensure * it has been fully instantiated before interacting with it. * * @param numberOfWindows an int representing the desired number of windows to wait for. * @return boolean true if the number of windows is currently numberOfWindows, false if not */ public static ExpectedCondition<Boolean> numberOfWindowsToBe(final int numberOfWindows) { return new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { driver.getWindowHandles(); return driver.getWindowHandles().size() == numberOfWindows; } }; } /* * Return the element found by hook that contains the specified attribute value if expected, * or the reverse if not. * * @param hook The hook used to find the element * @param attribute The attribute to check * @param value The expected value of the attribute * @param expectValue If the value is expected or not * @return The element if the specified value contains the specified attributed, null otherwise */ public static ExpectedCondition<WebDriverElement> hasAttribute(final Hook hook, final String attribute, final String value, final boolean expectValue) { return new ExpectedCondition<WebDriverElement>() { @Override public WebDriverElement apply(WebDriver driver) { try { WebElement element = findElement(hook, driver); /* Split the string so that we can match the attribute. If we are waiting for a value of "red", then "red left-aligned large" should match that, but "starred" should not. */ for (String currentValue : element.getAttribute(attribute).split("\\s")) { if (currentValue.equals(value)) { return expectValue ? createWebDriverElement(hook, element, driver) : null; } } return expectValue ? null : createWebDriverElement(hook, element, driver); } catch (CandybeanException | StaleElementReferenceException e) { return null; } } @Override public String toString() { return attribute + (expectValue? " is ": " isn't ") + value; } }; } /* * Return the element found by hook that contains the specified attribute value via regex if expecting match, or * reverse if expectValue is false * * @param hook The hook used to find the element * @param attribute The attribute to check * @param regex String regex of the expected value of the attribute * @param expectValue If the value is expected or not * @return The element if the specified value contains the specified attributed, null otherwise * @throws CandybeanException If the element is not found */ public static ExpectedCondition<WebDriverElement> hasRegexAttribute(final Hook hook, final String attribute, final String regex, final boolean expectValue) throws CandybeanException { return new ExpectedCondition<WebDriverElement>() { @Override public WebDriverElement apply(WebDriver driver) { try { WebElement element = findElement(hook, driver); Pattern p = Pattern.compile(regex); Matcher m = p.matcher(element.getAttribute(attribute)); return expectValue ? (m.matches() ? createWebDriverElement(hook, element, driver) : null) :(m.matches() ? null : createWebDriverElement(hook, element, driver)); } catch (CandybeanException | StaleElementReferenceException e) { return null; } } @Override public String toString() { return attribute + (expectValue? " matches ": " does not match ") + regex; } }; } }