Liking cljdoc? Tell your friends :D

testing.pw

Playwright-native testing library for Clojure.

Provides:

  • with-ui — main test macro; manages browser + tracing lifecycle. Produces a trace.zip per test, viewable in Playwright Trace Viewer (npx playwright show-trace / trace.playwright.dev). Binds *current-page* dynamic var for the duration of the test. (with-pw is a deprecated alias for with-ui.)
  • with-api — macro that provides an APIRequestContext bound to client. If *current-page* is already bound (i.e. inside with-pw), derives the client from the existing traced BrowserContext so HTTP calls appear in the trace. Otherwise starts a lightweight headless browser+context with tracing and tears it down after body.
  • step — annotate a logical step in the trace (group of actions). Reads page from *current-page*; no explicit context needed.
  • pw-screenshot — manually attach a labelled screenshot to the trace. Reads page from *current-page*; no explicit page arg needed.

Design goals

  • Zero JUnit dependency — pure Playwright Java API on the JVM.
  • Every test automatically produces target/pw-traces/<test-name>-<ts>.zip
  • Traces are enriched with custom step annotations via BrowserContext.tracing().group() / groupEnd(), which appear as named groups in the Trace Viewer action list.
  • A separate testing.pw-report namespace can aggregate all zips into a static HTML dashboard.

Usage

(ns my.tests (:require [clojure.test :refer [deftest is]] [testing.pw :refer [pw-screenshot step with-ui with-api]]))

(deftest homepage-test (with-ui [page] (step "Navigate to example.com" (fn [] (.navigate page "https://example.com") (is (clojure.string/includes? (.title page) "Example"))))

  (step "Verify heading"
    (fn []
      (let [h1 (.locator page "h1")]
        (is (= "Example Domain" (.textContent h1))))))))
Playwright-native testing library for Clojure.

Provides:
- `with-ui`       — main test macro; manages browser + tracing lifecycle.
                     Produces a trace.zip per test, viewable in Playwright
                     Trace Viewer (npx playwright show-trace / trace.playwright.dev).
                     Binds `*current-page*` dynamic var for the duration of the test.
                     (`with-pw` is a deprecated alias for `with-ui`.)
- `with-api`      — macro that provides an APIRequestContext bound to `client`.
                     If `*current-page*` is already bound (i.e. inside `with-pw`),
                     derives the client from the existing traced BrowserContext so
                     HTTP calls appear in the trace.  Otherwise starts a lightweight
                     headless browser+context with tracing and tears it down after body.
- `step`          — annotate a logical step in the trace (group of actions).
                     Reads page from `*current-page*`; no explicit context needed.
- `pw-screenshot` — manually attach a labelled screenshot to the trace.
                     Reads page from `*current-page*`; no explicit page arg needed.

Design goals
============
* Zero JUnit dependency — pure Playwright Java API on the JVM.
* Every test automatically produces target/pw-traces/<test-name>-<ts>.zip
* Traces are enriched with custom step annotations via
  BrowserContext.tracing().group() / groupEnd(), which appear as named
  groups in the Trace Viewer action list.
* A separate `testing.pw-report` namespace can aggregate all zips into
  a static HTML dashboard.

Usage
=====
  (ns my.tests
    (:require [clojure.test :refer [deftest is]]
              [testing.pw  :refer [pw-screenshot step with-ui with-api]]))

  (deftest homepage-test
    (with-ui [page]
      (step "Navigate to example.com"
        (fn []
          (.navigate page "https://example.com")
          (is (clojure.string/includes? (.title page) "Example"))))

      (step "Verify heading"
        (fn []
          (let [h1 (.locator page "h1")]
            (is (= "Example Domain" (.textContent h1))))))))
raw docstring

*current-api*clj

Dynamically bound to the Playwright APIRequestContext inside with-api. Allows nested helpers to access the REST client without explicit threading.

Dynamically bound to the Playwright APIRequestContext inside `with-api`.
Allows nested helpers to access the REST client without explicit threading.
raw docstring

*current-page*clj

Dynamically bound to the Playwright Page inside with-pw. step and pw-screenshot read this automatically so callers never need to pass page/context explicitly.

Dynamically bound to the Playwright Page inside `with-pw`.
`step` and `pw-screenshot` read this automatically so callers never need to
pass page/context explicitly.
raw docstring

*verbose-steps*clj

When true, step prints each step name, timing, and pass/fail to stdout. Automatically set to true when running headed (:headless false or -Dplaywright.browser.headless=false). Can also be bound manually.

