All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.server.htmlrunner.HTMLLauncher Maven / Gradle / Ivy

There is a newer version: 4.5.0
Show newest version
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you 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.openqa.selenium.server.htmlrunner;

import static java.util.concurrent.TimeUnit.SECONDS;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.net.PortProber;
import org.openqa.selenium.opera.OperaDriver;
import org.openqa.selenium.safari.SafariDriver;
import org.seleniumhq.jetty9.server.Connector;
import org.seleniumhq.jetty9.server.HttpConfiguration;
import org.seleniumhq.jetty9.server.HttpConnectionFactory;
import org.seleniumhq.jetty9.server.Server;
import org.seleniumhq.jetty9.server.ServerConnector;
import org.seleniumhq.jetty9.server.handler.ContextHandler;
import org.seleniumhq.jetty9.server.handler.ResourceHandler;
import org.seleniumhq.jetty9.util.resource.PathResource;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Runs HTML Selenium test suites.
 */
public class HTMLLauncher {

  //    java -jar selenium-server-standalone-.jar -htmlSuite "*firefox"
  //    "http://www.google.com" "c:\absolute\path\to\my\HTMLSuite.html"
  //    "c:\absolute\path\to\my\results.html"
  private static Logger log = Logger.getLogger(HTMLLauncher.class.getName());

  private Server server;

  /**
   * Launches a single HTML Selenium test suite.
   *
   * @param browser - the browserString ("*firefox", "*iexplore" or an executable path)
   * @param startURL - the start URL for the browser
   * @param suiteURL - the relative URL to the HTML suite
   * @param outputFile - The file to which we'll output the HTML results
   * @param timeoutInSeconds - the amount of time (in seconds) to wait for the browser to finish
   * @return PASS or FAIL
   * @throws IOException if we can't write the output file
   */
  public String runHTMLSuite(
    String browser,
    String startURL,
    String suiteURL,
    File outputFile,
    long timeoutInSeconds,
    String userExtensions) throws IOException {
    File parent = outputFile.getParentFile();
    if (parent != null && !parent.exists()) {
      parent.mkdirs();
    }
    if (outputFile.exists() && !outputFile.canWrite()) {
      throw new IOException("Can't write to outputFile: " + outputFile.getAbsolutePath());
    }
    long timeoutInMs = 1000L * timeoutInSeconds;
    if (timeoutInMs < 0) {
      log.warning("Looks like the timeout overflowed, so resetting it to the maximum.");
      timeoutInMs = Long.MAX_VALUE;
    }

    WebDriver driver = null;
    try {
      driver = createDriver(browser);
      URL suiteUrl = determineSuiteUrl(startURL, suiteURL);

      driver.get(suiteUrl.toString());
      Selenium selenium = new WebDriverBackedSelenium(driver, startURL);
      selenium.setTimeout(String.valueOf(timeoutInMs));
      if (userExtensions != null) {
        selenium.setExtensionJs(userExtensions);
      }
      List allTables = driver.findElements(By.id("suiteTable"));
      if (allTables.isEmpty()) {
        throw new RuntimeException("Unable to find suite table: " + driver.getPageSource());
      }
      Results results = new CoreTestSuite(suiteUrl.toString()).run(driver, selenium, new URL(startURL));

      HTMLTestResults htmlResults = results.toSuiteResult();
      try (Writer writer = Files.newBufferedWriter(outputFile.toPath())) {
        htmlResults.write(writer);
      }

      return results.isSuccessful() ? "PASSED" : "FAILED";
    } finally {
      if (server != null) {
        try {
          server.stop();
        } catch (Exception e) {
          // Nothing sane to do. Log the error and carry on
          log.log(Level.INFO, "Exception shutting down server. You may ignore this.", e);
        }
      }

      if (driver != null) {
        driver.quit();
      }
    }
  }

  private URL determineSuiteUrl(String startURL, String suiteURL) throws IOException {
    if (suiteURL.startsWith("https://") || suiteURL.startsWith("http://")) {
      return verifySuiteUrl(new URL(suiteURL));
    }

    // Is the suiteURL a file?
    Path path = Paths.get(suiteURL).toAbsolutePath();
    if (Files.exists(path)) {
      // Not all drivers can read files from the disk, so we need to host the suite somewhere.
      server = new Server();
      HttpConfiguration httpConfig = new HttpConfiguration();

      ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
      int port = PortProber.findFreePort();
      http.setPort(port);
      http.setIdleTimeout(500000);
      server.setConnectors(new Connector[]{http});

      ResourceHandler handler = new ResourceHandler();
      handler.setDirectoriesListed(true);
      handler.setWelcomeFiles(new String[]{path.getFileName().toString(), "index.html"});
      handler
          .setBaseResource(new PathResource(path.toFile().getParentFile().toPath().toRealPath()));

      ContextHandler context = new ContextHandler("/tests");
      context.setHandler(handler);

      server.setHandler(context);
      try {
        server.start();
      } catch (Exception e) {
        throw new IOException(e);
      }

      PortProber.waitForPortUp(port, 15, SECONDS);

      URL serverUrl = server.getURI().toURL();
      return new URL(serverUrl.getProtocol(), serverUrl.getHost(), serverUrl.getPort(),
                     "/tests/");
    }

    // Well then, it must be a URL relative to whatever the browserUrl. Probe and find out.
    URL browser = new URL(startURL);
    return verifySuiteUrl(new URL(browser, suiteURL));
  }

  private URL verifySuiteUrl(URL url) throws IOException {
    // Now probe.
    URLConnection connection = url.openConnection();
    if (!(connection instanceof HttpURLConnection)) {
      throw new IOException("The HTMLLauncher only supports relative HTTP URLs");
    }
    HttpURLConnection httpConnection = (HttpURLConnection) connection;
    httpConnection.setInstanceFollowRedirects(true);
    httpConnection.setRequestMethod("HEAD");
    int responseCode = httpConnection.getResponseCode();
    if (responseCode != HttpURLConnection.HTTP_OK) {
      throw new IOException("Invalid suite URL: " + url);
    }
    return url;
 }

  public static int mainInt(String... args) throws Exception {
    Args processed = new Args();
    JCommander jCommander = new JCommander(processed);
    jCommander.setCaseSensitiveOptions(false);
    try {
      jCommander.parse(args);
    } catch (ParameterException ex) {
      StringBuilder help = new StringBuilder();
      jCommander.usage(help);
      System.err.print(ex.getMessage() + "\n" + help);
      return 0;
    }

    if (processed.help) {
      StringBuilder help = new StringBuilder();
      jCommander.usage(help);
      System.err.print(help);
      return 0;
    }

    if (!validateArgs(processed)) {
      return -1;
    }

    Path resultsPath = Paths.get(processed.htmlSuite.get(3));
    Files.createDirectories(resultsPath);

    String suite = processed.htmlSuite.get(2);
    String startURL = processed.htmlSuite.get(1);
    String[] browsers = new String[] {processed.htmlSuite.get(0)};

    HTMLLauncher launcher = new HTMLLauncher();

    boolean passed = true;
    for (String browser : browsers) {
      // Turns out that Windows doesn't like "*" in a path name
      String reportFileName = browser.contains(" ") ? browser.substring(0, browser.indexOf(' ')) : browser;
      File results = resultsPath.resolve(reportFileName.substring(1) + ".results.html").toFile();
      String result = "FAILED";

      try {
        long timeout;
        try {
          timeout = Long.parseLong(processed.timeout);
        } catch (NumberFormatException e) {
          System.err.println("Timeout does not appear to be a number: " + processed.timeout);
          return -2;
        }
        result = launcher.runHTMLSuite(browser, startURL, suite, results, timeout, processed.userExtensions);
        passed &= "PASSED".equals(result);
      } catch (Throwable e) {
        log.log(Level.WARNING, "Test of browser failed: " + browser, e);
        passed = false;
      }
    }

    return passed ? 1 : 0;
  }

  private static boolean validateArgs(Args processed) {
    if (processed.multiWindow) {
      System.err.println("Multi-window mode is longer used as an option and will be ignored.");
    }

    if (processed.port != 0) {
      System.err.println("Port is longer used as an option and will be ignored.");
    }

    if (processed.trustAllSSLCertificates) {
      System.err.println("Trusting all ssl certificates is no longer a user-settable option.");
    }

    return true;
  }

  public static void main(String[] args) throws Exception {
    System.exit(mainInt(args));
  }

  private WebDriver createDriver(String browser) {
    String[] parts = browser.split(" ", 2);
    browser = parts[0];
    switch (browser) {
      case "*chrome":
      case "*firefox":
      case "*firefoxproxy":
      case "*firefoxchrome":
      case "*pifirefox":
        FirefoxOptions options = new FirefoxOptions().setLegacy(false);
        if (parts.length > 1) {
          options.setBinary(parts[1]);
        }
        return new FirefoxDriver(options);

      case "*iehta":
      case "*iexplore":
      case "*iexploreproxy":
      case "*piiexplore":
        return new InternetExplorerDriver();

      case "*googlechrome":
        return new ChromeDriver();

      case "*MicrosoftEdge":
        return new EdgeDriver();

      case "*opera":
      case "*operablink":
        return new OperaDriver();

      case "*safari":
      case "*safariproxy":
        return new SafariDriver();

      default:
        throw new RuntimeException("Unrecognized browser: " + browser);
    }
  }

  public static class Args {
    @Parameter(
      names = "-htmlSuite",
      required = true,
      arity = 4,
      description = "Run an HTML Suite: \"*browser\" \"http://baseUrl.com\" \"path\\to\\HTMLSuite.html\" \"path\\to\\report\\dir\"")
    private List htmlSuite;

    @Parameter(
      names = "-timeout",
      description = "Timeout to use in seconds")
    private String timeout = "30";

    @Parameter(
      names = "-userExtensions",
      description = "User extensions to attempt to use."
    )
    private String userExtensions;

    @Parameter(
      names = "-multiwindow",
      hidden = true)
    private boolean multiWindow = true;

    @Parameter(
      names = "-port",
      hidden = true)
    private Integer port = 0;

    @Parameter(
      names = "-trustAllSSLCertificates",
      hidden = true)
    private boolean trustAllSSLCertificates;

    @Parameter(
      names = {"-help", "--help", "-h"},
      description = "This help message",
      help = true)
    private boolean help;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy