Browsers and E2E

Chekhov drives real Chromium, Firefox, and WebKit. Install matching browser binaries for the pinned Playwright npm version. A ChekhovSuite runs with ordinary sbt test.

Keep expensive tests in their own module

There is no skip flag. If sbt test on a library module must stay cheap (no Node, no browsers), do not put a ChekhovSuite there. Give the browser suites their own sbt project and leave the fast tests where they are:

lazy val core = (project in file("core"))
  // unit tests only

lazy val e2e = (project in file("e2e"))
  .dependsOn(core)
  .settings(
    libraryDependencies ++= Seq(
      "rocks.earlyeffect" %% "chekhov-zio-test" % "<version>" % Test,
      "rocks.earlyeffect" %% "chekhov-driver"   % "<version>" % Test,
    ),
  )

sbt core/test never launches Playwright. sbt e2e/test (or e2e/testFull) does. CI that should exercise the UI depends on the e2e module; scripted / publish jobs depend only on core.

Install browsers

In a consuming repo (no package.json required):

addSbtPlugin("rocks.earlyeffect" % "sbt-chekhov" % "<version>")
sbt chekhovInstall

That installs Playwright 1.62.1 (the protocol pin) and only the browsers in chekhovBrowsers. PLAYWRIGHT_DRIVER_CLI is an override only; a different version is a hard error that names the pin.

Locally, browsers land in Playwright’s OS cache (~/.cache/ms-playwright on Linux, ~/Library/Caches/ms-playwright on macOS). The pinned CLI is cached under the Chekhov cache (~/Library/Caches/chekhov on macOS, ~/.cache/chekhov on Linux), not whatever npx last downloaded.

This Chekhov repo still uses ./scripts/install-browsers.sh (or sbt pwInstall) next to its own package.json pin. zipx CI sets PLAYWRIGHT_BROWSERS_PATH to target/ms-playwright so browsers ride the LocalDir sbt actions/cache key (same epoch / run_id restore chain as compile products).

Pick engines

One sbt list is both installed and executed. ChekhovSuite runs the spec once per entry (a labeled copy each). chekhovInstall installs that list only: no extra engines, no ffmpeg.

chekhovBrowsers := Seq(ChekhovBrowser.Firefox)
// or several:
chekhovBrowsers := Seq(ChekhovBrowser.Chromium, ChekhovBrowser.Firefox)

chekhovBrowser := "firefox" still works and sets a one-element list. ChekhovJSEnv uses the first entry. Without the plugin, -Dchekhov.browsers=firefox,chromium or ChekhovSuite.forBrowsers(ChekhovBrowser.Firefox, …) does the same fan-out.

(
  ChekhovBrowser.fromString("chromium"),
  ChekhovBrowser.fromString("firefox"),
  ChekhovBrowser.fromString("webkit"),
  ChekhovBrowser.fromString("netscape"),
)
(Some(Chromium),Some(Firefox),Some(WebKit),None)

Use a system browser

Point Chekhov at a browser already on the machine instead of the revision sbt chekhovInstall downloaded. Three keys do it; each reads a -Dchekhov.* system property first, then a CHEKHOV_* environment variable (props win):

KeyPropertyEnv var
Browser binarychekhov.executablePathCHEKHOV_EXECUTABLE_PATH
Installed channel (Chromium only)chekhov.channelCHEKHOV_CHANNEL
Extra process argschekhov.launchArgsCHEKHOV_LAUNCH_ARGS

Setting either executablePath or channel skips the pinned-browser-revision check. The Playwright driver CLI is still required and must match the pin, so keep running sbt chekhovInstall; only the downloaded browser binary is bypassed.

CHEKHOV_EXECUTABLE_PATH=/usr/bin/chromium CHEKHOV_LAUNCH_ARGS="--no-sandbox" sbt e2e/testFull

launchArgs is a flag list: comma or whitespace separates arguments, and an argument that takes a value uses --flag=value. An empty string is treated as unset for every key, so CHEKHOV_HEADLESS="" leaves headless at its default. The example resolves the keys exactly the way the driver does: the property wins over the env var, the empty value stays unset, and the flag list splits into two args.

{
  val config = ChekhovConfig.fromProps(
    props = Map("chekhov.executablePath" -> "/usr/bin/chromium", "chekhov.headless" -> ""),
    env = Map("CHEKHOV_EXECUTABLE_PATH" -> "/opt/ignored", "CHEKHOV_LAUNCH_ARGS" -> "--no-sandbox, --disable-gpu"),
  )
  (config.executablePath, config.headless, config.launchArgs)
}
(Some(/usr/bin/chromium),true,List(--no-sandbox, --disable-gpu))