com.ibm.sbt.automation.core.environment.TestEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.sbt.automation.core.environment.TestEnvironment.java

Source

/*
 *  Copyright IBM Corp. 2013
 * 
 * Licensed 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 com.ibm.sbt.automation.core.environment;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.safari.SafariDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.ibm.commons.runtime.Context;
import com.ibm.commons.util.StringUtil;
import com.ibm.sbt.automation.core.test.BaseTest;
import com.ibm.sbt.automation.core.test.BaseTest.AuthType;
import com.ibm.sbt.automation.core.test.pageobjects.ResultPage;
import com.ibm.sbt.automation.core.utils.Trace;
import com.ibm.sbt.services.endpoints.BasicEndpoint;
import com.ibm.sbt.services.endpoints.EndpointFactory;

/**
 * @author mkataria
 * @author mwallace
 * 
 * @since Jan 10, 2013
 */
public abstract class TestEnvironment extends com.ibm.sbt.test.lib.TestEnvironment {

    private WebDriver webDriver;
    private boolean quitDriver = true;
    private int loginTimeout = 20;
    private Properties properties;
    private Map<String, String> snippetParams = new HashMap<String, String>();
    private boolean smartCloud;

    protected String jsLib;

    // Need to create property bundles for localizable values
    protected String BasicLoginTitle = "Authentication";
    protected String OAuth10LoginTitle = "Log In";
    protected String OAuth20LoginTitle = "Log In to IBM Connections";
    protected String OAuth20AuthTitle = "Authorize access to IBM Connections";
    private boolean takeScreenshot;
    private String screenshotsPath;

    static final public String PROP_ENVIRONMENT = "testingEnvironment"; //$NON-NLS-1$
    static final public String PROP_JAVASCRIPT_LIB = "jslib"; //$NON-NLS-1$
    static final public String PROP_USERNAME = "username"; //$NON-NLS-1$
    static final public String PROP_PASSWORD = "password"; //$NON-NLS-1$
    static final public String PROP_RESTART_BROWSER = "restart.browser"; //$NON-NLS-1$
    static final public String PROP_LOGIN_TIMEOUT = "login.timeout"; //$NON-NLS-1$
    static final public String PROP_BROWSER = "browser"; //$NON-NLS-1$
    static final public String PROP_WEBDRIVER_IE_DRIVER = "webdriver.ie.driver"; //$NON-NLS-1$
    static final public String PROP_WEBDRIVER_CHROMER_DRIVER = "webdriver.chrome.driver"; //$NON-NLS-1$
    static final public String PROP_CHROME_BINARY = "chrome.binary"; //$NON-NLS-1$
    static final public String PROP_USER_DIR = "user.dir"; //$NON-NLS-1$
    static final public String PROP_SBT_SAMPLE_WEB_URL = "sbt.sample.web.url"; //$NON-NLS-1$
    static final public String PROP_SBT_PLAYGROUND_URL = "playground.web.url"; //$NON-NLS-1$
    static final public String PROP_ACME_SAMPLE_URL = "acme.url";
    static final public String PROP_BASIC_LOGINFORMID = "basic.loginFormId"; //$NON-NLS-1$
    static final public String PROP_BASIC_USERNAMEID = "basic.usernameId"; //$NON-NLS-1$
    static final public String PROP_BASIC_PASSWORDID = "basic.passwordId"; //$NON-NLS-1$
    static final public String PROP_BASIC_SUBMITID = "basic.submitId"; //$NON-NLS-1$
    static final public String PROP_BASIC_USERNAME = "basic.username"; //$NON-NLS-1$
    static final public String PROP_BASIC_PASSWORD = "basic.password"; //$NON-NLS-1$
    static final public String PROP_OAUTH10_LOGINFORMID = "oauth10.loginFormId"; //$NON-NLS-1$
    static final public String PROP_OAUTH10_USERNAMEID = "oauth10.usernameId"; //$NON-NLS-1$
    static final public String PROP_OAUTH10_PASSWORDID = "oauth10.passwordId"; //$NON-NLS-1$
    static final public String PROP_OAUTH10_SUBMITID = "oauth10.submitId"; //$NON-NLS-1$
    static final public String PROP_OAUTH10_USERNAME = "oauth10.username"; //$NON-NLS-1$
    static final public String PROP_OAUTH10_PASSWORD = "oauth10.password"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_LOGINFORMXPATH = "oauth20.loginFormXPath"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_USERNAMEXPATH = "oauth20.usernameXPath"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_PASSWORDXPATH = "oauth20.passwordXPath"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_SUBMITXPATH = "oauth20.submitXPath"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_GRANTXPATH = "oauth20.grantXPath"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_USERNAME = "oauth20.username"; //$NON-NLS-1$
    static final public String PROP_OAUTH20_PASSWORD = "oauth20.password"; //$NON-NLS-1$
    static final public String PROP_GENERATE_SCREENSHOTS = "screenshots.enabled"; //$NON-NLS-1$
    static final public String PROP_SCREENSHOTS_BASE_PATH = "screenshots.base"; //$NON-NLS-1$
    static final public String PROP_ENABLE_SMARTCLOUD = "enable.smartcloud"; //$NON-NLS-1$
    static final public String PROP_ENABLED_TRACE = "enable.trace"; //$NON-NLS-1$
    static final public String PROP_FIREBUG_ENABLED = "firebug.enabled"; //$NON-NLS-1$
    static final public String PROP_ENABLE_MOCKTRANSPORT = "enable.mocktransport"; //$NON-NLS-1$
    static final public String PROP_ENABLE_DEBUGTRANSPORT = "enable.debugtransport"; //$NON-NLS-1$
    static final public String PROP_ENABLE_OVERWRITETESTDATA = "enabled.overwritetestdata"; //$NON-NLS-1$

