Skip to content

Commit f005384

Browse files
authored
Merge pull request #540 from scala-js/topic/tests
Add tests for {node + jsdom, chrome, firefox, webworkers}
2 parents 52d751a + df479a0 commit f005384

File tree

15 files changed

+388
-16
lines changed

15 files changed

+388
-16
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,11 @@ jobs:
2121
- name: Setup Scala
2222
uses: japgolly/[email protected]
2323

24-
- name: Build
25-
run: sbt -DCI=1 "++${{ matrix.scalaversion }}" package
26-
27-
- name: Test generate documentation
28-
run: sbt -DCI=1 "++${{ matrix.scalaversion }}" doc
29-
30-
- name: Build examples
31-
run: sbt -DCI=1 "++${{ matrix.scalaversion }}" example/compile
24+
- name: Build and test
25+
run: sbt -DCI=1 "++${{ matrix.scalaversion }}" test package doc
3226

3327
- name: Validate formatting
34-
run: sbt -DCI=1 "++${{ matrix.scalaversion }}" scalafmtCheck
28+
run: sbt -DCI=1 "++${{ matrix.scalaversion }}" dom/scalafmtCheck
3529

3630
- name: Validate api report
3731
if: matrix.scalaversion != '2.11.12' && matrix.scalaversion != '3.0.1'

build.sbt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@ ThisBuild / organization := "org.scala-js"
44
ThisBuild / shellPrompt := ((s: State) => Project.extract(s).currentRef.project + "> ")
55
ThisBuild / versionScheme := Some("early-semver")
66

7-
val root = Build.root
8-
val scalafixRules = Build.scalafixRules
9-
val dom = Build.dom
10-
val example = Build.example
11-
val readme = Build.readme
7+
val root = Build.root
8+
val scalafixRules = Build.scalafixRules
9+
val dom = Build.dom
10+
val testsShared = Build.testsShared
11+
val testsWebworker = Build.testsWebworker
12+
val testsChrome = Build.testsChrome
13+
val testsFirefox = Build.testsFirefox
14+
val testsNodeJsdom = Build.testsNodeJsdom
15+
val example = Build.example
16+
val readme = Build.readme
17+
18+
// TODO: Remove after dom project get it's own directory
19+
Global / onLoad ~= (_.andThen("project root" :: _))

prePR.sbt

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,43 @@ addCommandAlias("prePR", "+prePR_nonCross")
44

55
val prePR_nonCross = taskKey[Unit]("Performs all necessary work required before submitting a PR, for a single version of Scala.")
66

7+
// Unfortunately we can't just call `root/Test/compile` because it doesn't take aggregation into account :(
78
ThisBuild / prePR_nonCross := Def.sequential(
8-
root / clean,
9+
10+
Def.task {
11+
(root / clean).value
12+
(scalafixRules / clean).value
13+
(dom / clean).value
14+
(testsShared / clean).value
15+
(testsWebworker / clean).value
16+
(testsChrome / clean).value
17+
(testsFirefox / clean).value
18+
(testsNodeJsdom / clean).value
19+
(example / clean).value
20+
},
21+
922
dom / Compile / scalafmt,
1023
Def.taskDyn {
1124
if (scalaVersion.value.startsWith("2."))
1225
(dom / Compile / scalafix).toTask("")
1326
else
1427
Def.task[Unit]((dom / Compile / compile).value)
1528
},
16-
root / Compile / compile,
29+
30+
Def.task {
31+
(testsShared / Test / compile).value
32+
(testsWebworker / Test / compile).value
33+
(testsChrome / Test / compile).value
34+
(testsFirefox / Test / compile).value
35+
(testsNodeJsdom / Test / compile).value
36+
(example / Test / compile).value
37+
},
38+
39+
Def.taskDyn {
40+
if (scalaVersion.value.startsWith("2.12."))
41+
Def.task[Unit]((readme / Compile / compile).value)
42+
else
43+
Def.task(())
44+
},
45+
1746
).value

project/Build.scala

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import sbt._
22
import sbt.Keys._
3+
import java.util.concurrent.TimeUnit
4+
import org.openqa.selenium.{Capabilities, WebDriver}
5+
import org.openqa.selenium.chrome.{ChromeDriver, ChromeOptions}
6+
import org.openqa.selenium.firefox.{FirefoxOptions, FirefoxProfile}
7+
import org.openqa.selenium.remote.server.{DriverFactory, DriverProvider}
8+
import org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv
9+
import org.scalajs.jsenv.selenium.SeleniumJSEnv
10+
import org.scalajs.sbtplugin.ScalaJSJUnitPlugin
311
import org.scalajs.sbtplugin.ScalaJSPlugin
412
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
13+
import sbtbuildinfo.BuildInfoPlugin
14+
import sbtbuildinfo.BuildInfoPlugin.autoImport._
515
import scalafix.sbt.ScalafixPlugin
616
import scalafix.sbt.ScalafixPlugin.autoImport._
717
import scalatex.ScalatexReadme
@@ -21,6 +31,11 @@ object Build {
2131
.aggregate(
2232
scalafixRules,
2333
dom,
34+
testsShared,
35+
testsWebworker,
36+
testsChrome,
37+
testsFirefox,
38+
testsNodeJsdom,
2439
example,
2540
// readme, // This is a Scala 2.12 only module
2641
)
@@ -43,6 +58,85 @@ object Build {
4358
libraryDependencies += Dep.scalafixCore.value,
4459
)
4560

61+
lazy val testsShared = project
62+
.in(file("tests-shared"))
63+
.dependsOn(dom)
64+
.enablePlugins(ScalaJSPlugin, ScalaJSJUnitPlugin)
65+
.configure(commonSettings, crossScala, preventPublication, moveTestLibsToCompile)
66+
67+
lazy val testsWebworker = project
68+
.in(file("tests-webworker"))
69+
.dependsOn(testsShared)
70+
.enablePlugins(ScalaJSPlugin, ScalaJSJUnitPlugin, BuildInfoPlugin)
71+
.configure(commonSettings, crossScala, preventPublication, moveTestLibsToCompile)
72+
.settings(
73+
buildInfoKeys := Seq[BuildInfoKey](
74+
"wwJsPath" -> (Compile / fastOptJS / artifactPath).value.absolutePath,
75+
),
76+
buildInfoPackage := "org.scalajs.dom.tests.webworker",
77+
scalaJSUseMainModuleInitializer := true,
78+
)
79+
80+
def testsWebworkers: Project => Project = _
81+
.dependsOn(testsWebworker)
82+
.settings(
83+
Test / test := {
84+
val _ = (testsWebworker / Compile / fastOptJS).value
85+
(Test / test).value
86+
},
87+
)
88+
89+
lazy val testsChrome = project
90+
.in(file("tests-chrome"))
91+
.dependsOn(testsShared % Test)
92+
.enablePlugins(ScalaJSPlugin, ScalaJSJUnitPlugin)
93+
.configure(commonSettings, crossScala, preventPublication, testsWebworkers)
94+
.settings(
95+
Test / jsEnv := {
96+
System.setProperty("webdriver.chrome.silentOutput", "true")
97+
val o = new ChromeOptions()
98+
o.setHeadless(true)
99+
o.addArguments("--allow-file-access-from-files")
100+
val df = new DriverFactory {
101+
private[this] val default = SeleniumJSEnv.Config().driverFactory
102+
override def newInstance(c: Capabilities): WebDriver = {
103+
val d = default.newInstance(c).asInstanceOf[ChromeDriver]
104+
d.manage.timeouts.pageLoadTimeout(if (inCI) 10 else 1, TimeUnit.MINUTES)
105+
d.manage.timeouts.setScriptTimeout(if (inCI) 10 else 1, TimeUnit.MINUTES)
106+
d
107+
}
108+
override def registerDriverProvider(p: DriverProvider): Unit =
109+
default.registerDriverProvider(p)
110+
}
111+
new SeleniumJSEnv(o, SeleniumJSEnv.Config().withDriverFactory(df))
112+
},
113+
)
114+
115+
lazy val testsFirefox = project
116+
.in(file("tests-firefox"))
117+
.dependsOn(testsShared % Test)
118+
.enablePlugins(ScalaJSPlugin, ScalaJSJUnitPlugin)
119+
.configure(commonSettings, crossScala, preventPublication, testsWebworkers)
120+
.settings(
121+
Test / jsEnv := {
122+
val p = new FirefoxProfile()
123+
p.setPreference("privacy.file_unique_origin", false)
124+
val o = new FirefoxOptions()
125+
o.setProfile(p)
126+
o.setHeadless(true)
127+
new SeleniumJSEnv(o)
128+
},
129+
)
130+
131+
lazy val testsNodeJsdom = project
132+
.in(file("tests-node-jsdom"))
133+
.dependsOn(testsShared % Test)
134+
.enablePlugins(ScalaJSPlugin, ScalaJSJUnitPlugin)
135+
.configure(commonSettings, crossScala, preventPublication)
136+
.settings(
137+
Test / jsEnv := new JSDOMNodeJSEnv,
138+
)
139+
46140
lazy val example = project
47141
.dependsOn(dom)
48142
.enablePlugins(ScalaJSPlugin)

project/Lib.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sbt._
22
import sbt.Keys._
3+
import org.scalajs.sbtplugin.ScalaJSJUnitPlugin
34
import Dependencies._
45

56
object Lib {
@@ -22,6 +23,7 @@ object Lib {
2223
case Some((2, 13)) => "-Wunused:imports,patvars,locals,implicits" :: Nil
2324
case _ => Nil
2425
}),
26+
testOptions += Tests.Argument(TestFramework("com.novocode.junit.JUnitFramework"), "-v"),
2527
)
2628

