Skip to content

Commit 0ce99d1

Browse files
committed
Fix #115: Add ESModule support
1 parent 4ac4518 commit 0ce99d1

File tree

5 files changed

+129
-92
lines changed

5 files changed

+129
-92
lines changed

build.sbt

+3-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ lazy val seleniumJSEnv: Project = project.
7979
*/
8080
"org.seleniumhq.selenium" % "selenium-server" % "3.141.59",
8181
"org.scala-js" %% "scalajs-js-envs" % "1.1.1",
82+
"com.google.jimfs" % "jimfs" % "1.1",
8283
"org.scala-js" %% "scalajs-js-envs-test-kit" % "1.1.1" % "test",
8384
"com.novocode" % "junit-interface" % "0.11" % "test"
8485
),
@@ -135,5 +136,6 @@ lazy val seleniumJSHttpEnvTest: Project = project.
135136
SeleniumJSEnv.Config()
136137
.withMaterializeInServer("tmp", "http://localhost:8080/tmp/")
137138
)
138-
}
139+
},
140+
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.ESModule) }
139141
)
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,88 @@
11
package org.scalajs.jsenv.selenium
22

3+
import java.nio.charset.StandardCharsets
4+
import java.nio.file.{Files, Path}
5+
6+
import com.google.common.jimfs.Jimfs
7+
38
private[selenium] object JSSetup {
4-
def setupCode(enableCom: Boolean): String = {
5-
s"""
6-
|(function() {
7-
| // Buffers for console.log / console.error
8-
| var consoleLog = [];
9-
| var consoleError = [];
10-
|
11-
| // Buffer for errors.
12-
| var errors = [];
13-
|
14-
| // Buffer for outgoing messages.
15-
| var outMessages = [];
16-
|
17-
| // Buffer for incoming messages (used if onMessage not initalized).
18-
| var inMessages = [];
19-
|
20-
| // Callback for incoming messages.
21-
| var onMessage = null;
22-
|
23-
| function captureConsole(fun, buf) {
24-
| if (!fun) return fun;
25-
| return function() {
26-
| var strs = []
27-
| for (var i = 0; i < arguments.length; ++i)
28-
| strs.push(String(arguments[i]));
29-
|
30-
| buf.push(strs.join(" "));
31-
| return fun.apply(this, arguments);
32-
| }
33-
| }
34-
|
35-
| console.log = captureConsole(console.log, consoleLog);
36-
| console.error = captureConsole(console.error, consoleError);
37-
|
38-
| window.addEventListener('error', function(e) {
39-
| errors.push(e.message)
40-
| });
41-
|
42-
| if ($enableCom) {
43-
| this.scalajsCom = {
44-
| init: function(onMsg) {
45-
| onMessage = onMsg;
46-
| window.setTimeout(function() {
47-
| for (var m in inMessages)
48-
| onMessage(inMessages[m]);
49-
| inMessages = null;
50-
| });
51-
| },
52-
| send: function(msg) { outMessages.push(msg); }
53-
| }
54-
| }
55-
|
56-
| this.scalajsSeleniumInternalInterface = {
57-
| fetch: function() {
58-
| var res = {
59-
| consoleLog: consoleLog.slice(),
60-
| consoleError: consoleError.slice(),
61-
| errors: errors.slice(),
62-
| msgs: outMessages.slice()
63-
| }
64-
|
65-
| consoleLog.length = 0;
66-
| consoleError.length = 0;
67-
| errors.length = 0;
68-
| outMessages.length = 0;
69-
|
70-
| return res;
71-
| },
72-
| send: function(msg) {
73-
| if (inMessages !== null) inMessages.push(msg);
74-
| else onMessage(msg);
75-
| }
76-
| };
77-
|}).call(this)
78-
""".stripMargin
9+
def setupFile(enableCom: Boolean): Path = {
10+
Files.write(
11+
Jimfs.newFileSystem().getPath("setup.js"),
12+
s"""
13+
|(function() {
14+
| // Buffers for console.log / console.error
15+
| var consoleLog = [];
16+
| var consoleError = [];
17+
|
18+
| // Buffer for errors.
19+
| var errors = [];
20+
|
21+
| // Buffer for outgoing messages.
22+
| var outMessages = [];
23+
|
24+
| // Buffer for incoming messages (used if onMessage not initalized).
25+
| var inMessages = [];
26+
|
27+
| // Callback for incoming messages.
28+
| var onMessage = null;
29+
|
30+
| function captureConsole(fun, buf) {
31+
| if (!fun) return fun;
32+
| return function() {
33+
| var strs = []
34+
| for (var i = 0; i < arguments.length; ++i)
35+
| strs.push(String(arguments[i]));
36+
|
37+
| buf.push(strs.join(" "));
38+
| return fun.apply(this, arguments);
39+
| }
40+
| }
41+
|
42+
| console.log = captureConsole(console.log, consoleLog);
43+
| console.error = captureConsole(console.error, consoleError);
44+
|
45+
| window.addEventListener('error', function(e) {
46+
| errors.push(e.message)
47+
| });
48+
|
49+
| if ($enableCom) {
50+
| this.scalajsCom = {
51+
| init: function(onMsg) {
52+
| onMessage = onMsg;
53+
| window.setTimeout(function() {
54+
| for (var m in inMessages)
55+
| onMessage(inMessages[m]);
56+
| inMessages = null;
57+
| });
58+
| },
59+
| send: function(msg) { outMessages.push(msg); }
60+
| }
61+
| }
62+
|
63+
| this.scalajsSeleniumInternalInterface = {
64+
| fetch: function() {
65+
| var res = {
66+
| consoleLog: consoleLog.slice(),
67+
| consoleError: consoleError.slice(),
68+
| errors: errors.slice(),
69+
| msgs: outMessages.slice()
70+
| }
71+
|
72+
| consoleLog.length = 0;
73+
| consoleError.length = 0;
74+
| errors.length = 0;
75+
| outMessages.length = 0;
76+
|
77+
| return res;
78+
| },
79+
| send: function(msg) {
80+
| if (inMessages !== null) inMessages.push(msg);
81+
| else onMessage(msg);
82+
| }
83+
| };
84+
|}).call(this)
85+
""".stripMargin.getBytes(StandardCharsets.UTF_8)
86+
)
7987
}
8088
}

seleniumJSEnv/src/main/scala/org/scalajs/jsenv/selenium/SeleniumRun.scala

+21-16
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import scala.util.control.NonFatal
1010

1111
import java.util.concurrent.{ConcurrentLinkedQueue, Executors}
1212
import java.util.function.Consumer
13+
import java.nio.file.Path
14+
import java.net.URL
1315

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

132-
val scripts = input.map {
133-
case Input.Script(s) => s
134-
case _ => throw new UnsupportedInputException(input)
135-
}
136-
137134
try {
138135
withCleanup(FileMaterializer(config.materialization))(_.close()) { m =>
139-
val allScriptURLs = (
140-
m.materialize("setup.js", JSSetup.setupCode(enableCom)) +:
141-
scripts.map(m.materialize)
142-
)
143-
144-
val page = m.materialize("scalajsRun.html", htmlPage(allScriptURLs))
145-
136+
val setupJsPath = JSSetup.setupFile(enableCom)
137+
val page = m.materialize("scalajsRun.html", htmlPage(setupJsPath, input, m))
146138
withCleanup(newDriver())(maybeCleanupDriver(_, config)) { driver =>
147139
driver.navigate().to(page)
148140

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

174-
private def htmlPage(scripts: Seq[java.net.URL]): String = {
175-
val scriptTags =
176-
scripts.map(path => s"<script src='${path.toString}'></script>")
166+
private def htmlPage(setupJsPath: Path, input: Seq[Input], materializer: FileMaterializer): String = {
167+
val setupJs = makeTag(setupJsPath, "text/javascript", materializer)
168+
169+
val tags = input.map {
170+
case Input.Script(path) => makeTag(path, "text/javascript", materializer)
171+
case Input.ESModule(path) => makeTag(path, "module", materializer)
172+
case _ => throw new UnsupportedInputException(input)
173+
}
174+
175+
val allTags = setupJs +: tags
176+
177177
s"""<html>
178178
| <meta charset="UTF-8">
179179
| <body>
180-
| ${scriptTags.mkString("\n ")}
180+
| ${allTags.mkString("\n ")}
181181
| </body>
182182
|</html>
183183
""".stripMargin
184184
}
185185

186+
private def makeTag(path: Path, tpe: String, materializer: FileMaterializer): String = {
187+
val url = materializer.materialize(path)
188+
s"<script defer type='$tpe' src='${url.toString}'></script>"
189+
}
190+
186191
private class WindowOnErrorException(errs: List[String]) extends Exception(s"JS error: $errs")
187192

188193
private def consumer[A](f: A => Unit): Consumer[A] = new Consumer[A] {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.scalajs.jsenv.selenium
2+
3+
import scala.scalajs.js
4+
import scala.scalajs.js.annotation.JSImport
5+
6+
object CamelCase {
7+
def hello(input: String): String = s"Hello ${camelCase(input)}!"
8+
9+
@JSImport("https://cdn.skypack.dev/camelcase@^6.0.0", JSImport.Default)
10+
@js.native
11+
def camelCase(input: String): String = js.native
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.scalajs.jsenv.selenium
2+
3+
import org.junit.Assert._
4+
import org.junit.Test
5+
6+
class ModuleSupportTest {
7+
@Test def testBasicImport(): Unit = {
8+
assertEquals("Hello scalaJsSelenium!", CamelCase.hello("scala js selenium"))
9+
}
10+
}

0 commit comments

Comments
 (0)