ca.nrc.cadc.web.selenium.AbstractWebApplicationIntegrationTest.java Source code

Java tutorial

Introduction

Here is the source code for ca.nrc.cadc.web.selenium.AbstractWebApplicationIntegrationTest.java

Source

/*
 * ***********************************************************************
 * ******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
 * *************  CENTRE CANADIEN DE DONNES ASTRONOMIQUES  **************
 * <p/>
 * (c) 2010.                            (c) 2010.
 * Government of Canada                 Gouvernement du Canada
 * National Research Council            Conseil national de recherches
 * Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
 * All rights reserved                  Tous droits rservs
 * <p/>
 * NRC disclaims any warranties,        Le CNRC dnie toute garantie
 * expressed, implied, or               nonce, implicite ou lgale,
 * statutory, of any kind with          de quelque nature que ce
 * respect to the software,             soit, concernant le logiciel,
 * including without limitation         y compris sans restriction
 * any warranty of merchantability      toute garantie de valeur
 * or fitness for a particular          marchande ou de pertinence
 * purpose. NRC shall not be            pour un usage particulier.
 * liable in any event for any          Le CNRC ne pourra en aucun cas
 * damages, whether direct or           tre tenu responsable de tout
 * indirect, special or general,        dommage, direct ou indirect,
 * consequential or incidental,         particulier ou gnral,
 * arising from the use of the          accessoire ou fortuit, rsultant
 * software.  Neither the name          de l'utilisation du logiciel. Ni
 * of the National Research             le nom du Conseil National de
 * Council of Canada nor the            Recherches du Canada ni les noms
 * names of its contributors may        de ses  participants ne peuvent
 * be used to endorse or promote        tre utiliss pour approuver ou
 * products derived from this           promouvoir les produits drivs
 * software without specific prior      de ce logiciel sans autorisation
 * written permission.                  pralable et particulire
 * par crit.
 * <p/>
 * This file is part of the             Ce fichier fait partie du projet
 * OpenCADC project.                    OpenCADC.
 * <p/>
 * OpenCADC is free software:           OpenCADC est un logiciel libre ;
 * you can redistribute it and/or       vous pouvez le redistribuer ou le
 * modify it under the terms of         modifier suivant les termes de
 * the GNU Affero General Public        la GNU Affero General Public
 * License as published by the          License? telle que publie
 * Free Software Foundation,            par la Free Software Foundation
 * either version 3 of the              : soit la version 3 de cette
 * License, or (at your option)         licence, soit ( votre gr)
 * any later version.                   toute version ultrieure.
 * <p/>
 * OpenCADC is distributed in the       OpenCADC est distribu
 * hope that it will be useful,         dans lespoir quil vous
 * but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
 * without even the implied             GARANTIE : sans mme la garantie
 * warranty of MERCHANTABILITY          implicite de COMMERCIALISABILIT
 * or FITNESS FOR A PARTICULAR          ni dADQUATION  UN OBJECTIF
 * PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
 * General Public License for           Gnrale Publique GNU Affero
 * more details.                        pour plus de dtails.
 * <p/>
 * You should have received             Vous devriez avoir reu une
 * a copy of the GNU Affero             copie de la Licence Gnrale
 * General Public License along         Publique GNU Affero avec
 * with OpenCADC.  If not, see          OpenCADC ; si ce nest
 * <http://www.gnu.org/licenses/>.      pas le cas, consultez :
 * <http://www.gnu.org/licenses/>.
 * <p/>
 * ***********************************************************************
 */

package ca.nrc.cadc.web.selenium;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import junit.framework.AssertionFailedError;
import org.apache.commons.io.FileUtils;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.opera.OperaOptions;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.safari.SafariOptions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import ca.nrc.cadc.util.StringUtil;

/**
 * Subclasses of this should have the necessary tools to create an automated web application test.
 * <p>
 * TODO: Clean this up to only have shared code.
 */
public abstract class AbstractWebApplicationIntegrationTest {
    // One minute is just too long.
    private static final int TIMEOUT_IN_SECONDS = 60;
    private static final int TIMEOUT_IN_MILLISECONDS = (TIMEOUT_IN_SECONDS * 1000);
    private static final String SELENIUM_SERVER_URL_ENDPOINT = "/wd/hub";
    private static final Map<String, MutableCapabilities> CAPABILITIES_LOOKUP = new HashMap<>();

    static {
        CAPABILITIES_LOOKUP.put("firefox", new FirefoxOptions());
        CAPABILITIES_LOOKUP.put("safari", new SafariOptions());
        CAPABILITIES_LOOKUP.put("chrome", new ChromeOptions());
        CAPABILITIES_LOOKUP.put("opera", new OperaOptions());
    }