2729
def crossScala: Project => Project = _
@@ -100,4 +102,13 @@ object Lib {
100102
else
101103
sourceDir / "scala-old-collections"
102104

105+
def moveTestLibsToCompile: Project => Project =
106+
_.settings(
107+
libraryDependencies ~= { _.map(m =>
108+
if (m.configurations.contains(Test.name))
109+
m.withConfigurations(Some(Compile.name))
110+
else
111+
m
112+
)},
113+
)
103114
}

project/plugins.sbt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
libraryDependencies += "org.scala-js" %% "scalajs-env-jsdom-nodejs" % "1.1.0"
2+
libraryDependencies += "org.scala-js" %% "scalajs-env-selenium" % "1.1.1"
3+
14
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.30")
5+
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0")
26
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.5.7")
37
addSbtPlugin("com.lihaoyi" % "scalatex-sbt-plugin" % "0.3.11")
48
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.1")
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.scalajs.dom.tests.chrome
2+
3+
import org.scalajs.dom.tests.shared._
4+
import org.scalajs.dom.tests.webworker._
5+
6+
class ChromeTests extends SharedTests with WebWorkerTests
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.scalajs.dom.tests.firefox
2+
3+
import org.scalajs.dom.tests.shared._
4+
import org.scalajs.dom.tests.webworker._
5+
6+
class FirefoxTests extends SharedTests with WebWorkerTests
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.scalajs.dom.tests.node.jsdom
2+
3+
import org.scalajs.dom.tests.shared._
4+
5+
class NodeJsdomTests extends SharedTests
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.scalajs.dom.tests.shared
2+
3+
import org.junit.Assert
4+
import scala.concurrent._
5+
import scala.util._
6+
import scala.scalajs.js.timers._
7+
8+
object AsyncTesting {
9+
10+
type AsyncResult = Future[Try[Unit]]
11+
12+
implicit def global: ExecutionContext =
13+
ExecutionContext.global
14+
15+
def async(run: => Future[Any]): AsyncResult = {
16+
val p = Promise[Try[Unit]]()
17+
val timeout = setTimeout(1200) {
18+
p.tryComplete(Failure(new RuntimeException("Test timed out.")))
19+
}
20+
setTimeout(1) {
21+
run.onComplete { ta =>
22+
clearTimeout(timeout)
23+
p.complete(Success(ta.map(_ => ())))
24+
}
25+
}
26+
p.future
27+
}
28+
29+
implicit final class AsyncFutureOps[A](private val self: Future[A]) extends AnyVal {
30+
def tap(f: A => Any): Future[A] =
31+
self.map { a => f(a); a }
32+
33+
def assertEquals(expect: A): Future[A] =
34+
tap(Assert.assertEquals(expect, _))
35+
}
36+
37+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.scalajs.dom.tests.shared
2+
3+
import java.util.UUID
4+
import org.junit.Test
5+
import org.scalajs.dom._
6+
import org.scalajs.dom.raw._
7+
8+
trait SharedTests {
9+
import SharedTests._
10+
11+
// https://github.com/scala-js/scala-js-dom/issues/411 - console doesn't work in web workers
12+
@Test final def ConsoleLogTest(): Unit =
13+
console.log("Testing console.log")
14+
15+
// https://github.com/scala-js/scala-js-dom/pull/432 - Avoid forcing evaluation of crypto
16+
@Test final def CryptoNonStrictTest(): Unit = {
17+
val _ = crypto.HashAlgorithm
18+
}
19+
20+
@Test final def WindowIdbTest(): Unit =
21+
window.indexedDB.foreach(testIdb)
22+
23+
}
24+
25+
object SharedTests {
26+
def testIdb(idb: IDBFactory): Unit = {
27+
val open = idb.open(UUID.randomUUID().toString())
28+
open.onerror = (e: Event) => sys.error("idb open failed: " + e)
29+
// TODO: Test properly in a different PR
30+
}
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.scalajs.dom.tests.webworker
2+
3+
import org.scalajs.dom.raw._
4+
import scala.concurrent._
5+
import scala.scalajs.js
6+
import scala.util.Success
7+
8+
final class Client(worker: Worker) {
9+
import Protocol._
10+
11+
private var preInit = new js.Array[Message]
12+
private var promises = new js.Array[Promise[String]]
13+
14+
worker.onmessage = (e: MessageEvent) => {
15+
val m = e.data.asInstanceOf[Message]
16+
if (m._1 == ServerStarted) {
17+
preInit.foreach(worker.postMessage(_))
18+
preInit = null
19+
} else
20+
promises(m._1).complete(Success(m._2))
21+
}
22+
23+
def send(cmd: WebWorkerCmd): Future[String] = {
24+
val id = promises.length
25+
val p = Promise[String]()
26+
val m = Message(id, cmd.id)
27+
promises.push(p)
28+
if (preInit eq null)
29+
worker.postMessage(m)
30+
else
31+
preInit.push(m)
32+
p.future
33+
}
34+
}
35+
36+
object Client {
37+
38+
def workerUrl: String =
39+
"file://" + BuildInfo.wwJsPath
40+
41+
lazy val global =
42+
new Client(new Worker(workerUrl))
43+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.scalajs.dom.tests.webworker
2+
3+
import scala.scalajs.js
4+
5+
object Protocol {
6+
7+
type MsgId = Int
8+
type Message = js.Tuple2[MsgId, String]
9+
10+
def Message(id: MsgId, data: String): Message =
11+
js.Tuple2(id, data)
12+
13+
final val ServerStarted: MsgId = -1
14+
}

0 commit comments

Comments
 (0)