Package com.xebia.incubator.xebium

Source Code of com.xebia.incubator.xebium.SeleniumDriverFixture$SeleniumCommandResult

/*
* Copyright 2010-2012 Xebia b.v.
* Copyright 2010-2012 Xebium contributers
*
* 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.xebia.incubator.xebium;

import com.thoughtworks.selenium.CommandProcessor;
import com.thoughtworks.selenium.HttpCommandProcessor;
import com.thoughtworks.selenium.SeleniumException;
import com.xebia.incubator.xebium.fastseleniumemulation.FastWebDriverCommandProcessor;
import com.thoughtworks.selenium.webdriven.WebDriverCommandProcessor;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.xebia.incubator.xebium.FitNesseUtil.*;
import static org.apache.commons.lang.StringUtils.join;

/**
* Main fixture. Starts a browser session and execute commands.
*/
public class SeleniumDriverFixture {

  private static final Logger LOG = LoggerFactory.getLogger(SeleniumDriverFixture.class);

    private DefaultWebDriverSupplier defaultWebDriverSupplier = new DefaultWebDriverSupplier();

  private static final String ALIAS_PREFIX = "%";

  private CommandProcessor commandProcessor;

  // in milliseconds
  private long timeout = 30000;

  private long stepDelay = 0;

  private long pollDelay = 100;

  private boolean stopBrowserOnAssertion = true;

  private ScreenCapture screenCapture = new ScreenCapture();

  private LocatorCheck locatorCheck;

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

  /**
   * Xebium uses the 'selenium emulation' command set, which can be handled by Selenium via the
   * WebDriverCommandProcessor.
   *
   * Selenium's implementation is backwards-compatible, slow and buggy. We provide a FastWebDriverCommandProcessor
   * which replaces some common commands with faster direct WebDriver calls, at the expense of some
   * backwards-compatibility.
   *
   * Set this field to 'false' to fall back to the original implementation.
   *
   * This has to be done before the 'start browser on url' call.
   */
    private boolean useFastSeleniumEmulation = true;

  public SeleniumDriverFixture() {
    super();
  }

    private WebDriver defaultWebDriverInstance() {
      return defaultWebDriverSupplier.newWebDriver();
    }

    private CommandProcessor startWebDriverCommandProcessor(String browserUrl, WebDriver webDriver) {
    browserUrl = removeAnchorTag(browserUrl);
        WebDriverCommandProcessor driver;
        if (useFastSeleniumEmulation) {
            driver = new FastWebDriverCommandProcessor(browserUrl, webDriver);
        } else {
            driver = new WebDriverCommandProcessor(browserUrl, webDriver);
        }
        addMissingSeleneseCommands(driver);
        return driver;
  }

    private void addMissingSeleneseCommands(WebDriverCommandProcessor driver) {
        driver.addMethod("sendKeys", driver.getMethod("typeKeys"));
    }

    /**
     * Configure the custom Firefox preferences (javascript) file on the webdriver factory.
     *
     * @param filename
     */
    public void loadCustomBrowserPreferencesFromFile(String filename) {
        defaultWebDriverSupplier.setCustomProfilePreferencesFile(new File(filename));
    }

    public void useFastSeleniumEmulation(Boolean useFastSeleniumEmulation) {
        this.useFastSeleniumEmulation = useFastSeleniumEmulation;
    }

  /**
     * Configure the custom Firefox profiledirectory on the webdriver factory.
     *
   * @param directory
   */
  public void loadFirefoxProfileFromDirectory(String directory) {
        defaultWebDriverSupplier.setProfileDirectory(new File(directory));
  }

  /**
   * @param browser Name of the browser, as accepted by the DefaultWebDriverSupplier.
   */
  private void setBrowser(String browser) {
        defaultWebDriverSupplier.setBrowser(browser);
  }

    /**
     * <p><code>
     * | start driver | <i>$Driver</i> | on url | <i>http://localhost</i> |
     * </code></p>
     *
     * @param webDriver a WebDriver instance
     * @param browserUrl
     */
    public void startDriverOnUrl(final WebDriver webDriver, final String browserUrl) {
        setCommandProcessor(startWebDriverCommandProcessor(browserUrl, webDriver));
        setTimeoutOnSelenium();
        LOG.debug("Started command processor");
    }

