org.finra.jtaf.ewd.impl.DefaultExtWebDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.finra.jtaf.ewd.impl.DefaultExtWebDriver.java

Source

/*
 * (C) Copyright 2013 Java Test Automation Framework Contributors.
 *
 * 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 org.finra.jtaf.ewd.impl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.io.FileUtils;
import org.ccil.cowan.tagsoup.Parser;
import org.finra.jtaf.ewd.ExtWebDriver;
import org.finra.jtaf.ewd.HighlightProvider;
import org.finra.jtaf.ewd.TimeOutException;
import org.finra.jtaf.ewd.session.SessionManager;
import org.finra.jtaf.ewd.widget.IElement;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
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.WebElement;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import com.google.common.collect.Lists;

/**
 * DefaultExtWebDriver is the default implementation of the ExtWebDriver
 * interface to support more advanced window and frame handling on top of
 * WebDriver
 * 
 */
public class DefaultExtWebDriver implements ExtWebDriver, HighlightProvider {
    private static Logger logger = LoggerFactory.getLogger(ExtWebDriver.class.getPackage().getName());

    /**
     * The default maximum time that waiting methods should wait
     */
    private long maxRequestTimeout;

    /**
     * The underlying WebDriver instance
     */
    private WebDriver wd;

    /**
     * The configuration properties for this instance
     */
    private ClientProperties cp;

    /**
     * State tracking whether to use JavaScript for typing actions
     */
    private boolean useJavascriptType;

    /**
     * State tracking whether to use JavaScript for click actions
     */
    private boolean useJavascriptClick;

    /**
     * State tracking whether to focus on the window when clicking
     */
    private boolean doFocusOnClick;

    /**
     * Whether highlighting is turned on
     */
    private boolean isHighlight;

    /**
     * The list of currentWindowIds when they were last stored
     */
    private Set<String> currentWindowIds;

    /**
     * Tracking the frame selection hierarchy
     */
    private FrameNode lastSelectedFrame;

    /**
     * current session identifier
     */
    private String sessionId;

    /**
     * 
     */
    private Map<String, String> highlightColorMap;

    /**
     * Constructor for ExtWebDriver
     * 
     */
    public DefaultExtWebDriver() {
        useJavascriptType = false;
        useJavascriptClick = false;
        doFocusOnClick = true;
        currentWindowIds = new HashSet<String>();
    }

    @Override
    public void setWrappedDriver(WebDriver wd) {
        this.wd = wd;
    }

    /**
     * Sets the client configuration to use
     * 
     * @param cp
     *            client configuration
     */
    public void setClientProperties(ClientProperties cp) {
        this.cp = cp;
    }

    @Override
    public void setTypeMode(boolean useJavascript) {
        useJavascriptType = useJavascript;
    }

    @Override
    public boolean isJavascriptTypeMode() {
        return useJavascriptType;
    }

    @Override
    public void setClickMode(boolean useJavascript) {
        useJavascriptClick = useJavascript;
    }

    @Override
    public boolean isJavascriptClickMode() {
        return useJavascriptClick;
    }

    @Override
    public void setFocusOnClick(boolean doFocus) {
        doFocusOnClick = doFocus;
    }

    @Override
    public boolean isFocusOnClick() {
        return doFocusOnClick;
    }

    @Override
    public void setIsHighlight(boolean isHighlight) {
        this.isHighlight = isHighlight;
    }

    @Override
    public boolean isHighlight() {
        return isHighlight;
    }

    @Override
    public void setHighlightColors(Map<String, String> colorMap) {
        this.highlightColorMap = colorMap;

    }

    @Override
    public String getHighlightColor(String highlightMode) {
        return highlightColorMap.get(highlightMode.toUpperCase());
    }

    @Override
    public void open(String url) {
        wd.navigate().to(url);
    }

    @Override
    public void back() {
        wd.navigate().back();
    }

    @Override
    public void closeCurrentBrowser() {
        wd.close();
        lastSelectedFrame = null;
    }

    @Override
    public void close() {
        String browser = cp.getBrowser();
        // TODO: confirm that this is to compensate for the lack of unload
        // performed by the IEDriver and add other drivers which need this
        if (browser.equalsIgnoreCase("ie") || browser.equalsIgnoreCase("iexplore")
                || browser.equalsIgnoreCase("*iexplore")) {
            eval("window.onbeforeunload = function(e){};");
        }
        wd.quit();
        lastSelectedFrame = null;
    }

