Quickstart

Chekhov is a ZIO-first Playwright client: pinned protocol.yml becomes a typed Scala AST, a Node driver speaks the channel, and suites compose browser / context / page as layers.

If the UI shows a control, a test should be able to fire it.

A suite in one screenful

Add the suite stack (chekhov-zio-test + chekhov-driver), install browsers, set CHEKHOV_E2E=1, then:

import chekhov.*
import chekhov.ziotest.ChekhovSuite
import zio.test.*

object TodoSpec extends ChekhovSuite:
  def spec = suite("todo")(
    test("add") {
      for
        page <- Chekhov.page
        _    <- page.goto("/")
        _    <- page.fill("input.new-todo", "milk")
        _    <- page.press("input.new-todo", "Enter")
        text <- page.innerText(".todo-list")
      yield assertTrue(text.contains("milk"))
    }
  )

ChekhovSuite wires config + driver + a fresh page per test. Override chekhovConfig when you need a different browser, baseUrl, or artifactsDir. Point baseUrl at a running app (or use scoped Vite / static serve; see Serving the app).

How the stack fits

There is no com.microsoft.playwright JAR. Chekhov claims a curated set of channel methods and interprets them over the official Node driver. Scala.js DOM tests take a different path (ChekhovJSEnv + chekhov-dom); see JSEnv and chekhov-dom.

Claimed surface stays honest

ProtocolSurface is generated from the pinned protocol inventory. Navigation and input are Frame channel methods on the wire (public algebra still looks like page.goto / page.click). Coverage gates assert the allowlist against this inventory.

(
  ProtocolSurface.has("Frame", "goto"),
  ProtocolSurface.has("Frame", "click"),
  ProtocolSurface.has("Frame", "fill"),
  ProtocolSurface.has("Page", "screenshot"),
)
(true,true,true,true)