| Accessibility Snapshots | Inline Clojure via --eval | Visual Annotations | Agent Scaffolding |
![]() | ![]() | ![]() | ![]() |
Playwright's Java API is imperative and verbose — option builders, checked exceptions, manual resource cleanup. Clojure deserves better. And AI agents deserve more than a shell wrapper.
spel wraps Playwright Java with idiomatic Clojure: maps for options, anomaly maps for errors, with-* macros for lifecycle, and a native CLI binary for instant browser automation. It does everything a modern agentic workflow needs — in one tool.
--eval scripting, and zero brittle CSS selectors — agents reason and act in a loop without restarting.--eval — mix business logic with automation, call any GraalVM-bound function, compose scripts on the fly. No other browser tool lets an agent write and execute real code mid-session.Clojure library:
;; deps.edn
{:deps {com.blockether/spel {:mvn/version "0.4.0"}}}
Native CLI (download from GitHub releases):
# macOS (Apple Silicon)
curl -LO https://github.com/Blockether/spel/releases/latest/download/spel-macos-arm64
chmod +x spel-macos-arm64 && mv spel-macos-arm64 ~/.local/bin/spel
# Linux (amd64)
curl -LO https://github.com/Blockether/spel/releases/latest/download/spel-linux-amd64
chmod +x spel-linux-amd64 && mv spel-linux-amd64 ~/.local/bin/spel
# Linux (arm64)
curl -LO https://github.com/Blockether/spel/releases/latest/download/spel-linux-arm64
chmod +x spel-linux-arm64 && mv spel-linux-arm64 ~/.local/bin/spel
# Windows (PowerShell)
Invoke-WebRequest -Uri https://github.com/Blockether/spel/releases/latest/download/spel-windows-amd64.exe -OutFile spel.exe
Move-Item spel.exe "$env:LOCALAPPDATA\Microsoft\WindowsApps\spel.exe"
Add ~/.local/bin to your PATH:
export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc or ~/.zshrc
macOS Gatekeeper (binaries are not Apple-signed):
xattr -d com.apple.quarantine ~/.local/bin/spel
Post-install:
spel install # install browsers
spel version # verify installation
Behind a corporate SSL-inspecting proxy, spel install may fail with "PKIX path building failed". Use these env vars to add corporate CA certs:
| Env Var | Format | On missing file | Description |
|---|---|---|---|
SPEL_CA_BUNDLE | PEM file | Error | Extra CA certs (merged with defaults) |
NODE_EXTRA_CA_CERTS | PEM file | Warning, skips | Shared with Node.js subprocess |
SPEL_TRUSTSTORE | JKS/PKCS12 | Error | Truststore (merged with defaults) |
SPEL_TRUSTSTORE_TYPE | String | — | Default: JKS |
SPEL_TRUSTSTORE_PASSWORD | String | — | Default: empty |
export SPEL_CA_BUNDLE=/path/to/corporate-ca.pem
export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem
spel install --with-deps
All options merge with built-in defaults — public CDN certs continue to work.
(require '[com.blockether.spel.core :as core]
'[com.blockether.spel.page :as page])
(core/with-testing-page [pg]
(page/navigate pg "https://example.com")
(page/title pg))
;; => "Example Domain"
Pass an opts map for device emulation:
(core/with-testing-page {:device :iphone-14 :locale "fr-FR"} [pg]
(page/navigate pg "https://example.com"))
For explicit lifecycle control, with-playwright/with-browser/with-context/with-page nesting is available. See the full API reference.
Browser testing:
(core/with-testing-page [pg]
(page/navigate pg "https://example.com")
(page/title pg))
API testing:
(core/with-testing-api {:base-url "https://api.example.com"} [ctx]
(core/api-get ctx "/users"))
Combined UI + API — use page-api or with-page-api to share a single Playwright trace:
;; page-api: same context, same trace
(core/with-testing-page [pg]
(page/navigate pg "https://example.com/login")
(core/api-get (core/page-api pg) "/api/me"))
;; with-page-api: same context, different base-url
(core/with-testing-page [pg]
(page/navigate pg "https://example.com/login")
(core/with-page-api pg {:base-url "https://api.example.com"} [ctx]
(core/api-get ctx "/me")))
Important: Do NOT nest
with-testing-pageinsidewith-testing-api(or vice versa). Each creates its own Playwright instance, browser, and context — you get two separate traces instead of one. Usepage-api/with-page-apito combine UI and API testing under a single trace.
Test example using spel.allure (defdescribe, describe, it, expect):
(ns my-app.test
(:require
[com.blockether.spel.page :as page]
[com.blockether.spel.test-fixtures :refer [*page* with-playwright with-browser with-page]]
[com.blockether.spel.allure :refer [defdescribe describe expect it]]))
(defdescribe my-test
(describe "example.com"
{:context [with-playwright with-browser with-page]}
(it "navigates and asserts"
(page/navigate *page* "https://example.com")
(expect (= "Example Domain" (page/title *page*))))))
clojure -M:test --output nested --output com.blockether.spel.allure-reporter/allure
See SKILL.md for fixtures, steps, and attachments.
spel compiles to a native binary via GraalVM - no JVM startup, instant execution. The CLI provides commands for browser automation (open, screenshot, snapshot, annotate), a persistent browser daemon, session recording (codegen), PDF generation, and an --eval mode for inline Clojure scripting via SCI. Run spel --help for the full command list.
Point your AI agent at spel and let it write your E2E tests.
spel init-agents # OpenCode (default)
spel init-agents --loop=claude # Claude Code
spel init-agents --loop=vscode # VS Code / Copilot
spel init-agents --flavour=clojure-test # clojure.test instead of Lazytest
spel init-agents --no-tests # SKILL only (interactive dev)
| Flag | Default | Purpose |
|---|---|---|
--loop TARGET | opencode | Agent format: opencode, claude, vscode |
--ns NS | dir name | Base namespace for generated tests |
--flavour FLAVOUR | lazytest | Test framework: lazytest or clojure-test |
--no-tests | — | Scaffold only the SKILL (API reference) — no test agents |
--dry-run | — | Preview files without writing |
--force | — | Overwrite existing files |
--test-dir DIR | test-e2e | E2E test output directory |
--specs-dir DIR | test-e2e/specs | Test plans directory |
Record browser sessions as WebM files for debugging and CI artifacts.
(def ctx (core/new-context browser {:record-video-dir "videos"}))
See recording options and test fixtures.
Record browser sessions and transform them to idiomatic Clojure code.
spel codegen record -o recording.jsonl https://example.com
spel codegen recording.jsonl > my_test.clj
See full actions and output formats.
# Install browsers (via Playwright Java CLI)
clojure -M -e "(com.microsoft.playwright.CLI/main (into-array String [\"install\" \"--with-deps\"]))"
# Build JAR
clojure -T:build jar
# Build native image (requires GraalVM)
clojure -T:build native-image
# Run tests
make test
make test-allure
# Start REPL
make repl
See CHANGELOG.md.
Apache License 2.0 — see LICENSE.
Can you improve this documentation? These fine people already did:
Karol Wojcik, Michał Kruk & blockether-deployerEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |