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

com.carrotgarden.maven.scalor.scalajs.EnvJS.scala Maven / Gradle / Ivy

package com.carrotgarden.maven.scalor.scalajs

import java.io.File
import java.net.URLClassLoader
import java.nio.file.Files
import java.nio.file.StandardCopyOption

import org.apache.maven.plugins.annotations.Parameter
import org.webjars.WebJarExtractor

import com.carrotgarden.maven.scalor.base
import com.carrotgarden.maven.scalor.util
import com.carrotgarden.maven.tools.Description
import org.apache.maven.project.MavenProject

/**
 * Common Scala.js VM environment configuration.
 */
trait EnvConfAny extends AnyRef
  with base.ParamsAny {

  @Description( """
  Enable to log provided JS-VM environment configuration.
  Use to review actual settings used to create tesing JS-VM instance:
  - full path to the executable
  - process launch arguments
  - process environment variables
  - webjars scripts settings
  - runtime.js module settings 
  - etc.
""" ) @Parameter( property = "scalor.envconfLogConfig", defaultValue = "false" ) var envconfLogConfig : Boolean = _ @Description( """ System properties injected during environment configuration invocation. Normally includes settings for scala-js-junit-tools. Separator parameter: commonSequenceSeparator. Mapping parameter: commonMappingPattern. """ ) @Parameter( property = "scalor.envconfSystemProperties", defaultValue = """ basedir = ${project.basedir} ★ """ ) var envconfSystemProperties : String = _ @Description( """ Scala.js testing module script used to run tests inside JavaScript VM environment. Normally testing module script path should point to the result of Scala.js linker invocation. """ ) @Parameter( property = "scalor.envconfModulePath", defaultValue = "${project.build.testOutputDirectory}/META-INF/resources/webjars/${project.artifactId}/${project.version}/runtime-test.js" ) var envconfModulePath : File = _ @Description( """ Folder with extracted webjars resources. Specific scripts should be activated in JavaScript VM via parameter: envconfWebjarsScriptList Normally webjars folder should point to the extraction result of the webjars provisioning invocation. Absolute path. """ ) @Parameter( property = "scalor.envconfWebjarsFolder", defaultValue = "${project.basedir}/test-tool/webjars" ) var envconfWebjarsFolder : File = _ @Description( """ List of scripts which should be activated inside JavaScript VM during tests. These scripts must be provided inside the provisioning folder: envconfWebjarsFolder. Separator parameter: commonSequenceSeparator. Relative path. """ ) @Parameter( property = "scalor.envconfWebjarsScriptList", defaultValue = """ """ ) var envconfWebjarsScriptList : String = _ import com.carrotgarden.sjs.junit._ def paramExec : File def paramArgs : String def paramVars : String def paramType : String def configurationFile : File = { Context.configLocation.getCanonicalFile } /** * Report Scala.js JavaScript VM execution environment configuration. */ def configurationReport : String = { import com.carrotgarden.sjs.junit.Config._ val config = Context.configExtract() Config.configUnparse( config ) } /** * Persist Scala.js JavaScript VM execution environment configuration. */ def configureEnvironment() : Unit = { import com.carrotgarden.sjs.junit.Config._ // JS-VM settings. val envExec = paramExec.getCanonicalPath val envArgs = parseCommonList( paramArgs ).toList val envVars = parseCommonMapping( paramVars ) val envType = paramType val envConf = EnvConf( envExec = envExec, envArgs = envArgs, envVars = envVars, envType = envType ) // Webjars resources. val webjarsDir = envconfWebjarsFolder.getCanonicalPath val scriptList = parseCommonList( envconfWebjarsScriptList ).toList val webConf = WebConf( webjarsDir = webjarsDir, scriptList = scriptList ) // Generated runtime.js val path = envconfModulePath.getCanonicalPath val module = Config.Module( path = path ) // Final configuration. val config = Config( envConf = envConf, webConf = webConf, module = module ) // Publish to default location. Context.configPersist( config ) } } /** * Node.js Scala.js VM environment configuration. */ trait EnvConfNodejs extends EnvConfAny { @Description( """ Absolute path to the Node.js JavaScript VM executable. Normally should point to the provisioning extraction result. """ ) @Parameter( property = "scalor.envconfNodejsExec", defaultValue = "${project.basedir}/test-tool/node/node" ) var envconfNodejsExec : File = _ @Description( """ Arguments for the Node.js JavaScript VM executable. Separator parameter: commonSequenceSeparator. """ ) @Parameter( property = "scalor.envconfNodejsArgs", defaultValue = """ """ ) var envconfNodejsArgs : String = _ @Description( """ Environment variables for the Node.js JavaScript VM executable. Normally should define NODE_PATH to point to the Node.js modules provisioning result. Separator parameter: commonSequenceSeparator. Mapping parameter: commonMappingPattern. Some Node.js environment variables:
NODE_MODULE_CONTEXTS - set to 0 to disable inter-module isolation via sandboxing
NODE_DEBUG - set to comma-separated list of module names to enable logging
NODE_PATH - set to absolute path to the provisioned node_modules folder
""" ) @Parameter( property = "scalor.envconfNodejsVars", defaultValue = """ NODE_MODULE_CONTEXTS=0 ★ NODE_PATH=${project.basedir}/test-tool/node/node_modules ★ """ ) var envconfNodejsVars : String = _ @Description( """ Select JS-VM type. Available Node.js JavaScript VM environment types:
nodejs-basic - basic Node.js, for non-UI testing, does not expect any node modules
nodejs-jsdom - Node.js with JSdom.js, for broswer-UI testing, must provision 'jsdom' module
""" ) @Parameter( property = "scalor.envconfNodejsType", defaultValue = "nodejs-jsdom" ) var envconfNodejsType : String = _ override def paramExec = envconfNodejsExec override def paramArgs = envconfNodejsArgs override def paramVars = envconfNodejsVars override def paramType = envconfNodejsType } /** * Phantom.js Scala.js VM environment configuration. */ trait EnvConfPhantomjs extends EnvConfAny { @Description( """ Absolute path to the Phantom.js JavaScript VM executable. Normally should point to the provisioning extraction result. """ ) @Parameter( property = "scalor.envconfPhantomjsExec", defaultValue = "${project.basedir}/test-tool/phantomjs/phantomjs" ) var envconfPhantomjsExec : File = _ @Description( """ Arguments for the Phantom.js JavaScript VM executable. Separator parameter: commonSequenceSeparator. """ ) @Parameter( property = "scalor.envconfPhantomjsArgs", defaultValue = """ """ ) var envconfPhantomjsArgs : String = _ @Description( """ Environment variables for the Phantom.js JavaScript VM executable. Separator parameter: commonSequenceSeparator. """ ) @Parameter( property = "scalor.envconfPhantomjsVars", defaultValue = """ """ ) var envconfPhantomjsVars : String = _ @Description( """ Select JS-VM type. Available Phantom.js JavaScript VM environment types:
phantomjs-basic - basic Phantom.js, for broswer-UI testing
""" ) @Parameter( property = "scalor.envconfPhantomjsType", defaultValue = "phantomjs-basic" ) var envconfPhantomjsType : String = _ override def paramExec = envconfPhantomjsExec override def paramArgs = envconfPhantomjsArgs override def paramVars = envconfPhantomjsVars override def paramType = envconfPhantomjsType } /** * Shared external mojo execution parameters. */ trait EnvProvAny extends AnyRef with base.ParamsAny { @Description( """ Scala.js test-tool installaton folder used for testing. Contains provisioned Node.js, Phantom.js, Webjars, etc. Normally this folder should be excluded from source control and should not be a part of any clean execution. Absolute path. When changing, make sure to replace test-tool references elsewhere. """ ) @Parameter( property = "scalor.envprovTestToolFolder", defaultValue = "${project.basedir}/test-tool" ) var envprovTestToolFolder : File = _ def folderTestTool : String = { val folder = envprovTestToolFolder.getCanonicalFile folder.mkdirs folder.getCanonicalPath } def hasDetectExecutable( file : File ) = { file.exists() && file.isFile() && file.canRead() && file.canExecute() } } /** * Execute external plugin Mojo. * * https://github.com/eirslett/frontend-maven-plugin */ trait EnvProvNodejs extends EnvProvAny { self : base.Params => @Description( """ A folder inside envprovTestToolFolder which contains extracted Node.js. Relative path. """ ) @Parameter( property = "scalor.envprovNodejsFolder", defaultValue = "node" ) var envprovNodejsFolder : String = _ @Description( """ Node.js version used to provision node installation. Select from availabe versions. """ ) @Parameter( property = "scalor.envprovNodejsVersion", defaultValue = "10.0.0" ) var envprovNodejsVersion : String = _ @Description( """ List of Node.js NPM modules configured for provisioning. Uses format: module@version. Separator parameter: commonSequenceSeparator. Used by npm install. """ ) @Parameter( property = "scalor.envprovRnpmjsModules", defaultValue = """ [email protected][email protected][email protected] ★ """ ) var envprovRnpmjsModules : String = _ @Description( """ List of additional NPM invocation options. Separator parameter: commonSequenceSeparator. Used by npm install. """ ) @Parameter( property = "scalor.envprovRnpmjsOptions", defaultValue = """ --save ★ --silent ★ """ ) var envprovRnpmjsOptions : String = _ @Description( """ URL of the Node.js distribution download. """ ) @Parameter( property = "scalor.envprovNodejsURL", defaultValue = "http://nodejs.org/dist" ) var envprovNodejsURL : String = _ @Description( """ URL of the Node.js NPM module registry. Used by npm install. """ ) @Parameter( property = "scalor.envprovRnpmjsURL", defaultValue = "http://registry.npmjs.org" ) var envprovRnpmjsURL : String = _ def npmModuleList = parseCommonList( envprovRnpmjsModules ) def npmInstallOptions = parseCommonList( envprovRnpmjsOptions ) def folderTestToolNodejs : String = { val folder = new File( envprovTestToolFolder, envprovNodejsFolder ) folder.mkdirs() folder.getCanonicalPath } def folderTestToolModules : String = { val folder = new File( folderTestToolNodejs, "node_modules" ) folder.mkdirs() folder.getCanonicalPath } def folderWorkDir : String = { val folder = project.getBasedir folder.mkdirs() folder.getCanonicalPath } def provideNodeVersion = { if ( envprovNodejsVersion.startsWith( "v" ) ) { envprovNodejsVersion } else { s"v${envprovNodejsVersion}" } } val nodeModuleRegex = """([^@]+)(@[.\d]+)?""".r def parseModuleName( entry : String ) = { entry match { case nodeModuleRegex( name, _ ) => name } } /** * Expected location of executable. */ def extractedNodejs : File = { def hasFile( file : File ) = file.isFile() && file.exists() val folder = folderTestToolNodejs val fileNix = new File( folder, "node" ) val fileWin = new File( folder, "node.exe" ) val file = if ( hasFile( fileNix ) ) { fileNix } else if ( hasFile( fileWin ) ) { fileWin } else { util.Error.Throw( s"Missing extracted Node.js executable in ${folder}" ) } file.getCanonicalFile } def configuredNodejs : File = { val folder = folderTestToolNodejs val exec = "node" // Node.js convention. val file = new File( folder, exec ) file.getCanonicalFile } def provisionedModuleList : List[ File ] = { val folder = folderTestToolModules npmModuleList .map( entry => parseModuleName( entry ) ) .map( name => new File( folder, name ) ) .toList } /** * Verify if configured Node.js binary is present. */ def hasDetectNodejs = { hasDetectExecutable( configuredNodejs ) } /** * Verify if configured NPM modules are present. */ def hasDetectModules = { provisionedModuleList.forall( file => file.exists ) } /** * Install Node.js binary. */ def provisionNodejs() : Unit = { import org.twdata.maven.mojoexecutor.MojoExecutor._ val nodeVersion = provideNodeVersion // executeMojo( // plugin( // groupId( "com.github.eirslett" ), // artifactId( "frontend-maven-plugin" ), // version( "1.6" ) // ), // goal( "install-node-and-npm" ), // configuration( // element( "nodeVersion", nodeVersion ), // element( "nodeDownloadRoot", envprovNodejsURL ), // element( "workingDirectory", folderWorkDir ), // element( "installDirectory", folderTestTool ) // ), // executionEnvironment( project, session, buildManager ) // ) // val source = extractedNodejs val target = configuredNodejs Files.copy( source.toPath, target.toPath, StandardCopyOption.REPLACE_EXISTING ) target.setReadable( true ) target.setExecutable( true ) } /** * Invoke "npm install" to provision node_modules. */ def provisionModules() : Unit = { import org.twdata.maven.mojoexecutor.MojoExecutor._ val modules = npmModuleList.mkString( " " ) val options = npmInstallOptions.mkString( " " ) val prefix = s"--prefix ${folderTestToolNodejs}" val arguments = s"install ${modules} ${prefix} ${options}" // executeMojo( // plugin( // groupId( "com.github.eirslett" ), // artifactId( "frontend-maven-plugin" ), // version( "1.6" ) // ), // goal( "npm" ), // configuration( // element( "arguments", arguments ), // element( "npmRegistryURL", envprovRnpmjsURL ), // element( "workingDirectory", folderWorkDir ), // element( "installDirectory", folderTestTool ) // ), // executionEnvironment( project, session, buildManager ) // ) } } /** * Execute external plugin Mojo. * * https://github.com/klieber/phantomjs-maven-plugin */ trait EnvProvPhantomjs extends EnvProvAny { self : base.Params => @Description( """ Phantom.js distribution version. Must be present on Maven Central. """ ) @Parameter( property = "scalor.envprovPhantomjsVersion", defaultValue = "2.1.1" ) var envprovPhantomjsVersion : String = _ @Description( """ A folder inside envprovTestToolFolder which contains extracted Phantom.js. Relative path. """ ) @Parameter( property = "scalor.envprovPhantomjsFolder", defaultValue = "phantomjs" ) var envprovPhantomjsFolder : String = _ def folderTestToolPhantomjs : String = { val folder = new File( envprovTestToolFolder, envprovPhantomjsFolder ) folder.mkdirs() folder.getCanonicalPath } def configuredPhantomjs : File = { val folder = folderTestToolPhantomjs val exec = "phantomjs" // Phantom.js convention. val file = new File( folder, exec ) file.getCanonicalFile } /** * Verify if configured Phantom.js binary is present. */ def hasDetectPhantomjs = { hasDetectExecutable( configuredPhantomjs ) } /** * Phantom.js executable extraction path. */ val phantomExtractKey = "phantomjs.binary" /** * Expected location of executable. */ def extractedPhantomjs = { new File( project.getProperties.getProperty( phantomExtractKey ) ) } /** * Install Phantom.js binary. */ def provisionPhantomjs() : Unit = { import org.twdata.maven.mojoexecutor.MojoExecutor._ // executeMojo( // plugin( // groupId( "com.github.klieber" ), // artifactId( "phantomjs-maven-plugin" ), // version( "0.7" ) // ), // goal( "install" ), // configuration( // element( "version", envprovPhantomjsVersion ), // element( "propertyName", phantomExtractKey ), // element( "outputDirectory", folderTestToolPhantomjs ) // ), // executionEnvironment( project, session, buildManager ) // ) // val source = extractedPhantomjs val target = configuredPhantomjs Files.copy( source.toPath, target.toPath, StandardCopyOption.REPLACE_EXISTING ) target.setReadable( true ) target.setExecutable( true ) } } /** * Provision discovered webjars resources. */ trait EnvProvWebjars extends EnvProvAny { self : base.Params => @Description( """ A folder inside envprovTestToolFolder which contains extracted Webjars. Relative path. """ ) @Parameter( property = "scalor.envprovWebjarsFolder", defaultValue = "webjars" ) var envprovWebjarsFolder : String = _ /** * Webjars extraction folder. */ def configuredWebjars : File = { val folder = new File( folderTestTool, envprovWebjarsFolder ) folder.mkdirs() folder.getCanonicalFile } /** * Webjars discovery class path. */ def webjarsClassLoader : URLClassLoader = { val entryList = projectClassPath().map( _.toURI.toURL ) new URLClassLoader( entryList ) } /** * Provision discovered webjars resources. */ def provisionWebjarsResources() : Unit = { val cache = new WebJarExtractor.MemoryCache() val loader = webjarsClassLoader val extractor = new WebJarExtractor( cache, loader ) extractor.extractAllWebJarsTo( configuredWebjars ) } }