Test CLI applications interactively using tmux sessions. Use when testing TUI apps (ratatui dashboard, interactive prompts), verifying CLI command output, testing keyboard navigation, or validating terminal rendering. Launches commands in tmux, waits on conditions (never sleeps), captures frames, sends keypresses, and asserts on output. Specifically designed for gpu-cli but works with any CLI. Use this skill when asked to test, verify, or QA any terminal-based UI or CLI command flow.
Recursos
1Instal·la
npx skillscat add gpu-cli/skills/tmux-cli-test Instal·la des del registre de SkillsCat.
tmux CLI Testing
Test CLI applications by running them in tmux sessions, waiting for conditions, sending input, and asserting on output. Never sleep - always wait on a condition.
Helpers
Source the helper script for all primitives:
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.shQuick Reference
| Function | Purpose |
|---|---|
tmux_start <session> <cmd> |
Launch command in detached tmux session |
tmux_kill <session> |
Kill session |
tmux_is_alive <session> |
Check if session is running |
tmux_capture <session> |
Get pane text |
tmux_capture_ansi <session> |
Get pane text with ANSI codes |
tmux_wait_for <session> <text> [timeout] |
Poll until text appears |
tmux_wait_for_regex <session> <pattern> [timeout] |
Poll until regex matches |
tmux_wait_gone <session> <text> [timeout] |
Poll until text disappears |
tmux_wait_exit <session> [timeout] |
Poll until process exits |
tmux_send <session> <keys...> |
Send keys (tmux key names) |
tmux_type <session> <text> |
Type literal text |
tmux_assert_contains <session> <text> [label] |
Assert text present |
tmux_assert_not_contains <session> <text> [label] |
Assert text absent |
tmux_assert_matches <session> <pattern> [label] |
Assert regex matches |
tmux_send_and_wait <session> <keys> <text> [timeout] |
Send then wait |
tmux_test <session> <cmd> <ready_text> <fn> |
Full lifecycle test |
Configuration
Override defaults before calling functions:
TMUX_TEST_POLL_INTERVAL=0.3 # seconds between polls
TMUX_TEST_TIMEOUT=30 # max wait seconds
TMUX_TEST_WIDTH=120 # terminal columns
TMUX_TEST_HEIGHT=30 # terminal rowsWorkflow
Every test follows this pattern:
1. Start session with command
2. Wait for ready signal (text appearing)
3. Interact (send keys, wait for responses)
4. Assert on captured output
5. Kill sessionMinimal Example
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
tmux_start "test-help" "./crates/target/debug/gpu --help"
tmux_wait_for "test-help" "Usage:" 10
tmux_assert_contains "test-help" "run"
tmux_assert_contains "test-help" "dashboard"
tmux_kill "test-help"Using tmux_test for Lifecycle
test_help_page() {
local s="$1"
tmux_assert_contains "$s" "run" "help shows run command"
tmux_assert_contains "$s" "dashboard" "help shows dashboard command"
tmux_assert_not_contains "$s" "ERROR" "no errors in help"
}
tmux_test "help-test" "./crates/target/debug/gpu --help" "Usage:" test_help_pageGPU CLI Patterns
Testing the Dashboard (TUI)
source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
TMUX_TEST_WIDTH=140
TMUX_TEST_HEIGHT=35
GPU_BIN="./crates/target/debug/gpu"
tmux_start "dash" "$GPU_BIN dashboard"
tmux_wait_for "dash" "Pods" 15
# Verify panels rendered
tmux_assert_contains "dash" "Pods" "pods panel visible"
tmux_assert_contains "dash" "Jobs" "jobs panel visible"
# Navigate with keys
tmux_send "dash" j
tmux_send "dash" j
tmux_wait_for "dash" "▶" 5 # selection cursor
# Switch panel
tmux_send "dash" Tab
tmux_wait_for_regex "dash" "Jobs.*selected" 5
# Open help overlay
tmux_send "dash" '?'
tmux_wait_for "dash" "Help" 5
tmux_assert_contains "dash" "Keybindings" "help shows keybindings"
# Close help
tmux_send "dash" Escape
tmux_wait_gone "dash" "Keybindings" 5
# Quit
tmux_send "dash" q
tmux_wait_exit "dash" 5Testing Interactive Prompts
tmux_start "init" "$GPU_BIN init"
tmux_wait_for "init" "project" 10
# Type project name
tmux_type "init" "my-test-project"
tmux_send "init" Enter
# Wait for next prompt
tmux_wait_for "init" "GPU" 10
# Select GPU with arrow keys
tmux_send "init" j j Enter
tmux_wait_for "init" "provider" 10
tmux_kill "init"Testing Command Output
tmux_start "status" "$GPU_BIN status"
tmux_wait_for_regex "status" "(No active|Pod)" 10
FRAME=$(tmux_capture "status")
if echo "$FRAME" | grep -q "No active"; then
echo "No pods running - expected for cold test"
elif echo "$FRAME" | grep -q "Pod"; then
tmux_assert_matches "status" "Pod.*READY\|ACTIVE" "pod in valid state"
fi
tmux_wait_exit "status" 15Testing Error Handling
tmux_start "bad-cmd" "$GPU_BIN run --nonexistent-flag"
tmux_wait_for_regex "bad-cmd" "error|Error|unknown" 10
tmux_assert_not_contains "bad-cmd" "panic" "no panics on bad input"
tmux_wait_exit "bad-cmd" 5Testing Long-Running Commands with Ctrl-C
tmux_start "run-job" "$GPU_BIN run python -c 'import time; time.sleep(3600)'"
tmux_wait_for "run-job" "Running" 30
# Interrupt
tmux_send "run-job" C-c
tmux_wait_for_regex "run-job" "cancel|interrupt|stopped" 10
tmux_wait_exit "run-job" 10Docker Containers
For testing CLI apps running inside Docker containers (e.g., FTR testing), source the Docker helpers:
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"
# TMUX_DOCKER_SESSION="test" # default, override if neededSet TMUX_DOCKER_CONTAINER once, then all docker_tmux_* calls route through docker exec to that container. Same API as the local helpers but without session/container args:
| Function | Purpose |
|---|---|
docker_tmux_send <keys...> |
Send tmux keys |
docker_tmux_type <text> |
Type literal text |
docker_tmux_capture |
Get pane text |
docker_tmux_capture_ansi |
Get pane text with ANSI codes |
docker_tmux_wait_for <text> [timeout] |
Poll until text appears |
docker_tmux_wait_regex <pattern> [timeout] |
Poll until regex matches |
docker_tmux_wait_gone <text> [timeout] |
Poll until text disappears |
docker_tmux_assert_contains <text> [label] |
Assert text present |
docker_tmux_assert_not_contains <text> [label] |
Assert text absent |
docker_tmux_assert_matches <pattern> [label] |
Assert regex matches |
docker_tmux_send_and_wait <keys> <text> [timeout] |
Send then wait |
Docker Example
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"
docker_tmux_send "gpu dashboard" Enter
docker_tmux_wait_for "Pods" 15
docker_tmux_capture
docker_tmux_send j
docker_tmux_send "?"
docker_tmux_wait_for "Help" 5
docker_tmux_assert_contains "Keybindings" "help shows keybindings"
docker_tmux_send Escape
docker_tmux_wait_gone "Help" 5
docker_tmux_send qWriting Tests Inline
When no helper script is needed (quick one-off checks), use tmux commands directly:
# Start
tmux new-session -d -s test -x 120 -y 30 "./crates/target/debug/gpu dashboard"
# Poll for ready (inline wait loop)
for i in $(seq 1 100); do
tmux capture-pane -t test -p | grep -q "Pods" && break
sleep 0.3
done
# Assert
FRAME=$(tmux capture-pane -t test -p)
echo "$FRAME" | grep -q "Pods" && echo "PASS" || echo "FAIL"
# Cleanup
tmux send-keys -t test q
tmux kill-session -t test 2>/dev/nullAnti-Patterns
| Bad | Good | Why |
|---|---|---|
sleep 3 |
tmux_wait_for s "Ready" |
Sleeps are flaky and slow |
sleep 5 && tmux capture-pane |
tmux_wait_for s "expected" && tmux_capture s |
Condition, not duration |
| Hardcoded binary path | GPU_BIN=./crates/target/debug/gpu |
Easy to switch debug/release |
| No cleanup on failure | tmux_test or explicit tmux_kill |
Leftover sessions break next run |
grep -q without timeout loop |
tmux_wait_for |
Text may not be rendered yet |
Checking .len() of TUI text |
Check display content only | Unicode width != byte length |
Debugging Failed Tests
When a test fails, capture the frame for diagnosis:
# Capture what's actually on screen
tmux_capture "session-name"
# Save to file for comparison
tmux_capture_to_file "session-name" "/tmp/failed-frame.txt"
# Capture with colors to verify styling
tmux_capture_ansi "session-name"Session Naming Convention
Use descriptive, prefixed session names to avoid collisions:
gpu-test-dashboard
gpu-test-init
gpu-test-run-basic
gpu-test-status
gpu-test-error-handling