/** * Copyright 2005-2016 Red Hat, Inc. * * Red Hat licenses this file to you under the Apache License, version * 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package io.fabric8.selenium; import com.google.common.base.Function; import io.fabric8.kubernetes.api.KubernetesHelper; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.utils.Millis; import io.fabric8.utils.Strings; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.WebDriverWait; import java.util.Arrays; import java.util.List; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * A facade and helper methods for writing {@link WebDriver} based Selenium tests */ public class WebDriverFacade extends LogSupport { private final WebDriver driver; private final KubernetesClient client; private final String namespace; private long defaultTimeoutInSeconds = 60; public WebDriverFacade(WebDriver driver, KubernetesClient client, String namespace) { super(driver); this.driver = driver; this.client = client; this.namespace = namespace; } @Override public WebDriverFacade getFacade() { return this; } public String getNamespace() { return namespace; } public KubernetesClient getClient() { return client; } /** * Returns the service URL for the given service name */ public String getServiceUrl(String serviceName) { String url = KubernetesHelper.getServiceURL(client, serviceName, namespace, "http", true); assertNotNull("No external Service URL could be found for namespace: " + namespace + " and name: " + serviceName, url); assertTrue("No external Service URL could be found for namespace: " + namespace + " and name: " + serviceName, Strings.isNotBlank(url)); logInfo("Service " + serviceName + " in namespace: " + namespace + " URL = " + url); return url; } /** * Navigates to the given service name in the current namespace * * @return the URL navigated to */ public String navigateToService(String serviceName) { String url = getServiceUrl(serviceName); WebDriver driver = getDriver(); driver.navigate().to(url); return url; } /** * Finds an element or returns null if it could not be found */ public WebElement findOptionalElement(By by) { try { return getDriver().findElement(by); } catch (NoSuchElementException e) { return null; } catch (Throwable e) { logError("Failed to find " + by, e); return null; } } /** * Find an optinoal element from a given element or return null * * @param element * @param by */ public WebElement findOptionalElement(WebElement element, By by) { try { return element.findElement(by); } catch (NoSuchElementException e) { return null; } catch (Throwable e) { logError("Failed to find " + by, e); return null; } } /** * Finds the element for the `by`, clears the field and sends the given text * * @return the element or null if it is not found */ public WebElement clearAndSendKeys(By by, String text) { WebElement field = findOptionalElement(by); if (field != null) { field.clear(); field.sendKeys(text); } return field; } /** * Returns a form facade for submitting a form */ public FormFacade form() { return new FormFacade(this); } public boolean until(ExpectedCondition<Boolean> condition) { return until(condition.toString(), defaultTimeoutInSeconds, condition); } public boolean until(String message, ExpectedCondition<Boolean> condition) { return until(message, defaultTimeoutInSeconds, condition); } public boolean until(String message, long timeoutInSeconds, ExpectedCondition<Boolean> condition) { return new WebDriverWait(getDriver(), timeoutInSeconds).withMessage(message).until(condition); } public boolean untilLinkClicked(final By by) { return untilLinkClicked(defaultTimeoutInSeconds, by); } public boolean untilSelectedByVisibleText(By by, String value) { return untilSelectedByVisibleText(defaultTimeoutInSeconds, by, value); } public boolean untilSelectedByVisibleText(long timeoutInSeconds, final By by, final String value) { String message = "select " + by + " with value: " + value; return new WebDriverWait(getDriver(), timeoutInSeconds).withMessage(message).until(new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver webDriver) { WebElement element = findOptionalElement(by); if (element != null && element.isEnabled()) { Select select = new Select(element); try { select.selectByVisibleText(value); logInfo("" + by + " select " + select + " selected value: " + value); return true; } catch (NoSuchElementException e) { logWait("" + by + " select " + select + " does not yet have value: " + value); return false; } } else { logWait("" + by + " not enabled"); return false; } } }); } public boolean untilIsDisplayed(By firstBy, By secondBy) { return untilIsDisplayed(defaultTimeoutInSeconds, firstBy, secondBy); } public boolean untilIsDisplayed(long timeoutInSeconds, final By firstBy, final By secondBy) { String message = "" + firstBy + " then " + secondBy + " is displayed"; return until(message, timeoutInSeconds, new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver driver) { WebElement element = findOptionalElement(firstBy); if (element == null) { logWait("" + firstBy + ""); return false; } else { WebElement link = findOptionalElement(element, secondBy); if (link != null && link.isDisplayed()) { logInfo("" + firstBy + " then " + secondBy + " displayed"); return true; } else { logWait("" + firstBy + " then " + secondBy + " displayed"); return false; } } } }); } public boolean untilElementPredicate(final By by, final Function<WebElement, Boolean> elementPredicate) { return untilElementPredicate(defaultTimeoutInSeconds, by, elementPredicate); } public boolean untilElementPredicate(long timeoutInSeconds, final By by, final Function<WebElement, Boolean> elementPredicate) { String message = "" + by + " matches " + elementPredicate; return until(message, timeoutInSeconds, new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver driver) { WebElement element = findOptionalElement(by); if (element == null) { logWait("" + by + ""); return false; } else { Boolean value = elementPredicate.apply(element); if (value != null && value.booleanValue()) { logInfo("" + by + " matches " + elementPredicate + ""); return true; } else { logWait("" + by + " matches " + elementPredicate + ""); return false; } } } }); } public boolean untilIsDisplayed(final By by) { return untilIsDisplayed(defaultTimeoutInSeconds, by); } public boolean untilIsDisplayed(long timeoutInSeconds, final By by) { return untilElementPredicate(timeoutInSeconds, by, new Function<WebElement, Boolean>() { @Override public String toString() { return "element.isDisplayed()"; } @Override public Boolean apply(WebElement element) { return element.isDisplayed(); } }); } public boolean untilIsEnabled(final By by) { return untilIsEnabled(defaultTimeoutInSeconds, by); } public boolean untilIsEnabled(long timeoutInSeconds, final By by) { return untilElementPredicate(timeoutInSeconds, by, new Function<WebElement, Boolean>() { @Override public String toString() { return "element.isEnabled()"; } @Override public Boolean apply(WebElement element) { return element.isEnabled(); } }); } public boolean untilLinkClicked(long timeoutInSeconds, final By by) { String message = "click link " + by; return until(message, timeoutInSeconds, new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver driver) { WebElement link = findOptionalElement(by); if (link != null) { logInfo("Clicking link: " + by + ""); link.click(); logInfo("Clicked link: " + by + " now"); return true; } else { logInfo("Not found link " + by + ""); return false; } } }); } /** * Waits until one of the given elements is available */ public void untilOneOf(final By... bys) { final List<By> byList = Arrays.asList(bys); String message = "One of these is available: " + byList; until(message, defaultTimeoutInSeconds, new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver driver) { for (By by : bys) { WebElement element = findOptionalElement(by); if (element != null && element.isDisplayed() && element.isEnabled()) { logInfo("Found " + element + " for " + by + ""); return true; } } logInfo("Still not found any of " + byList + ""); return false; } }); } public void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { // ignore } } public boolean currentUrlStartsWith(String expectedUrl) { String currentUrl = getDriver().getCurrentUrl(); boolean answer = currentUrl != null && currentUrl.startsWith(expectedUrl); if (!answer) { logWarn("Current URL `" + currentUrl + "` does not start with `" + expectedUrl + "`"); } return answer; } public void assertCurrentUrlStartsWith(String expectedUrl) { String currentUrl = getDriver().getCurrentUrl(); boolean answer = currentUrl != null && currentUrl.startsWith(expectedUrl); if (!answer) { fail("Current URL `" + currentUrl + "` does not start with `" + expectedUrl + "`"); } } /** * Lets wait until the link is visible then click it. If the click doesn't work lets retry a few times * just in case */ public void untilLinkClickedLoop(By by, String expectedUrl) { for (int i = 0; i < 10; i++) { untilLinkClicked(by); sleep(Millis.seconds(10)); if (currentUrlStartsWith(expectedUrl)) { break; } else { logWarn("lets try re-clicking link: " + by); } } assertCurrentUrlStartsWith(expectedUrl); } public long getDefaultTimeoutInSeconds() { return defaultTimeoutInSeconds; } public void setDefaultTimeoutInSeconds(long defaultTimeoutInSeconds) { this.defaultTimeoutInSeconds = defaultTimeoutInSeconds; } }