# etaoin: a WebDriver client in pure Clojure

> Etaoin implements the WebDriver protocol in Clojure with no Selenium underneath, which means a browser test suite needs no stack of downloaded jars and no daemon to manage, and it keeps the whole thing in one namespace. The project is on Clojars, documented on cljdoc, and used in production by teams including Barrick Gold, Exoscale and Kevel.

**clj-commons/etaoin** — Pure Clojure Webdriver protocol implementation

- Repository: https://github.com/clj-commons/etaoin
- Website: https://cljdoc.org/d/etaoin
- Stars: 973 · Forks: 94
- Language: Clojure
- License: EPL-1.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/clj-commons-etaoin

## The WebDriver protocol, without Selenium

Etaoin is a pure Clojure implementation of the WebDriver protocol, and the name comes from a piece of folklore, Etaoin Shrdlu, a typing machine that came to life after a mysterious note was produced on it. The use case it exists for is automating a browser: testing frontend behaviour, simulating human actions, or whatever else you want a browser to do. The interesting decision is what it is not. Selenium is free, in the sense that it is the obvious choice, The project's own answer to that is Selenium-free: no big dependencies, no tons of downloaded jars, and in a Clojure project that avoids a second ecosystem in your pipeline. Etaoin speaks the same wire protocol and is implemented in the language you are already using, so the browser driver is the only external process in the picture. The project describes the result as Selenium-free, with no big dependencies and no tons of downloaded jars, and calls the library lightweight, fast, simple and compact, with just one main namespace and a couple of helpers.

## Browsers, drivers, and who starts the driver

The supported browsers are Chrome, Firefox, Safari and Edge, and the connection model has two options. Etaoin can either connect to a remote WebDriver process, which is what you want in a CI system where the browser runs in another container or another machine, or it can launch one for you, which is what you want on a developer laptop. That choice matters more than it sounds, because a browser driver is a stateful process with its own lifecycle, and the usual failure in browser testing is not the test but the driver. A related convenience is that you can run your unit tests directly from Emacs with the usual C-t t, which is a small thing that says something about the workflow the library was written for. The last capability is the one that separates this from a plain driver wrapper, the ability to imitate human-like behaviour with delays and typos. A test that types instantly into a search box is testing a different application from one that pauses and mistypes, and the second one finds the bugs the first one misses.

## Who uses it, and what they use it for

The readme lists a set of companies using Etaoin, and the names are a good signal about the kind of project it fits: Exoscale, Flyerbee, Barrick Gold, Doctor Evidence, Kevel, formerly Adzerk, and Guaranteed Rate. The example usages are more revealing than the logos. There is a write-up of end to end tests in Clojure with Etaoin and Testcontainers, which is the combination people need when the browser has to be provisioned on demand. There is a project that controls a SpaceX ISS docking simulator. The Babashka book uses it to watch and refresh the browser while making edits, which is a hot-reload loop for documentation. The rewrite-clj, cljdoc and test-doc-blocks projects use a doc-update-readme babashka task to generate contributor badges, which means the browser is part of a build pipeline rather than a test suite. re-find.web, Quil and clojurice all use it for their own test browser behaviour, integration tests and browser interaction tests. The pattern across all of them is the same, a tool that drives a browser as an ordinary function call from a script.

## Versioning, maintainers and how to report a problem

Etaoin uses a major.minor.patch-test-qualifier scheme with rules that are spelled out. The major number increments when a non alpha release API has been majorly broken, something the project would like to avoid. The minor number conveys significant new features or minor breakage. The patch number indicates bug fixes or minor changes and is the total number of releases to date, which is a useful property because it means the patch count is a release counter. The test qualifier is absent for stable releases and can be alpha, beta, rc1 or similar. The people behind it are listed in the readme, with a long contributor list, two current maintainers, Lee Read and Michiel Borkent, and the founder, Ivan Grishaev, who also wrote the article on the pitfalls of UI testing that the readme links to alongside a live coding session. The project says it is open to improvements and ideas, and gives one specific instruction for anyone with a failing test: submit an issue with your OS version, your browser and the console output, which is a reasonable minimum for a project whose failures depend on the environment.

