All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.unitils.selenium.WebDriverModule Maven / Gradle / Ivy

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. **/
    protected static final String BROWSER_NAME_KEY = PACKAGENAME + ".browser.name";

    protected  static final String REMOTE_CAPABILITIES = PACKAGENAME + ".remotedriver.capabilities";
    /** property Unitils.properties: defines the base url. **/
    protected static final String BASE_URL_KEY = PACKAGENAME + ".baseUrl";

    protected static final String REMOTE_URL_KEY = PACKAGENAME + ".remotedriver.url";

    /** property Unitils.properties: defines the proxy. **/
    protected static final String PROXY_HOST_KEY = PACKAGENAME + ".browser.proxy";

    /** property Unitils.properties: defines the folder where all the downloads are saved. **/
    protected static final String DOWNLOADPATH = PACKAGENAME + ".downloadpath";

    /** property Unitils.properties: defines all the downloadable types. **/
    protected static final String FILETYPE = PACKAGENAME + ".filetype";

    /** default value {@link WebDriverModule#FILETYPE}. **/
    protected 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. **/
    protected static final String FIREFOX_BINARY_KEY = PACKAGENAME + ".firefoxbinary";

    /** property Unitils.properties: defines location of the IE binary. **/
    protected static final String IE_BINARY_KEY = PACKAGENAME + ".iebinary";

    /** property Unitils.properties: defines the location of the chrome driver. **/
    protected static final String CHROME_DRIVER_KEY = PACKAGENAME + ".chromedriver";

    /** property Unitils.properties: defines the location of the chrome binary. **/
    protected static final String CHROME_BINARY_KEY = PACKAGENAME + ".chromebinary";

    /** property Unitils.properties: defines if unitils-selenium should log the selenium logs in the console. **/
    protected static final String LOGGINGPROP_CONSOLE_ENABLED = PACKAGENAME + ".logging.console.enabled";

    /** property Unitils.properties: should the performance info from selenium be logged? **/
    protected static final String LOGGINGPROP_PERFORMANCE = PACKAGENAME + ".logging.performance";

    /** property Unitils.properties: should the browser info from selenium be logged? **/
    protected static final String LOGGINGPROP_BROWSER = PACKAGENAME + ".logging.browser";

    /** property Unitils.properties: should the client info from selenium be logged? **/
    protected static final String LOGGINGPROP_CLIENT = PACKAGENAME + ".logging.client";

    /** property Unitils.properties: should the driver info from selenium be logged? **/
    protected static final String LOGGINGPROP_DRIVER = PACKAGENAME + ".logging.driver";

    /** property Unitils.properties: should the profiler info from selenium be logged? **/
    protected static final String LOGGINGPROP_PROFILER = PACKAGENAME + ".logging.profiler";

    /** property Unitils.properties: should the server info from selenium be logged? **/
    protected static final String LOGGINGPROP_SERVER = PACKAGENAME + ".logging.server";

    /** property Unitils.properties: all the info should be logged into a file? **/
    protected static final String LOGGING_FILE_PROP = PACKAGENAME + ".logging.file";

    /** property Unitils.properties: all the IE info must be logged? **/
    protected 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) {
        return PageFactory.initElements(webdriver, type);
    }

    @Override
    public TestListener getTestListener() {
        return new 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();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy