Back to list
fcoury

termwright-tui-testing

by fcoury

Playwright-like automation framework for terminal TUI applications

1🍴 0📅 Jan 22, 2026

SKILL.md


name: termwright-tui-testing description: Automates end-to-end testing and debugging of terminal UIs using Termwright. Use when testing TUI apps (ratatui, crossterm, ncurses), automating terminal interactions, capturing screenshots, or debugging terminal-based interfaces. Handles step files, daemon control, and artifact analysis. allowed-tools:

  • Bash
  • Read
  • Write
  • Edit
  • Glob
  • Grep

Termwright TUI Testing

Automate end-to-end testing of terminal user interfaces with Termwright's daemon-based architecture.

Quick Start

Run a Step File Test

# Execute YAML steps file with trace output
termwright run-steps --trace test.yaml

# Connect to existing daemon instead of spawning
termwright run-steps --connect /tmp/termwright-123.sock steps.yaml

Interactive Daemon Session

# Start daemon in background
SOCK=$(termwright daemon --background -- vim test.txt)

# Send commands via exec
termwright exec --socket "$SOCK" --method handshake
termwright exec --socket "$SOCK" --method wait_for_text --params '{"text":"VIM","timeout_ms":5000}'
termwright exec --socket "$SOCK" --method press --params '{"key":"i"}'
termwright exec --socket "$SOCK" --method type --params '{"text":"Hello"}'
termwright exec --socket "$SOCK" --method screen --params '{"format":"text"}'
termwright exec --socket "$SOCK" --method close

Quick Screenshot

termwright screenshot --wait-for "Ready" -o app.png -- ./my-tui-app

Core Workflows

1. Writing Step Files

Create YAML files for reproducible E2E tests:

session:
  command: vim
  args: [test.txt]
  cols: 120
  rows: 40

steps:
  - waitForText: {text: "VIM", timeoutMs: 5000}
  - press: {key: i}
  - type: {text: "Hello, world!"}
  - press: {key: Escape}
  - expectText: {text: "Hello, world!"}
  - screenshot: {name: vim-content}
  - type: {text: ":q!"}
  - press: {key: Enter}

artifacts:
  mode: always
  dir: ./test-artifacts

Available Steps:

StepPurposeExample
waitForTextWait for text to appear{text: "Ready", timeoutMs: 5000}
waitForPatternWait for regex match{pattern: "error|warning"}
waitForIdleWait for screen stability{idleMs: 500, timeoutMs: 5000}
pressPress a key{key: "Enter"}
typeType text{text: "hello"}
hotkeyModifier combo{ctrl: true, ch: "c"}
expectTextAssert text exists{text: "Success"}
expectPatternAssert pattern matches{pattern: "completed"}
screenshotCapture PNG{name: "result"}

Note: screenshot steps require artifacts mode onFailure or always (not off).

For full schema details, see step-schema.md.

2. Daemon Protocol

The daemon uses JSON-over-Unix-socket communication:

Request format:

{"id": 1, "method": "screen", "params": {"format": "text"}}

Response format:

{"id": 1, "result": "screen content...", "error": null}

Key Methods:

  • handshake - Initialize connection, get version info
  • screen - Get screen content (text/json/json_compact)
  • screenshot - Capture PNG (returns base64)
  • type / press / hotkey - Input simulation
  • wait_for_text / wait_for_pattern / wait_for_idle - Wait conditions
  • mouse_click / mouse_move - Mouse events
  • status - Check if process exited
  • close - Terminate daemon

For complete protocol reference, see protocol-reference.md.

3. Analyzing Failures

When tests fail and artifacts are enabled (onFailure or always), examine the output directory:

# Directory structure after run
./termwright-artifacts/20250119-143022/
├── failure-001-screen.txt    # Screen text at failure
├── failure-001-screen.json   # Full screen state with colors
├── vim-content.png           # Screenshot step output (if used)
└── trace.json                # Step execution trace (only with --trace)

Interpreting trace.json:

