Skip to content

Import the Node.js with jsdom env from the Scala.js core repository. #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jun 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2c7b6f9
Fix scala-js/scala-js#595: Test SBT plugin in published form on Travis
gzm0 May 7, 2014
1367e17
Fix scala-js/scala-js#1060: Cleanup scala.scalajs.test namespace
gzm0 Sep 19, 2014
8e8aaad
Add DOMJSEnv based on 'jsdom' in Node.js.
nicolasstucki Dec 21, 2015
7ccf30d
Remove Jasmine from sbtPluginTest.
nicolasstucki Apr 26, 2016
fdf02b9
Add history API support to JSDOMNodeJSEnv
julienrf Dec 5, 2016
8427547
Use VirtualJSFiles instead of ResolvedJSDependency in the JSEnv API.
sjrd Mar 28, 2017
b568d64
Do not use jsDependencies (nor jQuery) in sbt-plugin-test/withDOM.
sjrd Apr 5, 2017
4a75741
Unify the parameter names of external JS env constructors.
sjrd Apr 8, 2017
8d4006c
Merge '0.6.x' into 'master'.
sjrd Apr 9, 2017
703ec63
Fix scala-js/scala-js#2874: Remove the distinction libs/code in the J…
sjrd Apr 10, 2017
f582d26
Fix scala-js/scala-js#2902: Adapt to jsdom v10.
sjrd Apr 25, 2017
796d971
Merge '0.6.x' into 'master'.
sjrd Apr 28, 2017
ca40d6c
Recognize any "standard" JVM-style main object in the sbt plugin.
sjrd Jun 7, 2017
f06ed90
Introduce a new surface API for JS envs with `Config` objects.
sjrd Jun 9, 2017
3740ad5
Merge pull request scala-js/scala-js#3001 from sjrd/new-jsenv-api-0.6.x
gzm0 Jun 14, 2017
2768e29
Merge '0.6.x' into 'master'.
sjrd Jun 15, 2017
c354793
Remove the deprecated APIs in JS envs.
sjrd Jun 16, 2017
7501666
Fix scala-js/scala-js#2877: Internal JS envs config amenable to binco…
sjrd Jun 16, 2017
1c8cede
Extract JSDOMNodeJSEnv in its own project.
sjrd Jun 22, 2017
62df6b6
Import the Node.js with jsdom env from the Scala.js core repository.
sjrd Jun 23, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
sudo: false
language: scala
scala:
- 2.10.6
- 2.11.11
- 2.12.2
jdk:
- oraclejdk8
env:
- JSDOM_VERSION=9.12.0
- JSDOM_VERSION=10.0.0
install:
# The default ivy resolution takes way too much time, and times out Travis builds.
# We use coursier instead.
- mkdir -p $HOME/.sbt/0.13/plugins/
- echo 'addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC3")' > $HOME/.sbt/0.13/plugins/coursier.sbt
# We need a recent version of Node.js for jsdom
- nvm install 6
- nvm use 6
- node --version
# Of course we need jsdom
- npm install jsdom@$JSDOM_VERSION
# While there is no published version of Scala.js 1.x, we have to locally build a snapshot.
- git clone https://github.com/scala-js/scala-js.git
- cd scala-js
- git checkout d25fa8bba708977c68c093fdbc50958368f9602f
- sbt ++$TRAVIS_SCALA_VERSION compiler/publishLocal jUnitPlugin/publishLocal library/publishLocal testInterface/publishLocal jUnitRuntime/publishLocal ir/publishLocal tools/publishLocal jsEnvs/publishLocal jsEnvsTestKit/publishLocal nodeJSEnv/publishLocal
- sbt ++2.10.6 ir/publishLocal tools/publishLocal jsEnvs/publishLocal nodeJSEnv/publishLocal testAdapter/publishLocal sbtPlugin/publishLocal
- cd ..
script:
- sbt ++$TRAVIS_SCALA_VERSION scalajs-env-jsdom-nodejs/test scalajs-env-jsdom-nodejs/doc
- sbt ++$TRAVIS_SCALA_VERSION test-project/run test-project/test
cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt
- $HOME/.coursier/cache
before_cache:
- find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
- find $HOME/.sbt -name "*.lock" -print -delete
- rm $HOME/.sbt/0.13/plugins/coursier.sbt
88 changes: 88 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
inThisBuild(Seq(
version := "0.1.0-SNAPSHOT",
organization := "org.scala-js",

crossScalaVersions := Seq("2.10.6", "2.11.11", "2.12.2"),
scalaVersion := "2.11.11",
scalacOptions ++= Seq("-deprecation", "-feature", "-Xfatal-warnings"),

homepage := Some(url("https://www.scala-js.org/")),
licenses += ("BSD New",
url("https://github.com/scala-js/scala-js-env-jsdom-nodejs/blob/master/LICENSE")),
scmInfo := Some(ScmInfo(
url("https://github.com/scala-js/scala-js-env-jsdom-nodejs"),
"scm:git:git@github.com:scala-js/scala-js-env-jsdom-nodejs.git",
Some("scm:git:git@github.com:scala-js/scala-js-env-jsdom-nodejs.git")))
))

