You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@falcon.apache.org by ra...@apache.org on 2015/05/18 20:37:18 UTC
[2/2] falcon git commit: FALCON-1202: Add tests for EntityPage
contributed by Raghav Kumar Gautam
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 <ra...@apache.org>
Authored: Mon May 18 11:35:54 2015 -0700
Committer: Raghav Kumar Gautam <ra...@apache.org>
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);
+ }
+ }
+
}
}