Skip to content

Terminal Testing

Betamax can run interactive terminal programs, wait for expected output, and write structured state for assertions. This makes it useful as a terminal test harness in addition to a GIF generator.

Output target/snapshots/app.gif
Type "my-app --demo"
Enter
Wait+Screen "Ready"
State target/snapshots/ready.json
Screenshot target/snapshots/ready.png

The tape fails if expected text does not appear before the configured timeout. The PNG helps with visual review, while State JSON can be compared in snapshot tests.

Prefer asserting text and important style spans instead of every pixel. Pixel assertions are useful for renderer work, but they are often brittle for application behavior tests.

Good assertions include:

  • expected viewport text
  • expected scrollback text
  • selected style spans for important colors or attributes
  • cursor visibility and location when it affects behavior
  • whether a prompt, status line, or selected row appears
  • whether hidden setup avoided extra visible frames

Use waits as synchronization points:

Type "my-tui --scripted"
Enter
Wait+Screen "Loaded 12 rows"

Prefer Wait+Screen for TUIs because cursor location may move as the program draws. Use regex waits for dynamic output:

Wait+Screen "/Loaded [0-9]+ rows/"

Use Env to make application behavior deterministic:

Env NO_COLOR 1
Env TZ UTC
Env BETAMAX_TEST 1

Use Set CursorBlink false when animation frame differences do not matter to the test.

A common Rust test shape is:

  1. Run betamax run tests/fixtures/profile.tape.
  2. Read target/snapshots/profile.json.
  3. Deserialize it with serde_json.
  4. Assert targeted fields directly, or snapshot the whole file with insta.

The CLI is enough for this workflow today. The betamax-core crate already exposes tape parsing, running, artifacts, and terminal state types, so a more ergonomic library-first test API can grow on top of the same primitives.

State JSON should be the primary assertion artifact. Screenshots are best as debugging attachments when a snapshot changes. GIFs are useful when reviewing interaction timing, but they should usually not be the only automated assertion.

See Examples for scrollback.tape, text-styles.tape, and outputs.tape, which exercise the most useful testing paths.