    private static final String PROP_OVERRIDE_CONNECTIONS_BE = "connections.override.url"; //$NON-NLS-1$
    private static final String PROP_OVERRIDE_SMARTCLOUD_BE = "smartcloud.override.url"; //$NON-NLS-1$

    private static final String SELENIUM_HUB = "selenium.hub";

    static final String sourceClass = TestEnvironment.class.getName();
    static final Logger logger = Logger.getLogger(sourceClass);

    /**
     * Perform cleanup
     */
    public static void cleanup() {
        TestEnvironment environment = TestEnvironmentFactory.getEnvironment();
        if (environment != null) {
            environment.snippetParams.clear();
            environment.quitDriver();
        }
    }

    /**
     * Clean the browser state. The browser cookies are removed and it is
     * returned to the default empty page
     */
    public void cleanBrowserState() {
        if (webDriver != null) {
            // because the session cookie is HttpOnly, selenium can't clear it.
            // browser cleanup happens by cosing it and letting the next
            // getWebDriver()
            // call create a new browser from scratch
            webDriver.quit();
            webDriver = null;
        }
    }

    /**
     * Default constructor
     */
    public TestEnvironment() {
        setRequiresAuthentication(true);
        properties = loadProperties();

        // JS toolkit to test with
        jsLib = properties.getProperty(PROP_JAVASCRIPT_LIB);

        String restart = properties.getProperty(PROP_RESTART_BROWSER, "false");
        quitDriver = "true".equalsIgnoreCase(restart);

        String timeout = properties.getProperty(PROP_LOGIN_TIMEOUT, "30");
        loginTimeout = Integer.parseInt(timeout);

        String ts = properties.getProperty(PROP_GENERATE_SCREENSHOTS, "false");
        takeScreenshot = "true".equalsIgnoreCase(ts);

        // Where screenshot go
        screenshotsPath = properties.getProperty(PROP_SCREENSHOTS_BASE_PATH);
        if (takeScreenshot && StringUtil.isEmpty(screenshotsPath)) {
            logger.severe(
                    "no screenshot path defined, please define system property " + PROP_SCREENSHOTS_BASE_PATH);
            takeScreenshot = false;
        }

        // Enable SmartCloud
        String enableSmartCloud = properties.getProperty(PROP_ENABLE_SMARTCLOUD, "false");
        if ("true".equalsIgnoreCase(enableSmartCloud)) {
            enableSmartCloud();
        }
        //try harder to clean up for most unexpected/faulty conditions
        final TestEnvironment self = this;
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

            @Override
            public void run() {
                try {
                    self.cleanBrowserState();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }));
    }

    /**
     * Return the specified test property
     * 
     * @param name
     * @return {String}
     */
    public static String getProperty(String name) {
        TestEnvironment environment = TestEnvironmentFactory.getEnvironment();
        String envp = environment == null ? null : environment.properties.getProperty(name);
        if (envp == null)
            return com.ibm.sbt.test.lib.TestEnvironment.getProperty(name);
        return envp;
    }

    /**
     * Enable SmartCloud environment
     */
    public void enableSmartCloud() {
        smartCloud = true;
        logger.config("Enabling Smartcloud");
        addSnippetParam("env", getProperty("smartcloud.env"));
    }

    /**
     * Enable Connections OAuth2.9 environment
     */
    public void enableConnectionsOA2() {
        smartCloud = true;
        logger.config("Enabling Smartcloud");
        String env = getProperty("connectionsOA2.env");
        if (StringUtil.isEmpty(env)) {
            env = "connectionsOA2Environment";
        }
        addSnippetParam("env", env);
    }

    /**
     * Enable SmartCloud environment
     */
    public void disableSmartCloud() {
        smartCloud = false;
        removeSnippetParam("env");
    }

    /**
     * Return true if SmartCloud environment is enabled.
     * 
     * @return {boolean}
     */
    public boolean isSmartCloud() {
        return smartCloud;
    }

    /**
     * Return true if mock transport is being used.
     * 
     * @return {boolean}
     */
    public boolean isMockTransport() {
        return properties.getProperty(PROP_ENABLE_MOCKTRANSPORT, "false").equals("true");
    }

    /**
     * Return true if debug transport is being used.
     * 
     * @return {boolean}
     */
    public boolean isDebugTransport() {
        return properties.getProperty(PROP_ENABLE_DEBUGTRANSPORT, "false").equals("true");
    }

    /**
     * Return true if test data should be overwritten.
     * 
     * @return {boolean}
     */
    public boolean isOverwriteTestdata() {
        return properties.getProperty(PROP_ENABLE_OVERWRITETESTDATA, "false").equals("true");
    }

    /**
     * Remove a snippet param
     * 
     * @param key
     */
    public void removeSnippetParam(String key) {
        snippetParams.remove(key);
    }

    /**
     * Add a snippet param which will be passed to the snippet when it is
     * invoked
     * 
     * @param key
     * @param value
     */
    public void addSnippetParam(String key, String value) {
        snippetParams.put(key, value);
    }

    /**
     * Add a snippet param which will be passed to the snippet when it is
     * invoked
     * 
     * @param key
     * @param values
     */
    public void addSnippetParam(String key, String[] values) {
        snippetParams.put(key, StringUtil.concatStrings(values, ',', true));
    }

    /**
     * Return the WebDriver
     * 
     * @return {WebDriver}
     */
    public WebDriver getWebDriver() {
        // browser to test with
        assertTrue("Requesting webdriver while using the mock transport: " + System.getProperty("testMode"),
                System.getProperty("testMode") == null);
        String browserName = System.getProperty(PROP_BROWSER);
        if (webDriver == null) {
            logger.info("Creating WebDriver instance");
            if (StringUtil.isEmpty(System.getProperty(SELENIUM_HUB))) {

                if ("ie".equals(browserName)) {
                    initInternetExplorerDriver();
                } else if ("chrome".equals(browserName)) {
                    initChromeDriver();
                } else if ("safari".equals(browserName)) {
                    webDriver = new SafariDriver();
                } else if ("headless".equals(browserName)) {
                    HtmlUnitDriver driver = new HtmlUnitDriver(true);
                    webDriver = driver;
                } else {
                    webDriver = new FirefoxDriver();
                }
            } else {
                DesiredCapabilities cap = null;
                if ("ie".equals(browserName)) {
                    cap = DesiredCapabilities.internetExplorer();
                } else if ("chrome".equals(browserName)) {
                    cap = DesiredCapabilities.chrome();
                } else if ("safari".equals(browserName)) {
                    cap = DesiredCapabilities.safari();
                } else if ("headless".equals(browserName)) {
                    cap = DesiredCapabilities.htmlUnit();

                } else {
                    cap = DesiredCapabilities.firefox();
                }
                cap.setCapability(CapabilityType.TAKES_SCREENSHOT, true);
                cap.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
                try {
                    webDriver = new RemoteWebDriver(new URL(System.getProperty(SELENIUM_HUB)), cap);
                    webDriver = new Augmenter().augment(webDriver);
                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                }

            }

        }
        return webDriver;
    }

    /**
     * @return true to quit the driver after each test
     */
    public boolean isQuitDriver() {
        return quitDriver;
    }

    /**
     * Close the web driver
     */
    public void closeDriver() {
        if (webDriver != null) {
            webDriver.close();
        }
    }

    /**
     * Quit the web driver
     */
    public void quitDriver() {
        logger.info("Destroying WebDriver instance");
        if (webDriver != null) {
            webDriver.quit();
            webDriver = null;
        }
    }

    /**
     * Launch the specified snippet
     * 
     * @param baseTest
     * 
     * @return true if the snippet could be launched
     */
    public ResultPage launchSnippet(BaseTest baseTest) {
        if (logger.isLoggable(Level.FINE)) {
            logger.entering(sourceClass, "launchSnippet", new Object[] { baseTest });
        }

        if (properties.getProperty(PROP_FIREBUG_ENABLED, "false").equals("true")) {
            addSnippetParam("debug", "true");
        }
        if (properties.getProperty(PROP_ENABLE_MOCKTRANSPORT, "false").equals("true")) {
            addSnippetParam("mockTransport", "true");
        }
        if (properties.getProperty(PROP_ENABLE_DEBUGTRANSPORT, "false").equals("true")) {
            addSnippetParam("debugTransport", "true");
        }

        String launchUrl = computeLaunchUrl(baseTest);
        Trace.log(launchUrl);
        if (logger.isLoggable(Level.FINE)) {
            logger.exiting(sourceClass, "computeLaunchUrl", new Object[] { baseTest, launchUrl });
        }
        if (StringUtil.isEmpty(launchUrl)) {
            return null;
        }
        // TODO call this init
        webDriver = getWebDriver();
        String windowHandle = webDriver.getWindowHandle();

        webDriver.get(launchUrl);

        WebElement webElement = authenticate(baseTest, null, windowHandle);

        if (baseTest.getAuthType() != AuthType.NONE && baseTest.getAuthType() != AuthType.AUTO_DETECT) {
            if (!baseTest.isResultsReady()) { // try waiting some more for the
                // page to refresh
                baseTest.waitForResult(1);
            }
            if (!baseTest.isResultsReady()) { // let it go if the page is ready
                // according to the test's
                // condition
                assertNotNull("Unable to confirm authentication for: " + baseTest.getSnippetId(), webElement);
            }
        }

        return getPageObject();
    }

    // Protected stuff

    /**
     * Authenticate and return the WebElement for the specified match/condition
     * 
     * If authType == NONE then null is returned
     */
    protected WebElement authenticate(BaseTest baseTest, AuthType authType, String originalHandle) {
        if (logger.isLoggable(Level.FINE)) {
            logger.entering(sourceClass, "authenticate", new Object[] { baseTest });
        }

        if (authType == null) {
            authType = baseTest.getAuthType();
        }
        logger.info("Auth Type for snippet " + authType);
        switch (authType) {
        case NONE:
            break;
        case AUTO_DETECT:
            return handleAutoDetect(baseTest, originalHandle);
        case BASIC:
            handleBasicLogin(baseTest);
            break;
        case OAUTH10:
            handleOAuth10(baseTest);
            break;
        case OAUTH20:
            handleOAuth20(baseTest);
            break;
        }
        // restore window handle
        restoreWindowHandle(webDriver, originalHandle);

        // wait to confirm result page has displayed
        WebElement webElement = baseTest.waitForResult(loginTimeout);
        if (logger.isLoggable(Level.FINE)) {
            logger.exiting(sourceClass, "authenticate", webElement);
        }

        return webElement;
    }

    /*
     * Handle auto detection of the authentication mechanism
     */
    protected WebElement handleAutoDetect(BaseTest baseTest, String originalHandle) {
        WebElement form = searchAnyAuthenticationForm(baseTest, loginTimeout);
        if (baseTest.isResultsReady()) {
            // results for this test are ready so return them here
            return form;
        }

        AuthType authType = detectAuthType(baseTest, form);
        if (authType != AuthType.NONE) {
            Trace.log("handleAutoDetect: " + form.getText() + " - " + authType);
        }

        return authenticate(baseTest, authType, originalHandle);
    }

    /*
     * Handle basic authentication login
     */
    protected void handleBasicLogin(BaseTest baseTest) {
        String loginFormId = baseTest.getProperty(PROP_BASIC_LOGINFORMID);
        String usernameId = baseTest.getProperty(PROP_BASIC_USERNAMEID);
        String passwordId = baseTest.getProperty(PROP_BASIC_PASSWORDID);
        String submitId = baseTest.getProperty(PROP_BASIC_SUBMITID);

        String username = null;
        String password = null;
        if (isSmartCloud()) {
            username = baseTest.getProperty(PROP_OAUTH10_USERNAME);
            password = baseTest.getProperty(PROP_OAUTH10_PASSWORD);
        } else {
            username = baseTest.getProperty(PROP_BASIC_USERNAME);
            password = baseTest.getProperty(PROP_BASIC_PASSWORD);
        }

        WebElement loginForm = waitForLoginForm(loginTimeout, loginFormId, null, BasicLoginTitle, baseTest);
        if (baseTest.isResultsReady())
            return;
        if (loginForm != null) {
            WebElement usernameEl = loginForm.findElement(By.name(usernameId));
            WebElement passwordEl = loginForm.findElement(By.name(passwordId));
            WebElement submitEl = loginForm.findElement(By.name(submitId));
            usernameEl.sendKeys(username);
            passwordEl.sendKeys(password);
            submitEl.click();
        } else {
            // check if page was authenticated before
            if (baseTest.waitForResult(0) != null)
                return;
            fail("Unable to locate basic login form");
        }
    }

    /*
     * Handle OAuth1.0 authentication
     */
    protected void handleOAuth10(BaseTest baseTest) {
        String loginFormId = baseTest.getProperty(PROP_OAUTH10_LOGINFORMID);
        String usernameId = baseTest.getProperty(PROP_OAUTH10_USERNAMEID);
        String passwordId = baseTest.getProperty(PROP_OAUTH10_PASSWORDID);
        String submitId = baseTest.getProperty(PROP_OAUTH10_SUBMITID);
        String username = baseTest.getProperty(PROP_OAUTH10_USERNAME);
        String password = baseTest.getProperty(PROP_OAUTH10_PASSWORD);

        WebElement loginForm = waitForLoginForm(loginTimeout, loginFormId, null, OAuth10LoginTitle, baseTest);
        if (baseTest.isResultsReady())
            return;
        if (loginForm != null) {
            WebElement continueButton = loginForm.findElement(By.id("continue"));

            WebElement usernameEl = loginForm.findElement(By.name(usernameId));
            WebElement passwordEl = loginForm.findElement(By.name(passwordId));
            WebElement submitEl = loginForm.findElements(By.id(submitId)).get(0);
            usernameEl.sendKeys(username);
            if (continueButton != null) {
                continueButton.click();
                WebDriverWait wait = new WebDriverWait(webDriver, 10);
                WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(submitId)));
            }
            passwordEl.sendKeys(password);
            submitEl.click();
        } else {
            fail("Unable to locate OAuth1.0 login form");
        }
    }

    /*
     * Handle OAuth2.0 authentication
     */
    protected void handleOAuth20(BaseTest baseTest) {
        String loginFormXPath = baseTest.getProperty(PROP_OAUTH20_LOGINFORMXPATH);
        String usernameXPath = baseTest.getProperty(PROP_OAUTH20_USERNAMEXPATH);
        String passwordXPath = baseTest.getProperty(PROP_OAUTH20_PASSWORDXPATH);
        String submitXPath = baseTest.getProperty(PROP_OAUTH20_SUBMITXPATH);
        String grantXPath = baseTest.getProperty(PROP_OAUTH20_GRANTXPATH);
        String username = baseTest.getProperty(PROP_OAUTH20_USERNAME);
        String password = baseTest.getProperty(PROP_OAUTH20_PASSWORD);

        WebElement loginForm = waitForLoginForm(loginTimeout, null, loginFormXPath, OAuth20LoginTitle, baseTest);
        if (baseTest.isResultsReady())
            return;

        if (loginForm != null) {
            WebElement usernameEl = loginForm.findElement(By.xpath(usernameXPath));
            WebElement passwordEl = loginForm.findElement(By.xpath(passwordXPath));
            WebElement submitEl = loginForm.findElements(By.xpath(submitXPath)).get(0);
            usernameEl.sendKeys(username);
            passwordEl.sendKeys(password);
            submitEl.click();

            // wait for authorization popup
            WebElement authPage = waitForPopup(loginTimeout, OAuth20AuthTitle);
            if (authPage != null) {
                WebElement grantEl = authPage.findElement(By.xpath(grantXPath));
                grantEl.click();
            } else {
                fail("Unable to locate OAuth2.0 authorization page");
            }
        } else {
            fail("Unable to locate OAuth2.0 login form");
        }
    }

    /**
     * Detect what type of authentication is being used
     * 
     * @param baseTest
     * @param loginForm
     * @return {AuthType}
     */
    public AuthType detectAuthType(BaseTest baseTest, WebElement loginForm) {
        // if results are available then no need to check for authentication
        if (baseTest.isResultsReady()) {
            logger.info("results are ready, no auth necessary");
            return AuthType.NONE;
        }

        // look for all variations of login form
        String basicLoginFormId = baseTest.getProperty(PROP_BASIC_LOGINFORMID);
        String oauth10LoginFormId = baseTest.getProperty(PROP_OAUTH10_LOGINFORMID);
        String[] loginFormIds = { basicLoginFormId, oauth10LoginFormId };
        String oauth20LoginFormXPath = baseTest.getProperty(PROP_OAUTH20_LOGINFORMXPATH);
        String[] loginFormXPathExprs = { oauth20LoginFormXPath };
        if (loginForm != null) {
            String loginFormId = loginForm.getAttribute("id");
            if (basicLoginFormId.equals(loginFormId)) {
                return AuthType.BASIC;
            } else if (oauth10LoginFormId.equals(loginFormId)) {
                return AuthType.OAUTH10;
            } else {
                // TODO handle multiple login form xpath exprs
                return AuthType.OAUTH20;
            }
        }
        return AuthType.NONE;
    }

    /**
     * Wait the specified interval for the one of the authentication screens to
     * appear
     */
    private WebElement searchAnyAuthenticationForm(final BaseTest baseTest, final int secs) {
        String basicLoginFormId = baseTest.getProperty(PROP_BASIC_LOGINFORMID);
        String oauth10LoginFormId = baseTest.getProperty(PROP_OAUTH10_LOGINFORMID);
        String[] loginFormIds = { basicLoginFormId, oauth10LoginFormId };
        String oauth20LoginFormXPath = baseTest.getProperty(PROP_OAUTH20_LOGINFORMXPATH);
        String[] loginFormXPathExprs = { oauth20LoginFormXPath };
        String[] loginTitles = { BasicLoginTitle, OAuth10LoginTitle, OAuth20LoginTitle };
        return waitForLoginForm(secs, loginFormIds, loginFormXPathExprs, loginTitles, baseTest);
    }

    /**
     * Return true if the WebElement represents a login form
     */
    public boolean isLoginForm(BaseTest baseTest, WebElement webElement) {
        String basicLoginFormId = baseTest.getProperty(PROP_BASIC_LOGINFORMID);
        String oauth10LoginFormId = baseTest.getProperty(PROP_OAUTH10_LOGINFORMID);
        String[] loginFormIds = { basicLoginFormId, oauth10LoginFormId };
        String oauth20LoginFormXPath = baseTest.getProperty(PROP_OAUTH20_LOGINFORMXPATH);
        String[] loginFormXPathExprs = { oauth20LoginFormXPath };

        for (int i = 0; i < loginFormIds.length; i++) {
            if (StringUtil.isNotEmpty(loginFormIds[i])) {
                try {
                    if (loginFormIds[i] != null) {
                        WebElement loginForm = webElement.findElement(By.id(loginFormIds[i]));
                        if (loginForm != null) {
                            return true;
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
        for (int i = 0; i < loginFormXPathExprs.length; i++) {
            if (StringUtil.isNotEmpty(loginFormXPathExprs[i])) {
                try {
                    if (loginFormXPathExprs[i] != null) {
                        WebElement loginForm = webElement.findElement(By.xpath(loginFormXPathExprs[i]));
                        if (loginForm != null) {
                            return true;
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
        return false;
    }

    /**
     * Wait the specified interval for the popup with the specified title to
     * appear
     */
    public WebElement waitForPopup(final int secs, final String title) {
        return waitForPopups(secs, new String[] { title });
    }

    /**
     * Wait the specified interval for any popup with the specified titles to
     * appear
     */
    public WebElement waitForPopups(final int secs, final String[] titles) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs))
                    .until(new ExpectedCondition<WebElement>() {
                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(webDriver);
                            WebDriver popup = findPopup(titles);
                            if (popup != null) {
                                return popup.findElement(By.tagName("body"));
                            }
                            return null;
                        }
                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Wait the specified interval for the login form to appear
     */
    public WebElement waitForLoginForm(int secs, String id, String xpathExpr, String title,
            final BaseTest baseTest) {
        return waitForLoginForm(secs, new String[] { id }, new String[] { xpathExpr }, new String[] { title },
                baseTest);
    }

    /**
     * Wait the specified interval for the login form to appear
     */
    public WebElement waitForLoginForm(final int secs, final String[] ids, final String[] xpathExprs,
            final String[] titles, final BaseTest baseTest) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs)).pollingEvery(1, TimeUnit.SECONDS)
                    .until(new ExpectedCondition<WebElement>() {

                        boolean lookForResult = false;

                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(webDriver);

                            WebElement loginForm = findLoginForm(getPageObject(webDriver).getWebDriver(), ids,
                                    xpathExprs, titles);

                            if (loginForm == null && lookForResult) {
                                WebElement result = baseTest.waitForResult(0);
                                if (result != null) {
                                    // wait for result may not flag
                                    // ready if overridden
                                    baseTest.setResultsReady();
                                    // returning the result not a login
                                    // form
                                    return result;
                                }
                            }
                            lookForResult = true;
                            return loginForm;
                        }

                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Wait the specified interval for the specified web element to be available
     */
    public WebElement waitForElement(final String match, final int secs, final String condition) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs))
                    .until(new ExpectedCondition<WebElement>() {
                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(webDriver);
                            webDriver = getPageObject(webDriver).getWebDriver();
                            if (condition.equalsIgnoreCase("id")) {
                                return webDriver.findElement(By.id(match));
                            } else if (condition.equalsIgnoreCase("linkText")) {
                                return webDriver.findElement(By.linkText(match));
                            } else if (condition.equalsIgnoreCase("tagName")) {
                                return webDriver.findElement(By.tagName(match));
                            } else if (condition.equalsIgnoreCase("name")) {
                                return webDriver.findElement(By.name(match));
                            } else if (condition.equalsIgnoreCase("idWithText")) {
                                WebElement element = webDriver.findElement(By.id(match));
                                String text = element.getText();
                                if (StringUtil.isNotEmpty(text)) {
                                    return element;
                                }
                                String value = element.getAttribute("value");
                                if (StringUtil.isNotEmpty(value)) {
                                    return element;
                                }
                                return null;
                            } else if (condition.equalsIgnoreCase("idWithChild")) {
                                WebElement element = webDriver.findElement(By.id(match));
                                List<WebElement> children = element.findElements(By.xpath("*"));
                                if (!children.isEmpty()) {
                                    return element;
                                }
                                return null;
                            } else {
                                return webDriver.findElement(By.name(match));
                            }
                        }
                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Wait the specified interval for the specified web element to be available
     * and for it to have non empty content
     */
    public WebElement waitForText(final String id, final int secs) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs))
                    .until(new ExpectedCondition<WebElement>() {
                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(webDriver);
                            webDriver = getPageObject(webDriver).getWebDriver();
                            WebElement element = webDriver.findElement(By.id(id));
                            String text = element.getText();
                            if (StringUtil.isNotEmpty(text)) {
                                return element;
                            }
                            return null;
                        }
                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Wait the specified interval for the specified web element to be available
     * and for it to have non empty content
     * 
     * @param xPath
     *            This is the xPath expression used to find the element
     * @param secs
     *            This is the amount of seconds to wait before timing out
     * @param expectedText
     *            This is the text that you expect the element to have
     */
    public WebElement waitForText(final String xPath, final int secs, final String expectedText) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs))
                    .until(new ExpectedCondition<WebElement>() {
                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(webDriver);
                            webDriver = getPageObject(webDriver).getWebDriver();
                            WebElement element = webDriver.findElement(By.xpath(xPath));
                            String text = element.getText();
                            if (text != null && text.contains(expectedText)) {
                                return element;
                            }
                            return null;
                        }
                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Wait the specified interval for the specified web element to be available
     * with the specified children
     */
    public WebElement waitForChildren(final String tagName, final String xpath, final int secs) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs))
                    .until(new ExpectedCondition<WebElement>() {
                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(getPageObject(webDriver).getWebDriver());
                            WebElement tableElement = getPageObject(webDriver).getWebDriver()
                                    .findElement(By.tagName(tagName));
                            WebElement element = tableElement.findElement(By.xpath(xpath));
                            return element;
                        }
                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Wait the specified interval for the specified web element to be available
     */
    public WebElement waitForText(final String id, final String match, final int secs) {
        try {
            return (new WebDriverWait(getPageObject().getWebDriver(), secs))
                    .until(new ExpectedCondition<WebElement>() {
                        @Override
                        public WebElement apply(WebDriver webDriver) {
                            failIfPageCrashed(getPageObject(webDriver).getWebDriver());
                            WebElement element = getPageObject(webDriver).getWebDriver().findElement(By.id(id));
                            String text = element.getText();
                            if (text != null) {
                                if ("*".equals(match) || text.contains(match)) {
                                    return element;
                                }
                            }
                            return null;
                        }
                    });
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Dump the specified result page to the trace log
     */
    public void dumpWebElement(WebElement webElement) {
        Trace.log(webElement + " tagName:" + webElement.getTagName() + " text:" + webElement.getText() + " id:"
                + webElement.getAttribute("id") + " displayed:" + webElement.isDisplayed());

        List<WebElement> webElements = webElement.findElements(By.xpath("*"));
        if (webElements.size() > 0) {
            Trace.log("Children size: " + webElements.size());
            for (int i = 0; i < webElements.size(); i++) {
                WebElement nextElement = webElements.get(i);
                Trace.log("[" + i + "]" + nextElement + " tagName:" + webElement.getTagName() + " text:"
                        + nextElement.getText() + " id:" + nextElement.getAttribute("id") + " displayed:"
                        + nextElement.isDisplayed());
            }
        }
    }

    /**
     * Dump the page source to the trace log
     */
    public void dumpPageSource(WebDriver webDriver) {
        String pageSource = webDriver.getPageSource();
        Trace.log("Page source: " + pageSource);
    }

    /**
     * Find the login form and optionally include popups
     */
    protected WebElement findLoginForm(SearchContext webDriver, String[] ids, String[] xpathExprs,
            String[] titles) {
        WebElement loginForm = findLoginForm(webDriver, ids, xpathExprs);
        if (loginForm != null) {
            return loginForm;
        }

        if (titles != null) {
            // look for authentication popup
            WebDriver popup = findPopup(titles);
            if (popup != null) {
                loginForm = findLoginForm(popup, ids, xpathExprs);
                if (loginForm != null) {
                    return loginForm;
                }
            }
        }

        return null;
    }

    /**
     * Find the basic authentication login form
     */
    protected WebElement findLoginForm(SearchContext wd, String[] ids, String[] xpathExprs) {
        for (int i = 0; i < ids.length; i++) {
            if (StringUtil.isNotEmpty(ids[i])) {
                try {
                    if (ids[i] != null) {
                        WebElement loginForm = wd.findElement(By.id(ids[i]));
                        if (loginForm != null) {
                            return loginForm;
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
        for (int i = 0; i < xpathExprs.length; i++) {
            if (StringUtil.isNotEmpty(xpathExprs[i])) {
                try {
                    if (xpathExprs[i] != null) {
                        WebElement loginForm = wd.findElement(By.xpath(xpathExprs[i]));
                        if (loginForm != null) {
                            return loginForm;
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
        return null;
    }

    /**
     * Return a WebDriver for the window with the specified title
     */
    public WebDriver findPopup(String title) {
        return findPopup(new String[] { title });
    }

    /**
     * Return a WebDriver for the window with one of the specified titles
     */
    public WebDriver findPopup(String[] titles) {
        if (titles == null || titles.length == 0)
            return null;
        WebDriver webDriver = getWebDriver();
        WebDriver popup = null;
        Set<String> windowHandles = webDriver.getWindowHandles();
        Iterator<String> windowIterator = windowHandles.iterator();
        while (windowIterator.hasNext()) {
            String windowHandle = windowIterator.next();
            popup = webDriver.switchTo().window(windowHandle);
            String title = popup.getTitle();
            for (int i = 0; i < titles.length; i++) {
                if (title != null && title.contains(titles[i])) {
                    return popup;
                }
            }
        }
        return null;
    }

    /**
     * tests whether the current environment is using the specified library
     * 
     * @param lib
     *            a library identifier, like dojo or jquery
     * @return true if the environment is using the specified library
     */
    public boolean isLibrary(String lib) {
        if (jsLib == null || lib == null)
            return false;
        if (jsLib.startsWith(lib))
            return true;
        return false;
    }

    /**
     * tests the version identifier of the current library
     * 
     * @param version
     *            a library version, using non dotted format (i.e. 182 for
     *            jquery 1.8.2, 143 for dojo 1.4.3)
     * @return true if the environment is using the specified library
     */
    public boolean isLibraryVersion(String version) {
        if (jsLib == null || version == null)
            return false;
        if (!version.matches("[\\D]"))
            return false;
        return jsLib.replaceAll("[\\D]", "").equals(version);
    }

    /**
     * tests the version identifier of the current library
     * 
     * @param version
     *            a library version, using non dotted format (i.e. 182 for
     *            jquery 1.8.2, 143 for dojo 1.4.3)
     * @return {boolean}
     */
    public boolean isLibraryVersionGreatherThan(String version) {
        if (jsLib == null || version == null)
            return false;
        if (version.matches("[\\D]"))
            return false;
        return Integer.valueOf(jsLib.replaceAll("[\\D]", "")) > Integer.valueOf(version);
    }

    /**
     * Return the WebDriver being in use without creating it on demand
     * 
     * @return the current WebDriver
     */
    public WebDriver getCurrentDriver() {
        return webDriver;
    }

    /**
     * Return true if screenshots should be taken.
     * 
     * @return {boolean}
     */
    public boolean isTakeScreenshots() {
        return takeScreenshot;
    }

    /**
     * Return the path where screenshots should be stored.
     * 
     * @return {String}
     */
    public String getScreenshotsPath() {
        return screenshotsPath;
    }

    /**
     * @param webDriver
     * @param windowHandle
     */
    protected void restoreWindowHandle(WebDriver webDriver, String windowHandle) {
        Set<String> windowHandles = webDriver.getWindowHandles();
        if (!windowHandles.contains(windowHandle)) {
            // expected window handle doesn't exist
            Iterator<String> windowIterator = windowHandles.iterator();
            if (windowIterator.hasNext()) {
                windowHandle = windowIterator.next();
            }
        }
        webDriver.switchTo().window(windowHandle);
    }

    // Protected stuff

    /**
     * Load the test properties
     */
    protected Properties loadProperties() {
        Properties properties = new Properties();
        try {
            ClassLoader loader = getClass().getClassLoader();
            InputStream in = loader
                    .getResourceAsStream("com/ibm/sbt/automation/core/environment/TestEnvironment.properties");
            if (in != null) {
                properties.load(in);
                in.close();
            }
            for (Object key : properties.keySet()) {
                if (key == null)
                    continue;
                if (StringUtil.isNotEmpty(System.getProperty(key.toString()))) {
                    properties.put(key, System.getProperty(key.toString()));
                }
            }
        } catch (IOException ioe) {
        }
        return properties;
    }

    /**
     * @param baseTest
     * @param url
     * @return {String}
     */
    protected String addSnippetParams(BaseTest baseTest, String url) {
        Map<String, String> params = baseTest.getSnippetParams();
        Iterator<Entry<String, String>> entries = params.entrySet().iterator();
        while (entries.hasNext()) {
            Entry<String, String> entry = entries.next();
            if (url.indexOf("?") != -1)
                url += "&" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue());
            else
                url += "?" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue());
        }

        // add params from the environment but do not override the same
        // param from the test
        entries = snippetParams.entrySet().iterator();
        while (entries.hasNext()) {
            Entry<String, String> entry = entries.next();
            if (!params.containsKey(entry.getKey())) {
                if (url.indexOf("?") != -1)
                    url += "&" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue());
                else
                    url += "?" + entry.getKey() + "=" + URLEncoder.encode(entry.getValue());
            }
        }

        return url;
    }

    // Private stuff

    private void initInternetExplorerDriver() {
        String ieDriver = System.getProperty(PROP_WEBDRIVER_IE_DRIVER);
        if (StringUtil.isEmpty(ieDriver)) {
            String userDir = System.getProperty(PROP_USER_DIR);
            // TODO check the bitness of the OS and move this to test.properties
            String driverPath = userDir + "/../../../tools/com.ibm.sbtx.ci/selenium/iew32/IEDriverServer.exe";
            System.setProperty(PROP_WEBDRIVER_IE_DRIVER, driverPath);
        }
        webDriver = new InternetExplorerDriver() {
            @Override
            public void get(String url) {
                super.get(url);
                // FIX for self signed certificates
                String t = super.getCurrentUrl();
                if (t.contains("res://ieframe.dll/invalidcert.htm")) {
                    super.navigate().to("javascript:document.getElementById('overridelink').click()");
                }
            }
        };
    }

    private void initChromeDriver() {
        String chromeDriver = System.getProperty(PROP_WEBDRIVER_CHROMER_DRIVER);
        if (StringUtil.isEmpty(chromeDriver)) {
            String userDir = System.getProperty(PROP_USER_DIR);
            String driverPath = userDir + "/../../../tools/com.ibm.sbtx.ci/selenium/Chrome/chromedriver.exe";
            System.setProperty(PROP_WEBDRIVER_CHROMER_DRIVER, driverPath);
        }
        if (!StringUtil.isEmpty(System.getProperty(PROP_CHROME_BINARY))) {
            DesiredCapabilities capabilities = DesiredCapabilities.chrome();
            capabilities.setCapability("chrome.binary", System.getProperty(PROP_CHROME_BINARY));
            webDriver = new ChromeDriver(capabilities);
        } else {
            webDriver = new ChromeDriver();
        }
    }

    /**
     * this method tests for error pages from which the test cannot recover so
     * that instead of waiting the full timeout while testing for a until() to
     * happen we can fail the test early
     */
    private void failIfPageCrashed(WebDriver webDriver) {
        // TODO: populate with more conditions as we find them
        String text = getPageObject(webDriver).getText();
        if (webDriver.getTitle().contains("Apache Tomcat") && webDriver.getTitle().contains("Error report")) {
            fail(text);
        }
        if (text.startsWith("Error, unable to load snippet: ")) {
            fail(text);
        }
        if (text.contains("Unrecognized SSL message, plaintext connection?")) {
            fail("Cannot reach the quickstart image, probably firewall issues\n" + text);
        }
        if (text.contains("oauth_consumer_missing_subscription")) {
            fail("Missing OAuth configuration -> 'oauth_consumer_missing_subscription'\n" + text);
        }
        if (text.contains("Your account has been expired or suspended.")) {
            fail("Smartcloud credential probably expired\n" + text);
        }
        if (text.contains("Your account has expired or has been suspended.")) {
            fail("Smartcloud credential probably expired\n" + text);
        }
    }

    // Abstract stuff

    /**
     * Perform a login
     * 
     * @return true if the log in operation succeeded
     */
    abstract public boolean login();

    /**
     * Compute the launch URL for the specified test
     * 
     * @param baseTest
     * @return {String}
     */
    abstract public String computeLaunchUrl(BaseTest baseTest);

    /**
     * Return the result page for the current web driver
     * 
     * @return {ResultPage}
     */
    public ResultPage getPageObject() {
        return getPageObject(getCurrentDriver());
    }

    /**
     * Return the result page for the specified web driver
     * 
     * @param webDriver
     * @return {ResultPage}
     */
    abstract public ResultPage getPageObject(WebDriver webDriver);

    public void decorateContext(Context context) {
        try {
            if (!StringUtil.isEmpty(getProperty(PROP_OVERRIDE_CONNECTIONS_BE))) {
                BasicEndpoint connections = (BasicEndpoint) EndpointFactory.getEndpoint("connections");
                connections.setUrl(getProperty(PROP_OVERRIDE_CONNECTIONS_BE));
                context.getSessionMap().put("connections", connections);
            }

            if (!StringUtil.isEmpty(getProperty(PROP_OVERRIDE_SMARTCLOUD_BE))) {
                BasicEndpoint smartcloud = (BasicEndpoint) EndpointFactory.getEndpoint("smartcloud");
                smartcloud.setUrl(getProperty(PROP_OVERRIDE_SMARTCLOUD_BE));
                context.getSessionMap().put("smartcloud", smartcloud);
            }
        } catch (Throwable e) {
            logger.severe(e.getMessage());
        }
    }

    public String getEndpointName() {
        if (isSmartCloud()) {
            return "smartcloud";
        } else {
            return "connections";
        }
    }

}