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/06/04 20:53:00 UTC

[1/2] falcon git commit: FALCON-1249 Tests for process setup wizard conributed by Namit Maheshwari and Paul Isaychuk

Repository: falcon
Updated Branches:
  refs/heads/master 8ee6d45f0 -> ff9c78e3c


http://git-wip-us.apache.org/repos/asf/falcon/blob/ff9c78e3/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/searchUI/ProcessSetupTest.java
----------------------------------------------------------------------
diff --git a/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/searchUI/ProcessSetupTest.java b/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/searchUI/ProcessSetupTest.java
index 4e1474c..7852390 100644
--- a/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/searchUI/ProcessSetupTest.java
+++ b/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/searchUI/ProcessSetupTest.java
@@ -18,11 +18,14 @@
 
 package org.apache.falcon.regression.searchUI;
 
-import org.apache.commons.lang.StringUtils;
 import org.apache.falcon.entity.v0.Frequency;
+import org.apache.falcon.entity.v0.process.*;
+import org.apache.falcon.regression.Entities.ClusterMerlin;
 import org.apache.falcon.regression.Entities.ProcessMerlin;
 import org.apache.falcon.regression.core.bundle.Bundle;
 import org.apache.falcon.regression.core.helpers.ColoHelper;
+import org.apache.falcon.regression.core.response.ServiceResponse;
+import org.apache.falcon.regression.core.util.AssertUtil;
 import org.apache.falcon.regression.core.util.BundleUtil;
 import org.apache.falcon.regression.core.util.HadoopUtil;
 import org.apache.falcon.regression.core.util.OSUtil;
@@ -30,29 +33,68 @@ import org.apache.falcon.regression.testHelper.BaseUITestClass;
 import org.apache.falcon.regression.ui.search.LoginPage;
 import org.apache.falcon.regression.ui.search.ProcessWizardPage;
 import org.apache.falcon.regression.ui.search.SearchPage;
-import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
 import org.apache.log4j.Logger;
-import org.apache.oozie.client.OozieClient;
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import javax.xml.bind.JAXBException;
 import java.io.IOException;
+import java.net.URISyntaxException;
+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.TimeZone;
 
 /** UI tests for process creation. */
 @Test(groups = "search-ui")
 public class ProcessSetupTest extends BaseUITestClass {
     private static final Logger LOGGER = Logger.getLogger(ProcessSetupTest.class);
     private final ColoHelper cluster = servers.get(0);
-    private final FileSystem clusterFS = serverFS.get(0);
-    private final OozieClient clusterOC = serverOC.get(0);
     private String baseTestHDFSDir = cleanAndGetTestDir();
     private String aggregateWorkflowDir = baseTestHDFSDir + "/aggregator";
     private String feedInputPath = baseTestHDFSDir + "/input" + MINUTE_DATE_PATTERN;
     private String feedOutputPath = baseTestHDFSDir + "/output" + MINUTE_DATE_PATTERN;
     private ProcessWizardPage processWizardPage = null;
+    private final List<String> timeZones = new ArrayList<>(Arrays.asList(
+        "-Select timezone-", "UTC", "(GMT -12:00) Eniwetok, Kwajalein",
+        "(GMT -11:00) Midway Island, Samoa", "(GMT -10:00) Hawaii", "(GMT -9:00) Alaska",
+        "(GMT -8:00) Pacific Time (US & Canada)", "(GMT -7:00) Mountain Time (US & Canada)",
+        "(GMT -6:00) Central Time (US & Canada), Mexico City",
+        "(GMT -5:00) Eastern Time (US & Canada), Bogota, Lima",
+        "(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz", "(GMT -3:30) Newfoundland",
+        "(GMT -3:00) Brazil, Buenos Aires, Georgetown", "(GMT -2:00) Mid-Atlantic",
+        "(GMT -1:00 hour) Azores, Cape Verde Islands",
+        "(GMT) Western Europe Time, London, Lisbon, Casablanca",
+        "(GMT +1:00 hour) Brussels, Copenhagen, Madrid, Paris",
+        "(GMT +2:00) Kaliningrad, South Africa",
+        "(GMT +3:00) Baghdad, Riyadh, Moscow, St. Petersburg", "(GMT +3:30) Tehran",
+        "(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi", "(GMT +4:30) Kabul",
+        "(GMT +5:00) Ekaterinburg, Islamabad, Karachi, Tashkent",
+        "(GMT +5:30) Bombay, Calcutta, Madras, New Delhi", "(GMT +5:45) Kathmandu",
+        "(GMT +6:00) Almaty, Dhaka, Colombo", "(GMT +7:00) Bangkok, Hanoi, Jakarta",
+        "(GMT +8:00) Beijing, Perth, Singapore, Hong Kong",
+        "(GMT +9:00) Tokyo, Seoul, Osaka, Sapporo, Yakutsk", "(GMT +9:30) Adelaide, Darwin",
+        "(GMT +10:00) Eastern Australia, Guam, Vladivostok",
+        "(GMT +11:00) Magadan, Solomon Islands, New Caledonia",
+        "(GMT +12:00) Auckland, Wellington, Fiji, Kamchatka"
+    ));
+
+    private final List<String> timeUnits = new ArrayList<>(Arrays.asList("minutes", "hours", "days", "months"));
+    private final List<String> delayTimeUnits = new ArrayList<>(Arrays.asList("-Select delay-", "minutes",
+        "hours", "days", "months"));
+    private final List<String> parallel = new ArrayList<>(Arrays.asList("1", "2", "3", "4", "5", "6", "7",
+        "8", "9", "10", "11", "12"));
+    private final List<String> order = new ArrayList<>(Arrays.asList("-Select order-", "FIFO", "LIFO", "LAST_ONLY"));
+    private final List<String> policy =new ArrayList<>(Arrays.asList("-Select policy-", "periodic", "exp-backoff",
+        "final"));
+
+    private ProcessMerlin process;
 
     @BeforeMethod(alwaysRun = true)
     public void setup() throws IOException {
@@ -74,6 +116,10 @@ public class ProcessSetupTest extends BaseUITestClass {
         SearchPage searchPage = loginPage.doDefaultLogin();
         processWizardPage = searchPage.getPageHeader().doCreateProcess();
         processWizardPage.checkPage();
+        process = bundles[0].getProcessObject();
+        //we need to reduce name size to 39 symbols
+        process.setName(process.getName().substring(0, 39));
+        process.setTags("first=yes,second=yes,third=no");
     }
 
     @AfterMethod(alwaysRun = true)
@@ -82,6 +128,8 @@ public class ProcessSetupTest extends BaseUITestClass {
         closeBrowser();
     }
 
+    /* Step 1 tests */
+
     /**
      * Test header of the EntityPage.
      * Check that buttons (logout, entities, uploadXml, help, Falcon) are present, and names are
@@ -99,13 +147,929 @@ public class ProcessSetupTest extends BaseUITestClass {
     /**
      * Populate fields with valid values (name, tag, workflow, engine, version, wf path)
      * and check that user can go to the next step.
+     * @throws Exception
      */
     @Test
     public void testGeneralStepDefaultScenario() throws Exception {
-        final ProcessMerlin process = bundles[0].getProcessObject();
-        final List<String> tags = Arrays.asList("first=yes", "second=yes", "third=yes", "wrong=no");
-        process.setTags(StringUtils.join(tags, ","));
-        processWizardPage.doStep1(process);
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Assert on the click of next, the Page moves to the next page
+        processWizardPage.isFrequencyQuantityDisplayed(true);
+    }
+
+    /**
+     * Populate fields with valid values (name, tag, workflow, engine, version, wf path)
+     * Check that they are reflected on XML preview.
+     * @throws Exception
+     */
+    @Test
+    public void testGeneralStepXmlPreview() throws Exception{
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+
+        // Get process from XML Preview
+        ProcessMerlin processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+
+        // Assert all the values entered on the General Info Page
+        LOGGER.info(String.format("Comparing source process: %n%s%n and preview: %n%s%n.", process, processFromXML));
+        process.assertGeneralProperties(processFromXML);
+    }
+
+    /**
+     * Add few tags to the process. Click edit XML. Remove both tags from XML.
+     * Check that properties were removed from matching fields.
+     * Now click Edit XML again. Add new tag, Pig engine (instead of Oozie) with one of existing versions to the XML.
+     * Check that changes have been reflected on wizard page.
+     * @throws Exception
+     */
+    @Test
+    public void testGeneralStepEditXml() throws Exception{
+
+        // Set tag in process
+        process.setTags("first=yes,second=yes");
+
+        // Set tag and group on the Wizard
+        processWizardPage.setTags(process.getTags());
+
+        // Get XML, and set tag and group back to null
+        ProcessMerlin processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+        processFromXML.setTags(null);
+
+        // Now click EditXML and set the updated XML here
+        processWizardPage.clickEditXml();
+        String xmlToString = processFromXML.toString();
+        processWizardPage.setProcessXml(xmlToString);
+        processWizardPage.clickEditXml();
+
+        Thread.sleep(1000);
+        // Assert that there is only one Tag on the Wizard window
+        processWizardPage.isTagsDisplayed(0, true);
+        processWizardPage.isTagsDisplayed(1, false);
+
+        // Assert that the Tag value is empty on the Wizard window
+        Assert.assertEquals(processWizardPage.getTagKeyText(0), "",
+            "Tag Key Should be empty on the Wizard window");
+        Assert.assertEquals(processWizardPage.getTagValueText(0), "",
+            "Tag Value Should be empty on the Wizard window");
+
+        // Set Tag and Engine values
+        processFromXML.setTags("third=yes,fourth=no");
+        processFromXML.getWorkflow().setEngine(EngineType.PIG);
+        processFromXML.getWorkflow().setVersion("pig-0.13.0");
+
+        // Now click EditXML and set the updated XML here
+        processWizardPage.clickEditXml();
+        xmlToString = processFromXML.toString();
+        processWizardPage.setProcessXml(xmlToString);
+        processWizardPage.clickEditXml();
+
+        // Assert that there are two Tags on the Wizard window
+        processWizardPage.isTagsDisplayed(0, true);
+        processWizardPage.isTagsDisplayed(1, true);
+
+        // Assert that the Tag values are correct on the Wizard window
+        Assert.assertEquals(processWizardPage.getTagKeyText(0), "third",
+            "Unexpected Tag1 Key on the Wizard window");
+        Assert.assertEquals(processWizardPage.getTagValueText(0), "yes",
+            "Unexpected Tag1 Value on the Wizard window");
+        Assert.assertEquals(processWizardPage.getTagKeyText(1), "fourth",
+            "Unexpected Tag2 Key on the Wizard window");
+        Assert.assertEquals(processWizardPage.getTagValueText(1), "no",
+            "Unexpected Tag2 Value on the Wizard window");
+        Assert.assertEquals(processWizardPage.isPigRadioSelected(), true,
+            "Unexpected Engine on the Wizard window");
+        Assert.assertTrue(processWizardPage.getEngineVersionText().contains("pig-0.13.0"),
+            "Unexpected Engine Version on the Wizard window");
+    }
+
+    /**
+     * Add two tags to the process. Check that it is present.
+     * Delete the tag. Check that it has been removed.
+     * @throws Exception
+     */
+    @Test
+    public void testGeneralStepAddRemoveTag() throws Exception{
+
+        // Set tag in process
+        process.setTags("first=yes,second=yes");
+
+        // Set tag and group on the Wizard
+        processWizardPage.setTags(process.getTags());
+
+        // Assert that there are two Tags on the Wizard window
+        processWizardPage.isTagsDisplayed(0, true);
+        processWizardPage.isTagsDisplayed(1, true);
+
+        // Delete the tags
+        processWizardPage.deleteTags();
+
+        // Assert that there is only one Tag on the Wizard window
+        processWizardPage.isTagsDisplayed(0, true);
+        processWizardPage.isTagsDisplayed(1, false);
+
+        // Assert that the Tag value is empty on the Wizard window
+        Assert.assertEquals(processWizardPage.getTagKeyText(0), "",
+            "Tag Key Should be empty on the Wizard window");
+        Assert.assertEquals(processWizardPage.getTagValueText(0), "",
+            "Tag Value Should be empty on the Wizard window");
+    }
+
+    /* Step 2 tests */
+
+    /**
+     * Populate all fields with valid values (frequency, parallel, retry).
+     * Check that user can go to the next step.
+     * @throws Exception
+     */
+    @Test
+    public void testTimingStepDefaultScenario() throws Exception{
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+
+        processWizardPage.clickNext();
+        // Assert that user is able to go to next page
+        processWizardPage.isValidityStartDateDisplayed(true);
+    }
+
+    /**
+     * Populate fields with valid values (frequency, late arrival, availability flag, so on)
+     * Check that they are reflected on XML preview.
+     * @throws Exception
+     */
+    @Test
+    public void testTimingStepXmlPreview() throws Exception{
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+
+        // Get process from XML Preview
+        ProcessMerlin processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+
+        // Assert all the values entered on the Properties Page
+        LOGGER.info(String.format("Comparing source process: %n%s%n and preview: %n%s%n.", process, processFromXML));
+        process.assertPropertiesInfo(processFromXML);
+    }
+
+    /**
+     * Add some properties to the feed (frequency, late parallel). Click edit XML.
+     * Remove both properties from XML. Check that properties were removed from matching fields.
+     * Now click Edit XML again. Add timezone and order properties to the XML.
+     * Check that Timezone and order were enabled and set to values what we've populated in XML.
+     * @throws Exception
+     */
+    @Test
+    public void testTimingStepEditXml() throws Exception{
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set Frequency and Parallel values on the Properties Page
+        processWizardPage.setFrequencyQuantity("10");
+        processWizardPage.setFrequencyUnit("minutes");
+        processWizardPage.setMaxParallelInstances(5);
+
+        // Get process from XML Preview
+        ProcessMerlin processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+        processFromXML.setFrequency(null);
+        processFromXML.setParallel(1);
+
+        // Now click EditXML and set the updated XML here
+        processWizardPage.clickEditXml();
+        String xmlToString = processFromXML.toString();
+        processWizardPage.setProcessXml(xmlToString);
+        processWizardPage.clickEditXml();
+
+        // Assert Frequency and Parallel values
+        Assert.assertEquals(processWizardPage.getFrequencyQuantityText(), "",
+            "Frequency Quantity Should be empty on the Wizard window");
+        Assert.assertEquals(processWizardPage.getMaxParallelInstancesText(), "1",
+            "Unexpected Parallel on the Wizard window");
+
+        // Get process from XML Preview
+        processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+        // Set TimeZone and Order
+        TimeZone tz = TimeZone.getTimeZone("GMT-08:00");
+        processFromXML.setTimezone(tz);
+        processFromXML.setOrder(ExecutionType.LIFO);
+
+        // Now click EditXML and set the updated XML here
+        processWizardPage.clickEditXml();
+        xmlToString = processFromXML.toString();
+        processWizardPage.setProcessXml(xmlToString);
+        processWizardPage.clickEditXml();
+
+        // Assert TimeZone and Order
+        Assert.assertEquals(processWizardPage.getOrderText(), "LIFO",
+            "Unexpected Order on the Wizard window");
+        Assert.assertEquals(processWizardPage.getTimezoneText(), "GMT-08:00",
+            "Unexpected TimeZone on the Wizard window");
+    }
+
+    /**
+     * Check that timezone, frequency, parallel, order, retry policy,
+     * retry delay drop down lists contain correct items (time units etc.).
+     * @throws Exception
+     */
+    @Test
+    public void testTimingStepDropDownLists() throws Exception{
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Assert dropdown values
+        List<String> dropdownValues = processWizardPage.getTimezoneValues();
+        Assert.assertEquals(timeZones, dropdownValues, "TimeZone Values Are Not Equal");
+
+        dropdownValues = processWizardPage.getFrequencyUnitValues();
+        Assert.assertEquals(timeUnits, dropdownValues, "Frequency Unit Values Are Not Equal");
+
+        dropdownValues = processWizardPage.getMaxParallelInstancesValues();
+        Assert.assertEquals(parallel, dropdownValues, "Max Parallel Values Are Not Equal");
+
+        dropdownValues = processWizardPage.getOrderValues();
+        Assert.assertEquals(order, dropdownValues, "Order Unit Values Are Not Equal");
+
+        dropdownValues = processWizardPage.getRetryPolicyValues();
+        Assert.assertEquals(policy, dropdownValues, "Retry Policy Values Are Not Equal");
+
+        dropdownValues = processWizardPage.getRetryDelayUnitValues();
+        Assert.assertEquals(delayTimeUnits, dropdownValues, "Retry Delay Unit Values Are Not Equal");
+    }
+
+    /* Step 3 tests */
+
+    /**
+     * testClustersStepDefaultScenario
+     * Populate each field with correct values (name, validity ...). Check that
+     * user can go to the next step.
+     */
+    @Test
+    public void testClustersStepDefaultScenario()
+        throws URISyntaxException, IOException, AuthenticationException, InterruptedException, JAXBException {
+        bundles[0].submitClusters(cluster);
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+        processWizardPage.isAddInputButtonDisplayed(true);
+    }
+
+    /**
+     * Check that cluster drop down list contains correct list of items.
+     */
+    @Test
+    public void testClustersStepDropDownList()
+        throws URISyntaxException, IOException, AuthenticationException, InterruptedException, JAXBException {
+        //submit all clusters
+        List<String> clusters = new ArrayList<>();
+        ClusterMerlin clusterMerlin = bundles[0].getClusterElement();
+        String clusterName = clusterMerlin.getName();
+        for(int i = 1; i < 6; i++) {
+            clusterMerlin.setName(clusterName + i);
+            AssertUtil.assertSucceeded(cluster.getClusterHelper().submitEntity(clusterMerlin.toString()));
+            clusters.add(clusterMerlin.getName());
+        }
+        //go to clusters page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+        List<String> dropdownValues = new ArrayList<>(processWizardPage.getClustersFromDropDown());
+
+        //clean all clusters which belong to anything else then current test class
+        String testClassName = ProcessSetupTest.class.getSimpleName();
+        for(int i = 0; i < dropdownValues.size(); i++) {
+            if (!dropdownValues.get(i).contains(testClassName)) {
+                dropdownValues.remove(i);
+            }
+        }
+        Collections.sort(clusters);
+        Collections.sort(dropdownValues);
+        Assert.assertEquals(clusters, dropdownValues, "Clusters Drop Down Values Are Not Equal");
+    }
+
+    /**
+     * Click on validity start/end, check that pop up calendars have been shown.
+     */
+    @Test
+    public void testClustersStepPopupCalendars() {
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        //click on respective fields
+        processWizardPage.clickOnValidityStart();
+        processWizardPage.clickOnValidityEnd();
+    }
+
+    /**
+     * Click on add cluster. Check that new cluster block appears. Populate it with values
+     * and check that XML preview shows both clusters. Remove the cluster and check that XML
+     * has the only cluster.
+     */
+    @Test
+    public void testClustersStepAddDeleteCluster() throws Exception {
+        bundles[0].submitClusters(cluster);
+        //submit one extra cluster
+        ClusterMerlin clusterMerlin = bundles[0].getClusterElement();
+        clusterMerlin.setName(clusterMerlin.getName() + 1);
+        AssertUtil.assertSucceeded(cluster.getClusterHelper().submitEntity(clusterMerlin.toString()));
+
+        Cluster processCluster = new Cluster();
+        processCluster.setName(clusterMerlin.getName());
+        processCluster.setValidity(process.getClusters().getClusters().get(0).getValidity());
+        process.addProcessCluster(processCluster);
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Add clusters
+        processWizardPage.setClusters(process.getClusters());
+        ProcessMerlin xmlPreview = processWizardPage.getProcessMerlinFromProcessXml();
+
+        //compare clusters
+        LOGGER.info(String.format("Comparing clusters of process: %n%s%n and preview: %n%s%n.", process, xmlPreview));
+        ProcessMerlin.assertClustersEqual(process.getClusters().getClusters(), xmlPreview.getClusters().getClusters());
+
+        //delete one cluster and repeat the check
+        processWizardPage.deleteLastCluster();
+        xmlPreview = processWizardPage.getProcessMerlinFromProcessXml();
+        process.getClusters().getClusters().remove(1);
+
+        //compare clusters
+        LOGGER.info(String.format("Comparing clusters of process: %n%s%n and preview: %n%s%n.", process, xmlPreview));
+        ProcessMerlin.assertClustersEqual(process.getClusters().getClusters(), xmlPreview.getClusters().getClusters());
+    }
+
+    /**
+     * Populate all fields with valid values and check that they are reflected on XML
+     * preview. Click edit XML. Change validity and cluster name. Check that changes
+     * are reflected on XML as well as on wizard page. Edit xml again. Add new cluster
+     * and check that new cluster block has been added on wizard page.
+     */
+    @Test
+    public void testClusterStepEditXml() throws Exception {
+        bundles[0].submitClusters(cluster);
+        ClusterMerlin clusterMerlin = bundles[0].getClusterElement();
+        String firstClusterName = clusterMerlin.getName();
+        clusterMerlin.setName(firstClusterName + 2);
+        AssertUtil.assertSucceeded(cluster.getClusterHelper().submitEntity(clusterMerlin.toString()));
+
+        //set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        //set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        //set cluster
+        processWizardPage.setClusters(process.getClusters());
+
+        //compare preview and source data
+        ProcessMerlin xmlPreview = processWizardPage.getProcessMerlinFromProcessXml();
+        LOGGER.info(String.format("Comparing clusters of process: %n%s%n and preview: %n%s%n.", process, xmlPreview));
+        ProcessMerlin.assertClustersEqual(process.getClusters().getClusters(), xmlPreview.getClusters().getClusters());
+
+        //change validity and name and push it to xmlPreview
+        Date date = new Date();
+        xmlPreview.getClusters().getClusters().get(0).getValidity().setEnd(date);
+        xmlPreview.getClusters().getClusters().get(0).setName(clusterMerlin.getName());
+        processWizardPage.clickEditXml();
+        processWizardPage.setProcessXml(xmlPreview.toString());
+        processWizardPage.clickEditXml();
+
+        //check that validity end is changed on wizard
+        String endUI = processWizardPage.getValidityEnd();
+        SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm");
+        format.setTimeZone(TimeZone.getTimeZone("UTC"));
+        String endSource = format.format(date);
+        Assert.assertEquals(endUI, endSource, "Validity end should be updated on wizard.");
+        Assert.assertEquals(processWizardPage.getClusterName(0), clusterMerlin.getName(),
+            "Cluster name should be updated on wizard.");
+
+        //add cluster to process preview
+        int initCount = processWizardPage.getWizardClusterCount();
+        Cluster processCluster = new Cluster();
+        process = new ProcessMerlin(xmlPreview);
+        processCluster.setName(firstClusterName);
+        processCluster.setValidity(xmlPreview.getClusters().getClusters().get(0).getValidity());
+        process.addProcessCluster(processCluster);
+        processWizardPage.clickEditXml();
+        processWizardPage.setProcessXml(xmlPreview.toString());
+        processWizardPage.clickEditXml();
+
+        //check that changes are reflected on wizard
+        int finalCount = processWizardPage.getWizardClusterCount();
+        Assert.assertEquals(finalCount - initCount, 1, "Cluster should have been added to wizard.");
+        Assert.assertEquals(processWizardPage.getClusterName(1), firstClusterName,
+            "Cluster name should be updated on wizard.");
+    }
+
+    /* Step 4 tests */
+
+    /**
+     * Add input and output. Populate each field with correct values(name, feed, instance ...).
+     * Check that user can go to the next step.
+     * @throws Exception
+     */
+    @Test
+    public void testInOutStepDefaultScenario() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Input Output Page
+        processWizardPage.setInputOutputInfo(process);
+        processWizardPage.clickNext();
+
+        // Assert that user is able to go on the next page
+        processWizardPage.isSaveButtonDisplayed(true);
+
+
+    }
+
+    /**
+     * Check that user is allowed to go to the next page without adding inputs/outputs.
+     * @throws Exception
+     */
+    @Test
+    public void testInOutStepWithoutInOuts() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Do not set values on the Input Output Page
+        processWizardPage.clickNext();
+
+        // Assert that user is able to go on the next page
+        processWizardPage.isSaveButtonDisplayed(true);
+
+    }
+
+    /**
+     * Add input. Set instance with start time after end.
+     * Check that user is not allowed to go to the next step and has been notified with an alert.
+     * Check the same for invalid EL expression.
+     * @throws Exception
+     */
+    @Test
+    public void testInOutInvalidInstance() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set start date after end date in Input
+        process.getInputs().getInputs().get(0).setStart("now(0, 0)");
+        process.getInputs().getInputs().get(0).setEnd("now(0, -5)");
+
+        // Set Input Values on the Input Output Page
+        processWizardPage.setInputInfo(process.getInputs());
+        processWizardPage.clickNext();
+
+        // Assert User should not be allowed to go on the next page
+        processWizardPage.isSaveButtonDisplayed(false);
+
+        // Delete the current Input
+        processWizardPage.clickDeleteInput();
+
+        // Set invalid EL expression
+        process.getInputs().getInputs().get(0).setStart("bad(0, 0)");
+        process.getInputs().getInputs().get(0).setEnd("bad(0, 0)");
+
+        // Set new Input Values on the Input Output Page
+        processWizardPage.setInputInfo(process.getInputs());
+        processWizardPage.clickNext();
+
+        // Assert User should not be allowed to go on the next page
+        processWizardPage.isSaveButtonDisplayed(false);
+    }
+
+    /**
+     * Check that input/output feed drop down list contains correct list of feeds.
+     * @throws Exception
+     */
+    @Test
+    public void testInOutStepDropDownFeeds() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Click Add Input and Output Buttons
+        processWizardPage.clickAddInput();
+        processWizardPage.clickAddOutput();
+
+        List<String> expectedDropdownValues = new ArrayList<>();
+        expectedDropdownValues.add("-Select feed-");
+        expectedDropdownValues.add(process.getInputs().getInputs().get(0).getFeed());
+        expectedDropdownValues.add(process.getOutputs().getOutputs().get(0).getFeed());
+        Collections.sort(expectedDropdownValues);
+
+        // Assert Input and Output Feed Dropdown values
+        List<String> actualDropdownValues = processWizardPage.getInputValues(0);
+        Collections.sort(actualDropdownValues);
+        Assert.assertEquals(expectedDropdownValues, actualDropdownValues,
+            "Input Feed Dropdown Values Are Not Equal");
+
+        actualDropdownValues = processWizardPage.getOutputValues(0);
+        Assert.assertEquals(expectedDropdownValues, actualDropdownValues,
+            "Output Feed Dropdown Values Are Not Equal");
+    }
+
+    /**
+     * Add input. Check that it has been added to wizard as well as to XML preview.
+     * Click edit xml. Change input name and add output in xml.
+     * Check that output has been added on wizard page and input name has been changed as well.
+     * @throws Exception
+     */
+    @Test
+    public void testInOutStepPreviewEditXml() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set Input Values on the Input Output Page
+        processWizardPage.setInputInfo(process.getInputs());
+
+        // Assert Input values on Wizard
+        Assert.assertEquals(processWizardPage.getInputNameText(0), process.getInputs().getInputs().get(0).getName(),
+            "Unexpected Input Name on the Wizard window");
+        Assert.assertEquals(processWizardPage.getInputFeedText(0), process.getInputs().getInputs().get(0).getFeed(),
+            "Unexpected Input Feed on the Wizard window");
+        Assert.assertEquals(processWizardPage.getInputStartText(0), process.getInputs().getInputs().get(0).getStart(),
+            "Unexpected Input Start on the Wizard window");
+        Assert.assertEquals(processWizardPage.getInputEndText(0), process.getInputs().getInputs().get(0).getEnd(),
+            "Unexpected Input End on the Wizard window");
+
+        // Get process from XML Preview
+        ProcessMerlin processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+
+        // Assert Input values on the XML Preview
+        LOGGER.info(String.format("Comparing source process: %n%s%n and preview: %n%s%n.", process, processFromXML));
+        process.assertInputValues(processFromXML);
+
+        // Change Input Name and Set Output in the XML
+        processFromXML.getInputs().getInputs().get(0).setName("newInputData");
+        processFromXML.setOutputs(process.getOutputs());
+
+        // Now click EditXML and set the updated XML here
+        processWizardPage.clickEditXml();
+        String xmlToString = processFromXML.toString();
+        processWizardPage.setProcessXml(xmlToString);
+        processWizardPage.clickEditXml();
+
+        // Assert Input Name and Output values on Wizard
+        Assert.assertEquals(processWizardPage.getInputNameText(0), "newInputData",
+            "Unexpected Input Name on the Wizard window");
+        Assert.assertEquals(processWizardPage.getOutputNameText(0),
+            process.getOutputs().getOutputs().get(0).getName(),
+            "Unexpected Output Name on the Wizard window");
+        Assert.assertEquals(processWizardPage.getOutputFeedText(0),
+            process.getOutputs().getOutputs().get(0).getFeed(),
+            "Unexpected Output Feed on the Wizard window");
+        Assert.assertEquals(processWizardPage.getOutputInstanceText(0),
+            process.getOutputs().getOutputs().get(0).getInstance(),
+            "Unexpected Output Instance on the Wizard window");
+    }
+
+    /**
+     * Add input. Check that it has been added to wizard as well to XML.
+     * Delete it. Check that it has been removed from wizard as well as from XML.
+     * Repeat the same for the output.
+     * @throws Exception
+     */
+    @Test
+    public void testInOutStepAddDeleteInOut() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set Input Values on the Input Output Page
+        processWizardPage.setInputInfo(process.getInputs());
+
+        // Assert Input values on Wizard
+        Assert.assertEquals(processWizardPage.getInputNameText(0), process.getInputs().getInputs().get(0).getName(),
+            "Unexpected Input Name on the Wizard window");
+        Assert.assertEquals(processWizardPage.getInputFeedText(0), process.getInputs().getInputs().get(0).getFeed(),
+            "Unexpected Input Feed on the Wizard window");
+        Assert.assertEquals(processWizardPage.getInputStartText(0), process.getInputs().getInputs().get(0).getStart(),
+            "Unexpected Input Start on the Wizard window");
+        Assert.assertEquals(processWizardPage.getInputEndText(0), process.getInputs().getInputs().get(0).getEnd(),
+            "Unexpected Input End on the Wizard window");
+
+        // Get process from XML Preview
+        ProcessMerlin processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+
+        // Assert Input values on the XML Preview
+        LOGGER.info(String.format("Comparing source process: %n%s%n and preview: %n%s%n.", process, processFromXML));
+        process.assertInputValues(processFromXML);
+
+        // Delete the input
+        processWizardPage.clickDeleteInput();
+
+        // Assert on the click of delete Input the Input is deleted
+        processWizardPage.isInputNameDisplayed(0, false);
+
+        // Set Output Values on the Input Output Page
+        processWizardPage.setOutputInfo(process.getOutputs());
+
+        // Assert Output values on Wizard
+        Assert.assertEquals(processWizardPage.getOutputNameText(0),
+            process.getOutputs().getOutputs().get(0).getName(),
+            "Unexpected Output Name on the Wizard window");
+        Assert.assertEquals(processWizardPage.getOutputFeedText(0),
+            process.getOutputs().getOutputs().get(0).getFeed(),
+            "Unexpected Output Feed on the Wizard window");
+        Assert.assertEquals(processWizardPage.getOutputInstanceText(0),
+            process.getOutputs().getOutputs().get(0).getInstance(),
+            "Unexpected Output Instance on the Wizard window");
+
+        // Get process from XML Preview
+        processFromXML = processWizardPage.getProcessMerlinFromProcessXml();
+
+        // Assert Output values on the XML Preview
+        LOGGER.info(String.format("Comparing source process : %n%s%n and preview: %n%s%n.", process, processFromXML));
+        process.assertOutputValues(processFromXML);
+
+        // Delete the output
+        processWizardPage.clickDeleteOutput();
+
+        // Assert on the click of delete Input the Input is deleted
+        processWizardPage.isOutputNameDisplayed(0, false);
+    }
+
+    /* Step 5 tests */
+
+    /**
+     * Create process. Using API check that process was created.
+     * @throws Exception
+     */
+    @Test
+    public void testSummaryStepDefaultScenario() throws Exception{
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Input Output Page
+        processWizardPage.setInputOutputInfo(process);
+        processWizardPage.clickNext();
+
+        // Save the Process
+        processWizardPage.clickSave();
+
+        // Assert the response using API to validate if the feed creation went successfully
+        ServiceResponse response = prism.getProcessHelper().getEntityDefinition(process.toString());
+        AssertUtil.assertSucceeded(response);
     }
 