When true, `step` prints each step name, timing, and pass/fail to stdout.
Automatically set to true when running headed (`:headless false` or
`-Dplaywright.browser.headless=false`).  Can also be bound manually.
raw docstring

begin-stepclj

(begin-step context description)

Open a named group in the Playwright trace. Internal — use the step macro instead.

Open a named group in the Playwright trace.
Internal — use the `step` macro instead.
raw docstring

end-stepclj

(end-step context)

Close the most recently opened trace group. Internal — use the step macro instead.

Close the most recently opened trace group.
Internal — use the `step` macro instead.
raw docstring

pw-screenshotclj

(pw-screenshot label)

Take a screenshot and add it as an attachment annotation inside the current trace context. Returns the screenshot bytes.

Reads the current page from *current-page* (bound by with-pw).

Because Playwright's tracing automatically captures screenshots on every action, this is only needed for explicit labelled snapshots outside of normal user interactions.

Take a screenshot and add it as an attachment annotation inside the
current trace context.  Returns the screenshot bytes.

Reads the current page from `*current-page*` (bound by `with-pw`).

Because Playwright's tracing automatically captures screenshots on every
action, this is only needed for *explicit* labelled snapshots outside of
normal user interactions.
raw docstring

start-browserclj

(start-browser)
(start-browser opts)

Start a Playwright browser context with tracing enabled.

Options: :headless — boolean, default true. Overridden by JVM property -Dplaywright.browser.headless=false :browser — :chromium (default) | :webkit | :firefox. Defaults from JVM property -Dplaywright.browser or env PW_BROWSER, so a whole suite can be pointed at another engine (e.g. WebKit ≈ Safari) without editing tests. :test-name — string used to derive the trace filename. :output-dir — directory for trace zips, default 'target/pw-traces'. :slow-mo — milliseconds to slow down actions (useful headed), default 0.

Returns a map with: :playwright :browser :context :page :trace-path

Start a Playwright browser context with tracing enabled.

Options:
  :headless    — boolean, default true.
                  Overridden by JVM property -Dplaywright.browser.headless=false
  :browser     — :chromium (default) | :webkit | :firefox.
                  Defaults from JVM property -Dplaywright.browser or env
                  PW_BROWSER, so a whole suite can be pointed at another
                  engine (e.g. WebKit ≈ Safari) without editing tests.
  :test-name   — string used to derive the trace filename.
  :output-dir  — directory for trace zips, default 'target/pw-traces'.
  :slow-mo     — milliseconds to slow down actions (useful headed), default 0.

Returns a map with:
  :playwright  :browser  :context  :page  :trace-path
raw docstring

stepcljmacro

(step description f)

Execute f inside a named Playwright trace group.

(step "Log in" (fn [] (.fill page "#user" "admin") (.click page "#submit")))

Reads the current page from *current-page* (bound by with-pw). The step name appears as a collapsible group in the Trace Viewer action list.

When *verbose-steps* is true (automatically set in headed mode), step names, timings and pass/fail status are printed to stdout.

Execute f inside a named Playwright trace group.

  (step "Log in"
    (fn []
      (.fill page "#user" "admin")
      (.click page "#submit")))

Reads the current page from `*current-page*` (bound by `with-pw`).
The step name appears as a collapsible group in the Trace Viewer action list.

When `*verbose-steps*` is true (automatically set in headed mode), step
names, timings and pass/fail status are printed to stdout.
raw docstring

stop-browserclj

(stop-browser bm)
(stop-browser {:keys [playwright browser context page trace-path] :as _bm}
              status)

Stop tracing, save trace.zip, rename it with status suffix, then close everything. status is :pass or :fail (default :pass). Returns the java.nio.file.Path of the saved (and renamed) trace, or nil on error.

Stop tracing, save trace.zip, rename it with status suffix, then close everything.
`status` is :pass or :fail (default :pass).
Returns the java.nio.file.Path of the saved (and renamed) trace, or nil on error.
raw docstring

with-apicljmacro

(with-api [client-binding & [opts]] & body)

Provide a Playwright APIRequestContext bound to client-binding.

Behaviour depends on whether a browser session is already active:

  • Inside with-pw (*current-page* is bound): BrowserContext.request() returns the context's own APIRequestContext directly — no factory step needed. HTTP calls appear in the trace. The browser lifecycle is untouched.

  • Standalone (no *current-page*): Starts a fresh headless Chromium + BrowserContext with tracing enabled, produces a trace.zip just like with-pw, then closes everything. Uses Playwright.request().newContext(opts) to create the REST client.

Options map (optional second element of binding vector): :ignore-https-errors — boolean, default false (standalone mode only). All other options accepted by start-browser (:headless, :output-dir, :slow-mo) are forwarded when starting a standalone context.

Examples:

;; Standalone REST-only test — full trace produced (deftest config-fetch-test (with-api [client] (let [resp (.get client "https://jsonplaceholder.typicode.com/posts/1" nil)] (is (= 200 (.status resp))))))

;; Inside with-pw — HTTP recorded in the same browser trace (deftest mixed-test (with-pw [page] (step "Load UI" #(.navigate page "https://example.com")) (with-api [client] (let [resp (.get client "https://jsonplaceholder.typicode.com/posts/1" nil)] (is (= 200 (.status resp)))))))

Provide a Playwright APIRequestContext bound to `client-binding`.

Behaviour depends on whether a browser session is already active:

* Inside `with-pw` (`*current-page*` is bound):
    BrowserContext.request() returns the context's own APIRequestContext
    directly — no factory step needed.  HTTP calls appear in the trace.
    The browser lifecycle is untouched.

* Standalone (no `*current-page*`):
    Starts a fresh headless Chromium + BrowserContext with tracing enabled,
    produces a trace.zip just like `with-pw`, then closes everything.
    Uses Playwright.request().newContext(opts) to create the REST client.

Options map (optional second element of binding vector):
  :ignore-https-errors — boolean, default false (standalone mode only).
  All other options accepted by `start-browser` (:headless, :output-dir,
  :slow-mo) are forwarded when starting a standalone context.

Examples:

  ;; Standalone REST-only test — full trace produced
  (deftest config-fetch-test
    (with-api [client]
      (let [resp (.get client "https://jsonplaceholder.typicode.com/posts/1" nil)]
        (is (= 200 (.status resp))))))

  ;; Inside with-pw — HTTP recorded in the same browser trace
  (deftest mixed-test
    (with-pw [page]
      (step "Load UI" #(.navigate page "https://example.com"))
      (with-api [client]
        (let [resp (.get client "https://jsonplaceholder.typicode.com/posts/1" nil)]
          (is (= 200 (.status resp)))))))
raw docstring

with-pwcljmacro

(with-pw binding & body)

Deprecated alias for with-ui. Use with-ui instead.

Deprecated alias for `with-ui`. Use `with-ui` instead.
raw docstring

with-uicljmacro

(with-ui [page-binding & [opts]] & body)

Main test wrapper that provides a Playwright Page bound to page-binding and full tracing lifecycle management. Also binds *current-page* so step and pw-screenshot can be used without passing page explicitly.

Forms:

(with-ui [page] (step "Do something" #(.navigate page "https://...")) (step "Verify" #(is (= "Title" (.title page)))))

;; with explicit options: (with-ui [page {:headless false :output-dir "target/my-traces"}] ...)

Options (second element of binding vector, optional map): :headless — false to show a browser window (default true) :output-dir — directory for trace zips (default 'target/pw-traces') :slow-mo — ms delay between actions when headed (default 80)

What happens:

  1. Chromium is launched, tracing started (screenshots + snapshots + sources).
  2. Body forms execute with page bound to the Playwright Page and *current-page* dynamically bound to the same Page.
  3. Tracing stops and trace.zip is written to output-dir.
  4. Browser is closed regardless of test outcome.
  5. On success/failure the trace path is printed for quick opening.

Viewing a trace: npx playwright show-trace target/pw-traces/<name>.zip

or drag-drop the zip to https://trace.playwright.dev

Main test wrapper that provides a Playwright Page bound to `page-binding`
and full tracing lifecycle management.  Also binds `*current-page*` so
`step` and `pw-screenshot` can be used without passing page explicitly.

Forms:

  (with-ui [page]
    (step "Do something" #(.navigate page "https://..."))
    (step "Verify" #(is (= "Title" (.title page)))))

  ;; with explicit options:
  (with-ui [page {:headless false :output-dir "target/my-traces"}]
    ...)

Options (second element of binding vector, optional map):
  :headless    — false to show a browser window (default true)
  :output-dir  — directory for trace zips (default 'target/pw-traces')
  :slow-mo     — ms delay between actions when headed (default 80)

What happens:
  1. Chromium is launched, tracing started (screenshots + snapshots + sources).
  2. Body forms execute with `page` bound to the Playwright Page and
     `*current-page*` dynamically bound to the same Page.
  3. Tracing stops and trace.zip is written to output-dir.
  4. Browser is closed regardless of test outcome.
  5. On success/failure the trace path is printed for quick opening.

Viewing a trace:
  npx playwright show-trace target/pw-traces/<name>.zip
  # or drag-drop the zip to https://trace.playwright.dev
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close