/*
* © 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
*
* @date 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
*/
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
*/
public boolean isSmartCloud() {
return smartCloud;
}
/**
* Return true if mock transport is being used.
*
* @return
*/
public boolean isMockTransport() {
return properties.getProperty(PROP_ENABLE_MOCKTRANSPORT, "false")
.equals("true");
}
/**
* Return true if debug transport is being used.
*
* @return
*/
public boolean isDebugTransport() {
return properties.getProperty(PROP_ENABLE_DEBUGTRANSPORT, "false")
.equals("true");
}
/**
* Return true if test data should be overwritten.
*
* @return
*/
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 value
*/
public void addSnippetParam(String key, String[] values) {
snippetParams.put(key, StringUtil.concatStrings(values, ',', true));
}
/**
* Return the WebDriver
*
* @return
*/
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 snippetId
*
* @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 form
*
* @return
*/
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 lib
* 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 lib
* a library version, using non dotted format (i.e. 182 for
* jquery 1.8.2, 143 for dojo 1.4.3)
* @return
*/
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
*/
public boolean isTakeScreenshots() {
return takeScreenshot;
}
/**
* Return the path where screenshots should be stored.
*
* @return
*/
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
*/
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
*/
abstract public String computeLaunchUrl(BaseTest baseTest);
/**
* Return the result page for the current web driver
*
* @return
*/
public ResultPage getPageObject() {
return getPageObject(getCurrentDriver());
}
/**
* Return the result page for the specified web driver
*
* @param webDriver
* @return
*/
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";
}
}
}