JSEnv and chekhov-dom
JVM E2E drives the app from outside the page. JSEnv runs Scala.js tests inside a real
Playwright browser so you can assert on live DOM without js.Dynamic ceremony.
Wire ChekhovJSEnv
Published consumers: add the plugin (it depends on chekhov-jsenv) and set jsEnv:
// project/plugins.sbt
addSbtPlugin("rocks.earlyeffect" % "sbt-chekhov" % "<version>")
// build.sbt
import chekhov.sbt.ChekhovPlugin.autoImport.*
libraryDependencies += "rocks.earlyeffect" %%% "chekhov-dom" % "<version>" % Test
Test / jsEnv := chekhovJSEnv.value
// or: Test / jsEnv := ChekhovJSEnv()
// or: Test / jsEnv := ChekhovJSEnv(ChekhovBrowser.Firefox)
Prefer ModuleKind.ESModule (or otherwise materializable scripts) for linked test output.
ChekhovJSEnv materializes Input.Script / Input.ESModule onto a localhost page and bridges
scalajsCom via frame evaluateExpression.
This monorepo still uses an internal classpath bridge for jsenv-smoke / dom so CI need
not publish first. Consumers should not copy that bridge.
chekhov-dom helpers
import chekhov.dom.ChekhovDom.*
import zio.test.*
test("focus") {
withRoot { root =>
for
_ <- ZIO.succeed(/* mount UI under root */)
_ <- getByRole("textbox", root).click
start <- getByRole("textbox", root).selectionStart
yield assertTrue(start >= 0)
}
}
withRoot installs a scoped throwaway parent under document.body. Locators cover
testid / role / CSS; waitFor polls until the node appears. Depend on
org.scala-js:::scalajs-dom only (not ascent’s DOM facade).
chekhov-ascent
Ascent UI under JSEnv lives in chekhov-ascent (ChekhovAscent.withMounted), not
core. It mounts into a chekhov-dom root and tears down via ascent’s subscription bag:
libraryDependencies += "rocks.earlyeffect" %%% "chekhov-ascent" % "<version>" % Test
import chekhov.ascent.ChekhovAscent.withMounted
import chekhov.dom.*
withMounted(ui) { root =>
getByTestId("inc", root).click
}
Use chekhov-dom alone when you do not need ascent.
Smoke in this repo
CHEKHOV_E2E=1 sbt 'jsenv/testOnly chekhov.jsenv.JsEnvComSpec'
CHEKHOV_E2E=1 sbt jsenv-smoke/testFull
CHEKHOV_E2E=1 sbt dom/testFull
CHEKHOV_E2E=1 sbt ascent/testFull
ChekhovBrowser.fromString("firefox").map(_.channelName)Some(firefox)