+    /**
+     * Go through all properties which are shown on page. Check that they are equal to
+     * those which were populated in previous steps.
+     */
+    @Test
+    public void testSummaryStepAllProperties()
+        throws URISyntaxException, IOException, AuthenticationException, InterruptedException, JAXBException {
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        process.setTags("first=yes,second=yes,third=no");
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Input Output Page
+        processWizardPage.setInputOutputInfo(process);
+        processWizardPage.clickNext();
+
+        //get process from summary and compare it with the source
+        ProcessMerlin summaryProcess = ProcessMerlin.getEmptyProcess(process);
+        summaryProcess = processWizardPage.getProcessFromSummaryBox(summaryProcess);
+        summaryProcess.assertEquals(process);
+    }
+
+    /**
+     * Check that all properties which are shown on page are equal to those which are
+     * shown on XML Preview. Click Edit XML. Add new input. Check that it has been
+     * added on wizard.
+     */
+    @Test
+    public void testSummaryStepEditXml() throws Exception {
+
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        process.setTags("first=yes,second=yes,third=no");
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Input Output Page
+        processWizardPage.setInputOutputInfo(process);
+        processWizardPage.clickNext();
+
+        //get process from summary and from xml and compare them
+        ProcessMerlin summaryProcess = ProcessMerlin.getEmptyProcess(process);
+        summaryProcess = processWizardPage.getProcessFromSummaryBox(summaryProcess);
+        ProcessMerlin previewProcess = processWizardPage.getProcessMerlinFromProcessXml();
+        summaryProcess.assertEquals(previewProcess);
+
+        //add input to preview cluster
+        Input oldInput = previewProcess.getInputs().getInputs().get(0);
+        Input newInput = new Input();
+        newInput.setFeed(oldInput.getFeed());
+        newInput.setName("newInput");
+        newInput.setStart("now(-40, 0)");
+        newInput.setEnd("now(20, 0)");
+        previewProcess.getInputs().getInputs().add(newInput);
+
+        //push new process to xml preview
+        processWizardPage.clickEditXml();
+        processWizardPage.setProcessXml(previewProcess.toString());
+        processWizardPage.clickEditXml();
+
+        //get process from summary and check that new input is available
+        summaryProcess = processWizardPage.getProcessFromSummaryBox(ProcessMerlin.getEmptyProcess(summaryProcess));
+        LOGGER.info(String.format("Comparing summary : %n%s%n and preview: %n%s%n.", summaryProcess, previewProcess));
+        summaryProcess.assertInputValues(previewProcess);
+    }
+
+    /**
+     * Click on EditXML. Break xml. Check that entity is
+     * not accepted and preview xml is being reverted to previous state.
+     */
+    @Test
+    public void testSummaryStepEditXmlInvalidChanges()
+        throws Exception {
+        bundles[0].submitClusters(cluster);
+        bundles[0].submitFeeds(prism);
+
+        bundles[0].getInputFeedNameFromBundle();
+
+        process.setTags("first=yes,second=yes,third=no");
+        // Set values on the General Info Page
+        processWizardPage.setProcessGeneralInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Properties Page
+        processWizardPage.setProcessPropertiesInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Cluster Info Page
+        processWizardPage.setProcessClustersInfo(process);
+        processWizardPage.clickNext();
+
+        // Set values on the Input Output Page
+        processWizardPage.setInputOutputInfo(process);
+        processWizardPage.clickNext();
+
+        //get process from xml preview
+        ProcessMerlin previewProcess1 = processWizardPage.getProcessMerlinFromProcessXml();
+        String processString = previewProcess1.toString();
+
+        //damage the xml and populate it back to preview
+        processString = processString.substring(0, processString.length() - 3);
+        processWizardPage.clickEditXml();
+        processWizardPage.setProcessXml(processString);
+        processWizardPage.clickEditXml();
+
+        //get xml preview and compare with initial state
+        ProcessMerlin previewProcess2 = processWizardPage.getProcessMerlinFromProcessXml();
+        previewProcess2.assertEquals(previewProcess1);
+    }
 }