val commonSettings = Def.settings(
// Scaladoc linking
apiURL := {
val name = moduleName.value
val v = version.value
Some(url(s"https://www.scala-js.org/api/$name/$v/"))
},
autoAPIMappings := true,

publishMavenStyle := true,
publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
},
pomExtra := (
<developers>
<developer>
<id>sjrd</id>
<name>Sébastien Doeraene</name>
<url>https://github.com/sjrd/</url>
</developer>
<developer>
<id>gzm0</id>
<name>Tobias Schlatter</name>
<url>https://github.com/gzm0/</url>
</developer>
<developer>
<id>nicolasstucki</id>
<name>Nicolas Stucki</name>
<url>https://github.com/nicolasstucki/</url>
</developer>
</developers>
),
pomIncludeRepository := { _ => false }
)

lazy val root: Project = project.in(file(".")).
settings(
publishArtifact in Compile := false,
publish := {},
publishLocal := {},

clean := clean.dependsOn(
clean in `scalajs-env-jsdom-nodejs`,
clean in `test-project`
).value
)

lazy val `scalajs-env-jsdom-nodejs`: Project = project.in(file("jsdom-nodejs-env")).
settings(
commonSettings,

libraryDependencies ++= Seq(
"org.scala-js" %% "scalajs-js-envs" % scalaJSVersion,
"org.scala-js" %% "scalajs-nodejs-env" % scalaJSVersion,

"com.novocode" % "junit-interface" % "0.11" % "test",
"org.scala-js" %% "scalajs-js-envs-test-kit" % scalaJSVersion % "test"
)
)