## The container image that makes it work in CI

The Dockerfile in the repository shows what running browser tests in CI actually requires, and it is a longer list than you would guess. The base image is the official Clojure image on Temurin 21 with tools-deps, on a slim Debian. On top of that come the packages a headless browser needs: libnss3, the GTK 3 libraries, ALSA, the GBM development files, ImageMagick, bzip2, gnupg and ca-certificates, and a set of fonts including the Japanese Gothic IPA fonts and several X11 font packages, which is what stops a page rendering as boxes. There is also Xvfb, the virtual framebuffer, and fluxbox, a window manager, because a real browser still wants a display and a session even when nobody is looking at it. babashka is installed and used for the image build itself, and a marker file is created at /tmp/in_a_docker_build_for_etaoin so that commands can tell whether they are running inside a build. The image defaults to a non-root user, etaoin-user. The comment at the end of the package list explains why the image cannot be built with a normal layer cache, since the commands do not change but their impact does, so the build is meant to run with the cache disabled.

## Where the project stands

The release history is short and steady, with v1.1.41 in August 2024, v1.1.42 in September 2024 and v1.1.43 in April 2025, and the last push was on 2026-08-13, so the repository is being worked on more recently than the last tag. The licence is the Eclipse Public License, version 1.0 or any later version at your option, and the copyright is Ivan Grishaev for 2017 to 2020, which reflects where the project came from rather than where it is now. The distribution channels are the usual Clojure ones, the artifact is etaoin/etaoin on Clojars, the source is in the clj-commons GitHub organisation, and the API documentation and articles live on cljdoc, where there is a user guide and a developer guide as separate documents. The tooling around it is Clojure-native, with a deps.edn for the project, a bb.edn for babashka tasks, a build.clj for the build, a .clj-kondo configuration for the linter and a .cljfmt.edn for the formatter, and the README carries a badge saying the library is compatible with babashka, which tells you the library avoids anything the fast interpreter cannot run.

## Conclusion

Etaoin is the right choice if you write Clojure and want browser tests without Selenium in the loop, particularly for a team that already uses babashka and clj-kondo and wants one toolchain rather than two. It is the wrong choice if your organisation has standardised on Selenium, because Etaoin speaks the same protocol but drops the ecosystem that comes with it, and it is the wrong choice if you need a browser it does not support. Before you adopt it, read the user guide on cljdoc, check the driver binaries your CI images need, and pin a version, since the last release is from 2025 and the repository has moved on since.

## FAQ

### What is etaoin?

Etaoin is a pure Clojure implementation of the WebDriver protocol, used to automate a browser for frontend testing or to simulate human actions. It needs no Selenium, no large dependency set and no pile of downloaded jars, and the whole thing lives in one main namespace.

### Which browsers does etaoin support?

Chrome, Firefox, Safari and Edge. Etaoin can either connect to a remote WebDriver process or launch one itself, and it can imitate human-like behaviour such as delays and typos.

### How does etaoin handle versioning?

It uses major.minor.patch-test-qualifier. The major number increments when a non alpha release API has been majorly broken, minor conveys significant new features or minor breakage, patch is for bug fixes and counts the releases to date, and the test qualifier such as alpha or rc1 is absent in stable releases.

### What licence is etaoin under?

The Eclipse Public License, version 1.0 or, at your option, any later version, with the copyright held by Ivan Grishaev for 2017 to 2020. The artifact is published on Clojars as etaoin/etaoin.

## Sources

- [clj-commons/etaoin on GitHub](https://github.com/clj-commons/etaoin)
- [License: EPL-1.0](https://github.com/clj-commons/etaoin/blob/master/LICENSE)
- [Project website](https://cljdoc.org/d/etaoin)
- [README](https://github.com/clj-commons/etaoin/blob/master/README.md)
- [Releases](https://github.com/clj-commons/etaoin/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/clj-commons-etaoin
