org.safs.selenium.webdriver.lib.interpreter.WDTestRun.java Source code

Java tutorial

Introduction

Here is the source code for org.safs.selenium.webdriver.lib.interpreter.WDTestRun.java

Source

/** 
 * Copyright (C) SAS Institute, All rights reserved.
 * General Public License: http://www.opensource.org/licenses/gpl-license.php
 **/
package org.safs.selenium.webdriver.lib.interpreter;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.safs.SAFSException;
import org.safs.STAFHelper;
import org.safs.StringUtils;
import org.safs.selenium.webdriver.WebDriverGUIUtilities;
import org.safs.selenium.webdriver.lib.RemoteDriver;
import org.safs.selenium.webdriver.lib.SelectBrowser;
import org.safs.selenium.webdriver.lib.WDLibrary;
import org.safs.tools.stringutils.StringUtilities;

import com.sebuilder.interpreter.Locator;
import com.sebuilder.interpreter.Script;
import com.sebuilder.interpreter.Step;
import com.sebuilder.interpreter.StepType;
import com.sebuilder.interpreter.TestRun;
import com.sebuilder.interpreter.Verify;
import com.sebuilder.interpreter.steptype.ClickElement;
import com.sebuilder.interpreter.steptype.Get;
import com.sebuilder.interpreter.steptype.Store;
import com.sebuilder.interpreter.steptype.SwitchToFrame;
import com.sebuilder.interpreter.steptype.SwitchToFrameByIndex;
import com.sebuilder.interpreter.webdriverfactory.WebDriverFactory;

/**
 * The primary purpose of this class is to extend the TestRun support to include Step peeking, skipping, 
 * and Retry of specific Script Steps.
 * <p>
 * We also provide WDLocator instances using enhanced SearchObject search algorithms.
 * <p>
 * We've added the ability for embedded variable references in Scripts [ex: ${var}] to be handled sought 
 * in SAFSVARS if they are not found in the local variable Map.
 *    
 * @author canagl
 * <br>JUL 28, 2015 SCNTAX Added SwitchToFrame and SwitchToFrameIndex support
 * <br>MAR 01, 2016 CANAGL Added Support for SAFSVARS variable lookups if local Map has no reference.
 */
public class WDTestRun extends TestRun {

    public static final String VARREF_START = "${";
    public static final String VARREF_END = "}";
    public static final String MAPREF_PREFIX = "map:";

    public WDTestRun(Script script, int implicitlyWaitDriverTimeout, int pageLoadDriverTimeout,
            Map<String, String> initialVars) {
        super(script, implicitlyWaitDriverTimeout, pageLoadDriverTimeout, initialVars);
    }

    public WDTestRun(Script script, int implicitlyWaitDriverTimeout, int pageLoadDriverTimeout) {
        super(script, implicitlyWaitDriverTimeout, pageLoadDriverTimeout);
    }

    public WDTestRun(Script script, Log log, TestRun previousRun, int implicitlyWaitDriverTimeout,
            int pageLoadDriverTimeout, Map<String, String> initialVars) {
        super(script, log, previousRun, implicitlyWaitDriverTimeout, pageLoadDriverTimeout, initialVars);
    }

    public WDTestRun(Script script, Log log, WebDriverFactory webDriverFactory,
            HashMap<String, String> webDriverConfig, int implicitlyWaitDriverTimeout, int pageLoadDriverTimeout,
            Map<String, String> initialVars) {
        super(script, log, webDriverFactory, webDriverConfig, implicitlyWaitDriverTimeout, pageLoadDriverTimeout,
                initialVars);
    }

    public WDTestRun(Script script, Log log, WebDriverFactory webDriverFactory,
            HashMap<String, String> webDriverConfig, int implicitlyWaitDriverTimeout, int pageLoadDriverTimeout) {
        super(script, log, webDriverFactory, webDriverConfig, implicitlyWaitDriverTimeout, pageLoadDriverTimeout);
    }

    public WDTestRun(Script script, Log log, WebDriverFactory webDriverFactory,
            HashMap<String, String> webDriverConfig, Map<String, String> initialVars) {
        super(script, log, webDriverFactory, webDriverConfig, initialVars);
    }

    public WDTestRun(Script script, Log log, WebDriverFactory webDriverFactory,
            HashMap<String, String> webDriverConfig) {
        super(script, log, webDriverFactory, webDriverConfig);
    }

    public WDTestRun(Script script, Log log) {
        super(script, log);
    }

    public WDTestRun(Script script) {
        super(script);
    }

    /**
     * @param map name of app map.  may be null.
     * @param win name of app map section.  may be null.
     * @param item item in app map from specified or default app map section.
     * @return String value or null if no value exists.
     */
    public static String getAppMapItem(String map, String win, String item) {
        return WebDriverGUIUtilities._LASTINSTANCE.getSTAFHelper().getAppMapItem(map, win, item);
    }

    /**
     * @param varName variable value to retrieve
     * @return String value or null if no value exists.
     */
    public static String getVariableValue(String varName) {
        try {
            return WebDriverGUIUtilities._LASTINSTANCE.getSTAFHelper().getVariable(varName);
        } catch (SAFSException e) {
        }
        return null;
    }

    /**
     * Process any ${var} references.
     * Lookup potential variable values stored in the local Java Map object and, if not present there, 
     * see if it is available via SAFSVARS or SAFSMAPS.
     * <p>
      * Valid possible App Map Reference formats:
      * <p><pre>
      *   ${Map:ConstantName}              (default App Map)
      *   ${Map:WindowName:CompName}       (default App Map)
      *   ${Map:mapID:WindowName:CompName}
      *   </pre><p>
     * @param value
     * @return value with embedded variable references replaced.
     * @see WebDriverGUIUtilities#_LASTINSTANCE
     * @see STAFHelper#getVariable(String)
     */
    public String replaceVariableReferences(String value) {
        int start = -1;
        int end = -1;
        do {
            start = value.indexOf(VARREF_START);
            if (start > end) {
                // there must be a var name between the braces
                end = value.indexOf(VARREF_END, start + VARREF_START.length() + 1);
                if (end > start) {
                    String key = value.substring(start + VARREF_START.length(), end);
                    String val = null;
                    getLog().debug("WDTestRun seeking embedded variable reference '" + key + "'.");
                    try {
                        if (vars().containsKey(key)) {
                            val = vars().get(key);
                        } else {
                            // Valid possible App Map References:
                            // ${Map:ConstantName}          (default App Map)
                            // ${Map:WindowName:CompName} (default App Map)
                            // ${Map:mapID:WindowName:CompName}
                            if (key.toLowerCase().startsWith(MAPREF_PREFIX)) {
                                String map = key.substring(MAPREF_PREFIX.length());
                                String win = null;
                                String comp = null;
                                int col = map.lastIndexOf(':');
                                if (col < 0) {
                                    //only a COMP (or constant) name is provided
                                    comp = map;
                                    map = null;
                                } else {
                                    // extract explicit comp name
                                    comp = map.substring(col + 1);
                                    map = map.substring(0, col);
                                    col = map.lastIndexOf(':');
                                    if (col < 0) {
                                        win = map;
                                        map = null;
                                    } else {
                                        win = map.substring(col + 1);
                                        map = map.substring(0, col);
                                    }
                                }
                                // lookup App Map Reference mapid and winname may be null
                                val = getAppMapItem(map, win, comp);
                            } else {
                                val = getVariableValue(key);
                            }
                        }
                    } catch (Exception ignore) {
                    }
                    value = val == null ? value : value.replace(VARREF_START + key + VARREF_END, val);
                    getLog().debug("WDTestRun replacing variable reference '" + key + "' as '" + value + "'.");
                }
            }
        } while (start > -1 && end > start);
        return value;
    }

    /**
     * Fetches a Locator parameter from the current step.
     * @param paramName The parameter's name.
     * @return The Locator with any variable references in its value replaced.
     * @see #replaceVariableReferences(String)
     */
    @Override
    public Locator locator(String paramName) {
        if (paramName == null)
            throw new RuntimeException("WDTestRun.locator() paramName is null!");
        Step step = currentStep();
        if (step == null)
            throw new RuntimeException(
                    "WDTestRun.locator() currentStep() #" + (stepIndex + 1) + " is coming back null!");
        Locator loc = step.locatorParams.get(paramName);
        if (loc == null)
            throw new RuntimeException("WDTestRun.locator() step.locatorParams is coming back null!");

        WDLocator l = (loc instanceof WDLocator) ? (WDLocator) loc : new WDLocator(loc.type.name(), loc.value);
        step.locatorParams.put(paramName, l);
        getLog().info("");
        try {
            getLog().info("Locator class: " + l.getClass().getName());
        } catch (Exception x) {
        }
        try {
            getLog().info("Locator Type: " + l.wdtype);
        } catch (Exception x) {
        }
        try {
            getLog().info("Locator Value: " + l.value);
        } catch (Exception x) {
        }
        l.value = replaceVariableReferences(l.value);
        return l;
    }

    /**
     * @return the value of the current Step's named string parameter with any variable references replaced.
     * @see #replaceVariableReferences(String)
     */
    @Override
    public String string(String paramName) {
        String s = currentStep().stringParams.get(paramName);
        if (s == null) {
            throw new RuntimeException("Missing parameter \"" + paramName + "\" at step #" + (stepIndex + 1) + ".");
        }
        return replaceVariableReferences(s);
    }

    /** 
     * Retrieve the next Step that would be executed by this TestRun without incrementing the 
     * stepIndex counter.
     * <p>
     * Does NOT use hasNext()--which can prematurely shutdown the WebDriver.
     * <p>
     * If avoiding hasNext() the user should make sure cleanup() is called when finished to perform 
     * the normal cleanup operations done by hasNext() when it detects there are no more Steps to 
     * execute.
     * @return Step -- null if there are no more steps. 
     */
    public Step peekNext() {
        try {
            getLog().debug("WDTestRun Peeking step " + (stepIndex + 2) + ":"
                    + getScript().steps.get(stepIndex + 1).type.getClass().getSimpleName());
        } catch (Exception x) {
            getLog().debug("WDTestRun: There are no more Steps to Peek. null.");
            return null;
        }
        return getScript().steps.get(stepIndex + 1);
    }

    /**
     * Increment the stepIndex counter and return the Step that can be skipped or executed by this TestRun.
     * Uses peekNext() to check for a valid next Step.  
     * <p>
     * Does NOT use hasNext()--which can prematurely shutdown the WebDriver.
     * <p>
     * If avoiding hasNext() the user should make sure cleanup() is called when finished to perform 
     * the normal cleanup operations done by hasNext() when it detects there are no more Steps to 
     * execute.
     * @return Step -- null if there are no more steps.
     * @see #cleanup()
     */
    public Step popNext() {
        Step step = peekNext();
        if (step == null) {
            getLog().debug("WDTestRun: There are no more Steps to retrieve. null.");
            return null;
        }
        stepIndex++;
        getLog().debug("WDTestRun: retrieved step " + (stepIndex + 1));
        return step;
    }

    /**
     * Perform the normal cleanup done at the end of script execution.
     * Essentially, calls hasNext() with stepIndex indicating all steps have been executed.
     */
    public void cleanup() {
        getLog().debug("WDTestRun.cleanup invoked after step " + (stepIndex + 1));
        stepIndex = getScript().steps.size();
        if (!hasNext() && (getDriver() != null) && (getScript().closeDriver)) {
            getLog().debug("WDTestRun.cleanup closing RemoteWebDriver.");
            try {
                WDLibrary.closeBrowser();
            } catch (Exception x) {
                getLog().debug("WDTestRun.cleanup error closing RemoteWebDriver: " + x.getMessage());
            }
        }
    }

    /** @return True if there is another step to execute. */
    public boolean hasNext() {
        return stepIndex < getScript().steps.size() - 1;
    }

    /**
     * Execute the action represented in the Step.
     * This does NOT change or increment any counters of what Step is being executed 
     * in the Script.
     * <p>
     * If the StepType is an instanceof ClickElement we will use our own WDClickElement 
     * which uses our enhanced WDLibrary to perform the click.<br>
     * 
     * @param step
     * @return
     */
    public boolean runStep(Step step) {

        initRemoteWebDriver();

        try {
            StepType type = step.type;
            if (type instanceof ClickElement) {
                type = new WDClickElement();
            } else if (type instanceof Get) {
                type = new WDGet();
            } else if (type instanceof SwitchToFrame) {
                type = new WDSwitchToFrame();
            } else if (type instanceof SwitchToFrameByIndex) {
                type = new WDSwitchToFrameByIndex();
            } else if (type instanceof Store) {
                String varname = this.string("variable");
                String text = this.string("text");
                WebDriverGUIUtilities._LASTINSTANCE.getSTAFHelper().setVariable(varname, text);
            }
            return type.run(this);
        } catch (Throwable e) {

            this.log().debug("WDTestRun.runStep() " + e.getClass().getName() + ": " + e.getMessage(), e);
            RuntimeException t = new RuntimeException(currentStep() + " failed.", e);
            t.fillInStackTrace();
            throw t;
        }
    }

    /**
     * Executes the next step.  
     * Extracts the Step and executes via runStep for enhanced 
     * handling of some StepTypes.
     * @return True on success.
     * @see #runStep(Step)
     */
    @Override
    public boolean next() {
        if (stepIndex == -1) {
            getLog().debug("Starting test run.");
        }

        initRemoteWebDriver();

        getLog().debug("Running step " + (stepIndex + 2) + ":"
                + getScript().steps.get(stepIndex + 1).type.getClass().getSimpleName() + " step.");
        boolean result = runStep(getScript().steps.get(++stepIndex));
        if (!result) {
            // If a verify failed, we just note this but continue.
            if (currentStep().type instanceof Verify) {
                getLog().error(currentStep() + " failed.");
                return false;
            }
            // In all other cases, we throw an exception to stop the run.
            RuntimeException e = new RuntimeException(currentStep() + " failed.");
            e.fillInStackTrace();
            throw e; // continue?
        } else {
            return true;
        }
    }

    /* (non-Javadoc)
     * @see com.sebuilder.interpreter.TestRun#getDriver()
     */
    @Override
    public RemoteWebDriver getDriver() {
        getLog().info("WDTestRun.getDriver() invoked.");
        try {
            WebDriver webDriver = WDLibrary.getWebDriver();
            if (webDriver == null) {
                getLog().debug("WDTestRun.getDriver() is null.");
            } else {
                return (RemoteWebDriver) webDriver;
            }
        } catch (Exception x) {
            getLog().debug("WDTestRun.getDriver() " + x.getMessage());
        }
        return null;
    }

    /* (non-Javadoc)
     * @see com.sebuilder.interpreter.TestRun#driver()
     */
    @Override
    public RemoteWebDriver driver() {
        getLog().info("WDTestRun.driver() invoked. Calling getDriver();");
        // watch out for recursive loop!
        return getDriver();
    }

    /* (non-Javadoc)
     * CANAGL -- this seems to be called prior to EVERY single Step in the script.
     * So only initialize IF we don't already have one.
     * @see com.sebuilder.interpreter.TestRun#initRemoteWebDriver()
     */
    @Override
    public void initRemoteWebDriver() {
        getLog().info("WDTestRun.initRemoteWebDriver invoked.");
        if (getDriver() != null) {
            getLog().info("WDTestRun.initRemoteWebDriver detected session already exists...");
            return;
        }
        int timeout = 30; // TODO get from parameters in this object!
        try {
            WDLibrary.startBrowser(null, null, null, timeout, true);
        } catch (Throwable th) {
            String thmsg = th.getMessage();
            getLog().info(
                    "WDTestRun.initRemoteWebDriver initially failed to create a new WebDriver session. RemoteServer may not be running.");
            String seleniumhost = System.getProperty(SelectBrowser.SYSTEM_PROPERTY_SELENIUM_HOST,
                    SelectBrowser.DEFAULT_SELENIUM_HOST);
            String seleniumport = System.getProperty(SelectBrowser.SYSTEM_PROPERTY_SELENIUM_PORT,
                    SelectBrowser.DEFAULT_SELENIUM_PORT);
            if (seleniumhost.equals(SelectBrowser.DEFAULT_SELENIUM_HOST)) {
                getLog().info("WDTestRun.initRemoteWebDriver attempting to start a local RemoteServer...");
                boolean success = false;
                try {
                    String projectdir = WebDriverGUIUtilities._LASTINSTANCE.getSTAFHelper()
                            .getVariable(STAFHelper.SAFS_VAR_PROJECTDIRECTORY);
                    success = WebDriverGUIUtilities.startRemoteServer(projectdir);
                    if (success)
                        getLog().info("WDTestRun.initRemoteWebDriver started RemoteServer successfully.");
                    else
                        getLog().info(
                                "WDTestRun.initRemoteWebDriver starting RemoteServer internally not successful.");
                } catch (Exception x) {
                    getLog().info("WDTestRun.initRemoteWebDriver attempt to start RemoteServer internally: "
                            + x.getClass().getName() + ", " + x.getMessage());
                }
                if (!success) {
                    getLog().info(
                            "WDTestRun.initRemoteWebDriver attempting to start RemoteServer from external script...");
                    success = WebDriverGUIUtilities.startRemoteServer();
                }
                if (success) {
                    try {
                        WDLibrary.startBrowser(null, null, null, timeout, true);
                        getLog().info("WDTestRun.initRemoteWebDriver successful starting browser session.");
                    } catch (Throwable th2) {
                        getLog().debug("WDTestRun.initRemoteWebDriver failed to start a new WebDriver session:",
                                th2);
                    }
                } else {
                    getLog().debug("WDTestRun.initRemoteWebDriver failed to start the local RemoteServer!");
                }
            } else {
                getLog().info("WDTestRun.initRemoteWebDriver detected the expected RemoteServer '" + seleniumhost
                        + ":" + seleniumport + "' is not running and cannot be started locally.");
            }
        }
    }

}