  /**
   * <p><code>
   * | start browser | <i>firefox</i> | on url | <i>http://localhost</i> |
   * </code></p>
   *
   * @param browser
   * @param browserUrl
   */
  public void startBrowserOnUrl(final String browser, final String browserUrl) {
    setBrowser(browser);
        startDriverOnUrl(defaultWebDriverInstance(), browserUrl);
  }

  /**
   * <p><code>
   * | start browser | <i>firefox</i> | on url | <i>http://localhost</i> | using remote server |
   * </code></p>
   *
   * @param browser
   * @param browserUrl
   * @deprecated This call requires a Selenium 1 server. It is advised to use WebDriver.
   */
  public void startBrowserOnUrlUsingRemoteServer(final String browser, final String browserUrl) {
    startBrowserOnUrlUsingRemoteServerOnHost(browser, browserUrl, "localhost");
  }

  /**
   * <p><code>
   * | start browser | <i>firefox</i> | on url | <i>http://localhost</i> | using remote server on host | <i>localhost</i> |
   * </code></p>
   *
   * @param browser
   * @param browserUrl
   * @param serverHost
   * @deprecated This call requires a Selenium 1 server. It is advised to use WebDriver.
   */
  public void startBrowserOnUrlUsingRemoteServerOnHost(final String browser, final String browserUrl, final String serverHost) {
    startBrowserOnUrlUsingRemoteServerOnHostOnPort(browser, browserUrl, serverHost, 4444);
  }

  /**
   * <p><code>
   * | start browser | <i>firefox</i> | on url | <i>http://localhost</i> | using remote server on host | <i>localhost</i> | on port | <i>4444</i> |
   * </code></p>
   *
   * @param browser
   * @param browserUrl
   * @param serverHost
   * @param serverPort
   * @deprecated This call requires a Selenium 1 server. It is advised to use WebDriver.
   */
  public void startBrowserOnUrlUsingRemoteServerOnHostOnPort(final String browser, final String browserUrl, final String serverHost, final int serverPort) {
    setCommandProcessor(new HttpCommandProcessorAdapter(new HttpCommandProcessor(serverHost, serverPort, browser, removeAnchorTag(browserUrl))));
    commandProcessor.start();
    setTimeoutOnSelenium();
    LOG.debug("Started HTML command processor");
  }

  void setCommandProcessor(CommandProcessor commandProcessor) {
    this.commandProcessor = commandProcessor;
    screenCapture.setCommandProcessor(commandProcessor);
    locatorCheck = new LocatorCheck(commandProcessor);
    LOG.info("Started new command processor (timeout: " + timeout + "ms, step delay: " + stepDelay + "ms, poll interval: " + pollDelay + "ms)");
  }

  void setScreenCapture(ScreenCapture screenCapture) {
    this.screenCapture = screenCapture;
  }

  /**
   * <p><code>
   * | set timeout to | 500 |
   * </code></p>
   *
   * <p>Set the timeout, both local and on the running selenium server.</p>
   *
   * @param timeout Timeout in milliseconds (ms)
   */
  public void setTimeoutTo(long timeout) {
    this.timeout = timeout;
    if (commandProcessor != null) {
      setTimeoutOnSelenium();
    }
  }

  /**
   * <p><code>
   * | set timeout to | 500 | seconds |
   * </code></p>
   *
   * <p>Set the timeout, both local and on the running selenium server.</p>
   *
   * @param timeout Timeout in seconds
   */
  public void setTimeoutToSeconds(long timeout) {
    setTimeoutTo(timeout * 1000);
  }

  /**
   * Set the default timeout on the selenium instance.
   */
  private void setTimeoutOnSelenium() {
    executeCommand("setTimeout", new String[] { "" + this.timeout });
        WebDriver.Timeouts timeouts = getWebDriver().manage().timeouts();
        timeouts.setScriptTimeout(this.timeout, TimeUnit.MILLISECONDS);
        timeouts.pageLoadTimeout(this.timeout, TimeUnit.MILLISECONDS);
  }

  /**
   * <p>Set delay between steps.</p>
   * <p><code>
   * | set step delay to | 500 |
   * | set step delay to | slow |
   * | set step delay to | fast |
   * </code></p>
   *
   * @param stepDelay delay in milliseconds
   */
  public void setStepDelayTo(String stepDelay) {
    if ("slow".equals(stepDelay)) {
      this.stepDelay = 1000;
    } else if ("fast".equals(stepDelay)) {
      this.stepDelay = 0;
    } else {
      this.stepDelay = Long.parseLong(stepDelay);
    }
  }

  /**
   * <p>In case of an assertion (assert* selenese command), close the browser.</p>
   * <p><code>
   * | set stop browser on assertion | true |
   * </code></p>
   *
   * @param stopBrowserOnAssertion
   */
  public void setStopBrowserOnAssertion(boolean stopBrowserOnAssertion) {
    this.stopBrowserOnAssertion = stopBrowserOnAssertion;
  }

  /**
   * Instruct the driver to create screenshots
   * <p><code>
   * | save screenshot after | <i>failure</i> |
   * | save screenshot after | <i>error</i> |
   * </code></p>
   *
   * <p><code>
   * | save screenshot after | <i>every step</i> |
   * | save screenshot after | <i>step</i> |
   * </code></p>
   *
   * <p><code>
   * | save screenshot after | <i>nothing</i> |
   * | save screenshot after | <i>none</i> |
   * </code></p>
   */
  public void saveScreenshotAfter(String policy) throws IOException {
    screenCapture.setScreenshotPolicy(policy);
  }

  /**
   * <p><code>
   * | save screenshot after | <i>failure</i> | in folder | <i>http://files/testResults/screenshots/${PAGE_NAME} |
   * | save screenshot after | <i>error</i> |
   * </code></p>
   */
  public void saveScreenshotAfterInFolder(String policy, String baseDir) throws IOException {
    screenCapture.setScreenshotBaseDir(removeAnchorTag(baseDir));
    saveScreenshotAfter(policy);
  }

  /**
   * <p><code>
   * | ensure | do | <i>open</i> | on | <i>/</i> |
   * </code></p>
   *
   * @param command
   * @param target
   * @return
   */
  public boolean doOn(final String command, final String target) {
    LOG.info("Performing | " + command + " | " + target + " |");
    return executeDoCommand(command, new String[] { unalias(target) });
  }

  /**
   * <p><code>
   * | ensure | do | <i>type</i> | on | <i>searchString</i> | with | <i>some text</i> |
   * </code></p>
   *
   * @param command
   * @param target
   * @param value
   * @return
   */
  public boolean doOnWith(final String command, final String target, final String value) {
    LOG.info("Performing | " + command + " | " + target + " | " + value + " |");
    return executeDoCommand(command, new String[] { unalias(target), unalias(value) });
  }

  /**
   * <p><code>
   * | <i>$title=</i> | is | <i>getTitle</i> |
   * </code></p>
   *
   * @param command
   * @return
   */
  public String is(final String command) {
    LOG.info("Obtain result from  | " + command + " |");
    return is(command, new String[]{});
  }

  /**
   * Same as {@link #is(String)}, only with "on" statement, analog to "do-on" command.
   *
   * @param command
   * @return
   */
  public String isOn(final String command) {
    return is(command);
  }

  /**
   * <p><code>
   * | <i>$pageName=</i> | is | <i>getText</i> | on | <i>//span</i> |
   * </code></p>
   *
   * @param command
   * @param target
   * @return
   */
  public String isOn(final String command, final String target) {
    LOG.info("Obtain result from | " + command + " | " + target + " |");
    return is(command, new String[] { unalias(target) });
  }

    public String is(final String command, final String[] parameters) {
        ExtendedSeleniumCommand seleniumCommand = new ExtendedSeleniumCommand(command);
        String output = executeCommand(seleniumCommand, parameters, stepDelay);

        if (seleniumCommand.isBooleanCommand() && seleniumCommand.isNegateCommand()) {
             if ("true".equals(output)) {
                output = "false";
            } else if ("false".equals(output)) {
                output = "true";
            } else {
                throw new IllegalStateException("Illegal boolean value: '" + output + "'");
            }
        }

        return output;
    }

  /**
   * Same as {@link #isOn(String, String)}, only with "with" statement, analog to "do-on-with" command.
   *
   * @param command
   * @param target
   * @return
   */
  public String isOnWith(final String command, final String target) {
    return isOn(command, target);
  }

  /**
   * Add a new locator alias to the fixture.
   *
   * @param alias
   * @param locator
   */
  public void addAliasForLocator(String alias, String locator) {
    LOG.info("Add alias: '" + alias + "' for '" + locator + "'");
    aliases.put(alias, locator);
  }

  /**
   * Clear the aliases table.
   */
  public void clearAliases() {
    aliases.clear();
  }

  private String unalias(String value) {
    String result = value;
    if (value != null && value.startsWith(ALIAS_PREFIX)) {
      String alias = value.substring(ALIAS_PREFIX.length());
      String subst = aliases.get(alias);
      if (subst != null) {
          LOG.info("Expanded alias '" + alias + "' to '" + result + "'");
          result = subst;
      }
    }
    return result;
  }

  private boolean executeDoCommand(final String methodName, final String[] values) {

    ExtendedSeleniumCommand command = new ExtendedSeleniumCommand(methodName);

    SeleniumCommandResult commandResult;

    if (!locatorCheck.verifyElementPresent(command, values)) {
      commandResult = failure();
    } else if (command.requiresPolling()) {
      commandResult = executeDoCommandPolling(values, command);
    } else {
      commandResult = executeAndCheckResult(command, values, stepDelay);

      if (command.isCaptureEntirePageScreenshotCommand()) {
        writeToFile(values[0], commandResult.output);
      }
    }

    if (screenCapture.requireScreenshot(command, commandResult.result)) {
      screenCapture.captureScreenshot(methodName, values);
    }

    if (commandResult.failed() && command.isAssertCommand()) {
      if (stopBrowserOnAssertion) {
        stopBrowser();
      }
      throw new AssertionAndStopTestError(commandResult.output);
    }

    if (commandResult.hasException()) {
      throw new AssertionError(commandResult.getException());
    } else {
      return commandResult.result;
    }
  }

  private SeleniumCommandResult executeDoCommandPolling(String[] values, ExtendedSeleniumCommand command) {
    SeleniumCommandResult commandResult;
    long timeoutTime = System.currentTimeMillis() + timeout;

    do {
      commandResult = executeAndCheckResult(command, values, 0);
      if (commandResult.failed()) {
        delayIfNeeded(pollDelay);
      }
    } while (commandResult.failed() && timeoutTime > System.currentTimeMillis());

    LOG.info("WaitFor-command '" + command.getSeleniumCommand() (commandResult.succeeded() ? "' succeeded" : "' failed"));
    return commandResult;
  }

  private SeleniumCommandResult executeAndCheckResult(ExtendedSeleniumCommand command, String[] values, long delay) {
    try {
      String output = executeCommand(command, values, delay);

      if (command.requiresPolling() || command.isAssertCommand() || command.isVerifyCommand() || command.isWaitForCommand()) {
        String expected = values[values.length - 1];
        boolean result = checkResult(command, expected, output);
        LOG.info("Command '" + command.getSeleniumCommand() + "' returned '" + output + "' => " + (result ? "ok" : "not ok, expected '" + expected + "'"));

        return new SeleniumCommandResult(result, output, null);
      } else {
        LOG.info("Command '" + command.getSeleniumCommand() + "' returned '" + output + "'");
        return success(output);
      }
    } catch (Exception e) {
      return failure(e);
    }
  }

  private String executeCommand(final ExtendedSeleniumCommand command, final String[] values, long delay) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("executeCommand. Command: " + command.getSeleniumCommand() + " with values: [" + join(values, ", ") +"]");
    }

    if (commandProcessor == null) {
      throw new IllegalStateException("Command processor not running. " +
              "First start it by invoking startBrowserOnUrl.");
    }

    if ("pause".equals(command.getSeleniumCommand())) {
      pause(values[0]);
      return null;
    }

    String output = getCommandOutput(command, values);

    waitForPageLoadIfNeeded(command);
    delayIfNeeded(delay);

    return output;
  }

  private void waitForPageLoadIfNeeded(ExtendedSeleniumCommand command) {
    if (command.isAndWaitCommand()) {
      commandProcessor.doCommand("waitForPageToLoad", new String[] { "" + timeout });
    }
  }

  private String getCommandOutput(ExtendedSeleniumCommand command, String[] values) {
    try {
      if (command.returnTypeIsArray()) {
        return executeArrayCommand(command.getSeleniumCommand(), values);
      } else {
        return executeCommand(command.getSeleniumCommand(), values);
      }
    } catch (final SeleniumException e) {
      String output = "Execution of command failed: " + e.getMessage();
      LOG.error(output);

      if (!(command.isAssertCommand() || command.isVerifyCommand() || command.isWaitForCommand())) {
        throw e;
      }

      return output;
    }
  }

  private void delayIfNeeded(long delay) {
    if (delay > 0) {
      try {
        Thread.sleep(delay);
      } catch (Exception e) {
        LOG.warn("Step delay sleep command interrupted", e);
      }
    }
  }

  private void pause(String delayInMillis) {
    try {
      Thread.sleep(Long.parseLong(delayInMillis));
    } catch (Exception e) {
      LOG.warn("Pause command interrupted", e);
    }
  }

  private String executeCommand(String methodName, final String[] values) {
    String output = commandProcessor.doCommand(methodName, values);

    if (output != null && LOG.isDebugEnabled()) {
      LOG.debug("Command processor returned '" + output + "'");
    }

    return output;
  }

  private String executeArrayCommand(String methodName, final String[] values) {
    String[] output = commandProcessor.getStringArray(methodName, values);

    if (output != null && LOG.isDebugEnabled()) {
      LOG.debug("Command processor returned '" + Arrays.asList(output) + "'");
    }

    return stringArrayToString(output);
  }

  private boolean checkResult(ExtendedSeleniumCommand command, String expected, String actual) {
    return command.matches(expected, actual);
  }

  private void writeToFile(final String filename, final String output) {
    File file = asFile(filename);
    try {
            createParentDirectoryIfNeeded(file);

      ScreenCapture.writeToFile(file, output);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

    private void createParentDirectoryIfNeeded(File file) throws IOException {
        if (file.getParentFile() != null && !file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                throw new IOException("Could not create parent directory " + file.getParent());
            }
        }
    }

    public void stopBrowser() {
    commandProcessor.stop();
    commandProcessor = null;

    LOG.info("Command processor stopped");
  }

  public CommandProcessor getCommandProcessor() {
    return commandProcessor;
  }

  public WebDriver getWebDriver() {
    return commandProcessor instanceof WebDriverCommandProcessor
        ? ((WebDriverCommandProcessor) commandProcessor).getWrappedDriver()
        : null;
  }

  private static class SeleniumCommandResult {
    private final boolean result;

    private final String output;

    private final Exception exception;

    private SeleniumCommandResult(boolean result, String output, Exception e) {
      this.result = result;
      this.output = output;
      this.exception = e;
    }

    public boolean failed() {
      return !result;
    }

    public boolean succeeded() {
      return result;
    }

    public boolean hasException() {
      return exception != null;
    }

    public Exception getException() {
      return exception;
    }
  }

  private static SeleniumCommandResult success(String output) {
    return new SeleniumCommandResult(true, output, null);
  }

  private static SeleniumCommandResult failure() {
    return new SeleniumCommandResult(false, null, null);
  }

  private SeleniumCommandResult failure(Exception e) {
    return new SeleniumCommandResult(false, null, e);
  }


}
TOP

Related Classes of com.xebia.incubator.xebium.SeleniumDriverFixture$SeleniumCommandResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.