org.xframium.device.factory.DeviceWebDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.xframium.device.factory.DeviceWebDriver.java

Source

/*******************************************************************************
 * xFramium
 *
 * Copyright 2016 by Moreland Labs, Ltd. (http://www.morelandlabs.com)
 *
 * Some open source application is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or (at your option) any later version.
 *  
 * Some open source application is distributed in the hope that it will 
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with xFramium.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @license GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+>
 *******************************************************************************/
/*
 * 
 */
package org.xframium.device.factory;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.openqa.selenium.By.ByXPath;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ContextAware;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.HasInputDevices;
import org.openqa.selenium.interactions.Keyboard;
import org.openqa.selenium.interactions.Mouse;
import org.openqa.selenium.remote.DriverCommand;
import org.openqa.selenium.remote.ExecuteMethod;
import org.openqa.selenium.remote.RemoteExecuteMethod;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xframium.application.ApplicationRegistry;
import org.xframium.artifact.ArtifactType;
import org.xframium.device.ConnectedDevice;
import org.xframium.device.DeviceManager;
import org.xframium.device.artifact.Artifact;
import org.xframium.device.artifact.ArtifactProducer;
import org.xframium.device.cloud.CloudDescriptor;
import org.xframium.device.interrupt.DeviceInterrupt;
import org.xframium.device.interrupt.DeviceInterruptThread;
import org.xframium.spi.Device;
import org.xframium.spi.PropertyProvider;
import org.xframium.spi.driver.CachingDriver;
import org.xframium.spi.driver.DeviceProvider;
import org.xframium.spi.driver.NativeDriverProvider;
import org.xframium.spi.driver.ReportiumProvider;
import org.xframium.utility.XMLEscape;
import org.xml.sax.InputSource;
import com.perfecto.reportium.client.ReportiumClient;
import io.appium.java_client.AppiumDriver;

// TODO: Auto-generated Javadoc
/**
 * The Class DeviceWebDriver.
 */
public class DeviceWebDriver implements HasCapabilities, WebDriver, JavascriptExecutor, ContextAware, ExecuteMethod,
        ArtifactProducer, NativeDriverProvider, PropertyProvider, TakesScreenshot, DeviceProvider, HasInputDevices,
        CachingDriver, ReportiumProvider {

    private List<DeviceInterrupt> interruptList;

    private DeviceInterruptThread diThread = null;

    /** The web driver. */
    protected WebDriver webDriver;

    /** The execution id. */
    private String executionId;

    /** The report key. */
    private String reportKey;

    /** The wind tunnel report. */
    private String windTunnelReport;

    /** The device name. */
    private String deviceName;

    /** The current device. */
    private Device currentDevice;
    private Device populatedDevice;
    private CloudDescriptor cloud;

    public CloudDescriptor getCloud() {
        return cloud;
    }

    public void setCloud(CloudDescriptor cloud) {
        this.cloud = cloud;
    }

    private ReportiumClient reportiumClient;

    public ReportiumClient getReportiumClient() {
        return reportiumClient;
    }

    public void setReportiumClient(ReportiumClient reportiumClient) {
        this.reportiumClient = reportiumClient;
    }

    public Device getPopulatedDevice() {
        if (populatedDevice == null)
            return currentDevice;
        else
            return populatedDevice;
    }

    public void setPopulatedDevice(Device populatedDevice) {
        this.populatedDevice = populatedDevice;
    }

    /** The current context. */
    private String currentContext;

    /** The context handles. */
    private Set<String> contextHandles;

    /** The artifact producer. */
    private ArtifactProducer artifactProducer;

    /** The log. */
    protected Log log = LogFactory.getLog(DeviceWebDriver.class);

    /** The caching enabled. */
    private boolean cachingEnabled = true;

    /** The x path factory. */
    private XPathFactory xPathFactory = XPathFactory.newInstance();

    /** The cached document. */
    private Document cachedDocument = null;

    /** The context switch supported. */
    private boolean contextSwitchSupported = true;

    /** The Constant EXECUTION_ID. */
    private static final String EXECUTION_ID = "EXECUTION_ID";

    /** The Constant REPORT_KEY. */
    private static final String REPORT_KEY = "REPORT_KEY";

    /** The Constant DEVICE_NAME. */
    private static final String DEVICE_NAME = "DEVICE_NAME";

    /** The Constant WIND_TUNNEL. */
    private static final String WIND_TUNNEL = "WIND_TUNNEL";

    private Map<String, String> propertyMap = new HashMap<String, String>(20);

    /*
     * (non-Javadoc)
     * 
     * @see com.morelandLabs.spi.driver.DeviceProvider#getDevice()
     */
    @Override
    public Device getDevice() {
        return currentDevice;
    }

    @Override
    public String getPageSource() {

        String pageSource = webDriver.getPageSource();

        if (pageSource != null) {
            //if ( ApplicationRegistry.instance().getAUT() != null || ApplicationRegistry.instance().getAUT().isWeb() )
            //    return XMLEscape.toHTML( pageSource );
            //else
            return XMLEscape.toXML(pageSource);
        } else
            return "";
    }

    /**
     * Cache data.
     */
    public void cacheData() {
        if (!cachingEnabled)
            return;

        if (log.isInfoEnabled())
            log.info(Thread.currentThread().getName() + ": Caching page data");
        String pageSource = getPageSource();

        try {
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

            InputStreamReader streamReader = new InputStreamReader(new ByteArrayInputStream(pageSource.getBytes()),
                    "UTF-8");
            InputSource inputSource = new InputSource(streamReader);
            inputSource.setEncoding("UTF-8");

            cachedDocument = dBuilder.parse(inputSource);
            cachingEnabled = true;
        } catch (Exception e) {
            log.warn("CACHING HAS BEEN DISABLED", e);
            cachingEnabled = false;
            cachedDocument = null;
        }
    }

    /**
     * Instantiates a new device web driver.
     *
     * @param webDriver
     *            the web driver
     * @param cachingEnabled
     *            the caching enabled
     * @param currentDevice
     *            the current device
     */
    public DeviceWebDriver(WebDriver webDriver, boolean cachingEnabled, Device currentDevice) {
        this.webDriver = webDriver;
        this.cachingEnabled = cachingEnabled;
        this.currentDevice = currentDevice;
    }

    public boolean isConnected() {
        return webDriver != null;
    }

    public void setDeviceInterrupts(List<DeviceInterrupt> interruptList) {
        this.interruptList = interruptList;

        if (interruptList != null && interruptList.size() > 0) {
            diThread = new DeviceInterruptThread(interruptList, this);
            new Thread(diThread).start();
        }
    }

    /**
     * Sets the artifact producer.
     *
     * @param artifactProducer
     *            the new artifact producer
     */
    public void setArtifactProducer(ArtifactProducer artifactProducer) {
        this.artifactProducer = artifactProducer;
    }

    /**
     * Gets the execution id.
     *
     * @return the execution id
     */
    public String getExecutionId() {
        return executionId;
    }

    /**
     * Sets the execution id.
     *
     * @param executionId
     *            the new execution id
     */
    public void setExecutionId(String executionId) {
        this.executionId = executionId;
        DeviceManager.instance().setExecutionId(executionId);

        if (log.isInfoEnabled())
            log.info("Execution ID recorded as [" + executionId + "]");
    }

    /**
     * Gets the report key.
     *
     * @return the report key
     */
    public String getReportKey() {
        return reportKey;
    }

    /**
     * Sets the report key.
     *
     * @param reportKey
     *            the new report key
     */
    public void setReportKey(String reportKey) {
        this.reportKey = reportKey;
    }

    /**
     * Gets the wind tunnel report.
     *
     * @return the wind tunnel report
     */
    public String getWindTunnelReport() {
        return windTunnelReport;
    }

    /**
     * Sets the wind tunnel report.
     *
     * @param windTunnelReport
     *            the new wind tunnel report
     */
    public void setWindTunnelReport(String windTunnelReport) {
        this.windTunnelReport = windTunnelReport;
    }

    /**
     * Gets the device name.
     *
     * @return the device name
     */
    public String getDeviceName() {
        return deviceName;
    }

    /**
     * Sets the device name.
     *
     * @param deviceName
     *            the new device name
     */
    public void setDeviceName(String deviceName) {
        this.deviceName = deviceName;
    }

    /**
     * Gets the web driver.
     *
     * @return the web driver
     */
    public WebDriver getWebDriver() {
        return webDriver;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.perfectoMobile.gesture.factory.NativeDriverProvider#getNativeDriver()
     */
    @Override
    public WebDriver getNativeDriver() {
        return webDriver;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#get(java.lang.String)
     */
    public void get(String url) {
        webDriver.get(url);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#getCurrentUrl()
     */
    public String getCurrentUrl() {
        return webDriver.getCurrentUrl();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#getTitle()
     */
    public String getTitle() {
        return webDriver.getTitle();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#findElements(org.openqa.selenium.By)
     */
    public List<WebElement> findElements(By by) {
        if (cachingEnabled && cachedDocument == null)
            cacheData();

        if (cachingEnabled && cachedDocument != null) {
            try {
                XPath xPath = xPathFactory.newXPath();
                String path = by.toString();
                path = path.substring(path.indexOf(": ") + 2);
                NodeList nodes = (NodeList) xPath.evaluate(path, cachedDocument, XPathConstants.NODESET);

                List<WebElement> elementList = new ArrayList<WebElement>(10);

                for (int i = 0; i < nodes.getLength(); i++)
                    elementList.add(new CachedWebElement(this, webDriver, by, nodes.item(i)));

                return elementList;
            } catch (Exception e) {
                log.warn("Error reading from cache " + e.getMessage());
                cachingEnabled = false;
                cachedDocument = null;
            }
        }

        return webDriver.findElements(by);
    }

    public void clearCache() {
        cachedDocument = null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#findElement(org.openqa.selenium.By)
     */
    public WebElement findElement(By by) {
        if (by instanceof ByXPath) {
            if (cachingEnabled && cachedDocument == null)
                cacheData();

            if (cachingEnabled && cachedDocument != null) {
                try {
                    XPath xPath = xPathFactory.newXPath();
                    String path = by.toString();
                    path = path.substring(path.indexOf(": ") + 2);
                    Node node = (Node) xPath.evaluate(path, cachedDocument, XPathConstants.NODE);

                    if (node != null)
                        return new CachedWebElement(this, webDriver, by, node);
                    else
                        cachedDocument = null;
                } catch (Exception e) {
                    log.warn("Error reading from cache ", e);
                    cachingEnabled = false;
                    cachedDocument = null;
                }
            }
        }
        return new MorelandWebElement(this, webDriver.findElement(by));
    }

    public boolean isCachingEnabled() {
        return cachingEnabled;
    }

    public void setCachingEnabled(boolean cachingEnabled) {
        this.cachingEnabled = cachingEnabled;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#close()
     */
    public void close() {
        if (webDriver != null && webDriver instanceof AppiumDriver)
            try {
                ((AppiumDriver) webDriver).closeApp();
            } catch (Exception e) {
            }
        webDriver.close();

        if (diThread != null)
            diThread.stop();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#quit()
     */
    public void quit() {
        webDriver.quit();
        if (diThread != null)
            diThread.stop();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#getWindowHandles()
     */
    public Set<String> getWindowHandles() {
        return webDriver.getWindowHandles();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#getWindowHandle()
     */
    public String getWindowHandle() {
        try {
            return webDriver.getWindowHandle();
        } catch (Exception e) {
            return null;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#switchTo()
     */
    public TargetLocator switchTo() {
        return webDriver.switchTo();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#navigate()
     */
    public Navigation navigate() {
        return webDriver.navigate();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.WebDriver#manage()
     */
    public Options manage() {
        return webDriver.manage();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.ContextAware#context(java.lang.String)
     */
    public WebDriver context(String newContext) {
        if (!contextSwitchSupported)
            return webDriver;

        if (newContext == null || newContext.equals(currentContext))
            return webDriver;

        if (webDriver != null) {

            if (webDriver instanceof RemoteWebDriver) {
                log.info("Switching context to " + newContext);
                RemoteExecuteMethod executeMethod = new RemoteExecuteMethod((RemoteWebDriver) webDriver);
                Map<String, String> params = new HashMap<String, String>(5);
                params.put("name", newContext);
                executeMethod.execute(DriverCommand.SWITCH_TO_CONTEXT, params);
            } else if (webDriver instanceof AppiumDriver) {
                log.info("Switching context to " + newContext);
                ((AppiumDriver) webDriver).context(newContext);
            } else
                return null;

            if (newContext.equals(_getContext()))
                currentContext = newContext;
            else
                throw new IllegalStateException(
                        "Could not change context to " + newContext + " against " + webDriver);
        }

        return webDriver;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.ContextAware#getContext()
     */
    public String getContext() {
        if (currentContext != null || !contextSwitchSupported)
            return currentContext;

        currentContext = _getContext();

        return currentContext;
    }

    /**
     * _get context.
     *
     * @return the string
     */
    private String _getContext() {
        if (webDriver != null) {
            try {
                if (webDriver instanceof RemoteWebDriver) {
                    RemoteExecuteMethod executeMethod = new RemoteExecuteMethod((RemoteWebDriver) webDriver);
                    return (String) executeMethod.execute(DriverCommand.GET_CURRENT_CONTEXT_HANDLE, null);
                } else if (webDriver instanceof AppiumDriver) {
                    return ((AppiumDriver) webDriver).getContext();
                }
            } catch (Exception e) {
                log.warn("Context Switches are not supported - " + e.getMessage());
                contextSwitchSupported = false;
            }
        }

        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.ContextAware#getContextHandles()
     */
    public Set<String> getContextHandles() {
        if (contextHandles != null)
            return contextHandles;

        RemoteExecuteMethod executeMethod = new RemoteExecuteMethod((RemoteWebDriver) webDriver);
        List<String> handleList = (List<String>) executeMethod.execute(DriverCommand.GET_CONTEXT_HANDLES, null);

        contextHandles = new HashSet<String>(10);
        contextHandles.addAll(handleList);
        return contextHandles;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.openqa.selenium.remote.ExecuteMethod#execute(java.lang.String,
     * java.util.Map)
     */
    public Object execute(String commandName, Map<String, ?> parameters) {
        if (webDriver instanceof RemoteWebDriver) {
            // RemoteExecuteMethod executeMethod = new RemoteExecuteMethod(
            // (RemoteWebDriver) webDriver );
            return ((RemoteWebDriver) webDriver).executeScript(commandName, parameters);
            // return executeMethod.execute( commandName, parameters );
        } else
            throw new IllegalArgumentException("Attempting to execution a remote command on an unsupported driver");
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.perfectoMobile.device.artifact.ArtifactProducer#getArtifact(org.
     * openqa.selenium.WebDriver,
     * com.perfectoMobile.device.artifact.ArtifactProducer.ArtifactType,
     * com.perfectoMobile.device.ConnectedDevice)
     */
    public Artifact getArtifact(WebDriver webDriver, ArtifactType aType, ConnectedDevice connectedDevice,
            String testName, boolean success) {
        if (artifactProducer != null) {
            Map<String, String> parameterMap = new HashMap<String, String>(3);
            parameterMap.put(EXECUTION_ID, executionId);
            parameterMap.put(REPORT_KEY, reportKey);
            parameterMap.put(DEVICE_NAME, deviceName);
            parameterMap.put(WIND_TUNNEL, windTunnelReport);
            return artifactProducer.getArtifact(webDriver, aType, parameterMap, connectedDevice, testName, success);
        } else
            return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.perfectoMobile.device.artifact.ArtifactProducer#getArtifact(org.
     * openqa.selenium.WebDriver,
     * com.perfectoMobile.device.artifact.ArtifactProducer.ArtifactType,
     * java.util.Map, com.perfectoMobile.device.ConnectedDevice)
     */
    public Artifact getArtifact(WebDriver webDriver, ArtifactType aType, Map<String, String> parameterMap,
            ConnectedDevice connectedDevice, String testName, boolean success) {
        if (artifactProducer != null) {
            if (parameterMap == null)
                parameterMap = new HashMap<String, String>(3);

            parameterMap.put(EXECUTION_ID, executionId);
            parameterMap.put(REPORT_KEY, reportKey);
            parameterMap.put(DEVICE_NAME, deviceName);
            parameterMap.put(WIND_TUNNEL, windTunnelReport);

            return artifactProducer.getArtifact(webDriver, aType, parameterMap, connectedDevice, testName, success);
        } else
            return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.morelandLabs.spi.PropertyProvider#getProperty(java.lang.String)
     */
    @Override
    public String getProperty(String propertyName) {
        String returnValue = propertyMap.get(propertyName);

        if (returnValue != null)
            return returnValue;

        switch (propertyName) {
        case EXECUTION_ID:
            return executionId;

        case REPORT_KEY:
            return reportKey;

        case DEVICE_NAME:
            return deviceName;

        }
        return null;
    }

    @Override
    public void setProperty(String name, String value) {
        if (name != null && value != null)
            propertyMap.put(name, value);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.openqa.selenium.TakesScreenshot#getScreenshotAs(org.openqa.selenium.
     * OutputType)
     */
    @Override
    public <X> X getScreenshotAs(OutputType<X> target) throws WebDriverException {
        if (webDriver instanceof TakesScreenshot)
            return ((TakesScreenshot) webDriver).getScreenshotAs(target);
        else
            throw new IllegalArgumentException("Screenshot functionality not supported");
    }

    //
    // JavascriptExecutor Implementation
    //

    public Object executeScript(String script, Object... args) {
        return ((JavascriptExecutor) webDriver).executeScript(script, args);
    }

    public Object executeAsyncScript(String script, Object... args) {
        return ((JavascriptExecutor) webDriver).executeAsyncScript(script, args);
    }

    @Override
    public Keyboard getKeyboard() {
        if (webDriver instanceof HasInputDevices)
            return ((HasInputDevices) webDriver).getKeyboard();
        else
            return null;
    }

    @Override
    public Mouse getMouse() {
        if (webDriver instanceof HasInputDevices)
            return ((HasInputDevices) webDriver).getMouse();
        else
            return null;
    }

    @Override
    public Capabilities getCapabilities() {
        if (webDriver instanceof HasCapabilities)
            return ((HasCapabilities) webDriver).getCapabilities();
        else
            return null;
    }
}