    @Override
    public void forward() {
        wd.navigate().forward();
    }

    @Override
    public void refresh() {
        wd.navigate().refresh();
    }

    @Override
    public void storeCurrentWindowIds() {
        currentWindowIds = wd.getWindowHandles();
    }

    // TODO: verify that focus is not needed on other browsers
    @Override
    public void focus() {
        if (cp.getDebugMode() == true && (cp.getBrowser().equalsIgnoreCase("ie")
                || cp.getBrowser().equalsIgnoreCase("*iexplore") || cp.getBrowser().equalsIgnoreCase("iexplore")))

            windowFocus();
        else if (doFocusOnClick)
            windowFocus();
    }

    /**
     * Convenience method for {@link #focus()}
     * 
     */
    private void windowFocus() {
        this.eval("window.focus()");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.finra.jtaf.ewd.ExtWebDriver#selectWindow(java.lang.String) TODO:
     * use {@link org.finra.jtaf.ewd.timer.WaitForConditionTimer}
     */
    @Override
    public void selectWindow(String windowId) {
        long endTime = System.currentTimeMillis() + maxRequestTimeout;

        Set<String> currentWindowHandles = wd.getWindowHandles();
        boolean found = false;
        while (!found && System.currentTimeMillis() < endTime) {
            if (currentWindowHandles.contains(windowId)) {
                found = true;
            } else {
                currentWindowHandles = wd.getWindowHandles();
            }
        }

        if (found)
            wd.switchTo().window(windowId);
        else
            throw new TimeOutException(
                    "Could not select " + windowId + " within " + maxRequestTimeout + " milliseconds");
    }

    @Override
    public String selectPopupWindow() throws StaleWindowIdListException {
        if (currentWindowIds == null && currentWindowIds.size() > 0) {
            if (currentWindowIds == null) {
                throw new NullPointerException(
                        "WebDriver returned a null set of WindowIds to storeCurrentWindowIds()");
            }
            throw new StaleWindowIdListException(
                    "Must set current window IDs by caling storeCurrentWindowIds() before using this function",
                    Lists.newArrayList(currentWindowIds), Lists.newArrayList(wd.getWindowHandles()));
        }
        String windowId = null;

        Set<String> windowIds = null;
        long endTime = System.currentTimeMillis() + maxRequestTimeout;
        boolean found = false;
        while (!found && System.currentTimeMillis() < endTime) {
            windowIds = wd.getWindowHandles();
            if (windowIds.size() == (currentWindowIds.size() + 1)) {
                windowIds.removeAll(currentWindowIds);
                if (windowIds.size() != 1) {
                    throw new StaleWindowIdListException("Invalid set of current window IDs",
                            Lists.newArrayList(currentWindowIds), Lists.newArrayList(windowIds));
                }
                windowId = windowIds.iterator().next();
                selectWindow(windowId);
                found = true;
            }
        }

        if (!found) {
            throw new StaleWindowIdListException(
                    "Must set current window IDs by caling storeCurrentWindowIds() before using this function",
                    Lists.newArrayList(currentWindowIds), Lists.newArrayList(windowIds));
        }
        return windowId;
    }

    @Override
    public String[] getAllWindowIds() {
        Object[] windowIds = wd.getWindowHandles().toArray();
        String[] windowIdStringArray = new String[windowIds.length];
        int index = 0;
        for (Object o : windowIds) {
            windowIdStringArray[index] = (String) o;
            index++;
        }

        return windowIdStringArray;
    }

    /**
     * Returns the current window handle ID
     * 
     * @return the window handle ID
     */
    @Override
    public String getWindowId() {
        return wd.getWindowHandle();
    }

    /**
     * Maximizes the browser window
     */
    @Override
    public void currentWindowMaximize() {
        wd.manage().window().maximize();
    }

    /**
     * Scrolls the current browser window to specific position
     * 
     * @param i
     *            Horizontal position
     * @param j
     *            Vertical position
     */
    @Override
    public void windowScroll(int i, int j) {
        eval("window.scroll(" + i + "," + j + ");");
    }

    /**
     * Scrolls the current browser by specific amount
     * 
     * @param i
     *            Horizontal scroll amount
     * @param j
     *            Vertical scroll amount
     */
    @Override
    public void windowScrollBy(int i, int j) {
        eval("window.scrollBy(" + i + "," + j + ");");
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.finra.jtaf.ewd.ExtWebDriver#eval(java.lang.String)
     * 
     * TODO: use {@link org.finra.jtaf.ewd.timer.WaitForConditionTimer}
     */
    @Override
    public void eval(String javaScript) {
        try {
            // TODO: add configuration for JavaScript executor
            ((JavascriptExecutor) wd).executeScript(javaScript);

            // TODO: catch specific exceptions. If wd is not a JavaScript
            // executor, don't bother waiting.
        } catch (Exception e) {
            long time = System.currentTimeMillis() + 2000;
            boolean success = false;
            while (!success && System.currentTimeMillis() < time) {
                try {
                    ((JavascriptExecutor) wd).executeScript(javaScript);
                    success = true;
                } catch (Exception e2) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e1) {
                        // TODO: log
                    }
                    e = e2;
                }
            }

            if (!success) {
                // TODO: use specific exception type(s) for eval issues
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public String getHtmlSource() {
        selectLastFrame();
        return wd.getPageSource();
    }

    @Override
    public Map<String, String> getGeneratedHtmlSource() {
        try {

            if (lastSelectedFrame != null) {
                wd.switchTo().defaultContent();
            }

            FrameNode rootNode = new FrameNode();
            return getGeneratedHtmlSourceRecursive(rootNode, "FRAME[root]");

        } catch (Exception e) {
            // TODO: log
            return new HashMap<String, String>();
        } finally {
            doSelectLastFrame();
        }
    }

    /**
     * Gets the current html source from the dom for the every frame in the
     * current window recursively
     * 
     * @param currentFrame
     *            the current FrameNode
     * @param currFrameId
     *            the locator of the current frame
     * @return a map of each frame locator to the generated source
     * 
     * @see #getGeneratedHtmlSource() TODO: check/enforce currFrameId format
     */
    private Map<String, String> getGeneratedHtmlSourceRecursive(FrameNode currentFrame, String currFrameId)
            throws Exception {

        Map<String, String> frames = new HashMap<String, String>();

        if (!currentFrame.isRoot()) {
            wd.switchTo().defaultContent();

            Stack<FrameNode> framesToSelect = new Stack<FrameNode>();
            FrameNode currFrame = currentFrame;
            while (currFrame.getParent() != null) {
                framesToSelect.push(currFrame);
                currFrame = currFrame.getParent();
            }

            while (!framesToSelect.isEmpty()) {
                FrameNode fn = framesToSelect.pop();
                wd.switchTo().frame(fn.getFrame());
            }
        } else {
            wd.switchTo().defaultContent();
        }

        // Add current frame

        frames.put(currFrameId, wd.getPageSource());

        int iframeCount = wd.findElements(By.xpath("//iframe")).size();
        if (iframeCount == 0) {
            return frames;
        }
        for (int i = 1; i <= iframeCount; i++) {
            if (!currentFrame.isRoot()) {
                wd.switchTo().defaultContent();

                Stack<FrameNode> framesToSelect = new Stack<FrameNode>();
                FrameNode currFrame = currentFrame;
                while (currFrame.getParent() != null) {
                    framesToSelect.push(currFrame);
                    currFrame = currFrame.getParent();
                }

                while (!framesToSelect.isEmpty()) {
                    FrameNode fn = framesToSelect.pop();
                    wd.switchTo().frame(fn.getFrame());
                }
            } else {
                wd.switchTo().defaultContent();
            }

            FrameNode nextFrame = new FrameNode(currentFrame, By.xpath("(//iframe)[" + i + "]"),
                    this.getWrappedDriver().findElement(By.xpath("(//iframe)[" + i + "]")));
            String nextFrameId = currFrameId.substring(0, currFrameId.length() - 1) + "-" + i + "]";

            // Recursively add next frames
            frames.putAll(getGeneratedHtmlSourceRecursive(nextFrame, nextFrameId));
        }

        return frames;
    }

    @Override
    public void selectFrame(IElement element) throws Exception {
        try {
            WebElement frameElement = element.getWebElement();
            wd.switchTo().frame(frameElement);

            if (lastSelectedFrame != null) {
                FrameNode parentNode = lastSelectedFrame;
                lastSelectedFrame = new FrameNode(parentNode, element.getByLocator(), frameElement);
            } else {
                FrameNode parentNode = new FrameNode();
                lastSelectedFrame = new FrameNode(parentNode, element.getByLocator(), frameElement);
            }
            // TODO: look for a specific exception
        } catch (Exception e) {
            wd.switchTo().defaultContent();

            if (lastSelectedFrame == null) {
                WebElement frameElement = element.getWebElement();
                wd.switchTo().frame(frameElement);

                FrameNode parentNode = new FrameNode();
                lastSelectedFrame = new FrameNode(parentNode, element.getByLocator(), frameElement);
            } else {
                Stack<FrameNode> framesToSelect = new Stack<FrameNode>();
                FrameNode currFrame = lastSelectedFrame;
                while (currFrame.getParent() != null) {
                    framesToSelect.push(currFrame);
                    currFrame = currFrame.getParent();
                }

                while (!framesToSelect.isEmpty()) {
                    FrameNode fn = framesToSelect.pop();
                    wd.switchTo().frame(fn.getFrame());
                }
            }
        }
    }

    /**
     * Selects the top most frame
     */
    @Override
    public void unselectFrame() {
        wd.switchTo().defaultContent();
        lastSelectedFrame = null;
    }

    @Override
    public void selectLastFrame() {
        if (!cp.shouldSelectLastFrame())
            return;

        doSelectLastFrame();
    }

    private void doSelectLastFrame() {
        try {
            if (lastSelectedFrame != null) {
                Stack<FrameNode> framesToSelect = new Stack<FrameNode>();
                FrameNode currFrame = lastSelectedFrame;
                while (currFrame.getParent() != null) {
                    framesToSelect.push(currFrame.getParent());
                    currFrame = currFrame.getParent();
                }

                while (!framesToSelect.isEmpty()) {
                    FrameNode fn = framesToSelect.pop();
                    if (!fn.isRoot()) {
                        wd.switchTo().frame(fn.getFrame());
                    } else {
                        wd.switchTo().defaultContent();
                    }
                }

                try {
                    wd.switchTo().frame(lastSelectedFrame.getFrame());
                    // TODO: look for a specific exception
                } catch (Exception e) {
                    // TODO: log
                }
            }
            // TODO: look for a specific exception
        } catch (Exception e) {
            logger.info("Unable to select the frame", e);
        }
    }

    @Override
    public String evaluateXpath(String xpath) throws Exception {
        XPathFactory xpathFac = XPathFactory.newInstance();
        XPath theXpath = xpathFac.newXPath();

        String html = getHtmlSource();
        html = html.replaceAll(">\\s+<", "><");
        InputStream input = new ByteArrayInputStream(html.getBytes());

        XMLReader reader = new Parser();
        reader.setFeature(Parser.namespacesFeature, false);
        Transformer transformer = TransformerFactory.newInstance().newTransformer();

        DOMResult result = new DOMResult();
        transformer.transform(new SAXSource(reader, new InputSource(input)), result);

        Node htmlNode = result.getNode(); // This code gets a Node from the
        // result.
        return (String) theXpath.evaluate(xpath, htmlNode, XPathConstants.STRING);
    }

    @Override
    public void confirmNativeDialog() throws Exception {
        Alert alert = wd.switchTo().alert();
        alert.accept();
    }

    @Override
    public void cancelNativeDialog() {
        Alert alert = wd.switchTo().alert();
        alert.dismiss();
    }

    @Override
    public String getNativeDialogText() {
        Alert alert = wd.switchTo().alert();
        return alert.getText();
    }

    // TODO: determine if there is a better option than returning null
    @Override
    public String getBrowserName() {
        if (wd != null) {
            Capabilities capabilities = ((HasCapabilities) wd).getCapabilities();
            if (capabilities != null) {
                return capabilities.getBrowserName();
            }
            return null;
        }
        return null;
    }

    // TODO: determine if there is a better option than returning null
    @Override
    public String getBrowserVersion() {
        if (wd != null) {
            Capabilities capabilities = ((HasCapabilities) wd).getCapabilities();
            if (capabilities != null) {
                return capabilities.getVersion();
            }
            return null;
        }
        return null;
    }

    /**
     * 
     * TreeNode with parent references only to represent the path for a specific
     * child frame
     * 
     */
    private class FrameNode {

        private boolean isRoot;
        private final FrameNode parentNode;
        private final By iframeLocator;
        private final WebElement frame;

        /**
         * Default constructor for FrameNode
         * 
         */
        FrameNode() {
            this.setRoot(true);
            this.parentNode = null;
            this.iframeLocator = null;
            this.frame = null;
        }

        /**
         * Specific constructor for FrameNode
         * 
         * @param parentNode
         *            The parent FrameNode if it exists
         * @param iframeLocator
         *            The locator of the frame element
         * @param frame
         *            the WebElement frame itself
         */
        FrameNode(FrameNode parentNode, By iframeLocator, WebElement frame) {
            this.setRoot(false);
            this.parentNode = parentNode;
            this.iframeLocator = iframeLocator;
            this.frame = frame;
        }

        /**
         * Gets parent FrameNode
         * 
         * @return parent FrameNode
         */
        FrameNode getParent() {
            return parentNode;
        }

        /**
         * Gets the frame
         * 
         * @return WebElement representing the frame
         */
        WebElement getFrame() {
            return frame;
        }

        /**
         * Gets the locator for the frame
         *
         * @return String representing the locator of the frame element
         */
        By getFrameLocator() {
            return iframeLocator;
        }

        /**
         * Is the FrameNode representing the root of the page
         * 
         * @return the FrameNode the root
         */
        boolean isRoot() {
            return isRoot;
        }

        /**
         * Sets the FrameNode as the root
         * 
         * @param isRoot
         *            is the FrameNode the root
         */
        void setRoot(boolean isRoot) {
            this.isRoot = isRoot;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof FrameNode && ((FrameNode) o).getFrameLocator().equals(this.getFrameLocator())) {
                return true;
            }
            return false;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((iframeLocator == null) ? 0 : iframeLocator.hashCode());
            result = prime * result + ((parentNode == null) ? 0 : parentNode.hashCode());
            return result;
        }
    }

    @Override
    public WebElement findElement(By arg0) {
        return wd.findElement(arg0);
    }

    @Override
    public List<WebElement> findElements(By arg0) {
        return wd.findElements(arg0);
    }

    @Override
    public void get(String arg0) {
        wd.get(arg0);
    }

    @Override
    public String getCurrentUrl() {
        return wd.getCurrentUrl();
    }

    @Override
    public String getPageSource() {
        return wd.getPageSource();
    }

    @Override
    public String getTitle() {
        return wd.getTitle();
    }

    @Override
    public String getWindowHandle() {
        return wd.getWindowHandle();
    }

    @Override
    public Set<String> getWindowHandles() {
        return wd.getWindowHandles();
    }

    @Override
    public Options manage() {
        return wd.manage();
    }

    @Override
    public Navigation navigate() {
        return wd.navigate();
    }

    @Override
    public void quit() {
        wd.quit();
    }

    @Override
    public TargetLocator switchTo() {
        return wd.switchTo();
    }

    @Override
    public void setMaxRequestTimeout(String timeout) {
        this.maxRequestTimeout = Long.parseLong(timeout);
    }

    @Override
    public long getMaxRequestTimeout() {
        return maxRequestTimeout;
    }

    @Override
    public WebDriver getWrappedDriver() {
        return wd;
    }

    @Override
    public int getXpathCount(String string) {
        return wd.findElements(By.xpath(string)).size();
    }

    @Override
    public void setSessionId(String id) {
        this.sessionId = id;
    }

    @Override
    public String getSessionId() {
        return this.sessionId;
    }

    @Override
    public void takeScreenshotOfPage(File toSaveAs) throws IOException {
        File screenshot;
        if (!(wd instanceof RemoteWebDriver)) {
            screenshot = ((TakesScreenshot) wd).getScreenshotAs(OutputType.FILE);
        } else {
            Augmenter augmenter = new Augmenter();
            screenshot = ((TakesScreenshot) augmenter.augment(wd)).getScreenshotAs(OutputType.FILE);
        }
        FileUtils.copyFile(screenshot, toSaveAs);
    }
}