You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2017/05/10 13:37:36 UTC

[13/22] ambari git commit: AMBARI-20955. Integrate Log Search integration test framework with Selenium (oleewere)

AMBARI-20955. Integrate Log Search integration test framework with Selenium (oleewere)


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

Branch: refs/heads/branch-feature-AMBARI-12556
Commit: 3153b9bc2f3ea9b3330e84aa34c34fc9457f01ad
Parents: 8dae74c
Author: oleewere <ol...@gmail.com>
Authored: Mon May 8 16:31:22 2017 +0200
Committer: oleewere <ol...@gmail.com>
Committed: Tue May 9 14:05:50 2017 +0200

----------------------------------------------------------------------
 ambari-logsearch/README.md                      |   8 +-
 ambari-logsearch/ambari-logsearch-it/pom.xml    | 173 ++++++++++++---
 .../logsearch/domain/StoryDataRegistry.java     |  10 +
 .../logsearch/steps/AbstractLogSearchSteps.java | 162 ++++++++++++++
 .../logsearch/steps/LogSearchDockerSteps.java   | 116 +---------
 .../logsearch/steps/LogSearchUISteps.java       | 212 +++++++++++++++++++
 .../logsearch/story/LogSearchApiQueryStory.java |  22 --
 .../story/LogSearchBackendStories.java          |  84 ++++++++
 .../ambari/logsearch/story/LogSearchStory.java  |  60 ------
 .../logsearch/story/LogSearchUIStories.java     |  93 ++++++++
 .../logsearch/story/LogfeederParsingStory.java  |  22 --
 .../ambari/logsearch/web/AbstractPage.java      |  63 ++++++
 .../org/apache/ambari/logsearch/web/Home.java   |  39 ++++
 .../story/log_search_api_query_story.story      |  17 --
 .../story/logfeeder_parsing_story.story         |  20 --
 .../backend/log_search_api_query_story.story    |  17 ++
 .../backend/logfeeder_parsing_story.story       |  20 ++
 .../resources/stories/selenium/login.ui.story   |  20 ++
 ambari-logsearch/docker/Dockerfile              |  17 +-
 ambari-logsearch/docker/bin/start.sh            |   8 +
 ambari-logsearch/docker/logsearch-docker.sh     |  10 +-
 21 files changed, 901 insertions(+), 292 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/README.md
----------------------------------------------------------------------
diff --git a/ambari-logsearch/README.md b/ambari-logsearch/README.md
index 5c41fcd..4123a52 100644
--- a/ambari-logsearch/README.md
+++ b/ambari-logsearch/README.md
@@ -36,10 +36,14 @@ mvn -Dbuild-deb clean package
 
 ## Running Integration Tests
 
-By default integration tests are not a part of the build process, you need to set ${it.skip} variable to true (docker needed here too)
+By default integration tests are not a part of the build process, you need to set -Dbackend-tests or -Dselenium-tests (or you can use -Dall-tests to run both). To running the tests you will need docker here as well (right now docker-for-mac and unix are supported only).
 
 ```bash
 # from ambari-logsearch folder
-mvn clean integration-test -Dit.skip=false
+mvn clean integration-test -Dbackend-tests failsafe:verify
+# or run selenium tests with docker for mac, but before that you nedd to start xquartz
+xquartz
+# then in an another window you can start ui tests
+mvn clean integration-test -Dselenium-tests failsafe:verify
 ```
 Also you can run from the IDE, but make sure all of the ambari logsearch modules are built.

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/pom.xml b/ambari-logsearch/ambari-logsearch-it/pom.xml
index ee97e99..cdb76a5 100644
--- a/ambari-logsearch/ambari-logsearch-it/pom.xml
+++ b/ambari-logsearch/ambari-logsearch-it/pom.xml
@@ -33,8 +33,10 @@
   <properties>
     <it.skip>true</it.skip>
     <jbehave.version>4.0.5</jbehave.version>
+    <jbehave-selenium>3.5.5</jbehave-selenium>
     <jersey.version>2.23.1</jersey.version>
     <jackson-jaxrs.version>2.6.4</jackson-jaxrs.version>
+    <failsafe-plugin.version>2.20</failsafe-plugin.version>
     <forkCount>1</forkCount>
   </properties>
 
@@ -45,6 +47,11 @@
       <version>${jbehave.version}</version>
     </dependency>
     <dependency>
+      <groupId>org.jbehave.web</groupId>
+      <artifactId>jbehave-web-selenium</artifactId>
+      <version>${jbehave-selenium}</version>
+    </dependency>
+    <dependency>
       <groupId>org.apache.solr</groupId>
       <artifactId>solr-solrj</artifactId>
       <version>${solr.version}</version>
@@ -82,6 +89,12 @@
       <groupId>com.flipkart.zjsonpatch</groupId>
       <artifactId>zjsonpatch</artifactId>
       <version>0.2.4</version>
+      <exclusions>
+        <exclusion>
+          <groupId>com.google.guava</groupId>
+          <artifactId>guava</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.apache.ambari</groupId>
@@ -98,6 +111,11 @@
       <artifactId>ambari-logsearch-logfeeder</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>11.0.1</version>
+    </dependency>
   </dependencies>
 
   <build>
@@ -113,35 +131,132 @@
         <directory>src/test/resources</directory>
       </testResource>
     </testResources>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-failsafe-plugin</artifactId>
-        <executions>
-          <execution>
-            <id>run-integration-tests</id>
-            <phase>integration-test</phase>
-            <goals>
-              <goal>integration-test</goal>
-            </goals>
-            <configuration>
-              <includes>
-                <include>**/*Stories.java</include>
-                <include>**/*Story.java</include>
-              </includes>
-              <skip>${it.skip}</skip>
-            </configuration>
-          </execution>
-          <execution>
-            <id>verify-integration-tests</id>
-            <phase>verify</phase>
-            <goals>
-              <goal>verify</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
   </build>
 
