org.unitils.selenium.WebDriverModule Maven / Gradle / Ivy
The newest version!
package org.unitils.selenium;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.logging.LogEntry;
import org.openqa.selenium.support.PageFactory;
import org.unitils.core.Module;
import org.unitils.core.TestListener;
import org.unitils.core.UnitilsException;
import org.unitils.dbmaintainer.locator.ClassPathDataLocator;
import org.unitils.selenium.annotation.BaseUrl;
import org.unitils.selenium.annotation.TestWebDriver;
import org.unitils.selenium.annotation.WebPage;
import org.unitils.util.AnnotationUtils;
import org.unitils.util.PropertyUtils;
import org.unitils.util.ReflectionUtils;
/**
* This module creates new {@link WebDriver}s.
*
* @author Jeroen Horemans
* @author Thomas De Rycke
* @author Willemijn Wouters
*
* @since 1.0.0
*
*/
public class WebDriverModule implements Module {
/** base package unitils-selenium. **/
protected static final String PACKAGENAME = "org.unitils.selenium";
/** property Unitils.properties: defines the browser. **/
public static final String BROWSER_NAME_KEY = PACKAGENAME + ".browser.name";
public static final String REMOTE_CAPABILITIES = PACKAGENAME + ".remotedriver.capabilities";
/** property Unitils.properties: defines the base url. **/
public static final String BASE_URL_KEY = PACKAGENAME + ".baseUrl";
public static final String REMOTE_URL_KEY = PACKAGENAME + ".remotedriver.url";
/** property Unitils.properties: defines the proxy. **/
public static final String PROXY_HOST_KEY = PACKAGENAME + ".browser.proxy";
/** property Unitils.properties: defines the folder where all the downloads are saved. **/
public static final String DOWNLOADPATH = PACKAGENAME + ".downloadpath";
/** property Unitils.properties: defines all the downloadable types. **/
public static final String FILETYPE = PACKAGENAME + ".filetype";
/** default value {@link WebDriverModule#FILETYPE}. **/
public static final String LIST_AUTOMATICALLY_DOWNLOAD = "application/pdf, application/vnd.fdf, application/x-msdos-program, application/x-unknown-application-octet-stream, application/vnd.ms-powerpoint, application/excel, application/vnd.ms-publisher, application/x-unknown-message-rfc822, application/vnd.ms-excel, application/msword, application/x-mspublisher, application/x-tar, application/zip, application/x-gzip,application/x-stuffit,application/vnd.ms-works, application/powerpoint, application/rtf, application/postscript, application/x-gtar, video/quicktime, video/x-msvideo, video/mpeg, audio/x-wav, audio/x-midi, audio/x-aiff, application/octet-stream";
/** property Unitils.properties: defines the location of the firefox binary. **/
public static final String FIREFOX_BINARY_KEY = PACKAGENAME + ".firefoxbinary";
/** property Unitils.properties: defines location of the IE binary. **/
public static final String IE_BINARY_KEY = PACKAGENAME + ".iebinary";
/** property Unitils.properties: defines the location of the chrome driver. **/
public static final String CHROME_DRIVER_KEY = PACKAGENAME + ".chromedriver";
/** property Unitils.properties: defines the location of the chrome binary. **/
public static final String CHROME_BINARY_KEY = PACKAGENAME + ".chromebinary";
/** property Unitils.properties: defines if unitils-selenium should log the selenium logs in the console. **/
public static final String LOGGINGPROP_CONSOLE_ENABLED = PACKAGENAME + ".logging.console.enabled";
/** property Unitils.properties: should the performance info from selenium be logged? **/
public static final String LOGGINGPROP_PERFORMANCE = PACKAGENAME + ".logging.performance";
/** property Unitils.properties: should the browser info from selenium be logged? **/
public static final String LOGGINGPROP_BROWSER = PACKAGENAME + ".logging.browser";
/** property Unitils.properties: should the client info from selenium be logged? **/
public static final String LOGGINGPROP_CLIENT = PACKAGENAME + ".logging.client";
/** property Unitils.properties: should the driver info from selenium be logged? **/
public static final String LOGGINGPROP_DRIVER = PACKAGENAME + ".logging.driver";
/** property Unitils.properties: should the profiler info from selenium be logged? **/
public static final String LOGGINGPROP_PROFILER = PACKAGENAME + ".logging.profiler";
/** property Unitils.properties: should the server info from selenium be logged? **/
public static final String LOGGINGPROP_SERVER = PACKAGENAME + ".logging.server";
/** property Unitils.properties: all the info should be logged into a file? **/
public static final String LOGGING_FILE_PROP = PACKAGENAME + ".logging.file";
/** property Unitils.properties: all the IE info must be logged? **/
public static final String LOGGINGPROP_IE = PACKAGENAME + ".logging.IE";
private BrowserChoice browserChoice;
private String baseUrl;
private String proxyUrl;
private String downloadPath;
private String fileType;
private Properties configuration;
private static final Log LOGGER = LogFactory.getLog(WebDriverModule.class);
private ClassPathDataLocator dataLocator;
@Override
public void init(Properties configuration) {
dataLocator = new ClassPathDataLocator();
browserChoice = resolveBrowserChoice(configuration);
baseUrl = resolveBaseUrl(configuration);
proxyUrl = resolveProxyHost(configuration);
LOGGER.debug("Driver Module loaded");
downloadPath = PropertyUtils.getString(DOWNLOADPATH, "", configuration);
fileType = PropertyUtils.getString(FILETYPE, LIST_AUTOMATICALLY_DOWNLOAD, configuration);
this.configuration = configuration;
String bit = System.getProperty("sun.arch.data.model");
ClassLoader classLoader = getClass().getClassLoader();
checkFirefoxDriver();
checkChromeDriver(bit, classLoader);
checkIEDriver(bit, classLoader);
}
@Override
public void afterInit() {
// nothing for now.
}
/**
* Get the base URL out of unitils.properties.
*
* @param configuration : the {@link org.unitils.core.Unitils} configuration (unitils.properties).
* @return {@link String}
*/
private String resolveBaseUrl(Properties configuration) {
String result = configuration.getProperty(BASE_URL_KEY);
if (StringUtils.isEmpty(result)) {
throw new IllegalArgumentException("plz fill in a value in the unitils.properties for " + BASE_URL_KEY);
}
return result;
}
/**
* Gets the name of the browser out of unitils.properties.
*
* @param configuration : the {@link org.unitils.core.Unitils} configuration (unitils.properties).
* @return {@link BrowserChoice}
*/
private BrowserChoice resolveBrowserChoice(Properties configuration) {
String browserName = configuration.getProperty(BROWSER_NAME_KEY);
if (StringUtils.isEmpty(browserName)) {
LOGGER.info(BROWSER_NAME_KEY + " not set. Will choose browser FIREFOX");
return BrowserChoice.FIREFOX;
}
return BrowserChoice.valueOf(browserName);
}
/**
* Get the proxy host out of unitils.properties.
*
* @param configuration : the {@link org.unitils.core.Unitils} configuration (unitils.properties).
* @return {@link String}
*/
private String resolveProxyHost(Properties configuration) {
String result = configuration.getProperty(PROXY_HOST_KEY);
if (StringUtils.isEmpty(result)) {
LOGGER.info(PROXY_HOST_KEY + " not set. No proxy used");
return "";
}
LOGGER.info("proxy: [" + result + "] set. Proxy used");
return result;
}
/**
* Initialises the webdriver. The method searches if there are fields with the {@link TestWebDriver} and uses the browser choice (in
* unitils.properties) to create the driver.
*
* @param testObject : the testobject.
*/
public void initWebDriver(Object testObject) {
Set fields = AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), TestWebDriver.class);
if (fields.size() > 1) {
// warn if there are more than one fields with the @TestWebDriver
StringBuilder builder = new StringBuilder();
builder.append("There are more than one webdrivers.\n");
for (Field field : fields) {
builder.append("class: ");
builder.append(field.getDeclaringClass().getName());
builder.append(", field: ");
builder.append(field.getName());
builder.append("\n");
}
LOGGER.warn(builder.toString());
}
for (Field field : fields) {
WebDriver driver;
if (proxyUrl.isEmpty()) {
driver = WebDriverFactory.createDriver(browserChoice, getAbsoluteDownloadPath(downloadPath), fileType);
} else {
driver = WebDriverFactory.createDriver(browserChoice, proxyUrl, getAbsoluteDownloadPath(downloadPath), fileType);
}
driver.manage().deleteAllCookies();
ReflectionUtils.setFieldValue(testObject, field, driver);
}
}
/**
* Checks if the file exists and returns the absolute path of the location. If the location isn't found than an empty string will be
* returned.
*
* @param path : the relative or absolute path of a file.
* @return {@link String}
*/
protected static String getAbsoluteDownloadPath(String path) {
if (!StringUtils.isEmpty(path)) {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
return file.getAbsolutePath();
}
return "";
}
/**
* All the webdrivers (all the fields with the {@link TestWebDriver} of the testObject will be killed.
*
* @param testObject : the testobject.
*/
protected void killWebDriver(Object testObject) {
Set fields = AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), TestWebDriver.class);
for (Field field : fields) {
WebDriver driver = ReflectionUtils.getFieldValue(testObject, field);
LOGGER.debug("closing a driver that is on page : " + driver.getCurrentUrl());
driver.close();
driver.quit();
nastyDoubleCheck(driver);
nastyDoubleCheck(driver);
}
}
/**
* Checks if the driver is closed.
*
* @param driver : of type {@link WebDriver}
*/
private void nastyDoubleCheck(WebDriver driver) {
try {
Thread.sleep(500);
driver.getTitle();
driver.close();
driver.quit();
} catch (WebDriverException e) {
// continue
} catch (InterruptedException e) {
// continue
}
}
/**
* All the elements with the @BaseUrlString will be initialised with the base url value of the unitils.properties.
*
* @param testObject : the testobject.
*/
public void initBaseUrl(Object testObject) {
Set fields = AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), BaseUrl.class);
for (Field field : fields) {
ReflectionUtils.setFieldValue(testObject, field, baseUrl);
}
}
/**
* * Searches all the fields with the {@link WebPage} annotation and sets the correct elements.
*
* @param testObject : the testobject.
*/
public void initElements(Object testObject) {
// find fields that has the @WebPage annotation
Set fields = AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), WebPage.class);
// find the webdriver
Set webdrivers = AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), TestWebDriver.class);
if (!webdrivers.isEmpty()) {
// initialise the page and set the object in the correct field.
WebDriver webdriver = ReflectionUtils.getFieldValue(testObject, webdrivers.iterator().next());
for (Field field : fields) {
if (webdriver != null) {
ReflectionUtils.setFieldValue(testObject, field, getElement(webdriver, field.getType()));
}
}
} else {
LOGGER.error("The TestWebDriver cannot be found.");
}
}
/**
* This method checks if the location of the chrome driver is already in the system. If this is not the case than the method will look
* if {@link WebDriverModule#CHROME_DRIVER_KEY} is defined in the unitils.properties. If there isn't a valid location in the system or
* in the unitils.properties than this method will use the driver that is added with unitils.selenium.
*
* @param bit : should be 32 or 64
* @param classLoader : the classloader used by this class.
*/
protected void checkChromeDriver(String bit, ClassLoader classLoader) {
String driver = "chrome driver";
String pathChrome = "webdriver.chrome.driver";
String absPath = "";
if (!(checkIfDriverIsAlreadyInSystem(System.getProperty(pathChrome), driver, pathChrome) || checkIfDriverPropertyExistsInUnitilsProperties(CHROME_DRIVER_KEY, driver, pathChrome))) {
if (SystemUtils.IS_OS_WINDOWS) {
absPath = copyDriverIntoNewTempFile(dataLocator.loadResources("chromedriver_win.exe", true).get(0), "chromedriver_win.exe");
System.setProperty(pathChrome, absPath);
} else if (SystemUtils.IS_OS_MAC) {
// set drivers mac
absPath = copyDriverIntoNewTempFile(dataLocator.loadResources("chromedriver_mac32", true).get(0), "chromedriver_mac32");
System.setProperty(pathChrome, absPath);
} else if (SystemUtils.IS_OS_LINUX) {
absPath = copyDriverIntoNewTempFile(dataLocator.loadResources("chromedriver_linux32", true).get(0), "chromedriver_linux32");
System.setProperty(pathChrome, absPath);
}
LOGGER.info(createLogNewDriver(pathChrome, absPath));
}
}
/**
* This method checks if their is a system property 'webdriver.firefox.bin' and checks if it exists. Otherwise it will check the
* unitils.properties if their is a property {@link WebDriverModule#FIREFOX_BINARY_KEY} in the unitils.properties.
*/
protected void checkFirefoxDriver() {
String driver = "firefox driver";
String pathFirefox = "webdriver.firefox.bin";
if (!(checkIfDriverIsAlreadyInSystem(System.getProperty(pathFirefox), driver, pathFirefox) || checkIfDriverPropertyExistsInUnitilsProperties(FIREFOX_BINARY_KEY, driver, pathFirefox))) {
LOGGER.error("There is no firefox driver found.");
}
}
/**
* This method checks if there is a system property 'webdriver.ie.driver' and checks if the location exists. If this doesn't exist than
* he looks if you've defined another IE driver ( {@link WebDriverModule#IE_BINARY_KEY} in your unitils.properties. The last option is
* that it uses the driver that is given by unitils-selenium.
*
* @param bit : should be 32 or 64
* @param classLoader : the classloader used by this class.
*/
protected void checkIEDriver(String bit, ClassLoader classLoader) {
String pathIE = "webdriver.ie.driver";
String driver = "IE driver";
String absPath = "";
// this is only possible on WINDOWS
if (SystemUtils.IS_OS_WINDOWS) {
if (!(checkIfDriverIsAlreadyInSystem(System.getProperty(pathIE), driver, pathIE) || checkIfDriverPropertyExistsInUnitilsProperties(IE_BINARY_KEY, driver, pathIE))) {
if (bit.equals("32")) {
absPath = copyDriverIntoNewTempFile(dataLocator.loadResources("IEDriverServer.exe", true).get(0), "IEDriverServer.exe");
System.setProperty(pathIE, absPath);
} else if (bit.equals("64")) {
absPath = copyDriverIntoNewTempFile(dataLocator.loadResources("IEDriverServer_x64.exe", true).get(0), "IEDriverServer_x64.exe");
System.setProperty(pathIE, absPath);
}
LOGGER.info(createLogNewDriver(pathIE, absPath));
}
}
}
/**
* Check if the system variable already exists in the system and check if the variable is a valid location.
*
* @param systemProp : the location of the driver in the system.
* @param driver : the name of the driver.
* @param systemVarDriver : the name of the system property.
* @return boolean
*/
public boolean checkIfDriverIsAlreadyInSystem(String systemProp, String driver, String systemVarDriver) {
if (!StringUtils.isEmpty(systemProp) && new File(systemProp).exists()) {
StringBuilder builder = new StringBuilder().append("The ").append(driver).append(" already defined in the system (").append(systemVarDriver).append(") exists and will be used by Unitils. location:").append(systemProp);
LOGGER.info(builder);
return true;
}
return false;
}
/**
* Check if the key exists in the unitils.properties and check if the value for that key is a valid location.
*
* @param key : the key to find the correct driver location in the unitils.properties.
* @param driver : the type of driver.
* @param systemKey : the system key where the module should set the new location of the driver.
* @return boolean
*/
public boolean checkIfDriverPropertyExistsInUnitilsProperties(String key, String driver, String systemKey) {
String unitilsDriver = PropertyUtils.getString(key, "", configuration);
if (configuration.containsKey(key) && !StringUtils.isEmpty(unitilsDriver)) {
File driverFile = new File(unitilsDriver);
if (driverFile.exists()) {
StringBuilder builder = new StringBuilder().append("The WebdriverModule uses the ").append(driver).append(" from the unitils.properties. location: ").append(driverFile.toString());
LOGGER.info(builder.toString());
System.setProperty(systemKey, driverFile.getAbsolutePath());
return true;
} else {
StringBuilder builder = new StringBuilder().append("The location of the ").append(driver).append(" defined in the unitils.properties does not exists. location: ").append(driverFile.getAbsolutePath());
LOGGER.error(builder.toString());
}
}
return false;
}
/**
* Copies the driver into a new temp file.
*
* @param url : the location of the driver.
* @param name : the name of the driver.
* @return {@link String}
*/
public String copyDriverIntoNewTempFile(URL url, String name) {
StringBuilder result = new StringBuilder(TARGETSUREFIREREPORTS).append(name);
File destination = new File(result.toString());
try {
FileUtils.copyInputStreamToFile(url.openStream(), destination);
destination.setExecutable(true);
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
throw new UnitilsException("Unitils couldn't launch a new webdriver! Check your taskmanager if the driver is still active.", e);
}
return destination.getAbsolutePath();
}
/**
* location surefirereports.
*/
public static final String TARGETSUREFIREREPORTS = "target/surefire-reports/";
/**
* This is the actual method that creates an object of the correct type and initialises all the elements.
*
* @param webdriver : should be of type {@link WebDriver}
* @param type: the class that should be initialised.
* @return {@link Object}
*
*/
protected Object getElement(WebDriver webdriver, Class> type) {
Object webpage = PageFactory.initElements(webdriver, type);
for (Field field : AnnotationUtils.getFieldsAnnotatedWith(type, TestWebDriver.class)) {
ReflectionUtils.setFieldValue(webpage, field, webdriver);
}
initBaseUrl(webpage);
return webpage;
}
@Override
public TestListener getTestListener() {
return new WebdriverTestlistener();
}
public class WebdriverTestlistener extends TestListener{
@Override
public void beforeTestSetUp(Object testObject, Method testMethod) {
super.beforeTestSetUp(testObject, testMethod);
initWebDriver(testObject);
initBaseUrl(testObject);
initElements(testObject);
}
/**
* @see org.unitils.core.TestListener#afterTestMethod(java.lang.Object, java.lang.reflect.Method, java.lang.Throwable)
*/
@Override
public void afterTestMethod(Object testObject, Method testMethod, Throwable testThrowable) {
// write the console logging
// TODO: This doesn't work yet with IE.
if (PropertyUtils.getBoolean(WebDriverModule.LOGGINGPROP_CONSOLE_ENABLED, false, configuration)) {
LOGGER.info("BEGIN CONSOLE TEST: " + testMethod.getName());
if (!resolveBrowserChoice(configuration).equals(BrowserChoice.IE)) {
for (Field webdriverField : AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), TestWebDriver.class)) {
WebDriver webdriver = ReflectionUtils.getFieldValue(testObject, webdriverField);
for (String type : webdriver.manage().logs().getAvailableLogTypes()) {
try {
List entries = webdriver.manage().logs().get(type).getAll();
System.out.println(entries.size() + " " + type + " log entries found");
for (LogEntry entry : entries) {
if (!entry.getLevel().equals(Level.OFF)) {
SimpleDateFormat format = new SimpleDateFormat("YYMMddHHmmss");
System.out.println(format.format(new Date(entry.getTimestamp())) + " " + entry.getLevel() + " " + entry.getMessage());
}
}
} catch (Exception e) {
LOGGER.debug("Log " + type + " is not available.");
}
}
}
}
LOGGER.info("END CONSOLE TEST: " + testMethod.getName());
}
if (testThrowable != null) {
int i = 0;
try {
Set fields = AnnotationUtils.getFieldsAnnotatedWith(testObject.getClass(), TestWebDriver.class);
for (Field field : fields) {
WebDriver driver = ReflectionUtils.getFieldValue(testObject, field);
File surefire = new File(TARGETSUREFIREREPORTS);
if (!surefire.exists()) {
surefire.mkdir();
}
StringBuilder builder = new StringBuilder().append(testObject.getClass().getSimpleName()).append("-").append(testMethod.getName()).append("-").append(field.getName()).append("-").append(++i);
new ScreenshotTakingWebDriver(driver, baseUrl).saveScreenshot(surefire, builder.toString());
LOGGER.error("Something went wrong on this page: " + driver.getPageSource());
}
} catch (Exception e) {
LOGGER.error("The taking of the screenshot has made a terrible mistake! but we'll continue!", e);
}
}
}
/**
* @see org.unitils.core.TestListener#afterTestTearDown(java.lang.Object, java.lang.reflect.Method)
*/
@Override
public void afterTestTearDown(Object testObject, Method testMethod) {
killWebDriver(testObject);
super.afterTestTearDown(testObject, testMethod);
}
}
/**
* logmessage: The driver in unitils-selenium is used.
*
* @param systemVarDriver : the system key of the driver.
* @param absPath : the value of the system key of the driver.
* @return {@link String}
*/
protected static String createLogNewDriver(String systemVarDriver, String absPath) {
return new StringBuilder().append("The location of the variable defined in the ' ").append(systemVarDriver).append("' didn't exist, so we have changed the location of the variable to the driver added in unitils.selenium. location: ").append(absPath).toString();
}
}