com.winhong.dagger.BrowserEmulator.java Source code

Java tutorial

Introduction

Here is the source code for com.winhong.dagger.BrowserEmulator.java

Source

/*
 * Copyright (c) 2012-2013 NetEase, Inc. and other 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 com.winhong.dagger;

import com.thoughtworks.selenium.Wait;
import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.safari.SafariDriver;
import org.openqa.selenium.support.ui.Select;
import org.testng.Assert;
import org.testng.Reporter;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;

/**
 * BrowserEmulator is based on Selenium2 and adds some enhancements
 * @author ChenKan
 */
public class BrowserEmulator {

    RemoteWebDriver browserCore;
    WebDriverBackedSelenium browser;
    ChromeDriverService chromeServer;
    JavascriptExecutor javaScriptExecutor;

    int stepInterval = Integer.parseInt(GlobalSettings.stepInterval);
    int timeout = Integer.parseInt(GlobalSettings.timeout);

    private static Logger logger = Logger.getLogger(BrowserEmulator.class.getName());

    public BrowserEmulator() {
        setupBrowserCoreType(GlobalSettings.browserCoreType);
        browser = new WebDriverBackedSelenium(browserCore, "http://www.163.com/");
        javaScriptExecutor = (JavascriptExecutor) browserCore;
        logger.info("Started BrowserEmulator");
    }

    private void setupBrowserCoreType(int type) {
        if (type == 1) {
            browserCore = new FirefoxDriver();
            logger.info("Using Firefox");
            return;
        }
        if (type == 2) {
            chromeServer = new ChromeDriverService.Builder()
                    .usingDriverExecutable(new File(GlobalSettings.chromeDriverPath)).usingAnyFreePort().build();
            try {
                chromeServer.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
            DesiredCapabilities capabilities = DesiredCapabilities.chrome();
            String prox = GlobalSettings.prop.getProperty("http_proxy");
            if (prox != null) {
                capabilities.setCapability("chrome.switches",
                        Arrays.asList("--start-maximized", "--proxy-server=" + prox));
            } else {
                capabilities.setCapability("chrome.switches", Arrays.asList("--start-maximized"));
            }
            browserCore = new RemoteWebDriver(chromeServer.getUrl(), capabilities);
            logger.info("Using Chrome");
            return;
        }
        if (type == 3) {
            System.setProperty("webdriver.ie.driver", GlobalSettings.ieDriverPath);
            DesiredCapabilities capabilities = DesiredCapabilities.internetExplorer();
            capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,
                    true);
            browserCore = new InternetExplorerDriver(capabilities);
            logger.info("Using IE");
            return;
        }
        if (type == 4) {
            browserCore = new SafariDriver();
            logger.info("Using Safari");
            return;
        }

        Assert.fail("Incorrect browser type");
    }

    /**
     * Get the WebDriver instance embedded in BrowserEmulator
     * @return a WebDriver instance
     */
    public RemoteWebDriver getBrowserCore() {
        return browserCore;
    }

    /**
     * Get the WebDriverBackedSelenium instance embedded in BrowserEmulator
     * @return a WebDriverBackedSelenium instance
     */
    public WebDriverBackedSelenium getBrowser() {
        return browser;
    }

    /**
     * Get the JavascriptExecutor instance embedded in BrowserEmulator
     * @return a JavascriptExecutor instance
     */
    public JavascriptExecutor getJavaScriptExecutor() {
        return javaScriptExecutor;
    }

    /**
     * Open the URL
     * @param url
     *            the target URL
     */
    public void open(String url) {
        pause(stepInterval);
        try {
            browser.open(url);
        } catch (Exception e) {
            e.printStackTrace();
            handleFailure("Failed to open url " + url);
        }
        logger.info("Opened url " + url);
    }

    /**
     * Quit the browser
     */
    public void quit() {
        pause(stepInterval);
        browserCore.quit();
        if (GlobalSettings.browserCoreType == 2) {
            chromeServer.stop();
        }
        logger.info("Quitted BrowserEmulator");
    }

    /**
     * Click the page element
     * @param xpath
     *            the element's xpath
     */
    public void click(String xpath) {
        pause(stepInterval);
        expectElementExistOrNot(true, xpath, timeout);
        try {
            clickTheClickable(xpath, System.currentTimeMillis(), 2500);
        } catch (Exception e) {
            e.printStackTrace();
            handleFailure("Failed to click " + xpath);
        }
        logger.info("Clicked " + xpath);
    }

    /**
     * Click an element until it's clickable or timeout
     * @param xpath
     * @param startTime
     * @param timeout in millisecond
     * @throws Exception
     */
    private void clickTheClickable(String xpath, long startTime, int timeout) throws Exception {
        try {
            browserCore.findElementByXPath(xpath).click();
        } catch (Exception e) {
            if (System.currentTimeMillis() - startTime > timeout) {
                logger.info("Element " + xpath + " is unclickable");
                throw new Exception(e);
            } else {
                Thread.sleep(500);
                logger.info("Element " + xpath + " is unclickable, try again");
                clickTheClickable(xpath, startTime, timeout);
            }
        }
    }

    /**
     * Type text at the page element<br>
     * Before typing, try to clear existed text
     * @param xpath
     *            the element's xpath
     * @param text
     *            the input text
     */
    public void type(String xpath, String text) {
        pause(stepInterval);
        expectElementExistOrNot(true, xpath, timeout);

        WebElement we = browserCore.findElement(By.xpath(xpath));
        try {
            we.clear();
        } catch (Exception e) {
            logger.warn("Failed to clear text at " + xpath);
        }
        try {
            we.sendKeys(text);
        } catch (Exception e) {
            e.printStackTrace();
            handleFailure("Failed to type " + text + " at " + xpath);
        }

        logger.info("Type " + text + " at " + xpath);
    }

    /**
     * Hover on the page element
     * 
     * @param xpath
     *            the element's xpath
     */
    public void mouseOver(String xpath) {
        pause(stepInterval);
        expectElementExistOrNot(true, xpath, timeout);
        // First make mouse out of browser
        Robot rb = null;
        try {
            rb = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
        rb.mouseMove(0, 0);

        // Then hover
        WebElement we = browserCore.findElement(By.xpath(xpath));

        if (GlobalSettings.browserCoreType == 2) {
            try {
                Actions builder = new Actions(browserCore);
                builder.moveToElement(we).build().perform();
            } catch (Exception e) {
                e.printStackTrace();
                handleFailure("Failed to mouseover " + xpath);
            }

            logger.info("Mouseover " + xpath);
            return;
        }

        // Firefox and IE require multiple cycles, more than twice, to cause a
        // hovering effect
        if (GlobalSettings.browserCoreType == 1 || GlobalSettings.browserCoreType == 3) {
            for (int i = 0; i < 5; i++) {
                Actions builder = new Actions(browserCore);
                builder.moveToElement(we).build().perform();
            }
            logger.info("Mouseover " + xpath);
            return;
        }

        // Selenium doesn't support the Safari browser
        if (GlobalSettings.browserCoreType == 4) {
            Assert.fail("Mouseover is not supported for Safari now");
        }
        Assert.fail("Incorrect browser type");
    }

    /**
     * Switch window/tab
     * @param windowTitle
     *            the window/tab's title
     */
    public void selectWindow(String windowTitle) {
        pause(stepInterval);
        browser.selectWindow(windowTitle);
        logger.info("Switched to window " + windowTitle);
    }

    /**
     * Enter the iframe
     * @param xpath
     *            the iframe's xpath
     */
    public void enterFrame(String xpath) {
        pause(stepInterval);
        browserCore.switchTo().frame(browserCore.findElementByXPath(xpath));
        logger.info("Entered iframe " + xpath);
    }

    /**
     * Leave the iframe
     */
    public void leaveFrame() {
        pause(stepInterval);
        browserCore.switchTo().defaultContent();
        logger.info("Left the iframe");
    }

    /**
     * Refresh the browser
     */
    public void refresh() {
        pause(stepInterval);
        browserCore.navigate().refresh();
        logger.info("Refreshed");
    }

    /**
     * Mimic system-level keyboard event
     * @param keyCode
     *            such as KeyEvent.VK_TAB, KeyEvent.VK_F11
     */
    public void pressKeyboard(int keyCode) {
        pause(stepInterval);
        Robot rb = null;
        try {
            rb = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
        rb.keyPress(keyCode); // press key
        rb.delay(100); // delay 100ms
        rb.keyRelease(keyCode); // release key
        logger.info("Pressed key with code " + keyCode);
    }

    /**
     * Mimic system-level keyboard event with String
     * 
     * @param text
     * 
     */
    public void inputKeyboard(String text) {
        String cmd = System.getProperty("user.dir") + "\\res\\SeleniumCommand.exe" + " sendKeys " + text;

        Process p = null;
        try {
            p = Runtime.getRuntime().exec(cmd);
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            p.destroy();
        }
        logger.info("Pressed key with string " + text);
    }

    //TODO Mimic system-level mouse event

    /**
     * Expect some text exist or not on the page<br>
     * Expect text exist, but not found after timeout => Assert fail<br>
     * Expect text not exist, but found after timeout => Assert fail
     * @param expectExist
     *            true or false
     * @param text
     *            the expected text
     * @param timeout
     *            timeout in millisecond
     */
    public void expectTextExistOrNot(boolean expectExist, final String text, int timeout) {
        if (expectExist) {
            try {
                new Wait() {
                    public boolean until() {
                        return isTextPresent(text, -1);
                    }
                }.wait("Failed to find text " + text, timeout);
            } catch (Exception e) {
                e.printStackTrace();
                handleFailure("Failed to find text " + text);
            }
            logger.info("Found desired text " + text);
        } else {
            if (isTextPresent(text, timeout)) {
                handleFailure("Found undesired text " + text);
            } else {
                logger.info("Not found undesired text " + text);
            }
        }
    }

    /**
     * Expect an element exist or not on the page<br>
     * Expect element exist, but not found after timeout => Assert fail<br>
     * Expect element not exist, but found after timeout => Assert fail<br>
     * Here <b>exist</b> means <b>visible</b>
     * @param expectExist
     *            true or false
     * @param xpath
     *            the expected element's xpath
     * @param timeout
     *            timeout in millisecond
     */
    public void expectElementExistOrNot(boolean expectExist, final String xpath, int timeout) {
        if (expectExist) {
            try {
                new Wait() {
                    public boolean until() {
                        return isElementPresent(xpath, -1);
                    }
                }.wait("Failed to find element " + xpath, timeout);
            } catch (Exception e) {
                e.printStackTrace();
                handleFailure("Failed to find element " + xpath);
            }
            logger.info("Found desired element " + xpath);
        } else {
            if (isElementPresent(xpath, timeout)) {
                handleFailure("Found undesired element " + xpath);
            } else {
                logger.info("Not found undesired element " + xpath);
            }
        }
    }

    /**
     * Expect an element exist in timeout milliseconds, and click it
     * @param xpath
     * @param timeout
     */
    public void expectExistAndClick(final String xpath, int timeout) {
        expectElementExistOrNot(true, xpath, timeout);
        click(xpath);
    }

    /**
     * Expect an input element exist in timeout milliseconds and type text
     * @param xpath
     * @param timeout
     * @param text
     */
    public void expectExistAndType(final String xpath, int timeout, String text) {
        expectElementExistOrNot(true, xpath, timeout);
        type(xpath, text);
    }

    /**
     * ?
     * @param xpath
     *            the expected element's xpath
     * @param time
     *            wait a moment (in millisecond) before search element on page;<br>
     *            minus time means search element at once
     */
    public void waitUntilDisappear(final String xpath, int timeout) {
        for (int i = 0; i < timeout; i += 2) {
            if (!isElementPresent(xpath, 2000)) {
                return;
            }
        }
        handleFailure("Element will not disappear " + xpath);
    }

    /**
     * Is the text present on the page
     * @param text
     *            the expected text
     * @param time           
     *            wait a moment (in millisecond) before search text on page;<br>
     *            minus time means search text at once
     * @return
     */
    public boolean isTextPresent(String text, int time) {
        pause(time);
        boolean isPresent = browser.isTextPresent(text);
        if (isPresent) {
            logger.info("Found text " + text);
            return true;
        } else {
            logger.info("Not found text " + text);
            return false;
        }
    }

    /**
     * Is the element present on the page<br>
     * Here <b>present</b> means <b>visible</b>
     * @param xpath
     *            the expected element's xpath
     * @param time           
     *            wait a moment (in millisecond) before search element on page;<br>
     *            minus time means search element at once
     * @return
     */
    public boolean isElementPresent(String xpath, int time) {
        pause(time);
        boolean isPresent = browser.isElementPresent(xpath) && browserCore.findElementByXPath(xpath).isDisplayed();
        if (isPresent) {
            logger.info("Found element " + xpath);
            return true;
        } else {
            logger.info("Not found element" + xpath);
            return false;
        }
    }

    /**
     * Pause
     * @param time in millisecond
     */
    public void pause(int time) {
        if (time <= 0) {
            return;
        }
        try {
            Thread.sleep(time);
            logger.info("Pause " + time + " ms");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void handleFailure(String notice) {
        String png = LogTools.screenShot(this);
        String log = notice + " >> capture screenshot at " + png;
        logger.error(log);
        if (GlobalSettings.baseStorageUrl.lastIndexOf("/") == GlobalSettings.baseStorageUrl.length()) {
            GlobalSettings.baseStorageUrl = GlobalSettings.baseStorageUrl.substring(0,
                    GlobalSettings.baseStorageUrl.length() - 1);
        }
        Reporter.log(log + "<br/><img src=\"" + GlobalSettings.baseStorageUrl + "/" + png + "\" />");
        Assert.fail(log);
    }

    /**
     * Return text from specified web element.
     * @param xpath
     * @return
     */
    public String getText(String xpath) {
        WebElement element = this.getBrowserCore().findElement(By.xpath(xpath));
        return element.getText();
    }

    /**
     * Return attribute value from specified web element.
     * @param xpath
     * @param attr
     * @return
     */
    public String getAttribute(String xpath, String attr) {
        WebElement element = this.getBrowserCore().findElement(By.xpath(xpath));
        return element.getAttribute(attr);
    }

    /**
     * Select an option by visible text from &lt;select&gt; web element.
     * @param xpath
     * @param option
     */
    public void select(String xpath, String option) {
        WebElement element = this.browserCore.findElement(By.xpath(xpath));
        Select select = new Select(element);
        select.selectByVisibleText(option);
    }
}