[
  {"step": 1, "action": "waitForText", "duration_ms": 245, "before_hash": 123, "after_hash": 456, "error": null},
  {"step": 2, "action": "press", "duration_ms": 15, "before_hash": 456, "after_hash": 789, "error": null},
  {"step": 3, "action": "expectText", "duration_ms": 3001, "before_hash": 789, "after_hash": 789, "error": "timeout waiting for text"}
]
  • before_hash / after_hash - Detect if step changed screen
  • duration_ms - Identify slow operations
  • error - Null on success, message on failure

4. Parallel Testing with Hub

Run multiple TUI sessions simultaneously:

# Start 5 parallel sessions
termwright hub start --count 5 --output sessions.json -- ./my-app

# sessions.json contains socket paths for each
# Run tests against each socket in parallel

# Clean up all sessions
termwright hub stop --input sessions.json

CLI Reference

CommandPurpose
termwright runOne-shot capture (text or JSON)
termwright screenshotOne-shot PNG screenshot
termwright run-stepsExecute step file
termwright execSingle daemon command
termwright daemonStart long-lived daemon
termwright hub start/stopManage session pool
termwright fontsList available fonts

For all options, see cli-reference.md.

Shell Scripting Pattern

For custom test scripts without YAML:

#!/bin/bash
set -euo pipefail

# Start daemon
SOCK=$(termwright daemon --background -- htop)
trap "termwright exec --socket '$SOCK' --method close 2>/dev/null || true" EXIT

# Wait for socket
while ! [ -S "$SOCK" ]; do sleep 0.1; done

# Helper
tw() {
    termwright exec --socket "$SOCK" --method "$1" --params "${2:-null}"
}

# Test sequence
tw handshake
tw wait_for_idle '{"idle_ms":1000,"timeout_ms":10000}'

# Verify content
SCREEN=$(tw screen '{"format":"text"}' | jq -r '.result')
if echo "$SCREEN" | grep -q "CPU"; then
    echo "PASS: htop loaded"
else
    echo "FAIL: CPU info not found"
    exit 1
fi

# Screenshot
tw screenshot | jq -r '.result.png_base64' | base64 -d > htop.png
echo "Screenshot saved to htop.png"

Valid Key Names

For press and step files:

  • Navigation: Up, Down, Left, Right, Home, End, PageUp, PageDown
  • Special: Enter, Tab, Escape, Backspace, Delete, Insert
  • Function: F1 through F12
  • Characters: Any single character (a, A, 1, @, etc.)

Coordinate System

All coordinates are 0-indexed:

  • Row 0 = top line
  • Column 0 = leftmost character
  • Format: (row, col) for mouse operations

Debugging Tips

  1. Start with --trace - Always use trace output when developing tests
  2. Use wait_for_idle - Ensure screen has stabilized before assertions
  3. Check screen.txt - Compare expected vs actual content
  4. Inspect screen.json - Verify cursor position and colors
  5. Lower timeouts during dev - Faster feedback on failures
  6. Use artifacts: always - Capture state at every step while debugging

Common Patterns

Wait for App Startup

steps:
  - waitForIdle: {idleMs: 1000, timeoutMs: 10000}
  - waitForText: {text: "Ready", timeoutMs: 5000}
steps:
  - press: {key: "Escape"}           # Close any open dialog
  - waitForIdle: {idleMs: 200}
  - hotkey: {ctrl: true, ch: "s"}    # Save
  - waitForText: {text: "Saved"}
steps:
  - press: {key: "Down"}
  - press: {key: "Down"}
  - press: {key: "Enter"}
  - expectText: {text: "Selected item"}

Form Input

steps:
  - waitForText: {text: "Username:"}
  - type: {text: "admin"}
  - press: {key: "Tab"}
  - type: {text: "password123"}
  - press: {key: "Enter"}

Reference Files

Troubleshooting

IssueSolution
Socket not foundWait for socket file: while ! [ -S "$SOCK" ]; do sleep 0.1; done
Timeout on waitIncrease timeoutMs, check if text actually appears
Wrong screen contentUse wait_for_idle before assertions
Mouse not workingNot all TUIs support mouse; use keyboard navigation
Colors not capturedUse screen with format: json for full cell data

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon