/*- * #%L * jasmine-maven-plugin * %% * Copyright (C) 2010 - 2017 Justin Searls * %% * 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. * #L% */ package com.github.searls.jasmine.mojo; import com.github.klieber.phantomjs.locate.PhantomJsLocatorOptions; import com.github.klieber.phantomjs.locate.RepositoryDetails; import com.github.searls.jasmine.NullLog; import com.github.searls.jasmine.driver.WebDriverFactory; import com.github.searls.jasmine.format.JasmineResultLogger; import com.github.searls.jasmine.io.RelativizesFilePaths; import com.github.searls.jasmine.model.JasmineResult; import com.github.searls.jasmine.runner.CreatesRunner; import com.github.searls.jasmine.runner.ReporterType; import com.github.searls.jasmine.runner.SpecRunnerExecutor; import com.github.searls.jasmine.server.ResourceHandlerConfigurator; import com.github.searls.jasmine.server.ServerManager; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.jetty.server.Server; import org.openqa.selenium.WebDriver; import javax.inject.Inject; import java.io.File; import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Properties; /** * Execute specs using Selenium Web Driver. Uses PhantomJsDriver for head-less execution by default. */ @Mojo(name = "test", defaultPhase = LifecyclePhase.TEST, requiresDependencyResolution = ResolutionScope.TEST) public class TestMojo extends AbstractJasmineMojo { /** * Determines the Selenium WebDriver class we'll use to execute the tests. See the Selenium documentation for more details. * The plugin uses <a href="https://github.com/detro/ghostdriver">PhantomJSDriver</a> by default. * <br> * <p>Some valid examples:</p> * <ul> * <li>org.openqa.selenium.htmlunit.HtmlUnitDriver</li> * <li>org.openqa.selenium.phantomjs.PhantomJSDriver</li> * <li>org.openqa.selenium.firefox.FirefoxDriver</li> * <li>org.openqa.selenium.ie.InternetExplorerDriver</li> * </ul> * <br> * See the webDriverCapabilities property for configuring driver specific properties. * * @since 1.1.0 */ @Parameter(defaultValue = "org.openqa.selenium.phantomjs.PhantomJSDriver") protected String webDriverClassName; /** * <p>Web driver capabilities used to initialize a DesiredCapabilities instance when creating a web driver.</p> * <br> * <p>Capabilities value can be either a String, a List, or a Map.</p> * <br> * <p>Example:</p> * <pre> * <webDriverCapabilities> * <capability> * <name>phantomjs.binary.path</name> * <value>/opt/phantomjs/bin/phantomjs</value> * </capability> * <capability> * <name>phantomjs.cli.args</name> * <list> * <value>--disk-cache=true</value> * <value>--max-disk-cache-size=256</value> * </list> * </capability> * <capability> * <name>proxy</name> * <map> * <httpProxy>myproxyserver.com:8000</httpProxy> * </map> * </capability> * </webDriverCapabilities> * </pre> * * @since 1.3.1.1 */ @Parameter protected List<Capability> webDriverCapabilities = Collections.emptyList(); /** * <p>Determines the browser and version profile that HtmlUnit will simulate. This setting does nothing if the plugin is configured not to use HtmlUnit. * This maps 1-to-1 with the public static instances found in {@link com.gargoylesoftware.htmlunit.BrowserVersion}.</p> * <br> * <p>Some valid examples: CHROME, FIREFOX_17, INTERNET_EXPLORER_9, INTERNET_EXPLORER_10</p> * * @since 1.1.0 * @deprecated Use the webDriverCapabilities parameter instead. */ @Parameter(defaultValue = "FIREFOX_17") @Deprecated protected String browserVersion; /** * <p>Determines the format that jasmine:test will print to console.</p> * <p>Valid options:</p> * <ul> * <li>"documentation" - (default) - print specs in a nested format</li> * <li>"progress" - more terse, with a period for a passed specs and an 'F' for failures (e.g. '...F...')</li> * </ul> * * @since 1.1.0 */ @Parameter(defaultValue = "documentation") protected String format; /** * <p>Configure which version of PhantomJS should be used and how it should be found. The core of the * <a href="http://klieber.github.io/phantomjs-maven-plugin">phantomjs-maven-plugin</a> is used to provide this * functionality and this parameter should match the configuration of the * <a href="http://kylelieber.com/phantomjs-maven-plugin/install-mojo.html">phantomjs-maven-plugin install</a> goal.</p> * <br> * <p>Default Options:</p> * <pre> * <phantomjs> * <version>2.0.0</version> * <checkSystemPath>true</checkSystemPath> * <enforceVersion>true</enforceVersion> * <source>REPOSITORY</source> * <baseUrl></baseUrl> * <outputDirectory>target/phantomjs</outputDirectory> * </phantomjs> * </pre> * * @since 2.0 */ @Parameter(property = "phantomjs", defaultValue = "${phantomJs}") protected PhantomJsOptions phantomjs; /** * Keep the server alive after the <code>jasmine:test</code> goal exists. * Useful if you need to run further analysis on your tests, like collecting code coverage. * * @since 1.3.1.0 */ @Parameter(property = "keepServerAlive", defaultValue = "false") protected boolean keepServerAlive; @Parameter( defaultValue = "${repositorySystemSession}", readonly = true ) private RepositorySystemSession repositorySystemSession; @Parameter( defaultValue = "${project.remoteProjectRepositories}", readonly = true ) private List<RemoteRepository> remoteRepositories; @Parameter( defaultValue = "${session}", readonly = true ) private MavenSession mavenSession; private RepositorySystem repositorySystem; private final RelativizesFilePaths relativizesFilePaths; @Inject public TestMojo(RepositorySystem repositorySystem) { this.repositorySystem = repositorySystem; this.relativizesFilePaths = new RelativizesFilePaths(); } @Override public void execute() throws MojoExecutionException, MojoFailureException { if (!this.isSkipTests()) { super.execute(); } else { this.getLog().info("Skipping Jasmine Specs"); } } @Override public void run() throws Exception { ServerManager serverManager = this.getServerManager(); try { int port = serverManager.start(); setPortProperty(port); this.getLog().info("Executing Jasmine Specs"); JasmineResult result = this.executeSpecs(new URL(this.uriScheme + "://" + this.serverHostname + ":" + port)); this.logResults(result); this.throwAnySpecFailures(result); } finally { if (!keepServerAlive) { serverManager.stop(); } } } private ServerManager getServerManager() throws MojoExecutionException { Log log = this.debug ? this.getLog() : new NullLog(); CreatesRunner createsRunner = new CreatesRunner( this, log, this.specRunnerHtmlFileName, ReporterType.JsApiReporter); ResourceHandlerConfigurator configurator = new ResourceHandlerConfigurator( this, this.relativizesFilePaths, createsRunner); return new ServerManager(new Server(), getConnector(), configurator); } private void setPortProperty(int port) { this.mavenProject.getProperties().setProperty("jasmine.serverPort", String.valueOf(port)); } private JasmineResult executeSpecs(URL runner) throws Exception { WebDriver driver = this.createDriver(); JasmineResult result = new SpecRunnerExecutor().execute( runner, driver, this.timeout, this.debug, this.getLog(), this.format, getReporters(), getFileSystemReporters() ); return result; } private WebDriver createDriver() throws Exception { RepositoryDetails details = new RepositoryDetails(); details.setRemoteRepositories(remoteRepositories); details.setRepositorySystem(repositorySystem); details.setRepositorySystemSession(repositorySystemSession); configure(mavenSession.getUserProperties()); WebDriverFactory factory = new WebDriverFactory(); factory.setWebDriverCapabilities(webDriverCapabilities); factory.setWebDriverClassName(webDriverClassName); factory.setDebug(debug); factory.setBrowserVersion(browserVersion); factory.setPhantomJsLocatorOptions(phantomjs); factory.setRepositoryDetails(details); return factory.createWebDriver(); } private void configure(Properties properties) { phantomjs.setVersion( properties.getProperty("phantomjs.version", phantomjs.getVersion()) ); phantomjs.setSource( PhantomJsLocatorOptions.Source.valueOf( properties.getProperty("phantomjs.source", phantomjs.getSource().toString()) ) ); phantomjs.setOutputDirectory( new File(properties.getProperty("phantomjs.outputDirectory", phantomjs.getOutputDirectory().toString())) ); phantomjs.setBaseUrl( properties.getProperty("phantomjs.baseUrl", phantomjs.getBaseUrl()) ); phantomjs.setCheckSystemPath( configureBoolean(properties, "phantomjs.checkSystemPath", phantomjs.isCheckSystemPath()) ); phantomjs.setEnforceVersion( properties.getProperty("phantomjs.enforceVersion", phantomjs.getEnforceVersion()) ); } private boolean configureBoolean(Properties properties, String property, boolean defaultValue) { return Boolean.parseBoolean(properties.getProperty(property, Boolean.toString(defaultValue))); } private void logResults(JasmineResult result) { JasmineResultLogger resultLogger = new JasmineResultLogger(); resultLogger.setLog(this.getLog()); resultLogger.log(result); } private void throwAnySpecFailures(JasmineResult result) throws MojoFailureException { if (this.haltOnFailure && !result.didPass()) { throw new MojoFailureException("There were Jasmine spec failures."); } } }