/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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 org.apache.falcon.regression.ui.search; import com.google.common.util.concurrent.SimpleTimeLimiter; import com.google.common.util.concurrent.TimeLimiter; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.falcon.regression.core.enumsAndConstants.MerlinConstants; import org.apache.falcon.regression.core.util.TimeUtil; import org.apache.falcon.regression.ui.pages.Page; import org.apache.log4j.Logger; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.Select; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** Parent page object for all the search ui pages. */ public abstract class AbstractSearchPage extends Page { public static final String UI_URL = MerlinConstants.PRISM_URL; private static final Logger LOGGER = Logger.getLogger(AbstractSearchPage.class); public static final int PAGELOAD_TIMEOUT_THRESHOLD = 10; public static final int ALERT_LIFETIME = 3000; public AbstractSearchPage(WebDriver driver) { super(driver); waitForAngularToFinish(); LOGGER.info("Going to initialize Page Header."); pageHeader = PageFactory.initElements(driver, PageHeader.class); LOGGER.info("Initialization done."); } private PageHeader pageHeader; @FindBy(className = "mainUIView") protected WebElement mainUI; public PageHeader getPageHeader() { return pageHeader; } protected WebElement getParentElement(WebElement element) { return element.findElement(By.xpath("..")); } /** * A rough check to make sure that we are indeed on the correct page. */ public abstract void checkPage(); // Utility method to enter the data slowly on an element public static void sendKeysSlowly(WebElement webElement, String data){ for (String str : data.split("")) { webElement.sendKeys(str); } } public static void clearAndSet(WebElement webElement, String val) { webElement.clear(); webElement.sendKeys(val); } public static void clearAndSetSlowly(WebElement webElement, String val) { webElement.clear(); sendKeysSlowly(webElement, val); } protected WebElement findElementByNgModel(String ngModelName) { // trying to get an xpath that looks like: "//*[@ng-model='UIModel.retry.policy']" final String xpathExpression = "//*[@ng-model='" + ngModelName + "']"; final List<WebElement> webElements = driver.findElements(By.xpath(xpathExpression)); Assert.assertEquals(webElements.size(), 1, "Element is not unique for ng-model: " + ngModelName); return webElements.get(0); } protected void selectNgModelByVisibleText(String ngModelName, String visibleText) { final WebElement webElement = findElementByNgModel(ngModelName); final Select select = new Select(webElement); select.selectByVisibleText(visibleText); } protected void clearAndSetByNgModel(String ngModelName, String value) { final WebElement webElement = findElementByNgModel(ngModelName); clearAndSet(webElement, value); } protected void clearAndSetSlowlyByNgModel(String ngModelName, String value) { final WebElement webElement = findElementByNgModel(ngModelName); clearAndSetSlowly(webElement, value); } protected void clickById(String id) { final List<WebElement> webElements = driver.findElements(By.id(id)); Assert.assertEquals(webElements.size(), 1, "Element is not unique."); webElements.get(0).click(); } protected void clickByNgModel(String ngModelName) { final WebElement webElement = findElementByNgModel(ngModelName); webElement.click(); } // Utility method to get Dropdown Values public List<String> getDropdownValues(Select element){ List<WebElement> allOptions = element.getOptions(); List<String> values = new ArrayList<>(); for (WebElement option:allOptions){ values.add(option.getText()); } return values; } protected void waitForAngularToFinish() { final String javaScript = "return (window.angular != null) && " + "(angular.element(document).injector() != null) && " + "(angular.element(document).injector().get('$http').pendingRequests.length === 0)"; boolean isLoaded = false; for (int i = 0; i < PAGELOAD_TIMEOUT_THRESHOLD && !isLoaded; i++) { TimeLimiter timeLimiter = new SimpleTimeLimiter(); final JavascriptExecutor proxyJsExecutor = timeLimiter.newProxy((JavascriptExecutor) driver, JavascriptExecutor.class, 10, TimeUnit.SECONDS); try { final Object output = proxyJsExecutor.executeScript(javaScript); isLoaded = Boolean.valueOf(output.toString()); } catch (Exception e) { LOGGER.info("Checking of pending request failed because of: " + ExceptionUtils.getFullStackTrace(e)); } LOGGER.info(i+1 + ". waiting on angular to finish."); TimeUtil.sleepSeconds(1); } LOGGER.info("angular is done continuing..."); } public String getActiveAlertText() { if (waitForAlert()) { waitForAngularToFinish(); String script = "return $('div.messages.notifs > div:last-child').text();"; String message = (String)((JavascriptExecutor)driver).executeScript(script); return message.trim(); } else { return null; } } /** * Wait for active alert. Check it's lifetime (the period when alert is displayed). */ public void validateAlertLifetime() { final WebElement alertsBlock = driver.findElement(By.xpath("//div[@class='messages notifs']")); try { final MutablePair<Long, Long> pair = new MutablePair<>(Long.MAX_VALUE, Long.MAX_VALUE); // wait 5 seconds for alert to start blinking and record time of first blink new WebDriverWait(driver, 5, 100).until(new ExpectedCondition<Boolean>() { @Nullable @Override public Boolean apply(WebDriver webDriver) { String style = alertsBlock.getAttribute("style"); if ((style.contains("opacity") && !style.contains("opacity: 1;")) || style.contains("display: block;")) { pair.setLeft(System.currentTimeMillis()); return true; } return false; } }); // wait 5 seconds for alert to stop blinking and record time of stoppage for (int i = 0; i < ALERT_LIFETIME + 3000; i += 100) { String style = alertsBlock.getAttribute("style"); if (style.contains("display: none;")) { pair.setRight(Math.min(System.currentTimeMillis(), pair.getRight())); } else { pair.setRight(Long.MAX_VALUE); } TimeUtil.sleepSeconds(0.1); } long diff = pair.getRight() - pair.getLeft(); LOGGER.info(String.format("Alert was live %d millis.", pair.getRight() - pair.getLeft())); Assert.assertTrue(ALERT_LIFETIME <= diff, "Alert was present for too short period of time"); } catch (TimeoutException e) { Assert.fail("Alert didn't appear in 5 seconds."); } } /** * Wait for active alert. * @return true is alert is present */ protected boolean waitForAlert() { final WebElement alertsBlock = driver.findElement(By.xpath("//div[@class='messages notifs']")); try { new WebDriverWait(driver, 5).until(new ExpectedCondition<Boolean>() { @Nullable @Override public Boolean apply(WebDriver webDriver) { String style = alertsBlock.getAttribute("style"); return (style.contains("opacity") && !style.contains("opacity: 1;")) || style.contains("display: block;"); } }); return true; } catch (TimeoutException e) { return false; } } /** * Performs simple check of element presence. */ public WebElement getElementOrNull(String xpath) { try { return driver.findElement(By.xpath(xpath)); } catch (NoSuchElementException ignored) { return null; } } /** * Method imitates click on check box. If click is not performed method retries the click. * @param expectedState whether check box is expected to be enabled or not after click. */ protected void clickCheckBoxSecurely(WebElement checkBox, boolean expectedState) { double gap = 0.5; for (int attempt = 1; attempt <= (DEFAULT_TIMEOUT / gap); attempt++) { LOGGER.info("Attempt to click a check box: " + attempt); checkBox.click(); if (checkBox.isSelected() == expectedState) { return; } TimeUtil.sleepSeconds(gap); } Assert.fail("Check box state was not changed even in " + DEFAULT_TIMEOUT + " seconds."); } }