+  <profiles>
+    <profile>
+      <id>selenium-tests</id>
+      <activation>
+        <property>
+          <name>selenium-tests</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-failsafe-plugin</artifactId>
+            <version>${failsafe-plugin.version}</version>
+            <executions>
+              <execution>
+                <id>run-integration-tests</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>integration-test</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>**/*UIStories.java</include>
+                  </includes>
+                  <systemPropertyVariables>
+                    <log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+              <execution>
+                <id>verify-integration-tests</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>verify</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>backend-tests</id>
+      <activation>
+        <property>
+          <name>backend-tests</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-failsafe-plugin</artifactId>
+            <version>${failsafe-plugin.version}</version>
+            <executions>
+              <execution>
+                <id>run-integration-tests</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>integration-test</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>**/*BackendStories.java</include>
+                  </includes>
+                  <systemPropertyVariables>
+                    <log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+              <execution>
+                <id>verify-integration-tests</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>verify</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>all-tests</id>
+      <activation>
+        <property>
+          <name>all-tests</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-failsafe-plugin</artifactId>
+            <version>${failsafe-plugin.version}</version>
+            <executions>
+              <execution>
+                <id>run-integration-tests</id>
+                <phase>integration-test</phase>
+                <goals>
+                  <goal>integration-test</goal>
+                </goals>
+                <configuration>
+                  <includes>
+                    <include>**/*Stories.java</include>
+                  </includes>
+                  <systemPropertyVariables>
+                    <log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration>
+                  </systemPropertyVariables>
+                </configuration>
+              </execution>
+              <execution>
+                <id>verify-integration-tests</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>verify</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
 </project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/domain/StoryDataRegistry.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/domain/StoryDataRegistry.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/domain/StoryDataRegistry.java
index cb72376..41d6391 100644
--- a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/domain/StoryDataRegistry.java
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/domain/StoryDataRegistry.java
@@ -19,6 +19,7 @@
 package org.apache.ambari.logsearch.domain;
 
 import org.apache.solr.client.solrj.SolrClient;
+import org.jbehave.web.selenium.WebDriverProvider;
 
 public class StoryDataRegistry {
   public static final StoryDataRegistry INSTANCE = new StoryDataRegistry();
@@ -33,6 +34,7 @@ public class StoryDataRegistry {
   private final int zookeeperPort = 9983;
   private final String serviceLogsCollection = "hadoop_logs";
   private final String auditLogsCollection = "audit_logs";
+  private WebDriverProvider webDriverProvider;
 
   private StoryDataRegistry() {
   }
@@ -96,4 +98,12 @@ public class StoryDataRegistry {
   public void setLogsearchContainerStarted(boolean logsearchContainerStarted) {
     this.logsearchContainerStarted = logsearchContainerStarted;
   }
+
+  public WebDriverProvider getWebDriverProvider() {
+    return webDriverProvider;
+  }
+
+  public void setWebDriverProvider(WebDriverProvider webDriverProvider) {
+    this.webDriverProvider = webDriverProvider;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/AbstractLogSearchSteps.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/AbstractLogSearchSteps.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/AbstractLogSearchSteps.java
new file mode 100644
index 0000000..a7dd409
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/AbstractLogSearchSteps.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.steps;
+
+import org.apache.ambari.logsearch.domain.StoryDataRegistry;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.LBHttpSolrClient;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.response.SolrPingResponse;
+import org.apache.solr.common.SolrDocumentList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URL;
+
+public class AbstractLogSearchSteps {
+
+  private static final Logger LOG = LoggerFactory.getLogger(AbstractLogSearchSteps.class);
+
+  protected void initDockerContainer() throws Exception{
+    boolean logsearchStarted = StoryDataRegistry.INSTANCE.isLogsearchContainerStarted();
+    if (!logsearchStarted) {
+      LOG.info("Create new docker container for Log Search ...");
+      URL location = LogSearchDockerSteps.class.getProtectionDomain().getCodeSource().getLocation();
+      String ambariFolder = new File(location.toURI()).getParentFile().getParentFile().getParentFile().getParent();
+      StoryDataRegistry.INSTANCE.setAmbariFolder(ambariFolder);
+      String shellScriptLocation = ambariFolder + "/ambari-logsearch/docker/logsearch-docker.sh";
+      StoryDataRegistry.INSTANCE.setShellScriptLocation(shellScriptLocation);
+      String output = runCommand(new String[]{StoryDataRegistry.INSTANCE.getShellScriptLocation(), "start"});
+      LOG.info("Command output: {}", output);
+      StoryDataRegistry.INSTANCE.setLogsearchContainerStarted(true);
+
+      // TODO: create a script which returns the proper host for docker, use: runCommand or an env variable
+      String dockerHostFromUri = "localhost";
+
+      StoryDataRegistry.INSTANCE.setDockerHost(dockerHostFromUri);
+      checkHostAndPortReachable(dockerHostFromUri, StoryDataRegistry.INSTANCE.getLogsearchPort(), "LogSearch");
+      waitUntilSolrIsUp();
+      waitUntilSolrHasAnyData();
+
+      LOG.info("Waiting for logfeeder to finish the test log parsings... (10 sec)");
+      Thread.sleep(10000);
+    }
+  }
+
+  private void waitUntilSolrIsUp() throws Exception {
+    int maxTries = 30;
+    boolean solrIsUp = false;
+    String lastExceptionMessage = null;
+    for (int tries = 1; tries < maxTries; tries++) {
+      try {
+        SolrClient solrClient = new LBHttpSolrClient(String.format("http://%s:%d/solr/%s_shard0_replica1",
+          StoryDataRegistry.INSTANCE.getDockerHost(),
+          StoryDataRegistry.INSTANCE.getSolrPort(),
+          StoryDataRegistry.INSTANCE.getServiceLogsCollection()));
+        StoryDataRegistry.INSTANCE.setSolrClient(solrClient);
+        SolrPingResponse pingResponse = solrClient.ping();
+        if (pingResponse.getStatus() != 0) {
+          LOG.info("Solr is not up yet, Retrying... ({} tries)", tries);
+          Thread.sleep(2000);
+        } else {
+          solrIsUp = true;
+          LOG.info("Solr is up and running");
+          break;
+        }
+      } catch (Exception e) {
+        LOG.info("Error occurred during pinging solr. Retrying... ({} tries)", tries);
+        lastExceptionMessage = e.getMessage();
+        Thread.sleep(2000);
+      }
+    }
+
+    if (!solrIsUp) {
+      throw new IllegalStateException(String.format("Solr is not up after %d tries. Exception: %s", maxTries, lastExceptionMessage));
+    }
+  }
+
+  protected void waitUntilSolrHasAnyData() throws IOException, SolrServerException, InterruptedException {
+    boolean solrHasData = false;
+    int maxTries = 60;
+    String lastExceptionMessage = null;
+    for (int tries = 1; tries < maxTries; tries++) {
+      try {
+        SolrClient solrClient = StoryDataRegistry.INSTANCE.getSolrClient();
+        SolrQuery solrQuery = new SolrQuery();
+        solrQuery.setQuery("*:*");
+        QueryResponse queryResponse = solrClient.query(solrQuery);
+        SolrDocumentList list = queryResponse.getResults();
+        if (list.size() > 0) {
+          solrHasData = true;
+          break;
+        } else {
+          Thread.sleep(2000);
+          LOG.info("Solr has no data yet. Retrying... ({} tries)", tries);
+        }
+      } catch (Exception e) {
+        LOG.info("Error occurred during checking solr. Retrying... ({} tries)", tries);
+        lastExceptionMessage = e.getMessage();
+        Thread.sleep(2000);
+      }
+    }
+    if (!solrHasData) {
+      throw new IllegalStateException(String.format("Solr has no data after %d tries. Exception: %s", maxTries, lastExceptionMessage));
+    }
+  }
+
+
+  protected void checkHostAndPortReachable(String host, int port, String serviceName) throws InterruptedException {
+    boolean reachable = false;
+    int maxTries = 60;
+    for (int tries = 1; tries < maxTries; tries++ ) {
+      try (Socket socket = new Socket()) {
+        socket.connect(new InetSocketAddress(host, port), 1000);
+        reachable = true;
+        break;
+      } catch (IOException e) {
+        Thread.sleep(2000);
+        LOG.info("{} is not reachable yet. Retrying... ({} tries)", serviceName, tries);
+      }
+    }
+    if (!reachable) {
+      throw new IllegalStateException(String.format("%s is not reachable after %s tries", serviceName, maxTries));
+    }
+  }
+
+
+  protected String runCommand(String[] command) {
+    try {
+      LOG.info("Exec command: {}", StringUtils.join(command, " "));
+      Process process = Runtime.getRuntime().exec(command);
+      BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+      return reader.readLine();
+    } catch (Exception e) {
+      throw new RuntimeException("Error during execute shell command: ", e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchDockerSteps.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchDockerSteps.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchDockerSteps.java
index 32e8cba..cb67fcc 100644
--- a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchDockerSteps.java
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchDockerSteps.java
@@ -41,35 +41,13 @@ import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.URL;
 
-public class LogSearchDockerSteps {
+public class LogSearchDockerSteps extends AbstractLogSearchSteps {
 
   private static final Logger LOG = LoggerFactory.getLogger(LogSearchDockerSteps.class);
 
   @Given("logsearch docker container")
   public void setupLogSearchContainer() throws Exception {
-    boolean logsearchStarted = StoryDataRegistry.INSTANCE.isLogsearchContainerStarted();
-    if (!logsearchStarted) {
-      LOG.info("Create new docker container for Log Search ..");
-      URL location = LogSearchDockerSteps.class.getProtectionDomain().getCodeSource().getLocation();
-      String ambariFolder = new File(location.toURI()).getParentFile().getParentFile().getParentFile().getParent();
-      StoryDataRegistry.INSTANCE.setAmbariFolder(ambariFolder);
-      String shellScriptLocation = ambariFolder + "/ambari-logsearch/docker/logsearch-docker.sh";
-      StoryDataRegistry.INSTANCE.setShellScriptLocation(shellScriptLocation);
-      String output = runCommand(new String[]{StoryDataRegistry.INSTANCE.getShellScriptLocation(), "start"});
-      LOG.info("Command output: {}", output);
-      StoryDataRegistry.INSTANCE.setLogsearchContainerStarted(true);
-
-      // TODO: create a script which returns the proper host for docker, use: runCommand or an env variable
-      String dockerHostFromUri = "localhost";
-
-      StoryDataRegistry.INSTANCE.setDockerHost(dockerHostFromUri);
-      checkHostAndPortReachable(dockerHostFromUri, StoryDataRegistry.INSTANCE.getLogsearchPort(), "LogSearch");
-      waitUntilSolrIsUp();
-      waitUntilSolrHasAnyData();
-
-      LOG.info("Waiting for logfeeder to finish the test log parsings... (10 sec)");
-      Thread.sleep(10000);
-    }
+    initDockerContainer();
   }
 
   @When("logfeeder started (parse logs & send data to solr)")
@@ -78,7 +56,7 @@ public class LogSearchDockerSteps {
   }
 
   @BeforeStories
-  public void checkDockerApi() {
+  public void initDocker() throws Exception {
     // TODO: check docker is up
   }
 
@@ -86,92 +64,4 @@ public class LogSearchDockerSteps {
   public void removeLogSearchContainer() {
     runCommand(new String[]{StoryDataRegistry.INSTANCE.getShellScriptLocation(), "stop"});
   }
-
-  private void waitUntilSolrIsUp() throws Exception {
-    int maxTries = 30;
-    boolean solrIsUp = false;
-    for (int tries = 1; tries < maxTries; tries++) {
-      try {
-        SolrClient solrClient = new LBHttpSolrClient(String.format("http://%s:%d/solr/%s_shard0_replica1",
-          StoryDataRegistry.INSTANCE.getDockerHost(),
-          StoryDataRegistry.INSTANCE.getSolrPort(),
-          StoryDataRegistry.INSTANCE.getServiceLogsCollection()));
-        StoryDataRegistry.INSTANCE.setSolrClient(solrClient);
-        SolrPingResponse pingResponse = solrClient.ping();
-        if (pingResponse.getStatus() != 0) {
-          LOG.info("Solr is not up yet, retrying... ({})", tries);
-          Thread.sleep(2000);
-        } else {
-          solrIsUp = true;
-          LOG.info("Solr is up and running");
-          break;
-        }
-      } catch (Exception e) {
-        LOG.error("Error occurred during pinging solr ({}). retrying {} times", e.getMessage(), tries);
-        Thread.sleep(2000);
-      }
-    }
-
-    if (!solrIsUp) {
-      throw new IllegalStateException(String.format("Solr is not up after %d tries", maxTries));
-    }
-  }
-
-  private void waitUntilSolrHasAnyData() throws IOException, SolrServerException, InterruptedException {
-    boolean solrHasData = false;
-
-    int maxTries = 60;
-    for (int tries = 1; tries < maxTries; tries++) {
-      try {
-        SolrClient solrClient = StoryDataRegistry.INSTANCE.getSolrClient();
-        SolrQuery solrQuery = new SolrQuery();
-        solrQuery.setQuery("*:*");
-        QueryResponse queryResponse = solrClient.query(solrQuery);
-        SolrDocumentList list = queryResponse.getResults();
-        if (list.size() > 0) {
-          solrHasData = true;
-          break;
-        } else {
-          Thread.sleep(2000);
-          LOG.info("Solr has no data yet, retrying... ({} tries)", tries);
-        }
-      } catch (Exception e) {
-        LOG.error("Error occurred during checking solr ({}). retrying {} times", e.getMessage(), tries);
-        Thread.sleep(2000);
-      }
-    }
-    if (!solrHasData) {
-      throw new IllegalStateException(String.format("Solr has no data after %d tries", maxTries));
-    }
-  }
-
-
-  private void checkHostAndPortReachable(String host, int port, String serviceName) throws InterruptedException {
-    boolean reachable = false;
-    int maxTries = 60;
-    for (int tries = 1; tries < maxTries; tries++ ) {
-      try (Socket socket = new Socket()) {
-        socket.connect(new InetSocketAddress(host, port), 1000);
-        reachable = true;
-        break;
-      } catch (IOException e) {
-        Thread.sleep(2000);
-        LOG.info("{} is not reachable yet, retrying..", serviceName);
-      }
-    }
-    if (!reachable) {
-      throw new IllegalStateException(String.format("%s is not reachable after %s tries", serviceName, maxTries));
-    }
-  }
-
-
-  private String runCommand(String[] command) {
-    try {
-      Process process = Runtime.getRuntime().exec(command);
-      BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
-      return reader.readLine();
-    } catch (Exception e) {
-      throw new RuntimeException("Error during execute shell command: ", e);
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchUISteps.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchUISteps.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchUISteps.java
new file mode 100644
index 0000000..b40a2bc
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/steps/LogSearchUISteps.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.steps;
+
+import junit.framework.Assert;
+import org.apache.ambari.logsearch.domain.StoryDataRegistry;
+import org.apache.ambari.logsearch.web.Home;
+import org.jbehave.core.annotations.AfterScenario;
+import org.jbehave.core.annotations.AfterStories;
+import org.jbehave.core.annotations.AfterStory;
+import org.jbehave.core.annotations.BeforeScenario;
+import org.jbehave.core.annotations.BeforeStories;
+import org.jbehave.core.annotations.BeforeStory;
+import org.jbehave.core.annotations.Given;
+import org.jbehave.core.annotations.Named;
+import org.jbehave.core.annotations.Then;
+import org.jbehave.core.annotations.When;
+import org.jbehave.web.selenium.WebDriverProvider;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+public class LogSearchUISteps extends AbstractLogSearchSteps {
+
+  private static final Logger LOG = LoggerFactory.getLogger(LogSearchUISteps.class);
+
+  private final WebDriverProvider driverProvider;
+
+  private Home home;
+
+  public LogSearchUISteps(WebDriverProvider driverProvider) {
+    this.driverProvider = driverProvider;
+  }
+
+  @BeforeScenario
+  public void initHomePage() {
+    home = new Home(driverProvider);
+    LOG.info("Init home page: {}", home.getCurrentUrl());
+  }
+
+  @AfterScenario
+  public void deleteCookies() {
+    LOG.info("Delete all cookies...");
+    home.manage().deleteAllCookies();
+  }
+
+  @BeforeStories
+  public void beforeStories() throws Exception {
+    initDockerContainer();
+    LOG.info("Initialize web driver...");
+    StoryDataRegistry.INSTANCE.getWebDriverProvider().initialize();
+    LOG.info("Web driver details: {}",  StoryDataRegistry.INSTANCE.getWebDriverProvider().get().toString());
+  }
+
+  @AfterStory
+  public void closePage() throws Exception {
+    LOG.info("Closing web driver");
+    StoryDataRegistry.INSTANCE.getWebDriverProvider().end();
+  }
+
+  @Given("open logsearch home page")
+  public void initBrowser() {
+    LOG.info("Delete all cookies...");
+    home.manage().deleteAllCookies();
+    LOG.info("Open home page: {}", home.getCurrentUrl());
+    home.open();
+  }
+
+  @When("login with $username / $password")
+  public void login(@Named("username") String userName, @Named("password") String password) {
+    LOG.info("Type username: {}", userName);
+    home.findElement(By.id("username")).sendKeys(userName);
+    LOG.info("Type password: {}", password);
+    home.findElement(By.id("password")).sendKeys(password);
+    LOG.info("Click on Sign In button.");
+    home.findElement(By.className("custLogin")).click();
+    closeTourPopup();
+  }
+
+  @Then("page contains text: '$text'")
+  public void contains(@Named("text") String text) {
+    LOG.info("Check page contains text: '{}'", text);
+    home.found(text);
+  }
+
+  @Then("page does not contain text: '$text'")
+  public void notContains(@Named("text") String text) {
+    LOG.info("Check page does not contain text: '{}'", text);
+    home.notFound(text);
+  }
+
+  @When("wait $seconds seconds")
+  public void waitSeconds(@Named("second") String second) {
+    LOG.info("Wait {} seconds...", second);
+    home.manage().timeouts().implicitlyWait(Integer.parseInt(second), TimeUnit.SECONDS);
+  }
+
+  @When("click on element: $xpath (xpath)")
+  public void clickOnElementByXPath(@Named("xpath") String xPath) {
+    LOG.info("Click on element by xpath: '{}'", xPath);
+    driverProvider.get().findElement(By.xpath(xPath)).click();
+  }
+
+  @When("click on element: $id (id)")
+  public void clickOnElementById(@Named("id") String id) {
+    LOG.info("Click on element by id: '{}'", id);
+    driverProvider.get().findElement(By.xpath(id)).click();
+  }
+
+  @When("click on element: $css (css selector)")
+  public void clickOnElementByCssSelector(@Named("css") String cssSelector) {
+    LOG.info("Click on element by css selector: '{}'", cssSelector);
+    driverProvider.get().findElement(By.cssSelector(cssSelector)).click();
+  }
+
+  @Then("element exists with xpath: $xpath")
+  public void findByXPath(@Named("xpath") String xPath) {
+    LOG.info("Find element by xpath: '{}'", xPath);
+    Assert.assertNotNull(home.findElement(By.xpath(xPath)));
+  }
+
+  @Then("element exists with xpath: $id")
+  public void findById(@Named("id") String id) {
+    LOG.info("Find element by id: '{}'", id);
+    Assert.assertNotNull(home.findElement(By.id(id)));
+  }
+
+  @Then("element exists with css selector: $css")
+  public void findByCssSelector(@Named("css") String cssSelector) {
+    LOG.info("Find element by css selector: '{}'", cssSelector);
+    Assert.assertNotNull(home.findElement(By.cssSelector(cssSelector)));
+  }
+
+  @Then("element text equals '$text', with xpath $xpath")
+  public void equalsByXPath(@Named("text") String text, @Named("xpath") String xPath) {
+    LOG.info("Check text of the element (xpath: '{}') equals with '{}'", xPath, text);
+    Assert.assertEquals(text, home.findElement(By.xpath(xPath)).getText());
+  }
+
+  @Then("element text equals '$text' with id $id")
+  public void equalsyId(@Named("text") String text, @Named("id") String id) {
+    LOG.info("Check text of the element (id: '{}') equals with '{}'", id, text);
+    Assert.assertEquals(text, home.findElement(By.id(id)).getText());
+  }
+
+  @Then("element text equals '$text' with css selector $css")
+  public void equalsCssSelector(@Named("text") String text, @Named("css") String cssSelector) {
+    LOG.info("Check text of the element (css selector: '{}') equals with '{}'", cssSelector, text);
+    Assert.assertEquals(text, home.findElement(By.cssSelector(cssSelector)).getText());
+  }
+
+  @Then("element does not exist with xpath: $xpath")
+  public void doNotFindByXPath(@Named("xpath") String xPath) {
+    try {
+      LOG.info("Check that element does not exist with xpath: {}", xPath);
+      home.findElement(By.xpath(xPath));
+      Assert.fail(String.format("Element is found. xPath: '%s'", xPath));
+    } catch (NoSuchElementException e) {
+      // success
+    }
+  }
+
+  @Then("element does not exist with xpath: $id")
+  public void doNotFindById(@Named("id") String id) {
+    try {
+      LOG.info("Check that element does not exist with id: {}", id);
+      home.findElement(By.xpath(id));
+      Assert.fail(String.format("Element is found. id: '%s'", id));
+    } catch (NoSuchElementException e) {
+      // success
+    }
+  }
+
+  @Then("element does not exist with css selector: $css")
+  public void doNotFindByCssSelector(@Named("css") String cssSelector) {
+    try {
+      LOG.info("Check that element does not exist with css selector: {}", cssSelector);
+      home.findElement(By.xpath(cssSelector));
+      Assert.fail(String.format("Element is found. css selector: '%s'", cssSelector));
+    } catch (NoSuchElementException e) {
+      // success
+    }
+  }
+
+  private void closeTourPopup() {
+    LOG.info("Close Tour popup if needed.");
+    try {
+      home.findElement(By.cssSelector("div.modal-footer > button.btn.btn-default")).click();
+    } catch (NoSuchElementException ex) {
+      // do nothing - no popup
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchApiQueryStory.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchApiQueryStory.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchApiQueryStory.java
deleted file mode 100644
index 45455bf..0000000
--- a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchApiQueryStory.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.ambari.logsearch.story;
-
-public class LogSearchApiQueryStory extends LogSearchStory {
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchBackendStories.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchBackendStories.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchBackendStories.java
new file mode 100644
index 0000000..46f2928
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchBackendStories.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.story;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import org.apache.ambari.logsearch.steps.LogSearchApiSteps;
+import org.apache.ambari.logsearch.steps.SolrSteps;
+import org.apache.ambari.logsearch.steps.LogSearchDockerSteps;
+import org.jbehave.core.configuration.Configuration;
+import org.jbehave.core.configuration.MostUsefulConfiguration;
+import org.jbehave.core.embedder.executors.SameThreadExecutors;
+import org.jbehave.core.io.LoadFromClasspath;
+import org.jbehave.core.io.StoryFinder;
+import org.jbehave.core.io.StoryPathResolver;
+import org.jbehave.core.io.UnderscoredCamelCaseResolver;
+import org.jbehave.core.junit.JUnitStories;
+import org.jbehave.core.junit.JUnitStory;
+import org.jbehave.core.reporters.Format;
+import org.jbehave.core.reporters.StoryReporterBuilder;
+import org.jbehave.core.steps.InjectableStepsFactory;
+import org.jbehave.core.steps.InstanceStepsFactory;
+import org.junit.Test;
+
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.jbehave.core.io.CodeLocations.codeLocationFromClass;
+
+public class LogSearchBackendStories extends JUnitStories {
+
+  @Override
+  public Configuration configuration() {
+    return new MostUsefulConfiguration()
+      .useStoryLoader(new LoadFromClasspath(this.getClass()))
+      .useStoryReporterBuilder(
+        new StoryReporterBuilder().withFailureTrace(true).withDefaultFormats().withFormats(Format.CONSOLE, Format.TXT));
+  }
+
+  @Override
+  public InjectableStepsFactory stepsFactory() {
+    return new InstanceStepsFactory(configuration(),
+      new LogSearchDockerSteps(),
+      new SolrSteps(),
+      new LogSearchApiSteps());
+  }
+
+  @Test
+  public void run() throws Throwable {
+    super.run();
+  }
+
+  @Override
+  protected List<String> storyPaths() {
+    List<String> backendStories = new StoryFinder()
+      .findPaths(codeLocationFromClass(this.getClass()).getFile(), Arrays.asList("**/*.story"), null);
+    return Lists.newArrayList(Collections2.filter(backendStories, new Predicate<String>() {
+      @Override
+      public boolean apply(String storyFileName) {
+        return !storyFileName.endsWith("ui.story");
+      }
+    }));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchStory.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchStory.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchStory.java
deleted file mode 100644
index ce6b9cb..0000000
--- a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchStory.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.ambari.logsearch.story;
-
-import org.apache.ambari.logsearch.steps.LogSearchApiSteps;
-import org.apache.ambari.logsearch.steps.SolrSteps;
-import org.apache.ambari.logsearch.steps.LogSearchDockerSteps;
-import org.jbehave.core.configuration.Configuration;
-import org.jbehave.core.configuration.MostUsefulConfiguration;
-import org.jbehave.core.io.LoadFromClasspath;
-import org.jbehave.core.io.StoryPathResolver;
-import org.jbehave.core.io.UnderscoredCamelCaseResolver;
-import org.jbehave.core.junit.JUnitStory;
-import org.jbehave.core.reporters.Format;
-import org.jbehave.core.reporters.StoryReporterBuilder;
-import org.jbehave.core.steps.InjectableStepsFactory;
-import org.jbehave.core.steps.InstanceStepsFactory;
-import org.junit.Test;
-
-abstract public class LogSearchStory extends JUnitStory {
-  @Override
-  public Configuration configuration() {
-    StoryPathResolver storyPathResolver = new UnderscoredCamelCaseResolver(".story");
-    return new MostUsefulConfiguration()
-      .useStoryPathResolver(storyPathResolver)
-      .useStoryLoader(new LoadFromClasspath(this.getClass()))
-      .useStoryReporterBuilder(
-        new StoryReporterBuilder().withFailureTrace(true).withDefaultFormats().withFormats(Format.CONSOLE, Format.TXT));
-  }
-
-  @Override
-  public InjectableStepsFactory stepsFactory() {
-    return new InstanceStepsFactory(configuration(),
-      new LogSearchDockerSteps(),
-      new SolrSteps(),
-      new LogSearchApiSteps());
-  }
-
-  @Test
-  public void run() throws Throwable {
-    super.run();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchUIStories.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchUIStories.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchUIStories.java
new file mode 100644
index 0000000..eb2a180
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogSearchUIStories.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.story;
+
+import org.apache.ambari.logsearch.domain.StoryDataRegistry;
+import org.apache.ambari.logsearch.steps.LogSearchDockerSteps;
+import org.apache.ambari.logsearch.steps.LogSearchUISteps;
+import org.jbehave.core.configuration.Configuration;
+import org.jbehave.core.Embeddable;
+import org.jbehave.core.embedder.executors.SameThreadExecutors;
+import org.jbehave.core.io.LoadFromClasspath;
+import org.jbehave.core.io.StoryFinder;
+import org.jbehave.core.junit.JUnitStories;
+import org.jbehave.core.reporters.StoryReporterBuilder;
+import org.jbehave.core.steps.InjectableStepsFactory;
+import org.jbehave.core.steps.InstanceStepsFactory;
+import org.jbehave.web.selenium.RemoteWebDriverProvider;
+import org.jbehave.web.selenium.SeleniumConfiguration;
+import org.jbehave.web.selenium.SeleniumContext;
+import org.jbehave.web.selenium.WebDriverProvider;
+import org.jbehave.web.selenium.WebDriverScreenshotOnFailure;
+import org.openqa.selenium.Platform;
+import org.openqa.selenium.remote.DesiredCapabilities;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.jbehave.core.io.CodeLocations.codeLocationFromClass;
+import static org.jbehave.core.reporters.Format.CONSOLE;
+import static org.jbehave.core.reporters.Format.HTML;
+import static org.jbehave.core.reporters.Format.TXT;
+import static org.jbehave.core.reporters.Format.XML;
+
+public class LogSearchUIStories extends JUnitStories {
+
+  private WebDriverProvider driverProvider;
+  private SeleniumContext context;
+
+  public LogSearchUIStories() {
+    // TODO: get docker host from a runCommand funtion
+    String hubUrl = "http://localhost:4444/wd/hub";
+    System.setProperty("REMOTE_WEBDRIVER_URL", hubUrl);
+    DesiredCapabilities capability = DesiredCapabilities.firefox();
+    capability.setPlatform(Platform.LINUX);
+    capability.setVersion("45.8.0");
+    driverProvider = new RemoteWebDriverProvider(capability);
+    StoryDataRegistry.INSTANCE.setWebDriverProvider(driverProvider);
+    context = new SeleniumContext();
+    configuredEmbedder().useExecutorService(new SameThreadExecutors().create(configuredEmbedder().embedderControls()));
+  }
+
+  @Override
+  public Configuration configuration() {
+    Class<? extends Embeddable> embeddableClass = this.getClass();
+    return new SeleniumConfiguration()
+      .useSeleniumContext(context)
+      .useWebDriverProvider(driverProvider)
+      .useStoryLoader(new LoadFromClasspath(embeddableClass))
+      .useStoryReporterBuilder(new StoryReporterBuilder()
+        .withCodeLocation(codeLocationFromClass(embeddableClass))
+        .withDefaultFormats()
+        .withFormats(CONSOLE, TXT, HTML, XML));
+  }
+
+  @Override
+  public InjectableStepsFactory stepsFactory() {
+    Configuration configuration = configuration();
+    return new InstanceStepsFactory(configuration, new LogSearchDockerSteps(), new LogSearchUISteps(driverProvider),
+      new WebDriverScreenshotOnFailure(driverProvider, configuration.storyReporterBuilder()));
+  }
+
+  @Override
+  protected List<String> storyPaths() {
+    return new StoryFinder()
+      .findPaths(codeLocationFromClass(this.getClass()).getFile(), Arrays.asList("**/*.ui.story"), null);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogfeederParsingStory.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogfeederParsingStory.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogfeederParsingStory.java
deleted file mode 100644
index c502cc4..0000000
--- a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/story/LogfeederParsingStory.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.ambari.logsearch.story;
-
-public class LogfeederParsingStory extends LogSearchStory {
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/AbstractPage.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/AbstractPage.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/AbstractPage.java
new file mode 100644
index 0000000..b6d0a58
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/AbstractPage.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.web;
+
+import org.jbehave.web.selenium.WebDriverPage;
+import org.jbehave.web.selenium.WebDriverProvider;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.fail;
+
+public abstract class AbstractPage extends WebDriverPage {
+
+  public AbstractPage(WebDriverProvider driverProvider) {
+    super(driverProvider);
+  }
+
+  public void found(String text) {
+    found(getPageSource(), text);
+  }
+
+  public void found(String pageSource, String text) {
+    if (!pageSource.contains(escapeHtml(text))) {
+      fail("Text: '" + text + "' not found in page '" + pageSource + "'");
+    }
+  }
+
+  public void found(List<String> texts) {
+    for (String text : texts) {
+      found(text);
+    }
+  }
+
+  public void notFound(String text) {
+    notFound(getPageSource(), text);
+  }
+
+  public void notFound(String pageSource, String text) {
+    assertThat(pageSource.contains(escapeHtml(text)), is(false));
+  }
+
+  private String escapeHtml(String text) {
+    return text.replace("<", "&lt;").replace(">", "&gt;");
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/Home.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/Home.java b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/Home.java
new file mode 100644
index 0000000..6c576d4
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/java/org/apache/ambari/logsearch/web/Home.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.ambari.logsearch.web;
+
+import org.apache.ambari.logsearch.domain.StoryDataRegistry;
+import org.jbehave.web.selenium.WebDriverProvider;
+
+import java.util.concurrent.TimeUnit;
+
+public class Home extends AbstractPage {
+
+  public Home(WebDriverProvider driverProvider) {
+    super(driverProvider);
+  }
+
+  public void open() {
+    get(String.format("http://%s:%d/index.html",
+      StoryDataRegistry.INSTANCE.getDockerHost(),
+      StoryDataRegistry.INSTANCE.getLogsearchPort()));
+    manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/log_search_api_query_story.story
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/log_search_api_query_story.story b/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/log_search_api_query_story.story
deleted file mode 100644
index cfaa359..0000000
--- a/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/log_search_api_query_story.story
+++ /dev/null
@@ -1,17 +0,0 @@
-Meta:
-
-Narrative:
-As a user
-I want to perform queries against Log Search api
-So that I can validate the json outputs
-
-Scenario: scenario description
-
-Given logsearch docker container
-When LogSearch api query sent: <apiQuery>
-Then The api query result is <jsonResult>
-
-Examples:
-|apiQuery|jsonResult|
-|/api/v1/service/logs/schema/fields|service-log-schema.json|
-|/api/v1/service/logs/levels/counts?page=0&pageSize=25&startIndex=0&q=*%3A*|service-log-level-counts-values.json|
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/logfeeder_parsing_story.story
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/logfeeder_parsing_story.story b/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/logfeeder_parsing_story.story
deleted file mode 100644
index 388e624..0000000
--- a/ambari-logsearch/ambari-logsearch-it/src/test/resources/org/apache/ambari/logsearch/story/logfeeder_parsing_story.story
+++ /dev/null
@@ -1,20 +0,0 @@
-Story Service logs are parsed and stored into Solr
-
-Narrative:
-As a user
-I want to start logsearch/logfeeder/solr components in a docker container with test logs
-So that I can parse and store the logs into Solr
-
-Scenario: Number of logs for components
-
-Given logsearch docker container
-When logfeeder started (parse logs & send data to solr)
-Then the number of <component> docs is: <docSize>
-
-Examples:
-|component|docSize|
-|logsearch_app|1|
-|zookeeper|3|
-|hst_agent|4|
-|secure_log|11|
-|system_message|17|

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/log_search_api_query_story.story
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/log_search_api_query_story.story b/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/log_search_api_query_story.story
new file mode 100644
index 0000000..0af00f5
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/log_search_api_query_story.story
@@ -0,0 +1,17 @@
+Meta:
+
+Narrative:
+As a user
+I want to perform queries against Log Search api
+So that I can validate the json outputs
+
+Scenario: Log Search API JSON responses
+
+Given logsearch docker container
+When LogSearch api query sent: <apiQuery>
+Then The api query result is <jsonResult>
+
+Examples:
+|apiQuery|jsonResult|
+|/api/v1/service/logs/schema/fields|service-log-schema.json|
+|/api/v1/service/logs/levels/counts?page=0&pageSize=25&startIndex=0&q=*%3A*|service-log-level-counts-values.json|
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/logfeeder_parsing_story.story
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/logfeeder_parsing_story.story b/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/logfeeder_parsing_story.story
new file mode 100644
index 0000000..388e624
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/backend/logfeeder_parsing_story.story
@@ -0,0 +1,20 @@
+Story Service logs are parsed and stored into Solr
+
+Narrative:
+As a user
+I want to start logsearch/logfeeder/solr components in a docker container with test logs
+So that I can parse and store the logs into Solr
+
+Scenario: Number of logs for components
+
+Given logsearch docker container
+When logfeeder started (parse logs & send data to solr)
+Then the number of <component> docs is: <docSize>
+
+Examples:
+|component|docSize|
+|logsearch_app|1|
+|zookeeper|3|
+|hst_agent|4|
+|secure_log|11|
+|system_message|17|

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/selenium/login.ui.story
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/selenium/login.ui.story b/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/selenium/login.ui.story
new file mode 100644
index 0000000..543c211
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-it/src/test/resources/stories/selenium/login.ui.story
@@ -0,0 +1,20 @@
+Meta:
+
+Narrative:
+As a user
+I want to start LogSearch services and login to the UI
+So that I can validate the proper user
+
+Scenario: login with admin/admin
+
+Given logsearch docker container
+And open logsearch home page
+When login with admin / admin
+Then page contains text: 'Service Logs'
+
+Scenario: login with admin and wrong password
+
+Given logsearch docker container
+And open logsearch home page
+When login with admin / wrongpassword
+Then page does not contain text: 'Service Logs'
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/docker/Dockerfile
----------------------------------------------------------------------
diff --git a/ambari-logsearch/docker/Dockerfile b/ambari-logsearch/docker/Dockerfile
index 6e8ea3e..d399fc6 100644
--- a/ambari-logsearch/docker/Dockerfile
+++ b/ambari-logsearch/docker/Dockerfile
@@ -15,17 +15,22 @@ FROM centos:centos6
 RUN echo root:changeme | chpasswd
 
 RUN yum clean all -y && yum update -y
-RUN yum -y install vim wget rpm-build sudo which telnet tar openssh-server openssh-clients ntp git python-setuptools python-devel httpd lsof
+RUN yum -y install firefox-45.8.0-2.el6.centos xvfb xeyes vim wget rpm-build sudo which telnet tar openssh-server openssh-clients ntp git python-setuptools python-devel httpd lsof
 RUN rpm -e --nodeps --justdb glibc-common
 RUN yum -y install glibc-common
 
 ENV HOME /root
 
 #Install JAVA
-RUN wget --no-check-certificate --no-cookies --header "Cookie:oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u55-b13/jdk-7u55-linux-x64.rpm -O jdk-7u55-linux-x64.rpm
-RUN rpm -ivh jdk-7u55-linux-x64.rpm
+ENV JAVA_VERSION 8u31
+ENV BUILD_VERSION b13
+RUN wget --no-cookies --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/$JAVA_VERSION-$BUILD_VERSION/jdk-$JAVA_VERSION-linux-x64.rpm" -O jdk-8-linux-x64.rpm
+RUN rpm -ivh jdk-8-linux-x64.rpm
 ENV JAVA_HOME /usr/java/default/
 
+#Install Selenium server
+RUN wget --no-check-certificate -O /root/selenium-server-standalone.jar http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar
+
 #Install Maven
 RUN mkdir -p /opt/maven
 WORKDIR /opt/maven
@@ -34,7 +39,7 @@ RUN tar -xvzf /opt/maven/apache-maven-3.0.5-bin.tar.gz
 RUN rm -rf /opt/maven/apache-maven-3.0.5-bin.tar.gz
 
 ENV M2_HOME /opt/maven/apache-maven-3.0.5
-ENV MAVEN_OPTS -Xmx2048m -XX:MaxPermSize=256m
+ENV MAVEN_OPTS -Xmx2048m
 ENV PATH $PATH:$JAVA_HOME/bin:$M2_HOME/bin
 
 # SSH key
@@ -42,6 +47,8 @@ RUN ssh-keygen -f /root/.ssh/id_rsa -t rsa -N ''
 RUN cat /root/.ssh/id_rsa.pub > /root/.ssh/authorized_keys
 RUN chmod 600 /root/.ssh/authorized_keys
 RUN sed -ri 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
+RUN echo 'X11Forwarding yes\n' /etc/ssh/sshd_config
+RUN echo 'X11DisplayOffset 10\n' /etc/ssh/sshd_config
 
 #To allow bower install behind proxy. See https://github.com/bower/bower/issues/731
 RUN git config --global url."https://".insteadOf git://
@@ -54,7 +61,7 @@ RUN npm install -g brunch@1.7.20
 
 # Install Solr
 ENV SOLR_VERSION 5.5.2
-RUN wget --no-check-certificate -O /root/solr-$SOLR_VERSION.tgz http://archive.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz
+RUN wget --no-check-certificate -O /root/solr-$SOLR_VERSION.tgz http://public-repo-1.hortonworks.com/ARTIFACTS/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz
 RUN cd /root && tar -zxvf /root/solr-$SOLR_VERSION.tgz
 ADD bin/start.sh /root/start.sh
 ADD test-config /root/test-config

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/docker/bin/start.sh
----------------------------------------------------------------------
diff --git a/ambari-logsearch/docker/bin/start.sh b/ambari-logsearch/docker/bin/start.sh
index 1efc85c..28ebf65 100644
--- a/ambari-logsearch/docker/bin/start.sh
+++ b/ambari-logsearch/docker/bin/start.sh
@@ -92,6 +92,10 @@ function start_logfeeder() {
   touch /var/log/ambari-logsearch-logfeeder/logsearch-logfeeder.log
 }
 
+function start_selenium_server() {
+  nohup java -jar /root/selenium-server-standalone.jar > /var/log/selenium-test.log &
+}
+
 function log() {
   component_log=${COMPONENT_LOG:-"logsearch"}
   case $component_log in
@@ -101,6 +105,9 @@ function log() {
     "solr")
       tail -f /var/log/ambari-logsearch-solr/solr.log
      ;;
+    "selenium")
+      tail -f /var/log/selenium-test.log
+     ;;
      *)
       tail -f /var/log/ambari-logsearch-portal/logsearch-app.log
      ;;
@@ -109,6 +116,7 @@ function log() {
 
 create_config
 generate_keys
+start_selenium_server
 start_solr
 start_logsearch
 start_logfeeder

http://git-wip-us.apache.org/repos/asf/ambari/blob/3153b9bc/ambari-logsearch/docker/logsearch-docker.sh
----------------------------------------------------------------------
diff --git a/ambari-logsearch/docker/logsearch-docker.sh b/ambari-logsearch/docker/logsearch-docker.sh
index eab850e..4d53fa1 100755
--- a/ambari-logsearch/docker/logsearch-docker.sh
+++ b/ambari-logsearch/docker/logsearch-docker.sh
@@ -30,6 +30,11 @@ function build_logsearch_container() {
   popd
 }
 
+function get_docker_ip() {
+  local ip=$(ifconfig en0 | grep inet | awk '$1=="inet" {print $2}')
+  echo $ip
+}
+
 function start_logsearch_container() {
   setup_profile
   source $sdir/Profile
@@ -38,9 +43,10 @@ function start_logsearch_container() {
   popd
   : ${MAVEN_REPOSITORY_LOCATION:?"Please set the MAVEN_REPOSITORY_LOCATION in Profile"}
   kill_logsearch_container
+  local docker_ip=$(get_docker_ip)
   echo "Run Log Search container"
-  docker run -d --name logsearch --hostname logsearch.apache.org \
-    -v $AMBARI_LOCATION:/root/ambari -v $MAVEN_REPOSITORY_LOCATION:/root/.m2 $LOGSEARCH_EXPOSED_PORTS $LOGSEARCH_ENV_OPTS $LOGSEARCH_EXTRA_OPTS $LOGSEARCH_VOLUME_OPTS -p 9983:9983 \
+  docker run -d --name logsearch --hostname logsearch.apache.org -e DISPLAY=$docker_ip:0 \
+    -v $AMBARI_LOCATION:/root/ambari -v $MAVEN_REPOSITORY_LOCATION:/root/.m2 $LOGSEARCH_EXPOSED_PORTS $LOGSEARCH_ENV_OPTS $LOGSEARCH_EXTRA_OPTS $LOGSEARCH_VOLUME_OPTS -p 9983:9983 -p 4444:4444 -p 5910:5910 \
     -v $AMBARI_LOCATION/ambari-logsearch/ambari-logsearch-logfeeder/target/classes:/root/ambari/ambari-logsearch/ambari-logsearch-logfeeder/target/package/classes \
     -v $AMBARI_LOCATION/ambari-logsearch/ambari-logsearch-server/target/classes:/root/ambari/ambari-logsearch/ambari-logsearch-server/target/package/classes \
     -v $AMBARI_LOCATION/ambari-logsearch/ambari-logsearch-web/src/main/webapp:/root/ambari/ambari-logsearch/ambari-logsearch-server/target/package/classes/webapps/app \