[2/2] falcon git commit: FALCON-1249 Tests for process setup wizard conributed by Namit Maheshwari and Paul Isaychuk

Posted by ra...@apache.org.
FALCON-1249 Tests for process setup wizard conributed by Namit Maheshwari and Paul Isaychuk


Project: http://git-wip-us.apache.org/repos/asf/falcon/repo
Commit: http://git-wip-us.apache.org/repos/asf/falcon/commit/ff9c78e3
Tree: http://git-wip-us.apache.org/repos/asf/falcon/tree/ff9c78e3
Diff: http://git-wip-us.apache.org/repos/asf/falcon/diff/ff9c78e3

Branch: refs/heads/master
Commit: ff9c78e3c82e45a4e3543924fffe834773dc3880
Parents: 8ee6d45
Author: Raghav Kumar Gautam <ra...@apache.org>
Authored: Thu Jun 4 11:51:00 2015 -0700
Committer: Raghav Kumar Gautam <ra...@apache.org>
Committed: Thu Jun 4 11:52:46 2015 -0700

----------------------------------------------------------------------
 falcon-regression/CHANGES.txt                   |   2 +
 .../regression/Entities/ProcessMerlin.java      | 178 +++-
 .../ui/search/AbstractSearchPage.java           |   1 +
 .../regression/ui/search/ProcessWizardPage.java | 806 ++++++++++++++-
 .../regression/searchUI/ProcessSetupTest.java   | 982 ++++++++++++++++++-
 5 files changed, 1919 insertions(+), 50 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/falcon/blob/ff9c78e3/falcon-regression/CHANGES.txt
----------------------------------------------------------------------
diff --git a/falcon-regression/CHANGES.txt b/falcon-regression/CHANGES.txt
index d9af012..46799e8 100644
--- a/falcon-regression/CHANGES.txt
+++ b/falcon-regression/CHANGES.txt
@@ -5,6 +5,8 @@ Trunk (Unreleased)
   INCOMPATIBLE CHANGES
 
   NEW FEATURES
+   FALCON-1249 Tests for process setup wizard (Namit Maheshwari and Paul Isaychuk)
+
    FALCON-1242 Search UI test for entity upload button (Namit Maheshwari)
 
    FALCON-1222 Feed Wizard multiple tests (Namit Maheshwari)

http://git-wip-us.apache.org/repos/asf/falcon/blob/ff9c78e3/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 9e3d2d7..615587d 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
@@ -38,16 +38,22 @@ import org.apache.falcon.entity.v0.process.Property;
 import org.apache.falcon.entity.v0.process.Validity;
 import org.apache.falcon.entity.v0.process.Workflow;
 import org.apache.falcon.regression.core.util.TimeUtil;
+import org.apache.falcon.regression.core.util.Util;
+import org.apache.log4j.Logger;
 import org.testng.Assert;