    private int currentWaitTime;
    private boolean failOnTimeout;

    private static String seleniumServerURL = System.getProperty("selenium.server.url");
    protected static String webURL;
    protected static MutableCapabilities driverCapabilities;
    protected static WebDriver driver;
    protected static String username;
    protected static String password;

    @Rule
    public TestWatcher screenshotWatcher = new TestWatcher() {
        @Override
        protected void failed(Throwable e, Description description) {
            try {
                e.printStackTrace(System.err);
                captureScreenShot(description.getClassName() + "." + description.getMethodName());
            } catch (Exception e2) {
                System.err.println("Unable to capture screenshot: ");
                e2.printStackTrace(System.err);
            }
        }

        @Override
        protected void succeeded(Description description) {

        }
    };

    /**
     * Override to set up your specific external resource.
     */
    @BeforeClass
    public static void setUp() {
        try {
            final String seleniumURL = seleniumServerURL
                    + (seleniumServerURL.contains(SELENIUM_SERVER_URL_ENDPOINT) ? ""
                            : SELENIUM_SERVER_URL_ENDPOINT);
            System.out.println("Connecting to " + seleniumURL);

            final String browserDriverName = System.getProperty("driver");
            driverCapabilities = CAPABILITIES_LOOKUP.get(browserDriverName.toLowerCase());
            driverCapabilities.setCapability(CapabilityType.HAS_NATIVE_EVENTS, true);
            driverCapabilities.setCapability(CapabilityType.SUPPORTS_JAVASCRIPT, true);
            driverCapabilities.setCapability(CapabilityType.TAKES_SCREENSHOT, true);
            driverCapabilities.setCapability(CapabilityType.ACCEPT_INSECURE_CERTS, true);
            driverCapabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);

            driver = new RemoteWebDriver(new URL(seleniumURL), driverCapabilities);

            username = System.getProperty("user.name");
            password = System.getProperty("user.password");
            webURL = System.getProperty("web.app.url");
        } catch (MalformedURLException e) {
            System.err.println("Can't create URL.");
            e.printStackTrace(System.err);
            throw new RuntimeException(e);
        }

        driver.manage().window().maximize();

        final WebDriver.Timeouts timeouts = driver.manage().timeouts();

        // Safari does not support setTimeout.
        if (!driverCapabilities.getBrowserName().contains("afari")) {
            // Set the timeout to four minutes.
            timeouts.pageLoadTimeout(TIMEOUT_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
        }

        timeouts.setScriptTimeout(TIMEOUT_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
    }

    /**
     * Override to tear down your specific external resource.
     */
    @AfterClass
    public static void tearDown() {
        if (driver != null) {
            try {
                driver.manage().deleteAllCookies();
                driver.quit();
            } catch (Exception de) {
                System.err.println("Driver could not quit!");
                de.printStackTrace(System.err);
            }
        }

        System.out.println("Finished.");
    }

    protected void captureScreenShot(final String outputFileName) throws IOException {
        final String filename = outputFileName + ".png";
        final WebDriver augmentedDriver = new Augmenter().augment(driver);
        final File sourceFile = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);

        FileUtils.copyFile(sourceFile, new File("./" + filename));

        System.err.println(String.format("Saved screenshot as '%s'", filename));
    }

    public AbstractWebApplicationIntegrationTest() {
        System.out.println("Web URL: " + webURL);
        System.out.println("Selenium Server: " + seleniumServerURL);
        System.out.println("Done with Abstract Web Test constructor.");
    }

    public <T extends AbstractTestWebPage> T goToMain(final Class<T> pageClass) throws Exception {
        return goTo("", "", pageClass);
    }

    /**
     * Visit the given path with a query attached to it.  Return the page with
     * the given class.
     *
     * @param path      The navigation path.
     * @param query     The query.
     * @param pageClass The class of the returned instance.
     * @param <T>       The type of Page to return.
     * @return A page element.
     *
     * @throws Exception For any test execution errors
     */
    public <T extends AbstractTestWebPage> T goTo(final String path, final String query, final Class<T> pageClass)
            throws Exception {
        return goTo(webURL, path, query, pageClass);
    }

    /**
     * Visit the given path with a query attached to it.  Return the page with
     * the given class.
     *
     * @param baseURL   The base URL.
     * @param path      The navigation path.
     * @param query     The query.
     * @param pageClass The class of the returned instance.
     * @param <T>       The type of Page to return.
     * @return A page element.
     *
     * @throws Exception For any test execution errors
     */
    public <T extends AbstractTestWebPage> T goTo(final String baseURL, final String path, final String query,
            final Class<T> pageClass) throws Exception {
        final String webAppURL = baseURL + path + (StringUtil.hasText(query) ? ("?" + query) : "");
        System.out.println("Visiting: " + webAppURL);
        driver.get(webAppURL);

        final Class[] constructorArgTypes = new Class[] { WebDriver.class };
        final Constructor<T> constructor = pageClass.getConstructor(constructorArgTypes);
        return constructor.newInstance(driver);
    }

    public void goBack() {
        driver.navigate().back();
    }

    /**
     * Like assertTrue, but fails at the end of the test (during tearDown)
     *
     * @param b The boolean flag to check for truthiness.
     */
    public void verifyTrue(final boolean b) {
        if (!b) {
            throw new IllegalArgumentException("Verification failed.");
        }
    }

    public void verifyEquals(final Object o1, final Object o2) {
        verifyTrue(o1.equals(o2));
    }

    public void check(final By by) throws Exception {
        click(by);
    }

    public void uncheck(final By by) throws Exception {
        if (find(by).isSelected()) {
            click(by);
        }
    }

    public WebElement find(final By by) {
        try {
            return driver.findElement(by);
        } catch (Throwable e) {
            System.err.println("No element found: " + by.toString());
            return null;
        }
    }

    public void click(final By by) throws Exception {
        waitForElementPresent(by);
        click(find(by));
    }

    public void click(final WebElement elem) {
        elem.click();
    }

    public void resetForm() throws Exception {
        resetForm(By.cssSelector("[type=\"reset\"]"));
    }

    public void resetForm(final By resetButtonBy) throws Exception {
        click(resetButtonBy);
    }

    public void verifyElementChecked(final By by) throws Exception {
        verifyTrue(find(by).isSelected());
    }

    public void verifyElementUnChecked(final By by) throws Exception {
        verifyFalse(find(by).isSelected());
    }

    public boolean elementExists(final By by) throws Exception {
        return (find(by) != null);
    }

    public void verifyElementPresent(final By by) throws Exception {
        final WebElement webElement = find(by);
        verifyFalse(webElement == null);
    }

    public void verifyDisabledInput(final String idSelector) throws Exception {
        final Object obj = executeJavaScript("return document.getElementById('" + idSelector + "').disabled;");
        verifyTrue((obj != null) && ((Boolean) obj));
    }

    public void verifyElementNotPresent(final By by) throws Exception {
        verifyTrue((find(by) == null));
    }

    /**
     * Issue a drag and drop command.
     *
     * @param source      The source element.
     * @param destination The to (target) element to drop into.
     * @throws Exception For any test execution errors.
     */
    public void dragAndDrop(final By source, final By destination) throws Exception {
        (new Actions(driver)).dragAndDrop(find(source), find(destination)).perform();
    }

    /**
     * Scroll a container (e.g. div) until the element with elementID is
     * visible.
     *
     * @param elementID           The ID of the element to find.
     * @param containerToScrollID The container to scroll.
     * @throws Exception For any test execution errors
     */
    public void scrollVerticallyIntoView(final String elementID, final String containerToScrollID)
            throws Exception {
        final String script = "var myElement = document.getElementById('" + elementID + "');"
                + "var topPos = myElement.offsetTop;" + "document.getElementById('" + containerToScrollID
                + "').scrollTop = topPos;";

        ((JavascriptExecutor) driver).executeScript(script);
    }

    /**
     * Scroll the Grid.  This is for cadcVOTV grids.
     *
     * @param elementIDToScroll The ID of the container.
     * @throws Exception For any test execution errors
     */
    protected void scrollGrid(final String elementIDToScroll) throws Exception {
        final String findByClassNameLoop = "for (i in elems) {"
                + "if((' ' + elems[i].className + ' ').indexOf(' slick-viewport ') > -1) {"
                + "targetDiv = elems[i];break;" + "}}";
        final String script = "var objDiv = document.getElementById('" + elementIDToScroll
                + "'), targetDiv; var elems = objDiv.getElementsByTagName('div'), i;" + findByClassNameLoop
                + " targetDiv.scrollTop += 25;";

        executeJavaScript(script);
    }

    /**
     * Scroll the Grid.  This is for cadcVOTV grids.
     *
     * @param elementIDToScroll The ID of the container.
     * @throws Exception For any test execution errors
     */
    protected void scrollGridHorizontally(final String elementIDToScroll) throws Exception {
        final String findByClassNameLoop = "for (i in elems) {"
                + "if((' ' + elems[i].className + ' ').indexOf(' slick-pane-right ') > -1) {"
                + "targetDiv = elems[i];break;" + "}}";
        final String script = "var objDiv = document.getElementById('" + elementIDToScroll
                + "'), targetDiv; var elems = objDiv.getElementsByTagName('div'), i;" + findByClassNameLoop
                + " targetDiv.scrollRight += 125;";

        executeJavaScript(script);
    }

