org.finra.jtaf.ewd.widget.element.Element.java Source code

Java tutorial

Introduction

Here is the source code for org.finra.jtaf.ewd.widget.element.Element.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.widget.element;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.imageio.ImageIO;
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.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.timer.WaitForConditionTimer;
import org.finra.jtaf.ewd.timer.WaitForConditionTimer.ITimerCallback;
import org.finra.jtaf.ewd.timer.WidgetTimeoutException;
import org.finra.jtaf.ewd.widget.IElement;
import org.finra.jtaf.ewd.widget.WidgetException;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Point;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

/**
 * This is the base class for any element
 */
public class Element implements IElement {

    private ExtWebDriver gd;
    private final By locator;
    private static final long DEFAULT_INTERVAL = 100;

    protected enum HIGHLIGHT_MODES {
        FIND, GET, PUT, NONE
    }

    /**
     * 
     * @param locator
     *            XPath, ID, name, CSS Selector, class name, or tag name
     */
    public Element(String locator) {
        this.locator = new EByFirstMatching(locator);
    }

    /**
     *
     * @param locator a By which defines this element by its first match
     */
    public Element(By locator) {
        this.locator = locator;
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#getLocator()
     */
    @Override
    public String getLocator() {
        if (locator == null) {
            return null;
        } else if (locator instanceof StringLocatorAwareBy) {
            return ((StringLocatorAwareBy) locator).getLocator();
        } else {
            return locator.toString();
        }
    }

    @Override
    public By getByLocator() {
        return locator;
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#isElementPresent()
     */
    @Override
    public boolean isElementPresent() throws WidgetException {
        try {
            return isElementPresent_internal();
        } catch (Exception e) {
            throw new WidgetException("Error while determining whether element is present", locator, e);
        }
    }

    /**
     * Use the JavaXPath to determine if element is present. If not, then try
     * finding element. Return false if the element does not exist
     * 
     * @return true or false
     * @throws WidgetException
     */
    private boolean isElementPresent_internal() throws WidgetException {
        try {
            try {
                final boolean isPotentiallyXpathWithLocator = (locator instanceof EByFirstMatching)
                        || (locator instanceof EByXpath);
                if (isPotentiallyXpathWithLocator && isElementPresentJavaXPath())
                    return true;
            } catch (Exception e) {
                // Continue
            }

            findElement();
            return true;
        } catch (NoSuchElementException e) {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#isElementPresent(java.lang.Long)
     */
    @Override
    public boolean isElementPresent(long timeout) throws WidgetException {
        final long start = System.currentTimeMillis();
        final long end = start + timeout;
        while (true) {
            if (isElementPresent()) {
                return true;
            }

            // Putting the condition here will measure the time more accurately.
            if (System.currentTimeMillis() >= end) {
                return false;
            }
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#isElementPresent(java.lang.Boolean
     * )
     */
    @Override
    public boolean isElementPresent(boolean isJavaXPath) throws WidgetException {
        try {
            final boolean isPotentiallyXpathWithLocator = (locator instanceof EByFirstMatching)
                    || (locator instanceof EByXpath);

            if (isJavaXPath && isPotentiallyXpathWithLocator) {
                return isElementPresentJavaXPath();
            } else {
                findElement();
                return true;
            }
        } catch (NoSuchElementException e) {
            return false;
        } catch (Exception e) {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#isElementVisible()
     */
    @Override
    public boolean isElementVisible() throws WidgetException {
        try {
            return isVisible() && (getLocationX() > 0) && (getLocationY() > 0);
        } catch (Exception e) {
            throw new WidgetException("Error while determining whether element is visible", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#isElementVisible(java.lang.Long)
     */
    @Override
    public boolean isElementVisible(long timeout) throws WidgetException {
        long start = System.currentTimeMillis();
        long end = start + timeout;
        while (System.currentTimeMillis() < end) {
            if (isElementPresent() && isVisible()) {
                return true;
            }
            try {
                Thread.sleep(250);
            } catch (InterruptedException e) {
            }
        }
        return false;
    }

    /**
     * Determine if the element is visible or not
     * 
     * @return true or false
     * @throws WidgetException
     */
    private boolean isVisible() throws WidgetException {
        return findElement().isDisplayed();
    }

    /***
     * Determine if the element is within the bounds of the window
     * 
     * @return true or false
     * @throws WidgetException
     */
    public boolean isWithinBoundsOfWindow() throws WidgetException {

        JavascriptExecutor js = ((JavascriptExecutor) getGUIDriver().getWrappedDriver());

        // execute javascript to get scroll values
        Object top = js.executeScript("return document.body.scrollTop;");
        Object left = js.executeScript("return document.body.scrollLeft;");

        int scrollTop;
        int scrollLeft;
        try {
            scrollTop = Integer.parseInt(top + "");
            scrollLeft = Integer.parseInt(left + "");
        } catch (NumberFormatException e) {
            throw new WidgetException("There was an error parsing the scroll values from the page", getByLocator());
        }

        // calculate bounds
        Dimension dim = getGUIDriver().getWrappedDriver().manage().window().getSize();
        int windowWidth = dim.getWidth();
        int windowHeight = dim.getHeight();
        int x = ((Locatable) getWebElement()).getCoordinates().onPage().getX();
        int y = ((Locatable) getWebElement()).getCoordinates().onPage().getY();
        int relX = x - scrollLeft;
        int relY = y - scrollTop;
        return relX + findElement().getSize().getWidth() <= windowWidth
                && relY + findElement().getSize().getHeight() <= windowHeight && relX >= 0 && relY >= 0;
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForElementPresent()
     */
    @Override
    public void waitForElementPresent() throws WidgetException {
        try {
            waitForElementPresent(getGUIDriver().getMaxRequestTimeout());
        } catch (Exception e) {
            throw new WidgetException("Error while waiting for element to be present", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForElementPresent(java.lang
     * .Long)
     */
    @Override
    public void waitForElementPresent(long time) throws WidgetException {
        try {
            waitForCommand(new ITimerCallback() {

                @Override
                public boolean execute() {
                    try {
                        return isElementPresent();
                    } catch (WidgetException e) {
                        return false;
                    }
                }

                @Override
                public String toString() {
                    return "Waiting for Element: " + locator + " to be present";
                }
            }, time);
        } catch (WidgetTimeoutException e) {
            throw new WidgetException("Error while waiting for element to be present", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForElementNotPresent()
     */
    public void waitForElementNotPresent() throws WidgetException {
        waitForElementNotPresent(getGUIDriver().getMaxRequestTimeout());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForElementPresent(java.lang
     * .Long)
     */
    public void waitForElementNotPresent(final long time) throws WidgetException {
        try {
            waitForCommand(new ITimerCallback() {

                @Override
                public boolean execute() throws WidgetException {
                    return !isElementPresent();
                }

                @Override
                public String toString() {
                    return "Waiting for element with locator " + locator + " to not be present";
                }
            }, time);
        } catch (Exception e) {
            throw new WidgetException("Error while waiting for element to be not present", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForVisible()
     */
    public void waitForVisible() throws WidgetException {
        waitForVisible(getGUIDriver().getMaxRequestTimeout());
    }

    /**
     * Waits for specific element at locator to be visible within period of
     * specified time
     * 
     * @param time
     *            Milliseconds
     * @throws WidgetException
     */
    public void waitForVisible(final long time) throws WidgetException {
        try {
            waitForCommand(new ITimerCallback() {

                @Override
                public boolean execute() throws WidgetException {
                    return isVisible();
                }

                @Override
                public String toString() {
                    return "Waiting for element with locator: " + locator + " to be visible";
                }
            }, time);
        } catch (Exception e) {
            throw new WidgetException("Error while waiting for element to be visible", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForNotVisible()
     */
    public void waitForNotVisible() throws WidgetException {
        waitForNotVisible(getGUIDriver().getMaxRequestTimeout());
    }

    /**
     * Waits for specific element at locator to be not visible within period of
     * specified time
     * 
     * @param time
     *            Milliseconds
     * @throws WidgetException
     */
    public void waitForNotVisible(final long time) throws WidgetException {
        try {
            waitForCommand(new ITimerCallback() {

                @Override
                public boolean execute() throws WidgetException {
                    return !isVisible();
                }

                @Override
                public String toString() {
                    return "Waiting for element with locator: " + locator + " to not be visible";
                }
            }, time);
        } catch (Exception e) {
            throw new WidgetException("Error while waiting for element to be not visible", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#getAttribute(java.lang.String)
     */
    @Override
    public boolean isAttributePresent(String attributeName) throws WidgetException {
        try {
            WebElement webElement = null;
            String attrValue = null;

            // WebDriver get attribute
            try {
                webElement = findElement();
                attrValue = webElement.getAttribute(attributeName);
                highlight(HIGHLIGHT_MODES.GET);
            } catch (Exception e) {
                throw new RuntimeException("Exception happened at locator: " + locator + " attribute: "
                        + attributeName + e.getMessage());
            }

            if (attrValue != null) {
                return true;
            }

            return false;
        } catch (Exception e) {
            throw new WidgetException("Error while determining if attribute is present: " + attributeName, locator,
                    e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#getAttribute(java.lang.String)
     */
    @Override
    public String getAttribute(String attributeName) throws WidgetException {
        try {
            WebElement webElement = null;
            String attr = null;

            // WebDriver get attribute
            try {
                webElement = findElement();
                attr = webElement.getAttribute(attributeName);
                highlight(HIGHLIGHT_MODES.GET);
            } catch (NoSuchElementException e) {
                throw e;
            }

            if (attr != null && !attr.equals("")) {
                return attr;
            }

            return "";
        } catch (Exception e) {
            throw new WidgetException("Error while fetching attribute " + attributeName, locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#getLocationX()
     */
    @Override
    public int getLocationX() throws WidgetException {
        try {
            Point p = findElement().getLocation();
            return p.getX();
        } catch (Exception e) {
            throw new WidgetException("Error while fetching X location", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#getLocationY()
     */
    @Override
    public int getLocationY() throws WidgetException {
        try {
            Point p = findElement().getLocation();
            return p.getY();
        } catch (Exception e) {
            throw new WidgetException("Error while fetching Y location", locator, e);
        }
    }

    /**
     * Tell Selenium to use the provided WebDriver instance
     * 
     * @param guiDriver
     *            ExtendedWebDriver object
     */
    public void setGUIDriver(ExtWebDriver guiDriver) {
        gd = guiDriver;
    }

    /**
     * Get the WebDriver object to interact with the UI elements
     * 
     * @return ExtendedWebDriver specific type of WebDriver object
     *         (browser-specific)
     */
    public final ExtWebDriver getGUIDriver() {
        if (gd != null)
            return gd;
        return SessionManager.getInstance().getCurrentSession();
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#getText()
     */
    @Override
    public String getText() throws WidgetException {
        try {
            WebElement webElement = findElement();
            highlight(HIGHLIGHT_MODES.GET);
            return webElement.getText();
        } catch (Exception e) {
            throw new WidgetException("Error while fetching text", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#hasText()
     */
    public boolean hasText(String text) throws WidgetException {
        try {
            Element element = new Element(getByLocator());
            List<WebElement> childElements = element.getWebElement().findElements(By.xpath(".//*"));
            for (WebElement we : childElements) {
                if (we.getText().contains(text)) {
                    return true;
                }
            }

            return false;
        } catch (Exception e2) {
            throw new WidgetException("Error while determining if element has text '" + text + "'", getByLocator(),
                    e2);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#getCssValue(java.lang.String)
     */
    @Override
    public String getCssValue(String propertyName) throws WidgetException {
        try {
            //HUUPS I changed the implementation (FIX abug?) Why not use findElement?
            return getGUIDriver().getWrappedDriver().findElement(locator).getCssValue(propertyName);
        } catch (Exception e) {
            throw new WidgetException("Error while getting CSS value", locator, e);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForAttributeEqualTo(java.
     * lang.String, java.lang.String)
     */
    public void waitForAttributeEqualTo(String attributeName, String attributeValue) throws WidgetTimeoutException {
        waitForAttributeEqualTo(attributeName, attributeValue, getGUIDriver().getMaxRequestTimeout());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForAttributeEqualTo(java.
     * lang.String, java.lang.String, java.lang.Long)
     */
    public void waitForAttributeEqualTo(final String attributeName, final String attributeValue, long timeout)
            throws WidgetTimeoutException {
        waitForCommand(new ITimerCallback() {
            @Override
            public boolean execute() throws WidgetException {
                String val = getAttribute(attributeName);

                if (val == null && attributeName == null)
                    return true;

                if (val == null || attributeName == null)
                    return false;

                if (val.equals(attributeValue))
                    return true;

                return false;
            }

            @Override
            public String toString() {
                return "Waiting for attribute, " + ((attributeName == null) ? "" : attributeName)
                        + ", to be equal to: " + ((attributeValue == null) ? "" : attributeValue)
                        + " - for the element with the locator: " + locator;
            }
        }, timeout);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForAttributeNotEqualTo(java
     * .lang.String, java.lang.Stringg)
     */
    @Override
    public void waitForAttributeNotEqualTo(String attributeName, String attributeValue) throws WidgetException {
        waitForAttributeNotEqualTo(attributeName, attributeValue, getGUIDriver().getMaxRequestTimeout());

    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForText()
     */
    @Override
    public void waitForText() throws WidgetException {
        waitForText(getGUIDriver().getMaxRequestTimeout());

    }

    /**
     * Find the element using xpath, id, name, class name or tag name
     * 
     * @return WebElement the element identified by any of the properties
     * @throws WidgetException
     */

    protected WebElement findElement() throws WidgetException {
        return findElement(true);
    }

    private WebElement findElement(boolean doHighlight) {

        HIGHLIGHT_MODES highlightMode = null;
        if (doHighlight)
            highlightMode = HIGHLIGHT_MODES.FIND;
        else if (!doHighlight)
            highlightMode = HIGHLIGHT_MODES.NONE;

        getGUIDriver().selectLastFrame();
        WebDriver wd = getGUIDriver().getWrappedDriver();

        final WebElement webElement = wd.findElement(locator);
        try {
            highlight(highlightMode);
        } catch (WidgetException e) {
            //TODO Log would be nice.
        }
        return webElement;
    }

    /**
     * highlight an element for the FIND mode
     */
    public void highlight() throws WidgetException {
        highlight(HIGHLIGHT_MODES.FIND);
    }

    /**
     * Set the background color of a particular web element to a certain color
     *
     * @param color
     *            the color to use for highlight
     * @throws Exception
     */
    private void setBackgroundColor(String color) throws WidgetException {
        try {
            this.eval("arguments[0].style.backgroundColor = '" + color + "'");

        } catch (Exception e) {

        }
    }

    /**
     * Highlight a web element depending on one of the three default modes FIND,
     * GET and PUT
     * 
     * @param webElement
     *            the element to be highlighted
     * @param mode
     *            the mode which decides the color of highlight
     * @throws WidgetException
     */
    /*
     * protected void highlight(WebElement webElement, HIGHLIGHT_MODES mode)
     * throws WidgetException { if (!(getGUIDriver() instanceof
     * HighlightProvider)) { return; } HighlightProvider highDriver =
     * (HighlightProvider) getGUIDriver();
     * 
     * if (highDriver.isHighlight()) { setBackgroundColor(
     * highDriver.getHighlightColor(mode.toString())); } }
     */

    /**
     * Executes JavaScript code on the current element in the current frame or
     * window.
     * 
     * @param javascript
     *            the javascript code to be executed
     */
    @Override
    public void eval(String javascript) throws WidgetException {
        WebElement element = findElement(false);
        WebDriver wd = getGUIDriver().getWrappedDriver();
        try {
            ((JavascriptExecutor) wd).executeScript(javascript, element);
        } catch (Exception e) {
            long time = System.currentTimeMillis() + 2000;
            boolean success = false;
            while (!success && System.currentTimeMillis() < time) {
                try {
                    ((JavascriptExecutor) wd).executeScript(javascript, element);
                    success = true;
                } catch (Exception e2) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e1) {
                        // Ignore
                    }
                    e = e2;
                }
            }

            if (!success) {
                throw new RuntimeException(e);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#getWebElement()
     */
    @Override
    public WebElement getWebElement() throws WidgetException {
        return findElement();
    }

    /**
     * wait for timeout amount of time
     * 
     * @param callback
     * @param timeout
     * @throws WidgetTimeoutException
     */
    protected void waitForCommand(ITimerCallback callback, long timeout) throws WidgetTimeoutException {
        WaitForConditionTimer t = new WaitForConditionTimer(getByLocator(), callback);
        t.waitUntil(timeout);
    }

    /**
     * Use the Java Xpath API to determine if the element is present or not
     * 
     * @return boolean true or false as the case is
     * @throws Exception
     */
    private boolean isElementPresentJavaXPath() throws Exception {
        String xpath = ((StringLocatorAwareBy) getByLocator()).getLocator();
        try {
            xpath = formatXPathForJavaXPath(xpath);
            NodeList nodes = getNodeListUsingJavaXPath(xpath);
            if (nodes.getLength() > 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            throw new Exception("Error performing isElement present using Java XPath: " + xpath, e);
        }
    }

    /**
     * Format the xpath to work with Java XPath API
     * 
     * @param xpath
     *            the xpath to be formatted
     * @return String the formatted xpath
     * @throws Exception
     */
    private static String formatXPathForJavaXPath(String xpath) throws Exception {
        String newXPath = "";

        if (xpath.startsWith("xpath=")) {
            xpath = xpath.replace("xpath=", "");
        }

        boolean convertIndicator = true;
        boolean onSlash = false;
        boolean onSingleQuote = false;
        boolean onDoubleQuote = false;
        for (int i = 0; i < xpath.length(); i++) {
            char c = xpath.charAt(i);
            if (c == '/') {
                if (!onSingleQuote && !onDoubleQuote) {
                    if (convertIndicator) {
                        if (!onSlash) {
                            onSlash = true;
                        } else {
                            onSlash = false;
                        }
                    } else {
                        convertIndicator = true;
                        onSlash = true;
                    }
                }
            } else if (c == '[') {
                if (!onSingleQuote && !onDoubleQuote)
                    convertIndicator = false;
            } else if (c == ']') {
                if (!onSingleQuote && !onDoubleQuote)
                    convertIndicator = true;
            } else if (c == '\'') {
                if (!onSingleQuote)
                    onSingleQuote = true;
                else
                    onSingleQuote = false;
            } else if (c == '\"') {
                if (!onDoubleQuote)
                    onDoubleQuote = true;
                else
                    onDoubleQuote = false;
            }

            if (convertIndicator)
                newXPath = newXPath + String.valueOf(c).toLowerCase();
            else
                newXPath = newXPath + String.valueOf(c);
        }

        return newXPath;
    }

    /**
     * Get the list of nodes which satisfy the xpath expression passed in
     * 
     * @param xpath
     *            the input xpath expression
     * @return the nodeset of matching elements
     * @throws Exception
     */
    private NodeList getNodeListUsingJavaXPath(String xpath) throws Exception {
        XPathFactory xpathFac = XPathFactory.newInstance();
        XPath theXpath = xpathFac.newXPath();

        String html = getGUIDriver().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.
        NodeList nodes = (NodeList) theXpath.evaluate(xpath, htmlNode, XPathConstants.NODESET);

        return nodes;
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForText(java.lang.Long)
     */
    @Override
    public void waitForText(long time) throws WidgetException, WidgetTimeoutException {
        waitForCommand(new ITimerCallback() {

            @Override
            public boolean execute() throws WidgetException {
                String val = getText();

                if (val == null || val.equals(""))
                    return false;

                return true;
            }

            @Override
            public String toString() {
                return "Waiting for text on the element with the " + "locator: " + locator;
            }

        }, time);

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForNotAttribute(java.lang
     * .String, java.lang.String, java.lang.Long)
     */
    @Override
    public void waitForNotAttribute(String attributeName, String pattern, long timeout) throws WidgetException {
        waitForAttributePatternMatcher(attributeName, pattern, timeout, false);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForNotAttribute(java.lang
     * .String, java.lang.String)
     */
    @Override
    public void waitForNotAttribute(String attributeName, String pattern) throws WidgetException {
        waitForNotAttribute(attributeName, pattern, getGUIDriver().getMaxRequestTimeout());
    }

    /**
     * Matches an attribute value to a pattern that is passed.
     * 
     * @param attributeName
     *            the attribute whose value os to be compared
     * @param pattern
     *            the pattern to which to match
     * @param timeout
     *            time in milliseconds after which request timeout occurs
     * @param waitCondition
     *            true to match the attribute value and false to not match the
     *            value
     * @throws WidgetException
     */
    private void waitForAttributePatternMatcher(String attributeName, String pattern, long timeout,
            boolean waitCondition) throws WidgetException {
        long start = System.currentTimeMillis();
        long end = start + timeout;
        while (System.currentTimeMillis() < end) {
            String attribute = getAttribute(attributeName);
            if (attribute != null && attribute.matches(pattern) == waitCondition)
                return;
            try {
                Thread.sleep(DEFAULT_INTERVAL);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        throw new TimeOutException(
                "waitForAttribute " + " : timeout : Locator : " + locator + " Attribute : " + attributeName);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForAttribute(java.lang.String
     * , java.lang.String, java.lang.Long)
     */
    public void waitForAttribute(String attributeName, String pattern, long timeout) throws WidgetException {
        waitForAttributePatternMatcher(attributeName, pattern, timeout, true);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForAttribute(java.lang.String
     * , java.lang.String)
     */
    public void waitForAttribute(String attributeName, String pattern) throws WidgetException {
        waitForAttribute(attributeName, pattern, getGUIDriver().getMaxRequestTimeout());
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#waitForEnabled()
     */
    @Override
    public void waitForEnabled() throws WidgetException {
        waitForEnabled(getGUIDriver().getMaxRequestTimeout());

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForEnabled(java.lang.Long)
     */
    @Override
    public void waitForEnabled(long time) throws WidgetException {
        waitForCommand(new ITimerCallback() {

            @Override
            public boolean execute() throws WidgetException {
                return isEnabled();
            }

            @Override
            public String toString() {
                return "Waiting for element with the locator: " + locator + " to be enabled.";
            }
        }, time);

    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#isEnabled()
     */
    @Override
    public boolean isEnabled() throws WidgetException {
        return findElement().isEnabled();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#waitForAttributeNotEqualTo(java
     * .lang.String, java.lang.String, java.lang.Long)
     */
    public void waitForAttributeNotEqualTo(final String attributeName, final String attributeValue, long timeout)
            throws WidgetTimeoutException {
        waitForCommand(new ITimerCallback() {

            @Override
            public boolean execute() throws WidgetException {
                String val = getAttribute(attributeName);

                if (val == null && attributeName == null)
                    return true;

                if (val == null || attributeName == null)
                    return false;

                if (!val.equals(attributeValue))
                    return true;

                return false;
            }

            @Override
            public String toString() {
                return "Waiting for attribute, " + ((attributeName == null) ? "" : attributeName)
                        + ", to not equal: " + ((attributeValue == null) ? "" : attributeValue)
                        + " - for the element with the locator: " + locator;
            }
        }, timeout);

    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#isElementNotPresent()
     */
    @Override
    public boolean isElementNotPresent() throws WidgetException {
        if (!isElementPresent()) {
            return true;
        } else {
            return false;
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * qc.automation.framework.widget.IElement#isElementNotPresent(java.lang
     * .Long)
     */
    @Override
    public boolean isElementNotPresent(long timeout) throws WidgetException {
        final long start = System.currentTimeMillis();
        final long end = start + timeout;
        while (true) {
            if (!isElementPresent()) {
                return true;
            }

            // Once again, measures the time interval more accurately.
            if (System.currentTimeMillis() >= end) {
                return false;
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * Fires a javascript event on a web element
     * 
     * @param event
     *            the javascript code for the event
     */
    @Override
    public void fireEvent(String event) throws WidgetException {
        try {
            String javaScript = null;
            if (event.startsWith("mouse")) {
                javaScript = "if(document.createEvent){var evObj = document.createEvent('MouseEvents');evObj.initEvent('"
                        + event
                        + "', true, false); arguments[0].dispatchEvent(evObj);} else if(document.createEventObject) { arguments[0].fireEvent('on"
                        + event + "');}";
            } else {
                javaScript = "if(document.createEvent){var evObj = document.createEvent('HTMLEvents');evObj.initEvent('"
                        + event
                        + "', true, false); arguments[0].dispatchEvent(evObj);} else if(document.createEventObject) { arguments[0].fireEvent('on"
                        + event + "');}";
            }
            eval(javaScript);
        } catch (Exception e) {
            throw new WidgetException("Error while trying to fire event", getByLocator(), e);
        }
    }

    /**
     * Returns the text contained in the child nodes of the current element
     * 
     * @return Array of texts in children nodes
     */
    @Override
    public String[] getChildNodesValuesText() throws WidgetException {
        WebElement we = new Element(getByLocator()).getWebElement();
        List<WebElement> childNodes = we.findElements(By.xpath("./*"));
        String[] childText = new String[childNodes.size()];
        int i = 0;
        for (WebElement element : childNodes) {
            childText[i] = element.getText();
            i++;
        }
        return childText;
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#getInnerHTML()
     */
    @Override
    public String getInnerHTML() throws WidgetException {
        WebElement element = findElement();
        WebDriver wd = getGUIDriver().getWrappedDriver();
        String innerHtml = (String) ((JavascriptExecutor) wd).executeScript("return arguments[0].innerHTML;",
                element);
        return innerHtml;
    }

    /*
     * (non-Javadoc)
     * 
     * @see qc.automation.framework.widget.IElement#highlight(java.lang.String)
     */

    @Override
    public void highlight(String colorMode) throws WidgetException {
        doHighlight(colorMode);

    }

    /**
     * Find the current element and highlight it according to the mode
     * If mode is NONE, do nothing
     * @param mode
     * @throws WidgetException
     */
    public void highlight(HIGHLIGHT_MODES mode) throws WidgetException {
        if (mode.equals(HIGHLIGHT_MODES.NONE))
            return;
        else
            highlight(mode.toString());

    }

    /**
     * Highlight a web element by getting its color from the map loaded from
     * client properties file
     * @param colorMode
     *            the mode which decides the color of highlight
     * @throws WidgetException
     */

    private void doHighlight(String colorMode) throws WidgetException {
        if (!(getGUIDriver() instanceof HighlightProvider)) {
            return;
        }
        HighlightProvider highDriver = (HighlightProvider) getGUIDriver();

        if (highDriver.isHighlight()) {
            setBackgroundColor(highDriver.getHighlightColor(colorMode));
        }
    }

    /***
     * Scroll to this element
     */
    @Override
    public void scrollTo() throws WidgetException {
        WebElement we = findElement();
        Locatable l = ((Locatable) we);
        l.getCoordinates().inViewPort();
    }

    /***
     * Focus on this element
     */
    @Override
    public void focusOn() throws WidgetException {
        WebDriver driver = SessionManager.getInstance().getCurrentSession().getWrappedDriver();
        JavascriptExecutor jse = (JavascriptExecutor) driver;
        jse.executeScript("arguments[0].focus();", findElement());
    }

    /***
     * You can use this method to take screenshots of elements / control pictures to compare with.
     * Note that the file should be a png.
     * 
     * @param toSaveAs
     * @throws IOException 
     * @throws WidgetTimeoutException 
     */
    public void captureElementScreenshot(File toSaveAs) throws IOException, WidgetException {

        for (int i = 0; i < 10; i++) { //Loop up to 10x to ensure a clean screenshot was taken
            //log.info("Taking screen shot of locator " + element.getByLocator() + " ... attempt #" + (i+1));

            //Scroll to element
            this.scrollTo();

            //Take picture of the page
            WebDriver wd = getGUIDriver().getWrappedDriver();
            File screenshot;
            boolean isRemote = false;
            if (!(wd instanceof RemoteWebDriver)) {
                screenshot = ((TakesScreenshot) wd).getScreenshotAs(OutputType.FILE);
            } else {
                Augmenter augmenter = new Augmenter();
                screenshot = ((TakesScreenshot) augmenter.augment(wd)).getScreenshotAs(OutputType.FILE);
                isRemote = true;
            }
            BufferedImage fullImage = ImageIO.read(screenshot);
            WebElement ele = this.getWebElement();

            //Parse out the picture of the element
            Point point = ele.getLocation();
            int eleWidth = ele.getSize().getWidth();
            int eleHeight = ele.getSize().getHeight();
            int x;
            int y;
            if (isRemote) {
                x = ((Locatable) ele).getCoordinates().inViewPort().getX();
                y = ((Locatable) ele).getCoordinates().inViewPort().getY();
            } else {
                x = point.getX();
                y = point.getY();
            }
            //log.debug("Screenshot coordinates x: "+x+", y: "+y);
            BufferedImage eleScreenshot = fullImage.getSubimage(x, y, eleWidth, eleHeight);
            ImageIO.write(eleScreenshot, "png", screenshot);

            //Ensure clean snapshot (sometimes WebDriver takes bad pictures and they turn out all black)
            if (!isScreenshotBlack(ImageIO.read(screenshot))) {
                FileUtils.copyFile(screenshot, toSaveAs);
                break;
            }
        }
    }

    private boolean isScreenshotBlack(BufferedImage var) {
        double[] varArr = new double[var.getWidth() * var.getHeight() * 3];
        //unroll pixels
        for (int i = 0; i < var.getHeight(); i++) {
            for (int j = 0; j < var.getWidth(); j++) {
                varArr[i * var.getWidth() + j + 0] = new Color(var.getRGB(j, i)).getRed();
                varArr[i * var.getWidth() + j + 1] = new Color(var.getRGB(j, i)).getGreen();
                varArr[i * var.getWidth() + j + 2] = new Color(var.getRGB(j, i)).getBlue();
            }
        }

        //test if all are black
        for (int i = 0; i != varArr.length; i++) {
            if (varArr[i] != 0)
                return false;
        }
        return true;
    }

    private static class EByFirstMatching extends StringLocatorAwareBy {

        /**
         * New instance.
         * @param locator the locator as string.
         */
        private EByFirstMatching(String locator) {
            super(locator, new ByFirstMatching(By.xpath(locator), By.id(locator), By.name(locator),
                    By.cssSelector(locator), By.className(locator), By.tagName(locator)));
        }

    }

    /**
     * By implementation for legacy behaviour.
    */
    private static class ByFirstMatching extends By {
        final By[] bys;

        private ByFirstMatching(By... bys) {
            this.bys = bys;
        }

        /*
         * (non-Javadoc)
         * @see org.openqa.selenium.By#findElements(org.openqa.selenium.SearchContext)
         */
        @Override
        public List<WebElement> findElements(SearchContext context) {
            for (By by : bys) {
                try {
                    final List<WebElement> element = by.findElements(context);
                    if (element != null && element.size() > 0) {
                        return element;
                    }
                } catch (Exception e) {
                    // ignored
                }
            }
            return null;
        }

        /*
         * (non-Javadoc)
         *
         * @see org.openqa.selenium.By#toString()
         */
        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder("By first matching of ");
            for (By by : bys) {
                builder.append(by.toString()).append(" or ");
            }
            return builder.toString();
        }

    }

}