+import org.testng.asserts.SoftAssert;
 
 import javax.xml.bind.JAXBException;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 /** Class for representing a process xml. */
 public class ProcessMerlin extends Process {
+    private static final Logger LOGGER = Logger.getLogger(ProcessMerlin.class);
     public ProcessMerlin(String processData) {
         this((Process) TestEntityUtil.fromString(EntityType.PROCESS, processData));
     }
@@ -87,6 +93,34 @@ public class ProcessMerlin extends Process {
         return null;
     }
 
+    /**
+     * Compares two process cluster lists, if they are equal or not.
+     */
+    public static void assertClustersEqual(List<Cluster> clusters1, List<Cluster> clusters2) {
+        if (clusters1.size() != clusters2.size()) {
+            Assert.fail("Cluster sizes are different.");
+        }
+        Comparator<Cluster> clusterComparator = new Comparator<Cluster>() {
+            @Override
+            public int compare(Cluster cluster1, Cluster cluster2) {
+                return cluster1.getName().compareTo(cluster2.getName());
+            }
+        };
+        Collections.sort(clusters1, clusterComparator);
+        Collections.sort(clusters2, clusterComparator);
+        SoftAssert softAssert = new SoftAssert();
+        for(int i = 0; i < clusters1.size(); i++) {
+            Cluster cluster1 = clusters1.get(i);
+            Cluster cluster2 = clusters2.get(i);
+            softAssert.assertEquals(cluster1.getName(), cluster2.getName(), "Cluster names are different.");
+            softAssert.assertEquals(cluster1.getValidity().getStart(), cluster2.getValidity().getStart(),
+                String.format("Validity start is not the same for cluster %s", cluster1.getName()));
+            softAssert.assertEquals(cluster1.getValidity().getEnd(), cluster2.getValidity().getEnd(),
+                String.format("Cluster validity end is not the same for cluster %s", cluster1.getName()));
+        }
+        softAssert.assertAll();
+    }
+
     public Input getInputByName(String name) {
         for (Input input : getInputs().getInputs()) {
             if (input.getName().equals(name)) {
@@ -332,8 +366,6 @@ public class ProcessMerlin extends Process {
         getOutputs().getOutputs().add(out2);
     }
 
-
-
     /**
      * Adds one input into process.
      */
@@ -349,7 +381,6 @@ public class ProcessMerlin extends Process {
         getInputs().getInputs().add(in2);
     }
 
-
     public void setInputFeedWithEl(String inputFeedName, String startEl, String endEl) {
         Inputs inputs = new Inputs();
         Input input = new Input();
@@ -388,7 +419,6 @@ public class ProcessMerlin extends Process {
         this.setOutputs(outputs);
     }
 
-
     /**
      * Sets partition for each input, according to number of supplied partitions.
      *
@@ -432,8 +462,6 @@ public class ProcessMerlin extends Process {
         this.setTimeout(frq);
     }
 
-
-
     public void setWorkflow(String wfPath, String libPath, EngineType engineType) {
         Workflow w = this.getWorkflow();
         if (engineType != null) {
@@ -455,6 +483,144 @@ public class ProcessMerlin extends Process {
         return EntityType.PROCESS;
     }
 
+    public void assertGeneralProperties(ProcessMerlin newProcess){
+        SoftAssert softAssert = new SoftAssert();
+        // Assert all the the General Properties
+        softAssert.assertEquals(newProcess.getName(), getName(),
+            "Process Name is different");
+        softAssert.assertEquals(newProcess.getTags(), getTags(),
+            "Process Tags Value is different");
+        softAssert.assertEquals(newProcess.getWorkflow().getName(), getWorkflow().getName(),
+            "Process Workflow Name is different");
+        if (getWorkflow().getEngine() == EngineType.OOZIE || getWorkflow().getEngine() == null) {
+            softAssert.assertTrue(newProcess.getWorkflow().getEngine() == EngineType.OOZIE
+                || newProcess.getWorkflow().getEngine() == null, "Process Workflow Engine is different");
+        } else {
+            softAssert.assertEquals(newProcess.getWorkflow().getEngine().toString(),
+                getWorkflow().getEngine().toString(),
+                "Process Workflow Engine is different");
+        }
+        softAssert.assertEquals(newProcess.getWorkflow().getPath(), getWorkflow().getPath(),
+            "Process Workflow Path is different");
+        softAssert.assertEquals(newProcess.getACL().getOwner(), getACL().getOwner(),
+            "Process ACL Owner is different");
+        softAssert.assertEquals(newProcess.getACL().getGroup(), getACL().getGroup(),
+            "Process ACL Group is different");
+        softAssert.assertEquals(newProcess.getACL().getPermission(), getACL().getPermission(),
+            "Process ACL Permission is different");
+        softAssert.assertAll();
+    }
+
+    public void assertPropertiesInfo(ProcessMerlin newProcess){
+        SoftAssert softAssert = new SoftAssert();
+        // Assert all the Properties Info
+        softAssert.assertEquals(newProcess.getTimezone().getID(), getTimezone().getID(),
+            "Process TimeZone is different");
+        softAssert.assertEquals(newProcess.getFrequency().getFrequency(), getFrequency().getFrequency(),
+            "Process Frequency is different");
+        softAssert.assertEquals(newProcess.getFrequency().getTimeUnit().toString(),
+            getFrequency().getTimeUnit().toString(),
+            "Process Frequency Unit is different");
+        softAssert.assertEquals(newProcess.getParallel(), getParallel(),
+            "Process Parallel is different");
+        softAssert.assertEquals(newProcess.getOrder(), getOrder(),
+            "Process Order is different");
+        softAssert.assertEquals(newProcess.getRetry().getPolicy().value(),
+            getRetry().getPolicy().value(),
+            "Process Retry Policy is different");
+        softAssert.assertEquals(newProcess.getRetry().getAttempts(),
+            getRetry().getAttempts(),
+            "Process Retry Attempts is different");
+        softAssert.assertEquals(newProcess.getRetry().getDelay().getFrequency(),
+            getRetry().getDelay().getFrequency(),
+            "Process Delay Frequency is different");
+        softAssert.assertEquals(newProcess.getRetry().getDelay().getTimeUnit().name(),
+            getRetry().getDelay().getTimeUnit().name(),
+            "Process Delay Unit is different");
+        softAssert.assertAll();
+    }
+
+    /**
+     * Asserts equality of process inputs.
+     */
+    public void assertInputValues(ProcessMerlin newProcess){
+        Assert.assertEquals(newProcess.getInputs().getInputs().size(), getInputs().getInputs().size(),
+            "Processes have different number of inputs.");
+        SoftAssert softAssert = new SoftAssert();
+        // Assert all the Input values
+        for (int i = 0; i < newProcess.getInputs().getInputs().size(); i++) {
+            softAssert.assertEquals(newProcess.getInputs().getInputs().get(i).getName(),
+                getInputs().getInputs().get(i).getName(),
+                "Process Input Name is different");
+            softAssert.assertEquals(newProcess.getInputs().getInputs().get(i).getFeed(),
+                getInputs().getInputs().get(i).getFeed(),
+                "Process Input Feed is different");
+            softAssert.assertEquals(newProcess.getInputs().getInputs().get(i).getStart(),
+                getInputs().getInputs().get(i).getStart(),
+                "Process Input Start is different");
+            softAssert.assertEquals(newProcess.getInputs().getInputs().get(i).getEnd(),
+                getInputs().getInputs().get(i).getEnd(),
+                "Process Input End is different");
+        }
+        softAssert.assertAll();
+    }
+
+    /**
+     * Asserts equality of process outputs.
+     */
+    public void assertOutputValues(ProcessMerlin newProcess){
+        SoftAssert softAssert = new SoftAssert();
+        // Assert all the Output values
+        softAssert.assertEquals(newProcess.getOutputs().getOutputs().get(0).getName(),
+            getOutputs().getOutputs().get(0).getName(),
+            "Process Output Name is different");
+        softAssert.assertEquals(newProcess.getOutputs().getOutputs().get(0).getFeed(),
+            getOutputs().getOutputs().get(0).getFeed(),
+            "Process Output Feed is different");
+        softAssert.assertEquals(newProcess.getOutputs().getOutputs().get(0).getInstance(),
+            getOutputs().getOutputs().get(0).getInstance(),
+            "Process Output Instance is different");
+        softAssert.assertAll();
+    }
+
+    /**
+     * Asserts equality of two processes.
+     */
+    public void assertEquals(ProcessMerlin process) {
+        LOGGER.info(String.format("Comparing General Properties: source: %n%s%n and process: %n%n%s",
+            Util.prettyPrintXml(toString()), Util.prettyPrintXml(process.toString())));
+        assertGeneralProperties(process);
+        assertInputValues(process);
+        assertOutputValues(process);
+        assertPropertiesInfo(process);
+        assertClustersEqual(getClusters().getClusters(), process.getClusters().getClusters());
+    }
+
+    /**
+     * Creates an empty process definition.
+     */
+    public static ProcessMerlin getEmptyProcess(ProcessMerlin process) {
+        ProcessMerlin draft = new ProcessMerlin(process.toString());
+        draft.setName("");
+        draft.setTags("");
+        draft.setACL(null);
+        draft.getInputs().getInputs().clear();
+        draft.getOutputs().getOutputs().clear();
+        draft.setRetry(null);
+        draft.clearProcessCluster();
+        draft.getProperties().getProperties().clear();
+        draft.setFrequency(null);
+        draft.setOrder(null);
+        draft.setTimezone(null);
+        draft.setParallel(0);
+        Workflow workflow = new Workflow();
+        workflow.setName(null);
+        workflow.setPath(null);
+        workflow.setVersion(null);
+        workflow.setEngine(null);
+        draft.setWorkflow(null, null, null);
+        return draft;
+    }
 }
 
 

http://git-wip-us.apache.org/repos/asf/falcon/blob/ff9c78e3/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 7c1f7ad..a1e3e2e 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
@@ -33,6 +33,7 @@ import org.openqa.selenium.support.ui.Select;
 import java.util.ArrayList;
 import java.util.List;
 
+
 /** Parent page object for all the search ui pages. */
 public abstract class AbstractSearchPage extends Page {
 

http://git-wip-us.apache.org/repos/asf/falcon/blob/ff9c78e3/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/ProcessWizardPage.java
----------------------------------------------------------------------
diff --git a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/ProcessWizardPage.java b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/ProcessWizardPage.java
index e429a60..e796ba0 100644
--- a/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/ProcessWizardPage.java
+++ b/falcon-regression/merlin/src/main/java/org/apache/falcon/regression/ui/search/ProcessWizardPage.java
@@ -20,33 +20,79 @@ package org.apache.falcon.regression.ui.search;
 
 import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
+import org.apache.falcon.entity.v0.Frequency;
 import org.apache.falcon.entity.v0.process.ACL;
+import org.apache.falcon.entity.v0.process.Cluster;
+import org.apache.falcon.entity.v0.process.Clusters;
+import org.apache.falcon.entity.v0.process.EngineType;
+import org.apache.falcon.entity.v0.process.ExecutionType;
+import org.apache.falcon.entity.v0.process.Input;
+import org.apache.falcon.entity.v0.process.Inputs;
+import org.apache.falcon.entity.v0.process.Output;
+import org.apache.falcon.entity.v0.process.Outputs;
+import org.apache.falcon.entity.v0.process.PolicyType;
+import org.apache.falcon.entity.v0.process.Retry;
 import org.apache.falcon.entity.v0.process.Workflow;
+import org.apache.falcon.entity.v0.process.Validity;
 import org.apache.falcon.regression.Entities.ProcessMerlin;
 import org.apache.falcon.regression.core.util.UIAssert;
+import org.apache.log4j.Logger;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
 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.ExpectedConditions;
 import org.openqa.selenium.support.ui.Select;
-import org.openqa.selenium.support.ui.WebDriverWait;
 import org.testng.Assert;
 
-import java.util.Arrays;
+import java.text.SimpleDateFormat;
 import java.util.List;
 import java.util.TimeZone;
 
 /** Page object of the Process creation page. */
 public class ProcessWizardPage extends AbstractSearchPage {
 
+    private static final Logger LOGGER = Logger.getLogger(ProcessWizardPage.class);
+
     @FindBys({
         @FindBy(className = "mainUIView"),
         @FindBy(className = "entityForm")
     })
     private WebElement processBox;
 
+    @FindBy(xpath = "//textarea[@ng-model='prettyXml']")
+    private WebElement processXml;
+
+    @FindBy(xpath = "//form[@name='processForm']/div[1]")
+    private WebElement summaryBox;
+
+    @FindBys({
+        @FindBy(className = "mainUIView"),
+        @FindBy(className = "entityForm"),
+        @FindBy(className = "nextBtn")
+    })
+    private WebElement nextButton;
+
+    @FindBys({
+        @FindBy(className = "mainUIView"),
+        @FindBy(className = "entityForm"),
+        @FindBy(className = "prevBtn")
+    })
+    private WebElement previousButton;
+
+    @FindBys({
+        @FindBy(id = "editXmlButton")
+    })
+    private WebElement editXmlButton;
+
+    @FindBy(xpath = "//a[contains(.,'Cancel')]")
+    private WebElement cancelButton;
+
+    @FindBy(xpath = "//div[contains(@class,'formBoxContainer')]")
+    private WebElement formBox;
+
     public ProcessWizardPage(WebDriver driver) {
         super(driver);
     }
@@ -56,15 +102,40 @@ public class ProcessWizardPage extends AbstractSearchPage {
         UIAssert.assertDisplayed(processBox, "Process box");
     }
 
-    private WebElement getNextButton() {
-        return driver.findElement(By.id("nextButton"));
+    /**
+     * Completes step 1 and clicks next.
+     */
+    public void goToPropertiesStep(ProcessMerlin process) {
+        setProcessGeneralInfo(process);
+        clickNext();
+
+    }
+
+    public void goToClustersStep(ProcessMerlin process) {
+        goToPropertiesStep(process);
+
+        setProcessPropertiesInfo(process);
+        clickNext();
+    }
+
+    public void clickNext() {
+        nextButton.click();
     }
 
-    public void pressNext() {
-        getNextButton().click();
+    public void clickPrevious(){
+        previousButton.click();
     }
 
-    /*----- Step1 elements & operations ----*/
+    public void clickCancel(){
+        cancelButton.click();
+    }
+
+    public void clickEditXml(){
+        editXmlButton.click();
+    }
+
+    /*----- Step1 General info ----*/
+
     private WebElement getName() {
         return driver.findElement(By.id("entityNameField"));
     }
@@ -104,28 +175,51 @@ public class ProcessWizardPage extends AbstractSearchPage {
         }
     }
 
-    public void setTags(List<String> tags) {
-        deleteTags();
-        //create enough number of tag fields
-        final int numTags = tags.size();
-        for (int i = 0; i < numTags - 1; i++) {
-            getAddTagButton().click();
+    private WebElement getTagKey(int index) {
+        return processBox.findElements(By.xpath("//input[@ng-model='tag.key']")).get(index);
+    }
+    private WebElement getTagValue(int index) {
+        return processBox.findElements(By.xpath("//input[@ng-model='tag.value']")).get(index);
+    }
+
+    public void setTagKey(int index, String tagKey){
+        getTagKey(index).sendKeys(tagKey);
+    }
+    public void setTagValue(int index, String tagValue){
+        getTagValue(index).sendKeys(tagValue);
+    }
+
+    public void setTags(String tagsStr){
+        if (StringUtils.isEmpty(tagsStr)){
+            return;
         }
-        final List<WebElement> tagTextFields = getTagTextFields();
-        Assert.assertEquals(tagTextFields.size() % 2, 0,
-            "Number of text fields for tags should be even, found: " + tagTextFields.size());
-        for (int i = 0; i < (tagTextFields.size() / 2); i++) {
-            final String oneTag = tags.get(i);
-            final String[] tagParts = oneTag.split("=");
-            Assert.assertEquals(tagParts.length, 2,
-                "Each tag is expected to be of form key=value, found: " + oneTag);
-            String key = tagParts[0];
-            String val = tagParts[1];
-            tagTextFields.get(2 * i).sendKeys(key);
-            tagTextFields.get(2 * i + 1).sendKeys(val);
+        String[] tags = tagsStr.split(",");
+        for (int i = 0; i < tags.length; i++){
+            String[] keyValue = tags[i].split("=");
+            setTagKey(i, keyValue[0]);
+            setTagValue(i, keyValue[1]);
+            if (tags.length > i + 1){
+                getAddTagButton().click();
+            }
         }
     }
 
+    public String getTagKeyText(int index){
+        return getTagKey(index).getAttribute("value");
+    }
+
+    public String getTagValueText(int index){
+        return getTagValue(index).getAttribute("value");
+    }
+
+    public boolean isPigRadioSelected(){
+        return getPigRadio().isSelected();
+    }
+
+    public String getEngineVersionText(){
+        return getEngineVersion().getFirstSelectedOption().getAttribute("value");
+    }
+
     private WebElement getWfName() {
         return driver.findElement(By.id("workflowNameField"));
     }
@@ -168,6 +262,7 @@ public class ProcessWizardPage extends AbstractSearchPage {
             Assert.fail("Unexpected workflow engine: " + processWf.getEngine());
         }
         final String version = processWf.getVersion();
+        // The getVersion() method returns '1.0' if its null, hence the hack below
         if (StringUtils.isNotEmpty(version) && !version.equals("1.0")) {
             getEngineVersion().selectByVisibleText(version);
         }
@@ -200,24 +295,665 @@ public class ProcessWizardPage extends AbstractSearchPage {
         aclPerm.sendKeys(acl.getPermission());
     }
 
-    public void doStep1(ProcessMerlin process) {
+    public void setProcessGeneralInfo(ProcessMerlin process) {
         setName(process.getName());
         final String tags = StringUtils.trimToEmpty(process.getTags());
-        setTags(Arrays.asList(tags.split(",")));
+        setTags(tags);
         setWorkflow(process.getWorkflow());
         setAcl(process.getACL());
-        final WebElement step1Element = getName();
-        pressNext();
-        new WebDriverWait(driver, AbstractSearchPage.PAGELOAD_TIMEOUT_THRESHOLD).until(
-            ExpectedConditions.stalenessOf(step1Element));
     }
 
-    /*----- Step2 elements & operations ----*/
+    public void isFrequencyQuantityDisplayed(boolean isDisplayed) {
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getFrequencyQuantity(), "Frequency Quantity");
+        }else {
+            try{
+                getFrequencyQuantity();
+                Assert.fail("Frequency Quantity found");
+            } catch (Exception ex){
+                LOGGER.info("Frequency Quantity not found");
+            }
+        }
+    }
+
+    public void isValidityStartDateDisplayed(boolean isDisplayed) {
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getStartDate(), "Cluster Validity Start Date");
+        }else {
+            try{
+                getStartDate();
+                Assert.fail("Cluster Validity Start Date found");
+            } catch (Exception ex){
+                LOGGER.info("Cluster Validity Start Date not found");
+            }
+        }
+    }
+
+    public void isAddInputButtonDisplayed(boolean isDisplayed) {
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getAddInputButton(), "Add Input button.");
+        }else {
+            try{
+                getAddInputButton();
+                Assert.fail("Add Input Button found");
+            } catch (Exception ex){
+                LOGGER.info("Add Input Button not found");
+            }
+        }
+    }
+
+    public void isSaveButtonDisplayed(boolean isDisplayed) {
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getSaveProcessButton(), "Save Button");
+        }else {
+            try{
+                getSaveProcessButton();
+                Assert.fail("Save Process Button found");
+            } catch (Exception ex){
+                LOGGER.info("Save Process Button not found");
+            }
+        }
+    }
+
+    private WebElement getSaveProcessButton(){
+        return formBox.findElement(By.xpath("//button[contains(.,'Save')]"));
+    }
+
+    public void isTagsDisplayed(int index, boolean isDisplayed){
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getTagKey(index), "Tag Key Index - " + index);
+            UIAssert.assertDisplayed(getTagValue(index), "Tag Value Index - " + index);
+        }else{
+            try{
+                getTagKey(index);
+                Assert.fail("Tag Key Index - " + index + " found");
+            } catch (Exception ex){
+                LOGGER.info("Tag Key Index - " + index + " not found");
+            }
+            try{
+                getTagValue(index);
+                Assert.fail("Tag Key Value - " + index + " found");
+            } catch (Exception ex){
+                LOGGER.info("Tag Key Value - " + index + " not found");
+            }
+        }
+    }
+
+    /*----- Step2 Properties ----*/
+
     private Select getTimezone() {
-        return new Select(driver.findElement(By.id("timeZoneSelect")));
+        return new Select(formBox.findElement(By.xpath("//select[contains(@class, 'TZSelect')]")));
     }
 
     public void setTimezone(TimeZone timezone) {
-        getTimezone().selectByValue(timezone.getDisplayName());
+        if (timezone == null) {
+            return;
+        }
+        String timeZone = timezone.getID();
+        getTimezone().selectByValue(timeZone);
+    }
+
+    private WebElement getFrequencyQuantity() {
+        return processBox.findElement(By.xpath("//input[@ng-model='process.frequency.quantity']"));
+    }
+    private Select getFrequencyUnit() {
+        return new Select(processBox.findElement(By.xpath(
+            "//select[@ng-model='process.frequency.unit']")));
+    }
+
+    public String getFrequencyQuantityText(){
+        return getFrequencyQuantity().getAttribute("value");
+    }
+
+    public String getMaxParallelInstancesText(){
+        return getMaxParallelInstances().getFirstSelectedOption().getAttribute("value");
+    }
+
+    public String getTimezoneText(){
+        return getTimezone().getFirstSelectedOption().getAttribute("value");
+    }
+
+    public String getOrderText(){
+        return getOrder().getFirstSelectedOption().getAttribute("value");
+    }
+
+    public void setFrequencyQuantity(String frequencyQuantity){
+        getFrequencyQuantity().sendKeys(frequencyQuantity);
+    }
+    public void setFrequencyUnit(String frequencyUnit){
+        getFrequencyUnit().selectByVisibleText(frequencyUnit);
+    }
+
+    public List<String> getTimezoneValues(){
+        return getDropdownValues(getTimezone());
+    }
+
+    public List<String> getFrequencyUnitValues(){
+        return getDropdownValues(getFrequencyUnit());
+    }
+
+    public List<String> getMaxParallelInstancesValues(){
+        return getDropdownValues(getMaxParallelInstances());
+    }
+
+    public List<String> getOrderValues(){
+        return getDropdownValues(getOrder());
+    }
+
+    public List<String> getRetryPolicyValues(){
+        return getDropdownValues(getRetryPolicy());
+    }
+
+    public List<String> getRetryDelayUnitValues(){
+        return getDropdownValues(getRetryDelayUnit());
+    }
+
+    private Select getMaxParallelInstances(){
+        return new Select(formBox.findElement(By.xpath("//select[@ng-model='process.parallel']")));
+    }
+
+    public void setMaxParallelInstances(int quantity) {
+        getMaxParallelInstances().selectByValue(String.valueOf(quantity));
+    }
+
+    private Select getOrder(){
+        return new Select(formBox.findElement(By.xpath("//select[@ng-model='process.order']")));
+    }
+
+    public void setOrder(ExecutionType order) {
+        getOrder().selectByValue(order.value());
+    }
+
+    private Select getRetryPolicy(){
+        return new Select(formBox.findElement(By.xpath("//select[@ng-model='process.retry.policy']")));
+    }
+
+    private Select getRetryDelayUnit(){
+        return new Select(formBox.findElement(By.xpath("//select[@ng-model='process.retry.delay.unit']")));
+    }
+
+    private WebElement getAttempts(){
+        return formBox.findElement(By.id("attemptsField"));
+    }
+
+    private WebElement getDelayQuantity(){
+        return formBox.findElement(By.id("delayQuantity"));
+    }
+
+    public void setRetry(Retry retry) {
+        getRetryPolicy().selectByValue(retry.getPolicy().value());
+        getAttempts().sendKeys(String.valueOf(retry.getAttempts()));
+        getDelayQuantity().sendKeys(retry.getDelay().getFrequency());
+        getRetryDelayUnit().selectByValue(retry.getDelay().getTimeUnit().name());
+    }
+
+    /**
+     * Enter process info on Page 2 of processSetup Wizard.
+     */
+    public void setProcessPropertiesInfo(ProcessMerlin process) {
+        setTimezone(process.getTimezone());
+        setFrequencyQuantity(process.getFrequency().getFrequency());
+        setFrequencyUnit(process.getFrequency().getTimeUnit().toString());
+        setMaxParallelInstances(process.getParallel());
+        setOrder(process.getOrder());
+        setRetry(process.getRetry());
+    }
+
+    /*-----Step3 Clusters-------*/
+
+    public WebElement getStartDate() {
+        List<WebElement> inputs = driver.findElements(
+            By.xpath("//input[contains(@ng-model, 'cluster.validity.start.date')]"));
+        return inputs.get(inputs.size() - 1);
+    }
+
+    public WebElement getEndDate() {
+        List<WebElement> inputs = formBox.findElements(
+            By.xpath("//input[contains(@ng-model, 'cluster.validity.end.date')]"));
+        return inputs.get(inputs.size() - 1);
+    }
+
+    public String getValidityEnd() {
+        return String.format("%s %s:%s", getEndDate().getAttribute("value"), getEndHours().getAttribute("value"),
+            getEndMinutes().getAttribute("value"));
+    }
+
+    public WebElement getStartHours() {
+        List<WebElement> inputs = formBox.findElements(By.xpath("//input[contains(@ng-model, 'hours')]"));
+        return inputs.get(inputs.size() - 2);
+    }
+
+    public WebElement getEndHours() {
+        List<WebElement> inputs = formBox.findElements(By.xpath("//input[contains(@ng-model, 'hours')]"));
+        return inputs.get(inputs.size() - 1);
+    }
+
+    public WebElement getStartMinutes() {
+        List<WebElement> inputs = formBox.findElements(By.xpath("//input[contains(@ng-model, 'minutes')]"));
+        return inputs.get(inputs.size() - 2);
+    }
+
+    public WebElement getEndMinutes() {
+        List<WebElement> inputs = formBox.findElements(By.xpath("//input[contains(@ng-model, 'minutes')]"));
+        return inputs.get(inputs.size() - 1);
+    }
+
+    public WebElement getStartMeredian() {
+        List<WebElement> buttons = formBox.findElements(By.xpath("//td[@ng-show='showMeridian']/button"));
+        return buttons.get(buttons.size() - 2);
+    }
+
+    public WebElement getEndMeredian() {
+        List<WebElement> buttons = formBox.findElements(By.xpath("//td[@ng-show='showMeridian']/button"));
+        return buttons.get(buttons.size() - 1);
+    }
+
+    /**
+     * Retrieves the last cluster select.
+     */
+    public Select getClusterSelect() {
+        List<WebElement> selects = formBox.findElements(By.xpath("//select[contains(@ng-model, 'cluster.name')]"));
+        return new Select(selects.get(selects.size() - 1));
+    }
+
+    public void clickAddClusterButton() {
+        int initialSize = getWizardClusterCount();
+        formBox.findElement(By.xpath("//button[contains(., 'add cluster')]")).click();
+        int finalSize = getWizardClusterCount();
+        Assert.assertEquals(finalSize - initialSize, 1, "New cluster block should been added.");
+    }
+
+    /**
+     * Removes last cluster on the form.
+     */
+    public void deleteLastCluster() {
+        int initialSize = getWizardClusterCount();
+        List<WebElement> buttons = formBox.findElements(By.xpath("//button[contains(., 'delete')]"));
+        Assert.assertTrue(buttons.size() > 0,
+            "Delete button should be present. There should be at least 2 cluster blocks");
+        buttons.get(buttons.size() - 1).click();
+        int finalSize = getWizardClusterCount();
+        Assert.assertEquals(initialSize - finalSize, 1, "One cluster block should been removed.");
+    }
+
+    /**
+     * Sets multiple clusters in process.
+     */
+    public void setClusters(Clusters clusters) {
+        for (int i = 0; i < clusters.getClusters().size(); i++) {
+            if (i > 0) {
+                clickAddClusterButton();
+            }
+            setCluster(clusters.getClusters().get(i));
+        }
+    }
+
+    /**
+     * Fills the last cluster on the form.
+     */
+    public void setCluster(Cluster cluster) {
+        selectCluster(cluster.getName());
+        setClusterValidity(cluster);
+    }
+
+    /**
+     * Populates cluster form with values from process.Cluster object.
+     * @param cluster process process.Cluster object
+     */
+    public void setClusterValidity(Cluster cluster) {
+        SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy-hh-mm-a");
+        String start = format.format(cluster.getValidity().getStart());
+        String [] parts = start.split("-");
+        getStartDate().clear();
+        sendKeysSlowly(getStartDate(), parts[0]);
+        getStartHours().clear();
+        sendKeysSlowly(getStartHours(), parts[1]);
+        getStartMinutes().clear();
+        sendKeysSlowly(getStartMinutes(), parts[2]);
+        String meredian = getStartMeredian().getText();
+        if (!meredian.equals(parts[3])) {
+            getStartMeredian().click();
+        }
+        String end = format.format(cluster.getValidity().getEnd());
+        parts = end.split("-");
+        getEndDate().clear();
+        sendKeysSlowly(getEndDate(), parts[0]);
+        getEndHours().clear();
+        sendKeysSlowly(getEndHours(), parts[1]);
+        getEndMinutes().clear();
+        sendKeysSlowly(getEndMinutes(), parts[2]);
+        meredian = getEndMeredian().getText();
+        if (!meredian.equals(parts[3])) {
+            getEndMeredian().click();
+        }
+    }
+
+    public void selectCluster(String clusterName) {
+        getClusterSelect().selectByValue(clusterName);
+    }
+
+    public String getClusterName(int indx) {
+        List<WebElement> blocks = formBox.findElements(By.xpath("//div[contains(@class, 'processCluster')]"));
+        return new Select(blocks.get(indx).findElement(By.tagName("select")))
+            .getFirstSelectedOption().getText();
+    }
+
+    public int getWizardClusterCount() {
+        return formBox.findElements(By.xpath("//div[contains(@class, 'processCluster')]")).size();
+    }
+
+    public void setProcessClustersInfo(ProcessMerlin process) {
+        for (int i = 0; i < process.getClusters().getClusters().size(); i++) {
+            if (i >= 1) {
+                clickAddClusterButton();
+            }
+            setCluster(process.getClusters().getClusters().get(i));
+        }
+    }
+
+    public List<String> getClustersFromDropDown() {
+        return getDropdownValues(getClusterSelect());
+    }
+
+    public void clickOnValidityStart() {
+        getStartDate().click();
+        List<WebElement> calendars = formBox.findElements(By.xpath("//ul[@ng-model='date']"));
+        waitForAngularToFinish();
+        Assert.assertTrue(calendars.get(calendars.size() - 2).isDisplayed(), "Calendar should pop up.");
+    }
+
+    public void clickOnValidityEnd() {
+        getEndDate().click();
+        List<WebElement> calendars = formBox.findElements(By.xpath("//ul[@ng-model='date']"));
+        waitForAngularToFinish();
+        Assert.assertTrue(calendars.get(calendars.size() - 1).isDisplayed(), "Calendar should pop up.");
+    }
+
+    /* Step 4 - Inputs & Outputs*/
+
+    private WebElement getAddInputButton() {
+        return formBox.findElement(By.xpath("//button[contains(., 'add input')]"));
+    }
+
+    private WebElement getAddOutputButton() {
+        return formBox.findElement(By.xpath("//button[contains(., 'add output')]"));
+    }
+
+    private WebElement getDeleteInputButton() {
+        return formBox.findElement(By.xpath("//button[contains(., 'delete')]"));
+    }
+
+    private WebElement getInputName(int index) {
+        return formBox.findElements(By.xpath("//input[@ng-model='input.name']")).get(index);
+    }
+
+    private Select getInputFeed(int index) {
+        return new Select(formBox.findElements(By.xpath("//select[@ng-model='input.feed']")).get(index));
+    }
+
+    private WebElement getInputStart(int index) {
+        return formBox.findElements(By.xpath("//input[@ng-model='input.start']")).get(index);
+    }
+
+    private WebElement getInputEnd(int index) {
+        return formBox.findElements(By.xpath("//input[@ng-model='input.end']")).get(index);
+    }
+
+    public void setInputInfo(Inputs inputs){
+        for (int i = 0; i < inputs.getInputs().size(); i++) {
+            clickAddInput();
+            sendKeysSlowly(getInputName(i), inputs.getInputs().get(i).getName());
+            getInputFeed(i).selectByVisibleText(inputs.getInputs().get(i).getFeed());
+            sendKeysSlowly(getInputStart(i), inputs.getInputs().get(i).getStart());
+            sendKeysSlowly(getInputEnd(i), inputs.getInputs().get(i).getEnd());
+        }
+    }
+
+    public void clickAddInput(){
+        getAddInputButton().click();
+    }
+
+    public void clickAddOutput(){
+        getAddOutputButton().click();
+    }
+
+    public void clickDeleteInput(){
+        getDeleteInputButton().click();
+    }
+
+    private WebElement getDeleteOutputButton() {
+        return formBox.findElement(By.xpath("//button[contains(., 'delete')]"));
+    }
+
+    private WebElement getOutputName(int index) {
+        return formBox.findElements(By.xpath("//input[@ng-model='output.name']")).get(index);
+    }
+
+    private Select getOutputFeed(int index) {
+        return new Select(formBox.findElements(By.xpath("//select[@ng-model='output.feed']")).get(index));
+    }
+
+    private WebElement getOutputInstance(int index) {
+        return formBox.findElements(By.xpath("//input[@ng-model='output.outputInstance']")).get(index);
+    }
+
+    public void clickDeleteOutput(){
+        getDeleteOutputButton().click();
+    }
+
+    public void setOutputInfo(Outputs outputs){
+        for (int i = 0; i < outputs.getOutputs().size(); i++) {
+            clickAddOutput();
+            sendKeysSlowly(getOutputName(i), outputs.getOutputs().get(i).getName());
+            getOutputFeed(i).selectByVisibleText(outputs.getOutputs().get(i).getFeed());
+            sendKeysSlowly(getOutputInstance(i), outputs.getOutputs().get(i).getInstance());
+        }
+    }
+
+    public void setInputOutputInfo(ProcessMerlin process){
+        setInputInfo(process.getInputs());
+        setOutputInfo(process.getOutputs());
+    }
+
+    public List<String> getInputValues(int index){
+        return getDropdownValues(getInputFeed(index));
+    }
+
+    public List<String> getOutputValues(int index){
+        return getDropdownValues(getOutputFeed(index));
+    }
+
+    public String getInputNameText(int index){
+        return getInputName(index).getAttribute("value");
+    }
+
+    public String getInputFeedText(int index){
+        return getInputFeed(index).getFirstSelectedOption().getAttribute("value");
+    }
+
+    public String getInputStartText(int index){
+        return getInputStart(index).getAttribute("value");
+    }
+
+    public String getInputEndText(int index){
+        return getInputEnd(index).getAttribute("value");
+    }
+
+    public String getOutputNameText(int index){
+        return getOutputName(index).getAttribute("value");
+    }
+
+    public String getOutputFeedText(int index){
+        return getOutputFeed(index).getFirstSelectedOption().getAttribute("value");
+    }
+
+    public String getOutputInstanceText(int index){
+        return getOutputInstance(index).getAttribute("value");
+    }
+
+    public void isInputNameDisplayed(int index, boolean isDisplayed) {
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getInputName(index), "Input Name " + index);
+        }else {
+            try{
+                getInputName(index);
+                Assert.fail("Input Name " + index + " found");
+            } catch (Exception ex){
+                LOGGER.info("Input Name " + index + " not found");
+            }
+        }
+    }
+
+    public void isOutputNameDisplayed(int index, boolean isDisplayed) {
+        if (isDisplayed){
+            UIAssert.assertDisplayed(getOutputName(index), "Output Name " + index);
+        }else {
+            try{
+                getOutputName(index);
+                Assert.fail("Output Name " + index + " found");
+            } catch (Exception ex){
+                LOGGER.info("Output Name " + index + " not found");
+            }
+        }
+    }
+
+
+    /* Step 5 - Summary */
+
+    public void clickSave(){
+        getSaveProcessButton().click();
+    }
+
+    /**
+     * Creates ProcessMerlin object from xml preview string.
+     */
+    public ProcessMerlin getProcessMerlinFromProcessXml() throws Exception{
+        waitForAngularToFinish();
+        return new ProcessMerlin(processXml.getAttribute("value"));
+    }
+
+    /**
+     * Pushes xml string to xml preview.
+     */
+    public void setProcessXml(String xml) throws Exception{
+        processXml.clear();
+        processXml.sendKeys(xml);
+    }
+
+    /**
+     * Method gets text from summary box and parses it to ProcessMerlin object.
+     * @param draft empty ProcessMerlin object
+     */
+    public ProcessMerlin getProcessFromSummaryBox(ProcessMerlin draft) {
+        String text = summaryBox.getText().trim();
+        draft.setName(getProperty(text, null, "Tags", 2));
+        String currentBlock = text.substring(text.indexOf("Tags"), text.indexOf("Workflow"));
+        String [] parts;
+        parts = currentBlock.trim().split("\\n");
+        String tags = "";
+        for (int i = 1; i < parts.length; i++) {
+            String tag = parts[i];
+            if (!tag.contains("No tags")) {
+                tag = tag.replace(" ", "");
+                tags = tags + (tags.isEmpty() ? tag : "," + tag);
+            }
+        }
+        if (!tags.isEmpty()) {
+            draft.setTags(tags);
+        }
+        Workflow workflow = new Workflow();
+        workflow.setName(getProperty(text, "Workflow", "Engine", 2));
+        workflow.setEngine(EngineType.fromValue(getProperty(text, "Engine", "Version", 1)));
+        workflow.setVersion(getProperty(text, "Version", "Path", 1));
+        workflow.setPath(getProperty(text, "Path", "Timing", 1));
+        draft.setWorkflow(workflow);
+
+        draft.setTimezone(TimeZone.getTimeZone(getProperty(text, "Timing", "Frequency", 2)));
+        parts = getProperty(text, "Frequency", "Max. parallel instances", 1).split(" ");
+        draft.setFrequency(new Frequency(parts[1], Frequency.TimeUnit.valueOf(parts[2])));
+        draft.setParallel(Integer.parseInt(getProperty(text, "Max. parallel instances", "Order", 1)));
+        draft.setOrder(ExecutionType.fromValue(getProperty(text, "Order", "Retry", 1)));
+
+        Retry retry = new Retry();
+        retry.setPolicy(PolicyType.fromValue(getProperty(text, "Retry", "Attempts", 2)));
+        retry.setAttempts(Integer.parseInt(getProperty(text, "Attempts", "Delay", 1)));
+        parts = getProperty(text, "Delay", "Clusters", 1).split(" ");
+        retry.setDelay(new Frequency(parts[2], Frequency.TimeUnit.valueOf(parts[3])));
+        draft.setRetry(retry);
+
+        //get clusters
+        currentBlock = text.substring(text.indexOf("Clusters"), text.indexOf("Inputs"));
+        int last = 0;
+        while (last != -1) {
+            Cluster cluster = new Cluster();
+            cluster.setName(getProperty(currentBlock, "Name", "Validity", 1));
+            //remove the part which was used
+            currentBlock = currentBlock.substring(currentBlock.indexOf("Validity"));
+            //get validity
+            String start = getProperty(currentBlock, "Validity", "End", 2).split(" ")[1];
+            //check if there are other clusters
+            last = currentBlock.indexOf("Name");
+            String innerBlock = currentBlock.substring(currentBlock.indexOf("End"),
+                last != -1 ? last : currentBlock.length() - 1).trim();
+            parts = innerBlock.trim().split("\\n");
+            String end = parts[1].split(" ")[1];
+            Validity validity = new Validity();
+            DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'");
+            validity.setStart(formatter.parseDateTime(start.replaceAll("\"", "")).toDate());
+            validity.setEnd(formatter.parseDateTime(end.replaceAll("\"", "")).toDate());
+            cluster.setValidity(validity);
+            draft.getClusters().getClusters().add(cluster);
+        }
+        //get inputs
+        currentBlock = text.substring(text.indexOf("Inputs"), text.indexOf("Outputs"));
+        last = 0;
+        while (last != -1) {
+            Input input = new Input();
+            //get input name
+            input.setName(getProperty(currentBlock, "Name", "Feed", 1));
+            //remove the part which was used
+            currentBlock = currentBlock.substring(currentBlock.indexOf("Name") + 4);
+            //get input feed
+            input.setFeed(getProperty(currentBlock, "Feed", "Instance", 1));
+            //get input start
+            input.setStart(getProperty(currentBlock, "Instance", "End", 2));
+            //get input end
+            last = currentBlock.indexOf("Name");
+            String innerBlock = currentBlock.substring(currentBlock.indexOf("End"),
+                last != -1 ? last : currentBlock.length() - 1).trim();
+            parts = innerBlock.trim().split("\\n");
+            input.setEnd(parts[1]);
+            draft.getInputs().getInputs().add(input);
+            //remove part which was parsed
+            currentBlock = currentBlock.substring(currentBlock.indexOf("End") + 4);
+        }
+        //get outputs
+        currentBlock = text.substring(text.indexOf("Outputs"));
+        last = 0;
+        while (last != -1) {
+            Output output = new Output();
+            output.setName(getProperty(currentBlock, "Name", "Feed", 1));
+            //remove the part which was used
+            currentBlock = currentBlock.substring(currentBlock.indexOf("Feed"));
+            //get feed
+            output.setFeed(getProperty(currentBlock, "Feed", "Instance", 1));
+            last = currentBlock.indexOf("Name");
+            output.setInstance(getProperty(currentBlock, "Instance", "Name", 2));
+            draft.getOutputs().getOutputs().add(output);
+        }
+        return draft;
+    }
+
+    /**
+     * Retrieves property from source text.
+     */
+    private String getProperty(String block, String start, String end, int propertyIndex) {
+        int s = start != null ? block.indexOf(start) : 0;
+        s = s == -1 ? 0 : s;
+        int e = end != null ? block.indexOf(end) : block.length() - 1;
+        e = e == -1 ? block.length() : e;
+        String subBlock = block.substring(s, e).trim();
+        String [] parts = subBlock.trim().split("\\n");
+        return parts.length - 1 >= propertyIndex ? parts[propertyIndex].trim() : null;
     }
 }