lazy val `test-project`: Project = project.
enablePlugins(ScalaJSPlugin).
enablePlugins(ScalaJSJUnitPlugin).
settings(
scalaJSUseMainModuleInitializer := true,
jsEnv := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/* __ *\
** ________ ___ / / ___ __ ____ Scala.js JS envs **
** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2017, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
** /____/\___/_/ |_/____/_/ | |__/ /____/ **
** |/____/ **
\* */

package org.scalajs.jsenv.jsdomnodejs

import scala.collection.immutable

import java.io.OutputStream

import org.scalajs.core.tools.io._
import org.scalajs.jsenv._
import org.scalajs.jsenv.nodejs.AbstractNodeJSEnv

import org.scalajs.core.ir.Utils.escapeJS

class JSDOMNodeJSEnv(config: JSDOMNodeJSEnv.Config) extends AbstractNodeJSEnv {

def this() = this(JSDOMNodeJSEnv.Config())

protected def vmName: String = "Node.js with JSDOM"

protected def executable: String = config.executable

override protected def args: immutable.Seq[String] = config.args

override protected def env: Map[String, String] = config.env

// TODO We might want to make this configurable - not sure why it isn't
override protected def wantSourceMap: Boolean = false

override def jsRunner(files: Seq[VirtualJSFile]): JSRunner =
new DOMNodeRunner(files)

override def asyncRunner(files: Seq[VirtualJSFile]): AsyncJSRunner =
new AsyncDOMNodeRunner(files)

override def comRunner(files: Seq[VirtualJSFile]): ComJSRunner =
new ComDOMNodeRunner(files)

protected class DOMNodeRunner(files: Seq[VirtualJSFile])
extends ExtRunner(files) with AbstractDOMNodeRunner

protected class AsyncDOMNodeRunner(files: Seq[VirtualJSFile])
extends AsyncExtRunner(files) with AbstractDOMNodeRunner

protected class ComDOMNodeRunner(files: Seq[VirtualJSFile])
extends AsyncDOMNodeRunner(files) with NodeComJSRunner

protected trait AbstractDOMNodeRunner extends AbstractNodeRunner {

protected def codeWithJSDOMContext(): Seq[VirtualJSFile] = {
val scriptsPaths = getScriptsJSFiles().map {
case file: FileVirtualFile => file.path
case file => libCache.materialize(file).getAbsolutePath
}
val scriptsURIs =
scriptsPaths.map(path => new java.io.File(path).toURI.toASCIIString)
val scriptsURIsAsJSStrings = scriptsURIs.map('"' + escapeJS(_) + '"')
val jsDOMCode = {
s"""
|(function () {
| var jsdom;
| try {
| jsdom = require("jsdom/lib/old-api.js"); // jsdom >= 10.x
| } catch (e) {
| jsdom = require("jsdom"); // jsdom <= 9.x
| }
|
| var virtualConsole = jsdom.createVirtualConsole()
| .sendTo(console, { omitJsdomErrors: true });
| virtualConsole.on("jsdomError", function (error) {
| /* This inelegant if + console.error is the only way I found
| * to make sure the stack trace of the original error is
| * printed out.
| */
| if (error.detail && error.detail.stack)
| console.error(error.detail.stack);
|
| // Throw the error anew to make sure the whole execution fails
| throw error;
| });
|
| jsdom.env({
| html: "",
| url: "http://localhost/",
| virtualConsole: virtualConsole,
| created: function (error, window) {
| if (error == null) {
| window["__ScalaJSEnv"] = __ScalaJSEnv;
| window["scalajsCom"] = global.scalajsCom;
| } else {
| throw error;
| }
| },
| scripts: [${scriptsURIsAsJSStrings.mkString(", ")}]
| });
|})();
|""".stripMargin
}
Seq(new MemVirtualJSFile("codeWithJSDOMContext.js").withContent(jsDOMCode))
}

/** All the JS files that are passed to the VM.
*
* This method can overridden to provide custom behavior in subclasses.
*
* This method is overridden in `JSDOMNodeJSEnv` so that user-provided
* JS files (excluding "init" files) are executed as *scripts* within the
* jsdom environment, rather than being directly executed by the VM.
*
* The value returned by this method in `JSDOMNodeJSEnv` is
* `initFiles() ++ customInitFiles() ++ codeWithJSDOMContext()`.
*/
override protected def getJSFiles(): Seq[VirtualJSFile] =
initFiles() ++ customInitFiles() ++ codeWithJSDOMContext()

/** JS files to be loaded via scripts in the jsdom environment.
*
* This method can be overridden to provide a different list of scripts.
*
* The default value in `JSDOMNodeJSEnv` is `files`.
*/
protected def getScriptsJSFiles(): Seq[VirtualJSFile] =
files

// Send code to Stdin
override protected def sendVMStdin(out: OutputStream): Unit = {
/* Do not factor this method out into AbstractNodeRunner or when mixin in
* the traits it would use AbstractExtRunner.sendVMStdin due to
* linearization order.
*/
sendJS(getJSFiles(), out)
}
}
}

object JSDOMNodeJSEnv {
final class Config private (
val executable: String,
val args: List[String],
val env: Map[String, String]
) {
private def this() = {
this(
executable = "node",
args = Nil,
env = Map.empty
)
}

def withExecutable(executable: String): Config =
copy(executable = executable)

def withArgs(args: List[String]): Config =
copy(args = args)

def withEnv(env: Map[String, String]): Config =
copy(env = env)

private def copy(
executable: String = executable,
args: List[String] = args,
env: Map[String, String] = env
): Config = {
new Config(executable, args, env)
}
}

object Config {
/** Returns a default configuration for a [[JSDOMNodeJSEnv]].
*
* The defaults are:
*
* - `executable`: `"node"`
* - `args`: `Nil`
* - `env`: `Map.empty`
*/
def apply(): Config = new Config()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.scalajs.jsenv.jsdomnodejs

import org.scalajs.jsenv.test._

import org.junit.Test
import org.junit.Assert._

class JSDOMNodeJSEnvTest extends TimeoutComTests {

protected def newJSEnv: JSDOMNodeJSEnv = new JSDOMNodeJSEnv()

@Test
def historyAPI: Unit = {
"""|console.log(window.location.href);
|window.history.pushState({}, "", "/foo");
|console.log(window.location.href);
""".stripMargin hasOutput
"""|http://localhost/
|http://localhost/foo
|""".stripMargin
}

}
1 change: 1 addition & 0 deletions project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=0.13.15
6 changes: 6 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0-SNAPSHOT")

libraryDependencies += "org.scala-js" %% "scalajs-nodejs-env" % "1.0.0-SNAPSHOT"

unmanagedSourceDirectories in Compile +=
baseDirectory.value.getParentFile / "jsdom-nodejs-env/src/main/scala"
19 changes: 19 additions & 0 deletions test-project/src/main/scala/testproject/Lib.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package testproject

import scala.scalajs.js

object Lib {

val document: js.Dynamic = js.Dynamic.global.document

def getElementsByTagName(name: String): js.Array[js.Dynamic] =
document.getElementsByTagName(name).asInstanceOf[js.Array[js.Dynamic]]

/** appends a <p> with the message to the document */
def appendDocument(msg: String): Unit = {
val elem = document.createElement("p")
elem.appendChild(document.createTextNode(msg))
document.body.appendChild(elem)
}

}
12 changes: 12 additions & 0 deletions test-project/src/main/scala/testproject/TestApp.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package testproject

object TestApp {

def main(args: Array[String]): Unit = {
Lib.appendDocument("Hello World")
Lib.appendDocument("Still Here!")

println(Lib.getElementsByTagName("p").head.innerHTML)
}

}
16 changes: 16 additions & 0 deletions test-project/src/test/scala/testproject/LibTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package testproject

import scala.scalajs.js

import org.junit.Test
import org.junit.Assert._

class LibTest {
@Test def dummy_library_should_append_an_element(): Unit = {
def count = Lib.getElementsByTagName("p").length

val oldCount = count
Lib.appendDocument("foo")
assertEquals(1, count - oldCount)
}
}