chat: Add a simple chat web test
The test
- does the basic stuff (sign into chrome, get the extension...)
- visits the page and checks that the member is recognized (serving)
- sends a generic message "Vanadium Bot says Hello!" to everyone
and checks that it can be retrieved.
MultiPart: 1/2
Change-Id: I8d1b64e4e2255fc5e4b92590c306d94a4dcddfac
diff --git a/.gitignore b/.gitignore
index 6ffb93f..8f65158 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,8 @@
/clients/shell/credentials
/clients/shell/go/bin
/clients/shell/go/pkg
+/clients/web/test/ui/target
+/htmlReports
/node_modules
/tmp
/.v23
-
diff --git a/Makefile b/Makefile
index e55e677..18281a5 100644
--- a/Makefile
+++ b/Makefile
@@ -190,11 +190,55 @@
$(MAKE) -C $(VANADIUM_JS)/extension build-test
prova clients/web/test/test-*.js -f $(APP_FRAME) $(PROVA_OPTS) $(BROWSER_OPTS) $(BROWSER_OUTPUT_LOCAL)
+
+# Run UI tests for the chat web client.
+# These tests do not normally need to be run locally, but they can be if you
+# want to verify that the a specific version of chat is compatible with a
+# local (or live) version of the Vanadium extension.
+#
+# This test takes additional environment variables (typically temporary)
+# - GOOGLE_BOT_USERNAME and GOOGLE_BOT_PASSWORD (To sign into Google/Chrome)
+# - CHROME_WEBDRIVER (The path to the chrome web driver)
+# - WORKSPACE (optional, defaults to $V23_ROOT/release/projects/chat)
+# - TEST_URL (optional, defaults to https://chat.staging.v.io)
+# - NO_XVFB (optional, defaults to using Xvfb. Set to true to watch the test.)
+# - BUILD_EXTENSION (optional, defaults to using the live one. Set to true to
+# use a local build of the Vanadium extension.)
+#
+# In addition, this test requires that maven, Xvfb, and xvfb-run be installed.
+# The HTML report will be in $V23_ROOT/release/projects/chat/htmlReports
+WORKSPACE ?= $(V23_ROOT)/release/projects/chat
+TEST_URL ?= https://chat.staging.v.io
+ifndef NO_XVFB
+ XVFB := TMPDIR=/tmp xvfb-run -s '-ac -screen 0 1024x768x24'
+endif
+
+ifdef BUILD_EXTENSION
+ BUILD_EXTENSION_PROPERTY := "-DvanadiumExtensionPath=$(VANADIUM_JS)/extension/build"
+endif
+
+test-ui:
+ifdef BUILD_EXTENSION
+ make -B -C $(VANADIUM_JS)/extension build-dev
+endif
+ WORKSPACE=$(WORKSPACE) $(XVFB) \
+ mvn test \
+ -f=$(V23_ROOT)/release/projects/chat/clients/web/test/ui/pom.xml \
+ -Dtest=ChatUITest \
+ -DchromeDriverBin=$(CHROME_WEBDRIVER) \
+ -DhtmlReportsRelativePath=htmlReports \
+ -DgoogleBotUsername=$(GOOGLE_BOT_USERNAME) \
+ -DgoogleBotPassword=$(GOOGLE_BOT_PASSWORD) \
+ $(BUILD_EXTENSION_PROPERTY) \
+ -DtestUrl=$(TEST_URL)
+
clean:
- rm -rf node_modules
+ rm -rf build
rm -rf clients/shell/go/{bin,pkg}
rm -rf clients/shell/credentials
- rm -rf build
+ rm -rf clients/web/test/ui/target
+ rm -rf htmlReports
+ rm -rf node_modules
lint: node_modules
jshint .
diff --git a/clients/web/test/ui/pom.xml b/clients/web/test/ui/pom.xml
new file mode 100644
index 0000000..5fe189e
--- /dev/null
+++ b/clients/web/test/ui/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <name>Vanadium Chat</name>
+ <groupId>io.v.webdriver</groupId>
+ <artifactId>vanadium_chat</artifactId>
+ <version>0.1</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>2.3.22</version>
+ </dependency>
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-chrome-driver</artifactId>
+ <version>2.45.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.seleniumhq.selenium</groupId>
+ <artifactId>selenium-support</artifactId>
+ <version>2.45.0</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18</version>
+ <configuration>
+ <forkMode>always</forkMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.2</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals><goal>add-source</goal></goals>
+ <configuration>
+ <sources>
+ <source>${env.V23_ROOT}/release/javascript/core/test/ui/</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/clients/web/test/ui/src/test/java/io/v/webdriver/chat/ChatUITest.java b/clients/web/test/ui/src/test/java/io/v/webdriver/chat/ChatUITest.java
new file mode 100644
index 0000000..7020a81
--- /dev/null
+++ b/clients/web/test/ui/src/test/java/io/v/webdriver/chat/ChatUITest.java
@@ -0,0 +1,58 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.webdriver.chat;
+
+import org.junit.Test;
+
+import io.v.webdriver.VanadiumUITestBase;
+import io.v.webdriver.commonpages.OAuthLoginPage;
+import io.v.webdriver.htmlreport.HTMLReportData;
+import io.v.webdriver.htmlreport.HTMLReporter;
+
+/**
+ * UI tests for Vanadium Chat Web Application.
+ *
+ * @author alexfandrianto@google.com
+ */
+public class ChatUITest extends VanadiumUITestBase {
+ /**
+ * System property name for the test url. This will be set from the mvn command line.
+ */
+ private static final String PROPERTY_TEST_URL = "testUrl";
+
+ private static final String TEST_NAME_INIT_PROCESS = "Chat Initialization Process";
+
+ /**
+ * Tests initialization process.
+ * <p>
+ * The process includes signing into Chrome, installing Vanadium plugin, authenticating OAuth, and
+ * visiting Chat's landing page and sending a single message.
+ */
+ @Test
+ public void testInitProcess() throws Exception {
+ HTMLReportData reportData = new HTMLReportData(TEST_NAME_INIT_PROCESS, htmlReportsDir);
+ curHTMLReportData = reportData;
+
+ super.signInAndInstallExtension(reportData);
+
+ // Get the url for the Chat web app.
+ String url = System.getProperty(PROPERTY_TEST_URL);
+ System.out.printf("Url: %s\n", url);
+ MainPage mainPage = new MainPage(driver, url, reportData);
+ if (url.equals("https://chat.staging.v.io") || url.equals("https://chat.v.io")) {
+ // These are OAuth protected pages.
+ OAuthLoginPage oauthLoginPage = mainPage.goToPage();
+ oauthLoginPage.login();
+ } else {
+ mainPage.goWithoutTakingScreenshot();
+ }
+ super.handleCaveatTab(reportData);
+ mainPage.validatePage();
+
+ // Write html report.
+ HTMLReporter reporter = new HTMLReporter(reportData);
+ reporter.generateReport();
+ }
+}
diff --git a/clients/web/test/ui/src/test/java/io/v/webdriver/chat/MainPage.java b/clients/web/test/ui/src/test/java/io/v/webdriver/chat/MainPage.java
new file mode 100644
index 0000000..540aa8d
--- /dev/null
+++ b/clients/web/test/ui/src/test/java/io/v/webdriver/chat/MainPage.java
@@ -0,0 +1,172 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.webdriver.chat;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+
+import org.junit.Assert;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
+import org.openqa.selenium.TakesScreenshot;
+import org.openqa.selenium.TimeoutException;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import io.v.webdriver.Util;
+import io.v.webdriver.commonpages.OAuthLoginPage;
+import io.v.webdriver.commonpages.PageBase;
+import io.v.webdriver.htmlreport.HTMLReportData;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The main page of Vanadium Chat.
+ *
+ * @author alexfandrianto@google.com
+ */
+public class MainPage extends PageBase {
+
+ /**
+ * The google username is set when this page is constructed.
+ * It is used to identify the current chat user during the test.
+ */
+ private static final String PROPERTY_GOOGLE_BOT_USERNAME = "googleBotUsername";
+ private final String username;
+
+ /**
+ * Checks whether the specified user shows up in the members section.
+ * Note: Does not confirm that this particular tab has connected, so don't
+ * rely on this to work if the user is already in the chatroom from some other
+ * source.
+ */
+ private static class CheckMember implements Predicate<WebDriver> {
+ private final String user;
+
+ public CheckMember(String user) {
+ this.user = user;
+ }
+
+ @Override
+ public boolean apply(WebDriver driver) {
+ // The members are in a div with the 'members' class.
+ // The div has an unordered list of members, and the list items will eventually
+ // have the user inside of it.
+ System.out.println("There are " + driver.findElements(
+ By.xpath("//div[@class='members']/ul/li")).size() + " users in the chatroom.");
+ List<WebElement> matchingMembers = driver.findElements(
+ By.xpath("//div[@class='members']/ul/li/span[text()='" + user + "']"));
+
+ return matchingMembers.size() > 0;
+ }
+ }
+
+ /**
+ * Checks whether the given user sent a specific message.
+ * Note: This check is dumb, so don't rely on this to be accurate when
+ * checking if a message was sent twice.
+ */
+ private static class CheckMessage implements Predicate<WebDriver> {
+ private final String user;
+ private final String message;
+
+ public CheckMessage(String user, String message) {
+ this.user = user;
+ this.message = message;
+ }
+
+ @Override
+ public boolean apply(WebDriver driver) {
+ // The messages are in a div with the 'messages' class.
+ // Each message contains multiple spans. 'sender' contains the sender's
+ // username, while 'text' contains the message text.
+ List<WebElement> allMessages = driver.findElements(
+ By.xpath("//div[@class='messages']/div"));
+
+ System.out.println("There are " + allMessages.size() + " messages in the chatroom.");
+
+ for (WebElement messageElem : allMessages) {
+ // sender's text must match the user.
+ WebElement sender = messageElem.findElement(By.xpath("span[@class='sender' and text()='" + user + "']"));
+
+ // text's text must match the message.
+ WebElement text = messageElem.findElement(By.xpath("span[@class='text' and text()='" + message + "']"));
+
+ // We found a matching message if both sender and text are not null.
+ if (sender != null && text != null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ public MainPage(WebDriver driver, String url, HTMLReportData htmlReportData) {
+ super(driver, url, htmlReportData);
+
+ username = System.getProperty(PROPERTY_GOOGLE_BOT_USERNAME);
+ }
+
+ public OAuthLoginPage goToPage() {
+ super.goWithoutTakingScreenshot();
+ // The first time going to the main page, it will ask for oauth login.
+ return new OAuthLoginPage(driver, htmlReportData);
+ }
+
+ public void validatePage() {
+ // Verify that the user shows up in the member location.
+ log("Check that user is a member.");
+ checkUserIsMember();
+
+ // Verify that the user can send a chat and see it appear.
+ // Note: This check assumes the chats go through the server before showing
+ // up on the client side. As of writing, this is the case.
+ log("Send chat message.");
+ checkMessageIsDelivered();
+ }
+
+ public void checkUserIsMember() {
+ CheckMember memberChecker = new CheckMember(username);
+ try {
+ wait.until(memberChecker);
+ } catch(TimeoutException e) {
+ e.printStackTrace();
+ Assert.fail(e.toString());
+ }
+ Util.takeScreenshot((TakesScreenshot)driver, "is-member-" + username + ".png", "Is Member? " + username, htmlReportData);
+ }
+
+ // Sends a non-obtrusive message to all chatters using the chat app.
+ // Note: All users of Vanadium chat are in the same chat room, so the message
+ // sent should not be spammy.
+ public void checkMessageIsDelivered() {
+ // First, let's find the text box where we can enter our message.
+ // Since its React ID could easily change (autogenerated), we use xpath to identify it.
+ WebElement input = driver.findElement(By.xpath("//div[@class='compose']/form/input"));
+ String message = "Vanadium Bot says Hello!";
+ input.sendKeys(message);
+ Util.takeScreenshot((TakesScreenshot)driver, "message-written.png", "Message Written", htmlReportData);
+
+ // And then send the text.
+ input.sendKeys(Keys.RETURN);
+ Util.takeScreenshot((TakesScreenshot)driver, "message-sent.png", "Message Sent", htmlReportData);
+
+ // Now, wait until the text shows up on screen!
+ CheckMessage messageChecker = new CheckMessage(username, message);
+ try {
+ wait.until(messageChecker);
+ } catch(TimeoutException e) {
+ e.printStackTrace();
+ Assert.fail(e.toString());
+ }
+ Util.takeScreenshot((TakesScreenshot)driver, "message-received.png", "Message Received", htmlReportData);
+ }
+}