| name | tmux-tui-testing |
| description | Test TUI (Text User Interface) applications using tmux. Use this skill when you need to automate testing of terminal-based applications by sending keystrokes and capturing pane output. |
Automate testing of TUI applications by controlling them through tmux sessions. This enables programmatic interaction with terminal apps that render visual interfaces, allowing you to send input, wait for rendering, and assert on captured output.
<essential_principles>
Why tmux for TUI Testing
TUI applications render to a terminal rather than producing structured output. tmux provides:
- Detached sessions for headless testing
- Precise window/pane sizing for consistent layouts
- Keystroke injection via
send-keys
- Pane content capture with
capture-pane
- ANSI sequence preservation for color/style assertions
Critical Timing Considerations
TUI apps need time to:
- Start up and initialize
- Render after receiving input
- Complete async operations
Always add appropriate waits between actions. Start with 0.5-1s delays and adjust based on app behavior.
</essential_principles>
<core_commands>
Session Management
tmux new-session -d -s "$SESSION_NAME"
tmux new-window -t "$SESSION_NAME" -n "$WINDOW_NAME"
tmux resize-window -t "$SESSION_NAME:$WINDOW_NAME" -x $WIDTH -y $HEIGHT
tmux kill-session -t "$SESSION_NAME"
Sending Input
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" "your-command" Enter
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" Up
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" Down
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" Tab
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" Escape
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" C-c
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" C-d
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" Space
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" BSpace
tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" -l "literal text"
Capturing Output
tmux capture-pane -t "$SESSION_NAME:$WINDOW_NAME" -p
tmux capture-pane -t "$SESSION_NAME:$WINDOW_NAME" -p -e
tmux capture-pane -t "$SESSION_NAME:$WINDOW_NAME" -p -S -1000
</core_commands>
## Testing Workflow
- Setup: Create detached tmux session with fixed dimensions
- Launch: Start the TUI application in the session
- Wait: Allow time for app to initialize and render
- Interact: Send keystrokes to navigate/interact
- Wait: Allow time for UI to update after each interaction
- Capture: Get pane contents
- Assert: Check captured output for expected content
- Cleanup: Kill the tmux session
Example Test Sequence
SESSION="tui-test-$$"
WIDTH=80
HEIGHT=24
tmux new-session -d -s "$SESSION"
tmux resize-window -t "$SESSION" -x $WIDTH -y $HEIGHT
tmux send-keys -t "$SESSION" "./my-tui-app" Enter
sleep 1
tmux send-keys -t "$SESSION" Down Down Enter
sleep 0.5
OUTPUT=$(tmux capture-pane -t "$SESSION" -p)
if echo "$OUTPUT" | grep -q "Expected Text"; then
echo "PASS: Found expected content"
else
echo "FAIL: Expected content not found"
echo "Captured output:"
echo "$OUTPUT"
fi
tmux kill-session -t "$SESSION"
<common_patterns>
Pattern: Wait for Specific Text
wait_for_text() {
local session="$1"
local text="$2"
local timeout="${3:-10}"
local elapsed=0
while [ $elapsed -lt $timeout ]; do
if tmux capture-pane -t "$session" -p | grep -q "$text"; then
return 0
fi
sleep 0.2
elapsed=$((elapsed + 1))
done
return 1
}
if wait_for_text "$SESSION" "Ready"; then
echo "App is ready"
fi
Pattern: Snapshot Comparison
tmux capture-pane -t "$SESSION" -p > expected.txt
tmux capture-pane -t "$SESSION" -p > actual.txt
diff expected.txt actual.txt
Pattern: Test Multiple Scenarios
run_test() {
local name="$1"
local keys="$2"
local expected="$3"
tmux send-keys -t "$SESSION" C-c
sleep 0.3
tmux send-keys -t "$SESSION" "./app" Enter
sleep 1
tmux send-keys -t "$SESSION" $keys
sleep 0.5
if tmux capture-pane -t "$SESSION" -p | grep -q "$expected"; then
echo "PASS: $name"
else
echo "FAIL: $name"
fi
}
run_test "Navigate down" "Down Down" "Item 3"
run_test "Select item" "Enter" "Selected:"
</common_patterns>
<advanced_patterns>
Mouse Events
tmux set-option -t "$SESSION" mouse on
send_mouse_click() {
local session="$1"
local x="$2"
local y="$3"
printf '\e[<0;%d;%dM' "$x" "$y" | tmux load-buffer -
tmux paste-buffer -t "$session"
printf '\e[<0;%d;%dm' "$x" "$y" | tmux load-buffer -
tmux paste-buffer -t "$session"
}
send_mouse_click "$SESSION" 10 5
tmux send-keys -t "$SESSION" -M MouseDown1Pane 10 5
tmux send-keys -t "$SESSION" -M MouseUp1Pane 10 5
Clipboard Operations
echo "text to paste" | tmux load-buffer -
tmux paste-buffer -t "$SESSION"
tmux set-buffer "content to paste"
tmux capture-pane -t "$SESSION" -b capture-buffer
tmux show-buffer -b capture-buffer
tmux send-keys -t "$SESSION" "echo 'copied' | pbcopy" Enter
Multiple Panes
tmux split-window -t "$SESSION" -v
tmux split-window -t "$SESSION" -h
tmux send-keys -t "$SESSION:0.0" "left pane" Enter
tmux send-keys -t "$SESSION:0.1" "right pane" Enter
tmux resize-pane -t "$SESSION:0.0" -x 40
tmux resize-pane -t "$SESSION:0.1" -y 10
tmux capture-pane -t "$SESSION:0.1" -p
tmux select-pane -t "$SESSION:0.1"
tmux list-panes -t "$SESSION" -F "#{pane_index}: #{pane_width}x#{pane_height}"
Pattern: Testing Split-Pane Apps
setup_split_test() {
tmux new-session -d -s "$SESSION"
tmux resize-window -t "$SESSION" -x 120 -y 40
tmux send-keys -t "$SESSION" "./split-app" Enter
sleep 1
}
verify_pane_content() {
local pane="$1"
local expected="$2"
local content
content=$(tmux capture-pane -t "$SESSION:0.$pane" -p)
if echo "$content" | grep -q "$expected"; then
echo "PASS: Pane $pane contains '$expected'"
return 0
else
echo "FAIL: Pane $pane missing '$expected'"
return 1
fi
}
verify_pane_content 0 "file1.txt"
verify_pane_content 1 "Preview:"
Pattern: Scrolling and Viewport
tmux send-keys -t "$SESSION" PageUp
tmux send-keys -t "$SESSION" PageDown
tmux copy-mode -t "$SESSION"
tmux send-keys -t "$SESSION" -X scroll-up
tmux send-keys -t "$SESSION" -X scroll-down
tmux send-keys -t "$SESSION" q
tmux capture-pane -t "$SESSION" -p -S -100 -E -1
tmux capture-pane -t "$SESSION" -p -S - -E -
Pattern: Environment and Shell Setup
tmux send-keys -t "$SESSION" "export TERM=xterm-256color" Enter
tmux send-keys -t "$SESSION" "export NO_COLOR=1" Enter
tmux send-keys -t "$SESSION" "export LC_ALL=en_US.UTF-8" Enter
tmux send-keys -t "$SESSION" "env -i bash --norc --noprofile" Enter
tmux send-keys -t "$SESSION" "export TERM=xterm-256color" Enter
tmux send-keys -t "$SESSION" "cd /path/to/project" Enter
Pattern: Waiting for App Exit
wait_for_exit() {
local session="$1"
local timeout="${2:-30}"
local elapsed=0
while [ $elapsed -lt $timeout ]; do
if tmux capture-pane -t "$session" -p | grep -qE '^\$|^>|^%'; then
return 0
fi
sleep 0.5
elapsed=$((elapsed + 1))
done
return 1
}
tmux send-keys -t "$SESSION" "./app --batch-mode" Enter
if wait_for_exit "$SESSION" 60; then
echo "App completed"
else
echo "App timed out"
tmux send-keys -t "$SESSION" C-c
fi
Pattern: Capture with Line Numbers
capture_with_lines() {
local session="$1"
tmux capture-pane -t "$session" -p | nl -ba
}
assert_line() {
local session="$1"
local line_num="$2"
local expected="$3"
local actual
actual=$(tmux capture-pane -t "$session" -p | sed -n "${line_num}p")
if [ "$actual" = "$expected" ]; then
echo "PASS: Line $line_num matches"
return 0
else
echo "FAIL: Line $line_num"
echo " Expected: '$expected'"
echo " Actual: '$actual'"
return 1
fi
}
assert_line "$SESSION" 3 "=== My App v1.0 ==="
</advanced_patterns>
## Common Issues
Problem: Captured output is empty or incomplete
- Cause: App hasn't rendered yet
- Fix: Increase sleep duration after send-keys
Problem: Layout looks wrong in captures
- Cause: Window dimensions don't match app expectations
- Fix: Use
resize-window -x WIDTH -y HEIGHT before launching app
Problem: Special characters not sent correctly
- Cause: Using wrong key names
- Fix: Check
man tmux for correct key names (e.g., BSpace not Backspace)
Problem: ANSI codes making assertions difficult
- Cause: Using
-e flag when not needed
- Fix: Omit
-e for plain text, or strip ANSI with sed 's/\x1b\[[0-9;]*m//g'
Problem: Tests flaky/inconsistent
- Cause: Race conditions with async rendering
- Fix: Use
wait_for_text pattern instead of fixed sleeps
<success_criteria>
A successful TUI test:
- Runs in a detached tmux session (no display required)
- Uses fixed dimensions for reproducible layouts
- Waits appropriately for rendering
- Captures pane content and asserts on expected text
- Cleans up the session after completion
- Handles both success and failure cases gracefully
</success_criteria>