Skip to content

Commit

Permalink
Fix scala-js#115: Add ESModule support
Browse files Browse the repository at this point in the history
  • Loading branch information
mushtaq committed Sep 27, 2020
1 parent 4ac4518 commit 0ce99d1
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 92 deletions.
4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ lazy val seleniumJSEnv: Project = project.
*/
"org.seleniumhq.selenium" % "selenium-server" % "3.141.59",
"org.scala-js" %% "scalajs-js-envs" % "1.1.1",
"com.google.jimfs" % "jimfs" % "1.1",
"org.scala-js" %% "scalajs-js-envs-test-kit" % "1.1.1" % "test",
"com.novocode" % "junit-interface" % "0.11" % "test"
),
Expand Down Expand Up @@ -135,5 +136,6 @@ lazy val seleniumJSHttpEnvTest: Project = project.
SeleniumJSEnv.Config()
.withMaterializeInServer("tmp", "http://localhost:8080/tmp/")
)
}
},
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) }
)
158 changes: 83 additions & 75 deletions seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/JSSetup.scala
Original file line number Diff line number Diff line change
@@ -1,80 +1,88 @@
package org.scalajs.jsenv.selenium

import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}

import com.google.common.jimfs.Jimfs

private[selenium] object JSSetup {
def setupCode(enableCom: Boolean): String = {
s"""
|(function() {
| // Buffers for console.log / console.error
| var consoleLog = [];
| var consoleError = [];
|
| // Buffer for errors.
| var errors = [];
|
| // Buffer for outgoing messages.
| var outMessages = [];
|
| // Buffer for incoming messages (used if onMessage not initalized).
| var inMessages = [];
|
| // Callback for incoming messages.
| var onMessage = null;
|
| function captureConsole(fun, buf) {
| if (!fun) return fun;
| return function() {
| var strs = []
| for (var i = 0; i < arguments.length; ++i)
| strs.push(String(arguments[i]));
|
| buf.push(strs.join(" "));
| return fun.apply(this, arguments);
| }
| }
|
| console.log = captureConsole(console.log, consoleLog);
| console.error = captureConsole(console.error, consoleError);
|
| window.addEventListener('error', function(e) {
| errors.push(e.message)
| });
|
| if ($enableCom) {
| this.scalajsCom = {
| init: function(onMsg) {
| onMessage = onMsg;
| window.setTimeout(function() {
| for (var m in inMessages)
| onMessage(inMessages[m]);
| inMessages = null;
| });
| },
| send: function(msg) { outMessages.push(msg); }
| }
| }
|
| this.scalajsSeleniumInternalInterface = {
| fetch: function() {
| var res = {
| consoleLog: consoleLog.slice(),
| consoleError: consoleError.slice(),
| errors: errors.slice(),
| msgs: outMessages.slice()
| }
|
| consoleLog.length = 0;
| consoleError.length = 0;
| errors.length = 0;
| outMessages.length = 0;
|
| return res;
| },
| send: function(msg) {
| if (inMessages !== null) inMessages.push(msg);
| else onMessage(msg);
| }
| };
|}).call(this)
""".stripMargin
def setupFile(enableCom: Boolean): Path = {
Files.write(
Jimfs.newFileSystem().getPath("setup.js"),
s"""
|(function() {
| // Buffers for console.log / console.error
| var consoleLog = [];
| var consoleError = [];
|
| // Buffer for errors.
| var errors = [];
|
| // Buffer for outgoing messages.
| var outMessages = [];
|
| // Buffer for incoming messages (used if onMessage not initalized).
| var inMessages = [];
|
| // Callback for incoming messages.
| var onMessage = null;
|
| function captureConsole(fun, buf) {
| if (!fun) return fun;
| return function() {
| var strs = []
| for (var i = 0; i < arguments.length; ++i)
| strs.push(String(arguments[i]));
|
| buf.push(strs.join(" "));
| return fun.apply(this, arguments);
| }
| }
|
| console.log = captureConsole(console.log, consoleLog);
| console.error = captureConsole(console.error, consoleError);
|
| window.addEventListener('error', function(e) {
| errors.push(e.message)
| });
|
| if ($enableCom) {
| this.scalajsCom = {
| init: function(onMsg) {
| onMessage = onMsg;
| window.setTimeout(function() {
| for (var m in inMessages)
| onMessage(inMessages[m]);
| inMessages = null;
| });
| },
| send: function(msg) { outMessages.push(msg); }
| }
| }
|
| this.scalajsSeleniumInternalInterface = {
| fetch: function() {
| var res = {
| consoleLog: consoleLog.slice(),
| consoleError: consoleError.slice(),
| errors: errors.slice(),
| msgs: outMessages.slice()
| }
|
| consoleLog.length = 0;
| consoleError.length = 0;
| errors.length = 0;
| outMessages.length = 0;
|
| return res;
| },
| send: function(msg) {
| if (inMessages !== null) inMessages.push(msg);
| else onMessage(msg);
| }
| };
|}).call(this)
""".stripMargin.getBytes(StandardCharsets.UTF_8)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import scala.util.control.NonFatal

import java.util.concurrent.{ConcurrentLinkedQueue, Executors}
import java.util.function.Consumer
import java.nio.file.Path
import java.net.URL

private sealed class SeleniumRun(
driver: WebDriver with JavascriptExecutor,
Expand Down Expand Up @@ -129,20 +131,10 @@ private[selenium] object SeleniumRun {
newRun: Ctor[T], failed: Throwable => T): T = {
validator.validate(runConfig)

val scripts = input.map {
case Input.Script(s) => s
case _ => throw new UnsupportedInputException(input)
}

try {
withCleanup(FileMaterializer(config.materialization))(_.close()) { m =>
val allScriptURLs = (
m.materialize("setup.js", JSSetup.setupCode(enableCom)) +:
scripts.map(m.materialize)
)

val page = m.materialize("scalajsRun.html", htmlPage(allScriptURLs))

val setupJsPath = JSSetup.setupFile(enableCom)
val page = m.materialize("scalajsRun.html", htmlPage(setupJsPath, input, m))
withCleanup(newDriver())(maybeCleanupDriver(_, config)) { driver =>
driver.navigate().to(page)

Expand Down Expand Up @@ -171,18 +163,31 @@ private[selenium] object SeleniumRun {
private def maybeCleanupDriver(d: WebDriver, config: SeleniumJSEnv.Config) =
if (!config.keepAlive) d.close()

private def htmlPage(scripts: Seq[java.net.URL]): String = {
val scriptTags =
scripts.map(path => s"<script src='${path.toString}'></script>")
private def htmlPage(setupJsPath: Path, input: Seq[Input], materializer: FileMaterializer): String = {
val setupJs = makeTag(setupJsPath, "text/javascript", materializer)

val tags = input.map {
case Input.Script(path) => makeTag(path, "text/javascript", materializer)
case Input.ESModule(path) => makeTag(path, "module", materializer)
case _ => throw new UnsupportedInputException(input)
}

val allTags = setupJs +: tags

s"""<html>
| <meta charset="UTF-8">
| <body>
| ${scriptTags.mkString("\n ")}
| ${allTags.mkString("\n ")}
| </body>
|</html>
""".stripMargin
}

private def makeTag(path: Path, tpe: String, materializer: FileMaterializer): String = {
val url = materializer.materialize(path)
s"<script defer type='$tpe' src='${url.toString}'></script>"
}

private class WindowOnErrorException(errs: List[String]) extends Exception(s"JS error: $errs")

private def consumer[A](f: A => Unit): Consumer[A] = new Consumer[A] {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.scalajs.jsenv.selenium

import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport

object CamelCase {
def hello(input: String): String = s"Hello ${camelCase(input)}!"

@JSImport("https://cdn.skypack.dev/camelcase@^6.0.0", JSImport.Default)
@js.native
def camelCase(input: String): String = js.native
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.scalajs.jsenv.selenium

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

class ModuleSupportTest {
@Test def testBasicImport(): Unit = {
assertEquals("Hello scalaJsSelenium!", CamelCase.hello("scala js selenium"))
}
}

0 comments on commit 0ce99d1

Please sign in to comment.