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

au.com.agic.apptesting.utils.impl.LocalThreadWebDriverMapImpl Maven / Gradle / Ivy

package au.com.agic.apptesting.utils.impl;

import static au.com.agic.apptesting.constants.Constants.PHANTOMJS_LOGGING_LEVEL_SYSTEM_PROPERTY;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import au.com.agic.apptesting.constants.Constants;
import au.com.agic.apptesting.exception.ConfigurationException;
import au.com.agic.apptesting.profiles.configuration.UrlMapping;
import au.com.agic.apptesting.utils.ProxyDetails;
import au.com.agic.apptesting.utils.SystemPropertyUtils;
import au.com.agic.apptesting.utils.ThreadDetails;
import au.com.agic.apptesting.utils.ThreadWebDriverMap;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.opera.OperaDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.safari.SafariDriver;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import javax.validation.constraints.NotNull;

import javaslang.control.Try;

/**
 * A service that generates local web driver instances to test on the local pc. Assumes that Chrome
 * is present and installed in the default location, and that the webdriver.chrome.driver system
 * property has been set, and is pointing to a version of the driver downloaded from
 * http://chromedriver.storage.googleapis.com/index.html
 */
public class LocalThreadWebDriverMapImpl implements ThreadWebDriverMap {

	private static final int PHANTOM_JS_SCREEN_WIDTH = 1280;
	private static final int PHANTOM_JS_SCREEN_HEIGHT = 1024;
	private static final int PHNATOMJS_TIMEOUTS = 30;
	private static final SystemPropertyUtils SYSTEM_PROPERTY_UTILS = new SystemPropertyUtilsImpl();

	/**
	 * The mapping between thread ids and the webdrivers that they use for the tests
	 */
	private final Map threadIdToCapMap = new HashMap<>();

	/**
	 * The index of the Url we are going to be testing
	 */
	private int currentUrl;

	/**
	 * The index of the data set we are going to be testing
	 */
	private int currentDataset;

	/**
	 * The list of URLs associated with the application we are testing
	 */
	private List originalApplicationUrls;

	/**
	 * The directory that holds reports and other test script outputs
	 */
	private String reportDirectory;

	/**
	 * The values that can be input into the app
	 */
	private Map> originalDataSets;

	/**
	 * A list of temp folders to delete once the test is finished
	 */
	private List tempFolders;

	/**
	 * The port for the proxy
	 */
	private List> proxies;

	@Override
	public void initialise(
			@NotNull final List desiredCapabilities,
			@NotNull final List applicationUrls,
			@NotNull final Map> datasets,
			@NotNull final String myReportDirectory,
			@NotNull final List myTempFolders,
			@NotNull final List> myProxies) {

		checkNotNull(desiredCapabilities);
		checkNotNull(applicationUrls);
		checkNotNull(datasets);
		checkNotNull(myReportDirectory);
		checkNotNull(myTempFolders);
		checkNotNull(myProxies);

		originalApplicationUrls = new ArrayList<>(applicationUrls);
		originalDataSets = new HashMap<>(datasets);
		reportDirectory = myReportDirectory;
		tempFolders = new ArrayList<>(myTempFolders);
		proxies = new ArrayList<>(myProxies);
	}

	@Override
	public synchronized ThreadDetails getDesiredCapabilitiesForThread(@NotNull final String name) {
		if (threadIdToCapMap.containsKey(name)) {
			return threadIdToCapMap.get(name);
		}

		/*
		  Some validation checking
		*/
		if (originalApplicationUrls.isEmpty()) {
			throw new ConfigurationException(
				"There are no configurations available. "
				+ "Check configuration profiles have the required information in them");
		}

		/*
		  We have allocated our available configurations
		*/
		if (currentUrl >= originalApplicationUrls.size()) {
			throw new ConfigurationException("Configuration pool has been exhausted!");
		}

		/*
		  Get the details that the requesting thread will need
		*/
		final UrlMapping url = originalApplicationUrls.get(currentUrl);

		final Map dataSet = originalDataSets.containsKey(currentDataset)
			? new HashMap<>(originalDataSets.get(currentDataset))
			: new HashMap<>();

		/*
			Tick over to the next url when all the capabilities have been consumed
		 */
		++currentDataset;
		if (currentDataset >= getMaxDataSets()) {
			currentDataset = 0;
			++currentUrl;
		}

		/*
			Associate the new details with the thread
		 */
		final WebDriver remoteWebDriver = getWebDriver();

		final ThreadDetails threadDetails = new ThreadDetailsImpl(
			url,
			dataSet,
			reportDirectory,
			proxies,
			remoteWebDriver);

		threadIdToCapMap.put(name, threadDetails);

		return threadDetails;
	}

	private WebDriver getWebDriver() {
		final String browser = SYSTEM_PROPERTY_UTILS.getProperty(
			Constants.TEST_DESTINATION_SYSTEM_PROPERTY);

		/*
			Configure the proxy settings
		 */
		final DesiredCapabilities capabilities = DesiredCapabilities.htmlUnitWithJs();

		final Optional> mainProxy = proxies.stream()
			.filter(ProxyDetails::isMainProxy)
			.findFirst();

		if (mainProxy.isPresent()) {
			Proxy proxy = new Proxy();
			proxy.setHttpProxy("localhost:" + mainProxy.get().getPort());
			capabilities.setCapability("proxy", proxy);
		}

		if (Constants.FIREFOX.equalsIgnoreCase(browser)) {
			final String firefoxProfile = SYSTEM_PROPERTY_UTILS.getProperty(
				Constants.FIREFOX_PROFILE_SYSTEM_PROPERTY);

			/*
				If we have not specified a profile via the system properties, go ahead
				and create one here.
			 */
			if (StringUtils.isBlank(firefoxProfile)) {
				final FirefoxProfile profile = new FirefoxProfile();

				/*
					Set the proxy
				 */
				if (mainProxy.isPresent()) {

					profile.setPreference("network.proxy.type", 1);
					profile.setPreference("network.proxy.http", "localhost");
					profile.setPreference("network.proxy.http_port", mainProxy.get().getPort());
					profile.setPreference("network.proxy.ssl", "localhost");
					profile.setPreference("network.proxy.ssl_port", mainProxy.get().getPort());
					profile.setPreference("network.proxy.no_proxies_on", "");
				}

				return new FirefoxDriver(profile);
			}

			return new FirefoxDriver(capabilities);

		}

		if (Constants.SAFARI.equalsIgnoreCase(browser)) {
			return new SafariDriver(capabilities);
		}

		if (Constants.OPERA.equalsIgnoreCase(browser)) {
			return new OperaDriver(capabilities);
		}

		if (Constants.IE.equalsIgnoreCase(browser)) {
			return new InternetExplorerDriver(capabilities);
		}

		if (Constants.PHANTOMJS.equalsIgnoreCase(browser)) {

			/*
				PhantomJS will often report a lot of unnecessary errors, so by default
				we turn logging off. But you can override this behaviour with a
				system property.
			 */
			final String loggingLevel = StringUtils.defaultIfBlank(
				SYSTEM_PROPERTY_UTILS.getProperty(PHANTOMJS_LOGGING_LEVEL_SYSTEM_PROPERTY),
				Constants.DEFAULT_PHANTOM_JS_LOGGING_LEVEL
			);

			/*
				We need to ignore ssl errors
				https://vaadin.com/forum#!/thread/9200596
			 */
			final String[] cliArgs = {
				"--ignore-ssl-errors=true",
				"--webdriver-loglevel=" + loggingLevel};
			capabilities.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, cliArgs);

			final PhantomJSDriver retValue = new PhantomJSDriver(capabilities);

			/*
				This is required by PhantomJS
				https://github.com/angular/protractor/issues/585
			 */
			retValue.manage().window().setSize(
				new Dimension(PHANTOM_JS_SCREEN_WIDTH, PHANTOM_JS_SCREEN_HEIGHT));

			/*
				Give the dev servers a large timeout
			 */
			retValue.manage().timeouts()
				.pageLoadTimeout(PHNATOMJS_TIMEOUTS, TimeUnit.SECONDS)
				.implicitlyWait(PHNATOMJS_TIMEOUTS, TimeUnit.SECONDS);

			return retValue;
		}

		return new ChromeDriver(capabilities);
	}

	@Override
	public synchronized int getNumberCapabilities() {
		if (originalApplicationUrls.isEmpty()) {
			throw new ConfigurationException("No application URL specified");
		}
		return originalApplicationUrls.size() * Math.max(getMaxDataSets(), 1);
	}

	@Override
	public List getTempFolders() {
		return tempFolders;
	}

	private Integer getMaxDataSets() {
		try {
			final String maxDataSets =
				SYSTEM_PROPERTY_UTILS.getProperty(Constants.NUMBER_DATA_SETS_SYSTEM_PROPERTY);

			if (StringUtils.isNotBlank(maxDataSets)) {
				final Integer maxDataSetsNumber = Integer.parseInt(
					SYSTEM_PROPERTY_UTILS.getProperty(Constants.NUMBER_DATA_SETS_SYSTEM_PROPERTY));

				return Math.min(originalDataSets.size(), maxDataSetsNumber);
			}
		} catch (final NumberFormatException ignored) {
		  /*
			Input was not a number, so ignore it
		   */
		}

		return originalDataSets.size();
	}

	@Override
	public synchronized void shutdown() {
		for (final ThreadDetails webdriver : threadIdToCapMap.values()) {
			try {
				if (!leaveWindowsOpen()) {
					webdriver.getWebDriver().quit();
				}
			} catch (final Exception ignored) {
				// do nothing and continue closing the other webdrivers
			}
		}

        /*
            Clear the map
         */
		threadIdToCapMap.clear();

		/*
			Attemp to delete all the temp folders
		 */
		getTempFolders().stream()
			.forEach(e -> Try.run(() -> FileUtils.deleteDirectory(e)));

        /*
            Reset the list of available configurations
         */
		currentUrl = 0;
	}

	@Override
	public synchronized void shutdown(@NotNull final String name) {
		checkArgument(StringUtils.isNotBlank(name));

		if (threadIdToCapMap.containsKey(name)) {
			if (!leaveWindowsOpen()) {
				threadIdToCapMap.get(name).getWebDriver().quit();
			}

			threadIdToCapMap.remove(name);
		}
	}

	private boolean leaveWindowsOpen() {
		final String leaveWindowsOpen =
			SYSTEM_PROPERTY_UTILS.getProperty(Constants.LEAVE_WINDOWS_OPEN_SYSTEM_PROPERTY);
		final String browser =
			SYSTEM_PROPERTY_UTILS.getProperty(Constants.TEST_DESTINATION_SYSTEM_PROPERTY);

		/*
			We can't leave the window open for headless browsers
		 */
		return Boolean.parseBoolean(leaveWindowsOpen)
			&& !Constants.PHANTOMJS.equalsIgnoreCase(browser);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy