FALCON-1202: Add tests for EntityPage contributed by Raghav Kumar Gautam
Project: http://git-wip-us.apache.org/repos/asf/falcon/repo Commit: http://git-wip-us.apache.org/repos/asf/falcon/commit/fc2179f6 Tree: http://git-wip-us.apache.org/repos/asf/falcon/tree/fc2179f6 Diff: http://git-wip-us.apache.org/repos/asf/falcon/diff/fc2179f6 Branch: refs/heads/master Commit: fc2179f643a21e03b19005ad8aef2284c2aca5b1 Parents: ab93ac7 Author: Raghav Kumar Gautam <[email protected]> Authored: Mon May 18 11:35:54 2015 -0700 Committer: Raghav Kumar Gautam <[email protected]> Committed: Mon May 18 11:35:54 2015 -0700 ---------------------------------------------------------------------- falcon-regression/CHANGES.txt | 2 + .../regression/Entities/ClusterMerlin.java | 5 + .../falcon/regression/Entities/FeedMerlin.java | 19 +- .../regression/Entities/ProcessMerlin.java | 42 ++ .../helpers/entity/AbstractEntityHelper.java | 19 +- .../falcon/regression/core/util/UiUtil.java | 106 +++ .../falcon/regression/core/util/Util.java | 21 +- .../regression/testHelper/BaseUITestClass.java | 3 + .../ui/search/AbstractSearchPage.java | 26 +- .../falcon/regression/ui/search/EntityPage.java | 685 +++++++++++++++++++ .../falcon/regression/ui/search/PageHeader.java | 52 +- .../falcon/regression/ui/search/SearchPage.java | 97 +++ .../regression/searchUI/EntityPageTest.java | 597 ++++++++++++++++ 13 files changed, 1639 insertions(+), 35 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/CHANGES.txt ---------------------------------------------------------------------- diff --git a/falcon-regression/CHANGES.txt b/falcon-regression/CHANGES.txt index 07c03e9..296e5b8 100644 --- a/falcon-regression/CHANGES.txt +++ b/falcon-regression/CHANGES.txt @@ -5,6 +5,8 @@ Trunk (Unreleased) INCOMPATIBLE CHANGES NEW FEATURES + FALCON-1202 Add tests for EntityPage (Raghav Kumar Gautam) + FALCON-1210 Process Setup tests for testing header and general step default scenario (Raghav Kumar Gautam) FALCON-1201 Feed Setup tests for testing header and default scenario (Namit Maheshwari) http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ClusterMerlin.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ClusterMerlin.java b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ClusterMerlin.java index 5acf1eb..d8305c8 100644 --- a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ClusterMerlin.java +++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ClusterMerlin.java @@ -104,4 +104,9 @@ public class ClusterMerlin extends Cluster { } } + @Override + public EntityType getEntityType() { + return EntityType.CLUSTER; + } + } http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/FeedMerlin.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/FeedMerlin.java b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/FeedMerlin.java index 9dba50a..91ba8a1 100644 --- a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/FeedMerlin.java +++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/FeedMerlin.java @@ -74,6 +74,14 @@ public class FeedMerlin extends Feed { return new FeedMerlin(feedString); } + public List<String> getClusterNames() { + List<String> names = new ArrayList<>(); + for (Cluster cluster : getClusters().getClusters()) { + names.add(cluster.getName()); + } + return names; + } + /** * Sets custom feed property. * @param propertyName custom property name @@ -99,11 +107,13 @@ public class FeedMerlin extends Feed { } /** + * Return feed path of the specified type. * @return feed data path + * @param locationType the type of the location */ - public String getFeedPath() { + public String getFeedPath(LocationType locationType) { for (Location location : this.getLocations().getLocations()) { - if (location.getType() == LocationType.DATA) { + if (location.getType() == locationType) { return location.getPath(); } } @@ -374,4 +384,9 @@ public class FeedMerlin extends Feed { this.setTable(catalogTable); } + @Override + public EntityType getEntityType() { + return EntityType.FEED; + } + } http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ProcessMerlin.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ProcessMerlin.java b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ProcessMerlin.java index 4b06abc..9e3d2d7 100644 --- a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ProcessMerlin.java +++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/Entities/ProcessMerlin.java @@ -42,6 +42,7 @@ import org.testng.Assert; import javax.xml.bind.JAXBException; import java.io.StringWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -69,6 +70,41 @@ public class ProcessMerlin extends Process { return this; } + public List<String> getClusterNames() { + List<String> names = new ArrayList<>(); + for (Cluster cluster : getClusters().getClusters()) { + names.add(cluster.getName()); + } + return names; + } + + public Cluster getClusterByName(String name) { + for (Cluster cluster : getClusters().getClusters()) { + if (name.equals(cluster.getName())) { + return cluster; + } + } + return null; + } + + public Input getInputByName(String name) { + for (Input input : getInputs().getInputs()) { + if (input.getName().equals(name)) { + return input; + } + } + return null; + } + + public Output getOutputByName(String name) { + for (Output output : getOutputs().getOutputs()) { + if (output.getName().equals(name)) { + return output; + } + } + return null; + } + /** Fluent builder wrapper for cluster fragment of process entity . */ public static class ProcessClusterBuilder { private Cluster cluster = new Cluster(); @@ -413,6 +449,12 @@ public class ProcessMerlin extends Process { public String getFirstInputName() { return getInputs().getInputs().get(0).getName(); } + + @Override + public EntityType getEntityType() { + return EntityType.PROCESS; + } + } http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java index 08e11a1..4dce3f3 100644 --- a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java +++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java @@ -295,7 +295,7 @@ public abstract class AbstractEntityHelper { throws IOException, URISyntaxException, AuthenticationException, InterruptedException { LOGGER.info("Validating " + getEntityType() + ": \n" + Util.prettyPrintXml(data)); return Util.sendRequest(createUrl(this.hostname + URLS.VALIDATE_URL.getValue(), - getEntityType() + colo), "post", data, user); + getEntityType() + colo), "post", data, user); } public ServiceResponse schedule(String processData) @@ -414,6 +414,23 @@ public abstract class AbstractEntityHelper { .createAndSendRequestProcessInstance(url, params, allColo, user); } + public InstancesResult getProcessInstanceLogs(String entityName, String params) + throws IOException, URISyntaxException, AuthenticationException, InterruptedException { + return getProcessInstanceLogs(entityName, params, null); + } + + public InstancesResult getProcessInstanceLogs(String entityName, String params, + String user) + throws IOException, URISyntaxException, AuthenticationException, InterruptedException { + String url = createUrl(this.hostname + URLS.INSTANCE_LOGS.getValue(), getEntityType(), + entityName); + if (StringUtils.isNotEmpty(params)) { + url += "?"; + } + return (InstancesResult) InstanceUtil + .createAndSendRequestProcessInstance(url, params, allColo, user); + } + public InstancesResult getProcessInstanceSuspend( String readEntityName, String params) throws IOException, URISyntaxException, AuthenticationException, InterruptedException { http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/UiUtil.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/UiUtil.java b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/UiUtil.java new file mode 100644 index 0000000..6142332 --- /dev/null +++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/UiUtil.java @@ -0,0 +1,106 @@ +/** + * 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.core.util; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class for UI related tasks. + */ +public final class UiUtil { + private UiUtil() { + throw new AssertionError("Instantiating utility class..."); + } + private static final Logger LOGGER = Logger.getLogger(UiUtil.class); + + /** + * Convert the element to string representation. Useful for debugging/development. + * @param element element to be converted + * @param limitDepth the depth to traverse. Typically <=3 is good value. + * @return + */ + protected static String elementToString(WebElement element, Integer limitDepth) { + final StringBuilder retVal = + new StringBuilder("String representation of the element(first line is format):\n"); + retVal.append("-> tagname") + .append("(id)") + .append("(classes)") + .append("[extra-info]") + .append("\t") + .append("text") + .append("\n"); + retVal.append(elementToString("", element, limitDepth)); + return retVal.toString(); + } + + private static StringBuilder elementToString(String prefix, WebElement element, Integer + limitDepth) { + if (limitDepth != null && limitDepth == 0) { + return new StringBuilder(); + } + final Integer newDepth = limitDepth == null ? null : limitDepth - 1; + final StringBuilder elementStr = new StringBuilder(prefix); + List<String> extraInfo = new ArrayList<>(); + if (StringUtils.isNotBlank(element.getAttribute("ng-repeat"))) { + extraInfo.add("array"); + } + elementStr.append("-> ") + .append(element.getTagName()) + .append("(").append(element.getAttribute("id")).append(")") + .append("(").append(element.getAttribute("class")).append(")") + .append(extraInfo) + .append("\t").append(StringEscapeUtils.escapeJava(element.getText())); + final String childPrefix = prefix + "\t"; + final List<WebElement> childElements = element.findElements(By.xpath("./*")); + for (WebElement oneChildElement : childElements) { + StringBuilder childStr = elementToString(childPrefix, oneChildElement, newDepth); + if (childStr.length() > 0) { + elementStr.append("\n").append(childStr); + } + } + return elementStr; + } + + /** + * Highlight the element in the UI. Useful for development/debugging. + * Copied from http://www.testingdiaries.com/highlight-element-using-selenium-webdriver/ + * @param element the element to highlight + * @param driver the web driver in use + */ + public static void elementHighlight(WebElement element, WebDriver driver) { + for (int i = 0; i < 2; i++) { + JavascriptExecutor js = (JavascriptExecutor) driver; + js.executeScript( + "arguments[0].setAttribute('style', arguments[1]);", + element, "color: red; border: 3px solid red;"); + js.executeScript( + "arguments[0].setAttribute('style', arguments[1]);", + element, ""); + } + } +} http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/Util.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/Util.java b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/Util.java index 0cce072..62e8ae1 100644 --- a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/Util.java +++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/util/Util.java @@ -25,6 +25,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.jcraft.jsch.JSchException; import org.apache.falcon.entity.v0.EntityType; +import org.apache.falcon.entity.v0.feed.LocationType; import org.apache.falcon.regression.Entities.ClusterMerlin; import org.apache.falcon.regression.Entities.FeedMerlin; import org.apache.falcon.regression.Entities.ProcessMerlin; @@ -32,20 +33,18 @@ import org.apache.falcon.regression.core.helpers.ColoHelper; import org.apache.falcon.regression.core.helpers.entity.AbstractEntityHelper; import org.apache.falcon.regression.core.response.ServiceResponse; import org.apache.falcon.regression.core.supportClasses.JmsMessageConsumer; +import org.apache.falcon.request.BaseRequest; +import org.apache.falcon.request.RequestKeys; import org.apache.falcon.resource.APIResult; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; -import org.apache.http.HttpResponse; -import org.apache.falcon.request.BaseRequest; -import org.apache.falcon.request.RequestKeys; import org.apache.hadoop.security.authentication.client.AuthenticationException; - - +import org.apache.http.HttpResponse; +import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.testng.Assert; -import org.apache.log4j.Logger; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -54,10 +53,6 @@ import javax.jms.MapMessage; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; - import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; @@ -66,6 +61,9 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Enumeration; @@ -190,7 +188,7 @@ public final class Util { public static List<String> getHadoopDataFromDir(FileSystem fs, String feed, String dir) throws IOException { List<String> finalResult = new ArrayList<>(); - String feedPath = new FeedMerlin(feed).getFeedPath(); + String feedPath = new FeedMerlin(feed).getFeedPath(LocationType.DATA); int depth = feedPath.split(dir)[1].split("/").length - 1; List<Path> results = HadoopUtil.getAllDirsRecursivelyHDFS(fs, new Path(dir), depth); for (Path result : results) { @@ -393,6 +391,7 @@ public final class Util { INSTANCE_PARAMS("/api/instance/params"), INSTANCE_LIST("/api/instance/list"), INSTANCE_LISTING("/api/instance/listing"), + INSTANCE_LOGS("/api/instance/logs"), TOUCH_URL("/api/entities/touch"); private final String url; http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/testHelper/BaseUITestClass.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/testHelper/BaseUITestClass.java b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/testHelper/BaseUITestClass.java index caa9a38..205aad6 100644 --- a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/testHelper/BaseUITestClass.java +++ b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/testHelper/BaseUITestClass.java @@ -22,6 +22,8 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; +import java.util.concurrent.TimeUnit; + /** * Base class for UI test classes. */ @@ -39,6 +41,7 @@ public class BaseUITestClass extends BaseTestClass{ profile.setPreference("network.negotiate-auth.trusted-uris", "http://, https://"); driver = new FirefoxDriver(profile); + driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.manage().window().maximize(); } http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/AbstractSearchPage.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/AbstractSearchPage.java b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/AbstractSearchPage.java index e72ce67..3550604 100644 --- a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/AbstractSearchPage.java +++ b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/AbstractSearchPage.java @@ -19,8 +19,11 @@ package org.apache.falcon.regression.ui.search; 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.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -30,17 +33,19 @@ import org.openqa.selenium.support.PageFactory; public abstract class AbstractSearchPage extends Page { public static final String UI_URL = MerlinConstants.PRISM_URL; - public static final long PAGELOAD_TIMEOUT_THRESHOLD = 10; + private static final Logger LOGGER = Logger.getLogger(AbstractSearchPage.class); + public static final int PAGELOAD_TIMEOUT_THRESHOLD = 10; public AbstractSearchPage(WebDriver driver) { super(driver); + waitForAngularToFinish(); pageHeader = PageFactory.initElements(driver, PageHeader.class); } private PageHeader pageHeader; @FindBy(className = "mainUIView") - private WebElement mainUI; + protected WebElement mainUI; public PageHeader getPageHeader() { return pageHeader; @@ -56,10 +61,25 @@ public abstract class AbstractSearchPage extends Page { public abstract void checkPage(); // Utility method to enter the data slowly on an element - public void sendKeysSlowly(WebElement webElement, String data){ + public static void sendKeysSlowly(WebElement webElement, String data){ for (String str : data.split("")) { webElement.sendKeys(str); } } + + 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++) { + final Object output = ((JavascriptExecutor) driver).executeScript(javaScript); + isLoaded = Boolean.valueOf(output.toString()); + LOGGER.info(i+1 + ". waiting on angular to finish."); + TimeUtil.sleepSeconds(1); + } + LOGGER.info("angular is done continuing..."); + } + } http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/EntityPage.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/EntityPage.java b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/EntityPage.java new file mode 100644 index 0000000..0b7057e --- /dev/null +++ b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/EntityPage.java @@ -0,0 +1,685 @@ +/** + * 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.collect.ImmutableMap; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; +import org.apache.falcon.entity.v0.Frequency; +import org.apache.falcon.entity.v0.feed.Cluster; +import org.apache.falcon.entity.v0.feed.LocationType; +import org.apache.falcon.entity.v0.feed.Property; +import org.apache.falcon.entity.v0.process.Input; +import org.apache.falcon.entity.v0.process.Output; +import org.apache.falcon.entity.v0.process.Retry; +import org.apache.falcon.regression.Entities.FeedMerlin; +import org.apache.falcon.regression.Entities.ProcessMerlin; +import org.apache.falcon.regression.core.util.UIAssert; +import org.apache.falcon.resource.InstancesResult; +import org.apache.log4j.Logger; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.FindBys; +import org.openqa.selenium.support.ui.Select; +import org.testng.asserts.SoftAssert; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * Class representation of Search UI entity page. + */ +public class EntityPage extends AbstractSearchPage { + private static final Logger LOGGER = Logger.getLogger(EntityPage.class); + + /** + * Possible instance actions available on entity page. + */ + public enum InstanceAction { + Log, + Suspend, + Resume, + Rerun, + Kill + } + + public EntityPage(WebDriver driver) { + super(driver); + } + + private WebElement getEntityTitle() { + final WebElement title = driver.findElement(By.id("entity-title")); + UIAssert.assertDisplayed(title, "entity title"); + return title; + } + + @FindBys({ + @FindBy(className = "mainUIView"), + @FindBy(className = "dependencies-graph") + }) + private WebElement dependencyBox; + + + @FindBys({ + @FindBy(className = "mainUIView"), + @FindBy(xpath = "(.//*[contains(@class, 'detailsBox')])[2]") + }) + private WebElement instanceListBox; + + @FindBys({ + @FindBy(className = "mainUIView"), + @FindBy(className = "summaryBox") + }) + private WebElement propertiesBlock; + + public String getEntityName() { + UIAssert.assertDisplayed(getEntityTitle(), "Entity title"); + return getEntityTitle().getText().split(" ")[0]; + } + + @Override + public void checkPage() { + UIAssert.assertDisplayed(dependencyBox, "Dependency box"); + UIAssert.assertDisplayed(instanceListBox, "Instance list box"); + UIAssert.assertDisplayed(propertiesBlock, "Summary box"); + } + + public EntityPage refreshPage() { + final String entityName = getEntityName(); + SearchPage searchPage = getPageHeader().gotoHome(); + return searchPage.openEntityPage(entityName); + } + + public void checkFeedProperties(FeedMerlin feed) { + openProperties(); + + final WebElement propertiesBox = + propertiesBlock.findElement(By.xpath("//div[@ui-view='feedSummary']")); + UIAssert.assertDisplayed(propertiesBox, "Properties box"); + + //all the parts of the entity properties + final List<WebElement> propertyParts = propertiesBox.findElements(By.xpath("./div")); + //First set of properties + final WebElement generalBox = propertyParts.get(0); + final List<WebElement> generalParts = generalBox.findElements(By.xpath("./div")); + SoftAssert softAssert = new SoftAssert(); + //General + softAssert.assertEquals(generalParts.get(0).getText(), "General", "Unexpected heading"); + final List<WebElement> nameAndDesc = generalParts.get(1).findElements(By.xpath("./div")); + softAssert.assertEquals(nameAndDesc.get(0).getText(), "Name: " + feed.getName(), + "Unexpected feed name in properties."); + softAssert.assertEquals(nameAndDesc.get(1).getText(), "Description: " + feed.getDescription(), + "Unexpected description in properties."); + //Tags + softAssert.assertEquals(generalParts.get(2).getText(), "Tags", "Unexpected heading"); + softAssert.assertEquals(generalParts.get(3).getText(), + StringUtils.trimToEmpty(feed.getTags()), + "Unexpected tags"); + //Groups + softAssert.assertEquals(generalParts.get(4).getText(), "Groups", "Unexpected heading"); + softAssert.assertEquals(generalParts.get(5).getText(), + StringUtils.trimToEmpty(feed.getGroups()), + "Unexpected groups"); + //Access Control list + softAssert.assertEquals(generalParts.get(6).getText(), "Access Control List", + "Unexpected heading"); + final List<WebElement> ownerGrpPerm = generalParts.get(7).findElements(By.xpath("./div")); + softAssert.assertEquals(ownerGrpPerm.get(0).getText(), + "Owner: " + feed.getACL().getOwner(), "Unexpected owner"); + softAssert.assertEquals(ownerGrpPerm.get(1).getText(), + "Group: " + feed.getACL().getGroup(), "Unexpected group"); + softAssert.assertEquals(ownerGrpPerm.get(2).getText(), + "Permissions: " + feed.getACL().getPermission(), "Unexpected permission"); + //Schema + softAssert.assertEquals(generalParts.get(8).getText(), "Schema", + "Unexpected heading for general properties"); + final List<WebElement> locAndProvider = generalParts.get(9).findElements(By.xpath("./div")); + softAssert.assertEquals(locAndProvider.get(0).getText(), + "Location: " + feed.getSchema().getLocation(), "Unexpected schema locations"); + softAssert.assertEquals(locAndProvider.get(1).getText(), + "Provider: " + feed.getSchema().getProvider(), "Unexpected schema provider"); + //Properties + softAssert.assertEquals(generalParts.get(10).getText(), "Properties", + "Unexpected heading for general properties"); + final List<WebElement> freqLateAvail = generalParts.get(11).findElements(By.xpath("./div")); + final Frequency feedFrequency = feed.getFrequency(); + softAssert.assertEquals(freqLateAvail.get(0).getText(), + String.format("Frequency: Every %s %s", + feedFrequency.getFrequency(), feedFrequency.getTimeUnit()), + "Unexpected frequency"); + final Frequency feedLateCutoff = feed.getLateArrival().getCutOff(); + softAssert.assertEquals(freqLateAvail.get(1).getText(), + String.format("Late Arrival: Up to %s %s", + feedLateCutoff.getFrequency(), feedLateCutoff.getTimeUnit()), + "Unexpected late arrival"); + softAssert.assertEquals(freqLateAvail.get(2).getText(), + String.format("Availability Flag:%s", + StringUtils.trimToEmpty(feed.getAvailabilityFlag())), + "Unexpected availability flag"); + final List<WebElement> propertyElements = + generalParts.get(12).findElements(By.xpath("./div")); + List<String> displayedPropStr = new ArrayList<>(); + for (WebElement webElement : propertyElements) { + displayedPropStr.add(webElement.getText()); + } + Collections.sort(displayedPropStr); + final List<String> expectedPropStr = getFeedPropString(feed); + softAssert.assertEquals(displayedPropStr, expectedPropStr, + "Feed properties & displayed properties don't match. Expected: " + expectedPropStr + + " Actual: " + displayedPropStr); + //Storage type + softAssert.assertEquals(generalParts.get(13).getText(), "Default Storage Type:", + "Unexpected label for storage type."); + if (feed.getLocations() != null + && feed.getLocations().getLocations() != null + && feed.getLocations().getLocations().size() > 0) { + softAssert.assertEquals(generalParts.get(13).getText(), "File System", + "Unexpected storage type for feed."); + } else { + softAssert.fail("Need to add handler for other feed types."); + } + //Feed locations - Data followed by Stats followed by Meta + softAssert.assertEquals(generalParts.get(14).getText(), "Default Location:", + "Unexpected label for default location."); + softAssert.assertEquals(generalParts.get(15).getText(), + "Data\n" + feed.getFeedPath(LocationType.DATA), + "Unexpected label for feed data label"); + softAssert.assertEquals(generalParts.get(16).getText(), + "Stats\n" + feed.getFeedPath(LocationType.STATS), + "Unexpected label for feed stats label"); + softAssert.assertEquals(generalParts.get(17).getText(), + "Meta\n" + feed.getFeedPath(LocationType.META), + "Unexpected label for feed mata label"); + + //Second set of properties details with Source Cluster Properties + final WebElement clustersBox = propertyParts.get(1); + final List<WebElement> displayedClusters = clustersBox.findElements(By.xpath("./div")); + final List<Cluster> feedClusters = feed.getClusters().getClusters(); + //test needs to be fixed when we have support for more than one feed cluster + softAssert.assertEquals(feedClusters.size(), 1, + "Current UI has support for only one feed cluster."); + checkFeedCluster(displayedClusters.get(0), feedClusters.get(0), softAssert); + softAssert.assertAll(); + } + + private void openProperties() { + final WebElement heading = propertiesBlock.findElement(By.tagName("h4")); + assertEquals(heading.getText(), "Properties", + "Unexpected heading of properties box."); + final WebElement upButton = propertiesBlock.findElement(By.className("pointer")); + upButton.click(); + } + + private void checkFeedCluster(WebElement cluster, Cluster feedCluster, SoftAssert softAssert) { + final List<WebElement> clusterElements = cluster.findElements(By.xpath("./div")); + final String vClusterName = clusterElements.get(1).getText(); + softAssert.assertNotNull(feedCluster, + "Unexpected feed cluster is displayed: " + vClusterName); + final String clusterType = clusterElements.get(0).getText(); + softAssert.assertEquals(clusterType, + WordUtils.capitalize(feedCluster.getType().toString().toLowerCase() + " Cluster"), + "Unexpected cluster type for cluster: " + vClusterName); + softAssert.assertEquals(clusterElements.get(2).getText(), + "Start: " + feedCluster.getValidity().getStart() + + "\nEnd: " + feedCluster.getValidity().getEnd(), + "Unexpected validity of the cluster: " + vClusterName); + softAssert.assertEquals(clusterElements.get(3).getText(), "Timezone: UTC", + "Unexpected timezone for validity of the cluster: " + vClusterName); + softAssert.assertEquals(clusterElements.get(4).getText(), + "Retention: Archive in " + feedCluster.getRetention().getLimit().getFrequency() + + " " + feedCluster.getRetention().getLimit().getTimeUnit(), + "Unexpected retention associated with cluster: " + vClusterName); + } + + private List<String> getFeedPropString(FeedMerlin feed) { + List<String> retVals = new ArrayList<>(); + for (Property property : feed.getProperties().getProperties()) { + retVals.add(property.getName() + ": " + property.getValue()); + } + Collections.sort(retVals); + return retVals; + } + + public void checkProcessProperties(ProcessMerlin process) { + openProperties(); + + final WebElement propertiesBox = + propertiesBlock.findElement(By.xpath("//div[@ui-view='processSummary']")); + UIAssert.assertDisplayed(propertiesBox, "Properties box"); + final List<WebElement> propertiesParts = propertiesBox.findElements(By.xpath("./div")); + final WebElement generalPropBlock = propertiesParts.get(0); + final WebElement clusterPropBlock = propertiesParts.get(1); + final WebElement inputPropBlock = propertiesParts.get(2); + final WebElement outputPropBlock = propertiesParts.get(3); + + //checking general properties + final List<WebElement> generalPropParts = + generalPropBlock.findElement(By.xpath("./*")).findElements(By.xpath("./*")); + SoftAssert softAssert = new SoftAssert(); + softAssert.assertEquals(generalPropParts.get(0).getText(), "Process", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(1).getText(), "Name", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(2).getText(), process.getName(), + "Unexpected process name in general properties."); + softAssert.assertEquals(generalPropParts.get(3).getText(), "Tags", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(4).getText(), + StringUtils.defaultIfBlank(process.getTags(), "No tags selected"), + "Unexpected tags in general properties."); + softAssert.assertEquals(generalPropParts.get(5).getText(), "Workflow", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(6).getText(), "Name\nEngine\nVersion", + "Unexpected workflow properties in general properties."); + softAssert.assertEquals(generalPropParts.get(7).getText(), + String.format("%s%n%s%n%s", + StringUtils.defaultIfBlank(process.getWorkflow().getName(), ""), + process.getWorkflow().getEngine(), process.getWorkflow().getVersion()), + "Unexpected workflow properties in general properties."); + softAssert.assertEquals(generalPropParts.get(7).getText(), "Path", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(8).getText(), process.getWorkflow().getPath(), + "Unexpected workflow path in general properties."); + softAssert.assertEquals(generalPropParts.get(9).getText(), "Timing", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(10).getText(), "Timezone", + "Unexpected label in general properties."); + softAssert.assertEquals(generalPropParts.get(12).getText(), + String.format("Frequency%nEvery %s %s%n", process.getFrequency().getFrequency(), + process.getFrequency().getTimeUnit()) + + "Max. parallel instances\n" + process.getParallel() + + "\nOrder\n" + process.getOrder().toString(), + "Unexpected frequency/parallel/order info in general properties."); + softAssert.assertEquals(generalPropParts.get(13).getText(), "Retry", + "Unexpected label in general properties."); + final Retry processRetry = process.getRetry(); + softAssert.assertEquals(generalPropParts.get(14).getText(), + "Policy\n" + processRetry.getPolicy().toString().toLowerCase() + + "\nAttempts\n" + processRetry.getAttempts() + + "\nDelay\nUp to " + processRetry.getDelay().getFrequency() + + " " + processRetry.getDelay().getTimeUnit(), + "Unexpected policy/attempt/delay in general properties."); + + //checking cluster properties + final List<WebElement> allClusterProps = + clusterPropBlock.findElements(By.xpath("./div/div/div")); + final WebElement clustersHeading = clusterPropBlock.findElement(By.xpath(".//h5")); + softAssert.assertEquals(clustersHeading.getText(), "Clusters", + "Unexpected label in clusters heading"); + for (WebElement oneClusterProp : allClusterProps) { + final List<WebElement> clusterPropParts = oneClusterProp.findElements(By.xpath("./*")); + softAssert.assertEquals(clusterPropParts.get(0).getText(), "Name", + "Unexpected label in clusters properties"); + final String clusterName = clusterPropParts.get(1).getText(); + final org.apache.falcon.entity.v0.process.Cluster processCluster = + process.getClusterByName(clusterName); + softAssert.assertNotNull(processCluster, + "cluster with name " + clusterName + " was not present in process."); + softAssert.assertEquals(clusterName, processCluster.getName(), + "Unexpected cluster name in clusters properties"); + softAssert.assertEquals(clusterPropParts.get(2).getText(), "Validity", + "Unexpected label in clusters properties"); + softAssert.assertEquals(clusterPropParts.get(3).getText(), + "Start\n" + processCluster.getValidity().getStart() + + "\nEnd\n" + processCluster.getValidity().getEnd(), + "Unexpected start/end time in clusters properties"); + } + //checking inputs properties + final WebElement inputHeading = inputPropBlock.findElement(By.xpath(".//h5")); + softAssert.assertEquals(inputHeading.getText(), "Inputs", + "Unexpected heading for input properties."); + final List<WebElement> allInputsProps = + inputPropBlock.findElements(By.xpath("./div/div/*")); + for (WebElement oneInputProps : allInputsProps) { + final List<WebElement> inputPropParts = oneInputProps.findElements(By.xpath("./*")); + softAssert.assertEquals(inputPropParts.get(0).getText(), "Name", + "Unexpected label in input properties"); + final String inputName = inputPropParts.get(1).getText(); + final Input processInput = process.getInputByName(inputName); + softAssert.assertEquals(inputName, processInput.getName(), + "Unexpected input name in input properties"); + softAssert.assertEquals(inputPropParts.get(2).getText(), "Feed", + "Unexpected label in input properties"); + softAssert.assertEquals(inputPropParts.get(3).getText(), processInput.getFeed(), + "Unexpected label in input properties"); + softAssert.assertEquals(inputPropParts.get(4).getText(), "Instance", + "Unexpected label in input properties"); + softAssert.assertEquals(inputPropParts.get(5).getText(), + "Start\n" + processInput.getStart() + "\nEnd\n" + processInput.getEnd(), + "Unexpected start/end in input properties"); + } + final WebElement outputHeading = outputPropBlock.findElement(By.tagName("h5")); + softAssert.assertEquals(outputHeading.getText(), "Outputs", + "Unexpected label for output properties."); + final List<WebElement> allOutputsProps = + outputPropBlock.findElements(By.xpath("./div/div/*")); + for (WebElement oneOutputProps : allOutputsProps) { + final List<WebElement> outputPropParts = oneOutputProps.findElements(By.xpath("./*")); + softAssert.assertEquals(outputPropParts.get(0).getText(), "Name", + "Unexpected label in output properties"); + final String outputName = outputPropParts.get(1).getText(); + final Output processOutput = process.getOutputByName(outputName); + softAssert.assertEquals(outputName, processOutput.getName(), + "Unexpected output name in output properties"); + softAssert.assertEquals(outputPropParts.get(2).getText(), "Feed", + "Unexpected label in output properties"); + softAssert.assertEquals(outputPropParts.get(3).getText(), processOutput.getFeed(), + "Unexpected feed name in output properties"); + softAssert.assertEquals(outputPropParts.get(4).getText(), "Instance", + "Unexpected label in output properties"); + softAssert.assertEquals(outputPropParts.get(5).getText(), processOutput.getInstance(), + "Unexpected instance in output properties"); + softAssert.assertAll(); + } + } + + public InstanceSummary getInstanceSummary() { + return new InstanceSummary(this); + } + + /** + * Class representing all the displayed instance. + */ + public static class InstanceSummary { + private final WebElement instanceListBox; + private final WebElement summaryTableHeading; + + public InstanceSummary(EntityPage entityPage) { + instanceListBox = entityPage.instanceListBox; + UIAssert.assertDisplayed(instanceListBox, "instance list box"); + assertEquals(instanceListBox.findElement(By.tagName("h4")).getText(), + "Instances", + "Unexpected heading in instances box."); + + summaryTableHeading = instanceListBox.findElement(By.xpath(".//thead/tr")); + } + + private List<WebElement> getTableRows() { + return instanceListBox.findElements(By.xpath(".//tbody/tr")); + } + + public void performActionOnSelectedInstances(InstanceAction instanceAction) { + final List<WebElement> summaryTableBodyParts = getTableRows(); + final WebElement actionRibbon = summaryTableBodyParts.get(0); + for (WebElement element : actionRibbon.findElements(By.xpath("./td/div"))) { + if (InstanceAction.valueOf(element.getText()) == instanceAction) { + element.click(); + return; + } + } + } + + /** + * Get instance summary starting for all the pages. + * @return instance summary + */ + public List<OneInstanceSummary> getSummary() { + List<OneInstanceSummary> summary = new ArrayList<>(); + final List<WebElement> tableBody = getTableRows(); + //last line has page number + final WebElement pageNumberRow = tableBody.remove(tableBody.size() - 1); + final List<WebElement> pages = pageNumberRow.findElement(By.className("pagination")) + .findElements(By.className("ng-scope")); + final int numberOfPages = pages.size(); + for (int pageNumber = 1; pageNumber <= numberOfPages; ++pageNumber) { + //We want to use new web elements to avoid stale element issues + final List<WebElement> newTableBody = getTableRows(); + //last line has page number + final WebElement newPageNumberRow = newTableBody.remove(newTableBody.size() - 1); + final List<WebElement> newPages = + newPageNumberRow.findElement(By.className("pagination")) + .findElements(By.className("ng-scope")); + newPages.get(pageNumber-1).findElement(By.tagName("a")).click(); + summary.addAll(getSummaryInner()); + } + return summary; + } + + /** + * Get instance summary starting for the current page. + * @return instance summary + */ + private List<OneInstanceSummary> getSummaryInner() { + List<OneInstanceSummary> summary = new ArrayList<>(); + final List<WebElement> tableBody = getTableRows(); + //first line in body has buttons + tableBody.remove(0); + //last line has page number + tableBody.remove(tableBody.size() - 1); + //second last line is horizontal line + tableBody.remove(tableBody.size() - 1); + if (tableBody.size() == 1 + && tableBody.get(0).getText().equals("There are no results")) { + return summary; + } + for (WebElement oneSummaryRow : tableBody) { + summary.add(new OneInstanceSummary(oneSummaryRow)); + } + return summary; + } + + public void check() { + final List<WebElement> summaryHeadParts = getSummaryHeadParts(); + getSelectAllCheckBox(summaryHeadParts); + final WebElement instanceHeadLabel = summaryHeadParts.get(1); + assertEquals(instanceHeadLabel.getText(), "Instance", + "Unexpected label in instance summary heading"); + getSummaryStartedButton(); + getSummaryEndedButton(); + getStatusDropDown(); + } + + public void setInstanceSummaryStartTime(String timeStr) { + final WebElement startTimeButton = getSummaryStartedButton(); + startTimeButton.clear(); + sendKeysSlowly(startTimeButton, timeStr); + } + + public void setInstanceSummaryEndTime(String timeStr) { + final WebElement endTimeButton = getSummaryEndedButton(); + endTimeButton.clear(); + sendKeysSlowly(endTimeButton, timeStr); + } + + public void selectInstanceSummaryStatus(String labelText) { + getStatusDropDown().selectByVisibleText(labelText); + } + + public static OneInstanceSummary getOneSummary(final List<OneInstanceSummary> summaries, + final String nominalTime) { + for (OneInstanceSummary oneSummary : summaries) { + if (oneSummary.getNominalTime().equals(nominalTime)) { + return oneSummary; + } + } + return null; + } + + public void checkSummary(InstancesResult.Instance[] apiSummary) { + final List<OneInstanceSummary> summary = getSummary(); + assertEquals(apiSummary.length, summary.size(), + String.format("Length of the displayed instance summary is not same: %s %s", + Arrays.toString(apiSummary), summary)); + for (InstancesResult.Instance oneApiSummary : apiSummary) { + final OneInstanceSummary oneSummary = + getOneSummary(summary, oneApiSummary.instance); + assertEquals(oneApiSummary.instance, oneSummary.getNominalTime(), + "Nominal time of instance summary doesn't match."); + final SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm"); + final Date apiStartTime = oneApiSummary.getStartTime(); + if (apiStartTime == null) { + assertTrue(StringUtils.isEmpty(oneSummary.getStartTime()), + "Displayed start time : " + oneSummary + " is not " + + "consistent with start time of api which is null"); + } else { + assertEquals(oneSummary.getStartTime(), dateFormat.format(apiStartTime), + "Displayed start time : " + oneSummary + " is not " + + "consistent with start time of api: " + apiStartTime); + } + final Date apiEndTime = oneApiSummary.getEndTime(); + if (apiEndTime == null) { + assertTrue(StringUtils.isEmpty(oneSummary.getEndTime()), + "Displayed end time : " + oneSummary + " is not " + + "consistent end start time of api which is null"); + } else { + assertEquals(oneSummary.getEndTime(), dateFormat.format(apiEndTime), + "Displayed end time : " + oneSummary + " is not " + + "consistent with end time of api: " + apiEndTime); + } + assertEquals(oneApiSummary.status.toString(), oneSummary.getStatus(), + "Status of instance summary doesn't match."); + } + } + + public WebElement getSummaryStartedButton() { + final WebElement startedBox = getSummaryHeadParts().get(2); + assertEquals(startedBox.getText(), "Started ", + "Unexpected label in instance summary heading"); + return startedBox.findElement(By.tagName("input")); + } + + public WebElement getSummaryEndedButton() { + final WebElement endedBox = getSummaryHeadParts().get(3); + assertEquals(endedBox.getText(), "Ended ", + "Unexpected label in instance summary heading"); + return endedBox.findElement(By.tagName("input")); + } + + public Select getStatusDropDown() { + final WebElement statusBox = getSummaryHeadParts().get(4); + assertEquals(statusBox.getText(), + "Status \nALL\nRUNNING\nSUCCEEDED\nSUSPENDED\nKILLED", + "Unexpected label in instance summary heading"); + return new Select(statusBox.findElement(By.tagName("select"))); + } + + public List<WebElement> getSummaryHeadParts() { + return summaryTableHeading.findElements(By.xpath("./th/div")); + } + + public WebElement getSelectAllCheckBox(List<WebElement> summaryHeadParts) { + return summaryHeadParts.get(0).findElement(By.tagName("input")); + } + } + + /** + * Class representing summary of one instance. + */ + public static final class OneInstanceSummary { + private final WebElement oneInstanceSummary; + private final String startTime; + private final String endTime; + private final String status; + private final String nominalTime; + + private final Map<Object, Object> statusColorMap = ImmutableMap.builder() + .put("WAITING", "rgba(51, 51, 51, 1)") + .put("RUNNING", "") + .put("KILLED", "") + .put("SUCCEEDED", "") + .put("SUSPENDED", "") + .put("FAILED", "").build(); + private boolean isCheckBoxTicked; + + private OneInstanceSummary(WebElement oneInstanceSummary) { + this.oneInstanceSummary = oneInstanceSummary; + nominalTime = getNominalTimeButton().getText(); + startTime = getSummaryCols().get(2).getText(); + endTime = getSummaryCols().get(3).getText(); + + final WebElement statusElement = getSummaryCols().get(4); + assertTrue(statusElement.isDisplayed(), "Status should be displayed"); + final String statusText = statusElement.getText(); + final Object expectedColor = statusColorMap.get(statusText.trim()); + assertNotNull(expectedColor, + "Unexpected status: " + statusText + " not found in: " + statusColorMap); + //status color not checked + //final String actualColor = statusElement.getCssValue("color"); + //assertEquals(actualColor, expectedColor, + // "Unexpected color for status in process instances block: " + statusText); + status = statusText; + isCheckBoxTicked = getCheckBox().isSelected(); + } + + private List<WebElement> getSummaryCols() { + return oneInstanceSummary.findElements(By.tagName("td")); + } + + private WebElement getCheckBox() { + return getSummaryCols().get(0).findElement(By.tagName("input")); + } + + private WebElement getNominalTimeButton() { + return getSummaryCols().get(1); + } + + public String getStartTime() { + return startTime; + } + + public String getEndTime() { + return endTime; + } + + public String getStatus() { + return status; + } + public String getNominalTime() { + return nominalTime; + } + + public boolean isCheckBoxSelected() { + return isCheckBoxTicked; + } + + /** + * Click the checkbox corresponding to this result. It is the responsibility of the + * client to make sure that the web element for the instance is displayed and valid. + */ + public void clickCheckBox() { + getCheckBox().click(); + // Toggling of checkbox should change its internal state + // Note that we can't expect the web element to be displayed & valid at the point this + // object is used + isCheckBoxTicked = !isCheckBoxTicked; + } + + @Override + public String toString() { + return "OneInstanceSummary{" + + "checkBox=" + isCheckBoxSelected() + + ", nominalTime=" + getNominalTime() + + ", startTime=" + getStartTime() + + ", endTime=" + getEndTime() + + ", status=" + getStatus() + + "}"; + } + } +} http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/PageHeader.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/PageHeader.java b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/PageHeader.java index c166eaf..8af6101 100644 --- a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/PageHeader.java +++ b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/PageHeader.java @@ -28,6 +28,8 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.FindBys; import org.openqa.selenium.support.PageFactory; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; import java.util.ArrayList; @@ -106,6 +108,17 @@ public class PageHeader { "Unexpected text on logout button"); } + public SearchPage gotoHome() { + homeButton.click(); + final SearchPage searchPage = PageFactory.initElements(driver, SearchPage.class); + searchPage.checkPage(); + final PageHeader searchHeader = searchPage.getPageHeader(); + searchHeader.checkLoggedIn(); + Assert.assertEquals(searchHeader.getLoggedInUser(), LoginPage.UI_DEFAULT_USER, + "Unexpected user is displayed"); + return searchPage; + } + public void checkLoggedOut() { UIAssert.assertNotDisplayed(getLogoutButton(), "logout button"); } @@ -119,13 +132,10 @@ public class PageHeader { UIAssert.assertDisplayed(homeButton, "falcon logo"); Assert.assertEquals(homeButton.getText(), "Falcon", "Unexpected home button text"); UIAssert.assertDisplayed(falconLogo, "falcon logo"); - final String oldUrl = driver.getCurrentUrl(); - - homeButton.click(); - Assert.assertTrue(getHomeUrls().contains(driver.getCurrentUrl()), - "home button navigate to: " + driver.getCurrentUrl() + " instead of: " + getHomeUrls()); - driver.get(oldUrl); + final WebElement helpLink = loginHeaderBox.findElement(By.tagName("a")); + UIAssert.assertDisplayed(helpLink, "help link"); + final String oldUrl = driver.getCurrentUrl(); //displayed if user is logged in: create entity buttons, upload entity button, username if (getLogoutButton().isDisplayed()) { //checking create entity box @@ -133,15 +143,6 @@ public class PageHeader { final WebElement createEntityLabel = createEntityBox.findElement(By.tagName("h4")); Assert.assertEquals(createEntityLabel.getText(), "Create an entity", "Unexpected create entity text"); - doCreateCluster(); - driver.get(oldUrl); - doCreateFeed(); - driver.get(oldUrl); - doCreateProcess(); - driver.get(oldUrl); - doCreateMirror(); - driver.get(oldUrl); - //checking upload entity part UIAssert.assertDisplayed(uploadEntityBox, "Create entity box"); final WebElement uploadEntityLabel = uploadEntityBox.findElement(By.tagName("h4")); @@ -152,13 +153,28 @@ public class PageHeader { "Unexpected text on upload entity button"); //checking if logged-in username is displayed AssertUtil.assertNotEmpty(getLoggedInUser(), "Expecting logged-in username."); + + //create button navigation + doCreateCluster(); + driver.get(oldUrl); + doCreateFeed(); + driver.get(oldUrl); + doCreateProcess(); + driver.get(oldUrl); + doCreateMirror(); + driver.get(oldUrl); } + //home button navigation + homeButton.click(); + Assert.assertTrue(getHomeUrls().contains(driver.getCurrentUrl()), + "home button navigate to: " + driver.getCurrentUrl() + " instead of: " + getHomeUrls()); + driver.get(oldUrl); - //help link is always displayed - final WebElement helpLink = loginHeaderBox.findElement(By.tagName("a")); - UIAssert.assertDisplayed(helpLink, "help link"); + //help link navigation Assert.assertEquals(helpLink.getText(), "Help", "Help link expected to have text 'Help'"); helpLink.click(); + new WebDriverWait(driver, AbstractSearchPage.PAGELOAD_TIMEOUT_THRESHOLD).until( + ExpectedConditions.stalenessOf(helpLink)); Assert.assertEquals(driver.getCurrentUrl(), MerlinConstants.HELP_URL, "Unexpected help url"); driver.get(oldUrl); http://git-wip-us.apache.org/repos/asf/falcon/blob/fc2179f6/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/SearchPage.java ---------------------------------------------------------------------- diff --git a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/SearchPage.java b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/SearchPage.java index aabd9ca..b1a7963 100644 --- a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/SearchPage.java +++ b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/SearchPage.java @@ -18,6 +18,10 @@ package org.apache.falcon.regression.ui.search; +import org.apache.commons.lang.StringUtils; +import org.apache.falcon.entity.v0.Entity; +import org.apache.falcon.regression.Entities.FeedMerlin; +import org.apache.falcon.regression.Entities.ProcessMerlin; import org.apache.falcon.regression.core.util.TimeUtil; import org.apache.falcon.regression.core.util.UIAssert; import org.apache.log4j.Logger; @@ -27,6 +31,7 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.FindBys; +import org.openqa.selenium.support.PageFactory; import org.testng.Assert; import java.util.ArrayList; @@ -42,6 +47,8 @@ public class SearchPage extends AbstractSearchPage { private static final String CLASS_OF_SELECTED_ROW = "rowSelected"; private static final int ANIMATION_DELAY = 2; + private static final Logger LOGGER = Logger.getLogger(SearchPage.class); + public SearchPage(WebDriver driver) { super(driver); } @@ -105,6 +112,25 @@ public class SearchPage extends AbstractSearchPage { return searchResults; } + + public EntityPage openEntityPage(String entityName) { + return click(doSearch(entityName).get(0)); + } + + public EntityPage click(SearchResult result) { + LOGGER.info("attempting to click: " + result + " on search page."); + for (WebElement oneResultElement : getSearchResultElements()) { + final List<WebElement> resultParts = oneResultElement.findElements(By.tagName("td")); + final WebElement entityNameElement = resultParts.get(1); + final String entityName = entityNameElement.getText(); + if (entityName.equals(result.getEntityName())) { + entityNameElement.findElement(By.tagName("button")).click(); + return PageFactory.initElements(driver, EntityPage.class); + } + } + return null; + } + @Override public void checkPage() { UIAssert.assertDisplayed(searchBlock, "Cluster box"); @@ -366,9 +392,16 @@ public class SearchPage extends AbstractSearchPage { } public String getClusterName() { + Assert.assertFalse(clusterName.contains(","), "getClusterName() called" + + " in multi-cluster setup: " + clusterName + ", maybe use getClusterNames()"); return clusterName; } + public List<String> getClusterNames() { + return Arrays.asList(clusterName.split(",")); + } + + public String getType() { return type; } @@ -376,6 +409,70 @@ public class SearchPage extends AbstractSearchPage { public EntityStatus getStatus() { return status; } + + @Override + public String toString() { + return "SearchResult{" + + "isChecked=" + isChecked + + ", entityName='" + entityName + '\'' + + ", tags='" + tags + '\'' + + ", clusterName='" + clusterName + '\'' + + ", type='" + type + '\'' + + ", status='" + status + '\'' + + '}'; + } + + public static void assertEqual(List<SearchResult> searchResults, + List<Entity> expectedEntities, String errorMessage) { + Assert.assertEquals(searchResults.size(), expectedEntities.size(), errorMessage + + "(Length of lists don't match, searchResults: " + searchResults + + " expectedEntities: " + expectedEntities + ")"); + for (Entity entity : expectedEntities) { + boolean found = false; + for (SearchResult result : searchResults) { + //entities are same if they have same name & type + if (entity.getName().equals(result.entityName)) { + //entity type in SearchResults has a different meaning + //so, not comparing entity types + + //equality of cluster names + List<String> entityClusters = null; + switch (entity.getEntityType()) { + case FEED: + final FeedMerlin feed = (FeedMerlin) entity; + entityClusters = feed.getClusterNames(); + // tags equality check + Assert.assertEquals(result.getTags(), + StringUtils.trimToEmpty(feed.getTags()), + errorMessage + "(tags mismatch: " + result.entityName + + " & " + entity.toShortString() + ")"); + break; + case PROCESS: + final ProcessMerlin process = (ProcessMerlin) entity; + entityClusters = process.getClusterNames(); + // tags equality check + Assert.assertEquals(result.getTags(), + StringUtils.trimToEmpty(process.getTags()), + errorMessage + "(tags mismatch: " + result.entityName + + " & " + entity.toShortString() + ")"); + break; + default: + Assert.fail("Cluster entity is unexpected: " + entity); + break; + } + Collections.sort(entityClusters); + final List<String> actualClusters = result.getClusterNames(); + Collections.sort(actualClusters); + Assert.assertEquals(actualClusters, entityClusters, errorMessage + + "(cluster names mismatch: " + result + " " + entity + ")"); + found = true; + } + } + Assert.assertTrue(found, + "Entity: " + entity.toShortString() + " not found in: " + searchResults); + } + } + } }