    public void verifyTextPresent(final By by, final String value) throws Exception {
        verifyTrue(getText(by).contains(value));
    }

    public void verifyTextMatches(final By by, final String regex) throws Exception {
        verifyTrue(getText(by).matches(regex));
    }

    public void verifyText(final By by, final String value) throws Exception {
        verifyEquals(value, getText(by));
    }

    public String getText(final By by) throws Exception {
        return find(by).getText();
    }

    public boolean isTextPresent(final String text) throws Exception {
        return driver.getPageSource().contains(text);
    }

    public void verifyTextPresent(final String text) throws Exception {
        verifyTrue(isTextPresent(text));
    }

    public void verifyTextNotPresent(final String text) throws Exception {
        verifyFalse(isTextPresent(text));
    }

    public void verifyFalse(final boolean b) {
        if (b) {
            throw new IllegalArgumentException("Verification failed.");
        }
    }

    public String getName() {
        return this.getClass().getName();
    }

    public void waitForTextPresent(final String text) throws Exception {
        while (!driver.getPageSource().contains(text)) {
            waitOneSecond();
        }

        waitOneSecond();
        setCurrentWaitTime(0);
    }

    /**
     * Wait for text to be present in the given locator.
     *
     * @param by   Finder element.
     * @param text Text to wait for.
     * @throws Exception For any test execution errors
     * @deprecated @see AbstractTestWebPage#waitForTextPresent(By, String)
     */
    public void waitForTextPresent(final By by, final String text) throws Exception {
        waitForElementPresent(by);
        while (!find(by).getText().contains(text)) {
            waitFor(500L);
        }
    }

    public Object executeJavaScript(final String javaScript) throws Exception {
        return ((JavascriptExecutor) driver).executeScript(javaScript);
    }

    public void hover(final WebElement element) throws Exception {
        final Actions action = new Actions(driver);
        action.moveToElement(element).click().build().perform();
    }

    public void waitForElementVisible(final By by) throws Exception {
        assert (waitUntil(ExpectedConditions.visibilityOfElementLocated(by)) != null);
    }

    public void waitForElementInvisible(final By by) throws Exception {
        assert (waitUntil(ExpectedConditions.invisibilityOfElementLocated(by)) != null);
    }

    public void waitForElementPresent(final By by) throws Exception {
        if (waitUntil(ExpectedConditions.presenceOfElementLocated(by)) == null) {
            fail("Could not find " + by.toString());
        }
    }

    public void waitForElementNotPresent(final By by) throws Exception {
        waitUntil(ExpectedConditions.invisibilityOfElementLocated(by));
    }

    public <V> V waitUntil(final ExpectedCondition<V> expectedCondition) throws Exception {
        final WebDriverWait webDriverWait = new WebDriverWait(driver, TIMEOUT_IN_SECONDS);
        return webDriverWait.until(expectedCondition);
    }

    public String getCurrentWindowHandle() throws Exception {
        return driver.getWindowHandle();
    }

    public WebDriver selectWindow(final String windowHandle) throws Exception {
        return driver.switchTo().window(windowHandle);
    }

    public void closeWindow(final String windowHandle) throws Exception {
        selectWindow(windowHandle).close();
    }

    public void waitFor(final int seconds) throws Exception {
        int count = 0;
        while (count <= seconds) {
            waitOneSecond();
            count++;
        }

        setCurrentWaitTime(0);
    }

    public int getCurrentWaitTime() {
        return currentWaitTime;
    }

    protected void setCurrentWaitTime(final int currentWaitTime) {
        this.currentWaitTime = currentWaitTime;
    }

    /**
     * Fails a test with the given message.
     *
     * @param message Message to display explaining the failure.
     */
    public void fail(final String message) {
        throw new AssertionFailedError(message);
    }

    public boolean isFailOnTimeout() {
        return failOnTimeout;
    }

    protected void setFailOnTimeout(boolean failOnTimeout) {
        this.failOnTimeout = failOnTimeout;
    }

    /**
     * Wait one second.
     *
     * @throws Exception If anything went wrong.
     */
    public void waitOneSecond() throws Exception {
        if (isFailOnTimeout() && (getCurrentWaitTime() >= TIMEOUT_IN_MILLISECONDS)) {
            fail("Timed out.");
        } else {
            setCurrentWaitTime(getCurrentWaitTime() + 1000);
            waitFor(1000L);
        }
    }

    /**
     * Allow waiting for less than a second.
     *
     * @param milliseconds Time in milliseconds to wait.
     * @throws Exception For any test execution errors
     */
    public void waitFor(final long milliseconds) throws Exception {
        Thread.sleep(milliseconds);
    }
}