本文へ移動
cccskills
無料GitHub で公開

e2e-testing

Use when adding, running, or debugging proctmux end-to-end integration tests, especially PTY/TUI behavior, terminal snapshots, unified/client modes, timing flakes, VT100 emulator limits, or files under tests/e2e and internal/testharness/e2e.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md10.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Skill: e2e-testing

E2E integration tests for proctmux. This skill covers writing, running, and debugging end-to-end tests that exercise the full proctmux binary through a PTY-based test harness.

Architecture overview

The e2e harness spawns the real proctmux binary on a pseudo-terminal (40 rows x 120 cols), feeds it keystrokes, and reads its screen output through a minimal VT100 terminal emulator. Tests assert on what the terminal screen looks like at any moment.

Test code
  -> e2e.WriteConfig()          create temp proctmux.yaml
  -> e2e.Start*Session()        build binary, spawn on PTY
  -> sess.SendKeys()            write key escape sequences to PTY
  -> sess.WaitForSnapshot()     poll the VT100 screen until predicate matches
  -> sess.Stop()                SIGINT -> wait 5s -> SIGKILL (automatic via t.Cleanup)

Layers

LayerFileRole
Buildinternal/testharness/e2e/builder.goCompiles proctmux binary once per test process (sync.Once). Cached across tests.
Configinternal/testharness/e2e/config.goWriteConfig(t, yaml) creates a temp dir with proctmux.yaml. Auto-cleaned via t.TempDir().
Environmentinternal/testharness/e2e/env.goMerges test env vars (PROCTMUX_NO_ALTSCREEN=1, TERM=xterm-256color) into the process environment.
Keysinternal/testharness/e2e/keys.goNamed constants for terminal escape sequences (KeyCtrlW, KeyEnter, KeyUp, etc.).
Sessioninternal/testharness/e2e/session.goPTY management, VT100 emulator, snapshot/wait APIs. The core of the harness.
Launchersinternal/testharness/e2e/start.goEntry points: StartUnifiedSession, StartUnifiedToggleSession, StartClientSession, StartPrimaryAndClient.
Teststests/e2e/e2e_test.goTest cases with //go:build integration tag.

Running e2e tests

# Run all e2e tests
make test-e2e

# Run all e2e tests (manual)
go test -tags=integration ./tests/e2e -v

# Run a single e2e test
go test -tags=integration ./tests/e2e -run '^TestUnifiedToggle_ProcessListAndScrollback$' -v

# Run with timeout (default 10m, but set shorter for faster feedback)
go test -tags=integration ./tests/e2e -v -timeout 60s

The integration build tag is required. Without it, go test ./tests/e2e compiles to nothing. Unit tests (go test ./...) intentionally exclude e2e tests.

Writing a new e2e test

Template

//go:build integration

package e2e_test

import (
    "strings"
    "testing"
    "time"

    e2e "github.com/nick/proctmux/internal/testharness/e2e"
)

func TestMyFeature(t *testing.T) {
    // 1. Create config
    cfgDir, cfgPath := e2e.WriteConfig(t, `
log_file: /tmp/proctmux-test-myfeature.log
procs:
  my-proc:
    shell: "echo MY_MARKER && sleep 30"
    autostart: true
`)

    // 2. Start a session (pick the right mode)
    sess := e2e.StartUnifiedToggleSession(t, cfgDir, cfgPath)

    // 3. Wait for initial render
    if err := sess.WaitForSnapshot(5*time.Second, func(snap string) bool {
        return strings.Contains(snap, "my-proc")
    }); err != nil {
        t.Fatalf("initial render failed: %v\nSnapshot:\n%s", err, sess.Snapshot())
    }

    // 4. Interact with the TUI
    if err := sess.SendKeys(e2e.KeyCtrlW); err != nil {
        t.Fatalf("failed to send keys: %v", err)
    }

    // 5. Assert on new state
    if err := sess.WaitForSnapshot(5*time.Second, func(snap string) bool {
        return strings.Contains(snap, "MY_MARKER")
    }); err != nil {
        t.Fatalf("after toggle: %v\nSnapshot:\n%s", err, sess.Snapshot())
    }
}

Available session launchers

FunctionCLI flagsUse when
StartUnifiedToggleSession(t, cfgDir, cfgPath)--unified-toggle -f <path>Testing the toggle-view mode (in-process primary + process list/scrollback toggle)
StartUnifiedSession(t, cfgDir, cfgPath)--unified -f <path>Testing the split-pane mode (child process + terminal emulator)
StartClientSession(t, cfgDir, cfgPath)--client -f <path>Testing the standalone client TUI
StartPrimaryAndClient(t, cfgDir, cfgPath)Starts primary headless, then clientTesting the primary/client architecture with real IPC

All launchers accept variadic extraEnv ...string for injecting additional environment variables (e.g., "MY_VAR=1").

Available key constants

Defined in internal/testharness/e2e/keys.go:

ConstantBytesTerminal meaning
KeyEnter\rEnter/Return
KeyUp\x1b[AArrow up
KeyDown\x1b[BArrow down
KeyRight\x1b[CArrow right
KeyLeft\x1b[DArrow left
KeyCtrlC\x03Ctrl+C
KeyCtrlW\x17Ctrl+W
KeyCtrlH\x08Ctrl+H (backspace)
KeyCtrlL\x0cCtrl+L (clear)

To send a key not listed here, use sess.SendString("\x1b...") with the raw escape sequence, or sess.SendRunes('a', 'b') for printable characters.

Session API reference

MethodReturnsUse
sess.SendKeys(keys ...KeySequence)errorSend named key constants
sess.SendString(s string)errorSend raw bytes to PTY
sess.SendRunes(runes ...rune)errorSend individual characters
sess.Snapshot()stringCurrent rendered screen (what a user sees right now)
sess.CleanOutput()stringFull transcript with ANSI stripped (everything ever output)
sess.RawOutput()[]byteFull raw PTY bytes (ANSI codes intact)
sess.WaitForSnapshot(timeout, predicate)errorPoll screen every 25ms until predicate is true
sess.WaitFor(substring, timeout)errorPoll clean transcript until substring found
sess.WaitForRaw(substring, timeout)errorPoll raw bytes until substring found
sess.Stop()errorGraceful shutdown (automatic via t.Cleanup)

Snapshot vs CleanOutput

  • Snapshot() = what is on the 40x120 screen right now. Use this for "does the UI currently show X?" assertions. This is what you want 90% of the time.
  • CleanOutput() = everything the process has ever output, with ANSI codes stripped. Use this for "did this string ever appear?" checks (e.g., verifying a process wrote to stdout at some point, even if the screen has since changed).

Config patterns

Process that produces output then stays alive

procs:
  my-proc:
    shell: "echo SOME_MARKER && sleep 30"
    autostart: true

The sleep 30 keeps the process alive for the duration of the test. The SOME_MARKER gives the test a unique string to search for in the scrollback or output. Use autostart: true when you want the process running immediately.

Multiple processes

procs:
  proc-a:
    shell: "sleep 60"
  proc-b:
    shell: "sleep 60"

Process with a log file (for debugging)

log_file: /tmp/proctmux-test-myfeature.log
procs:
  my-proc:
    shell: "echo hello && sleep 30"
    autostart: true

The log file captures proctmux's internal logging (process start/stop, IPC messages, errors). Invaluable for debugging test failures.

Debugging failing tests

1. Read the snapshot on failure

Always include the snapshot in failure messages:

if err := sess.WaitForSnapshot(5*time.Second, func(snap string) bool {
    return strings.Contains(snap, "expected-text")
}); err != nil {
    t.Fatalf("assertion failed: %v\nSnapshot:\n%s", err, sess.Snapshot())
}

The snapshot shows exactly what the VT100 emulator rendered -- this is what the test "sees."

2. Check the log file

Add log_file: /tmp/proctmux-test-debug.log to your config YAML, run the failing test, then read the log:

cat /tmp/proctmux-test-debug.log

The log shows process lifecycle events (start, stop, exit), IPC commands, and any errors.

3. Add temporary debug logging

If the test snapshot shows unexpected content, add log.Printf() calls in the code being tested. The log output goes to the log file (not the PTY), so it won't interfere with the VT100 emulator.

4. Check for \r (carriage return) issues

Raw PTY output uses \r\n line endings. If lipgloss is rendering content with Width() padding, the \r can cause the cursor to reset to column 0 and padding spaces overwrite visible text. The tailLines() function in toggle_model.go strips \r for this reason. If you see blank lines where content should be, check for \r in the data.

5. VT100 emulator limitations

The test harness VT100 emulator is intentionally minimal. It handles:

  • Cursor movement (up/down/left/right/absolute position)
  • Erase display and erase line
  • Carriage return, newline, tab
  • Printable character writing with line wrapping

It does NOT handle:

  • Scrolling (cursor clamps at bottom row, does not scroll the buffer)
  • Alternate screen buffer (disabled via PROCTMUX_NO_ALTSCREEN=1)
  • SGR sequences (colors/bold/italic are ignored)
  • Mouse events
  • Unicode combining characters

If the TUI uses features the emulator doesn't support, the snapshot may not match what a real terminal shows.

6. Timing issues

  • WaitForSnapshot polls every 25ms. Allow at least 1-2 seconds for UI operations.
  • Use 5-second timeouts for most assertions. Slow CI machines need headroom.
  • Process autostart is asynchronous. Always WaitForSnapshot for the process to appear before interacting.
  • After sending keys, always WaitForSnapshot for the result rather than immediately reading Snapshot(). The TUI may not have processed the key yet.

7. DSR (Device Status Report)

Bubble Tea may send DSR queries (ESC[6n) to detect terminal size. The harness auto-responds with a hardcoded position (ESC[24;80R). If you see unexpected behavior related to terminal size detection, this is why.

Conventions

  • Build tag: always //go:build integration on the first line.
  • Package: e2e_test (external test package).
  • Test names: TestModeName_WhatIsBeingTested (e.g., TestUnifiedToggle_ProcessListAndScrollback).
  • Config: use e2e.WriteConfig() with inline YAML. Do not use fixture files.
  • Cleanup: automatic via t.Cleanup(). Do not call sess.Stop() manually.
  • Timeouts: 5 seconds for WaitForSnapshot is the standard.
  • Markers: use unique uppercase strings like HELLO_TOGGLE_E2E in process output to avoid false matches.
  • Log files: use /tmp/proctmux-test-<testname>.log for debugging. These are not cleaned up automatically.
  • Assertions: always include sess.Snapshot() in failure messages.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

agent-tui

無料

Automate terminal UI (TUI) apps with agent-tui for testing, inspection, demos, and scripted interactions. Use when automating CLI/TUI flows, regression testing terminal apps, verifying interactive behavior, or extracting structured text from terminal UIs. Also use when asked what agent-tui is, how it works, or to demo it. Do not use for web browsers, GUI apps, or non-terminal interfaces.

日本語の概要は準備中です。原文の説明を表示しています。

napisani/proctmux112026年10月8日 更新

Use this skill whenever the user wants to create, edit, explain, validate, or troubleshoot a proctmux.yaml/procmux.yaml file, add or change proctmux processes, configure lifecycle behavior such as stop signals/on_kill/autostart, customize unified-mode layout/keybindings/style, or enable Makefile/package.json process discovery. Use it even when the user says this casually, such as "add my dev server to proctmux", "fix this proctmux config", "write a config for these services", or "what YAML option controls focus".

日本語の概要は準備中です。原文の説明を表示しています。

napisani/proctmux112026年10月8日 更新

This skill should be used when designing terminal user interfaces, creating TUI layouts, choosing TUI color schemes, implementing keyboard navigation, building terminal dashboards, or working with any TUI framework. Activates on mentions of TUI design, terminal UI, Ratatui layout, Ink components, Textual widgets, Bubbletea views, terminal color palette, keybinding design, panel layout, split panes, terminal dashboard, box-drawing characters, sparklines, progress bars, modal dialogs, focus management, or terminal accessibility.

日本語の概要は準備中です。原文の説明を表示しています。

napisani/proctmux112026年10月8日 更新

Harness-first workflow for evaluating and improving terminal UIs through real interaction. Use this whenever the user wants to review a TUI experience, compare before/after behavior, identify UX friction, validate navigation or filtering flows, inspect visual stability, or turn observed interaction problems into concrete follow-up tests and improvement recommendations. Prefer this skill any time a TUI should be exercised through a reproducible PTY/session harness instead of only reading code.

日本語の概要は準備中です。原文の説明を表示しています。

napisani/proctmux112026年10月8日 更新

Use when reading or writing Zig files (.zig, build.zig, build.zig.zon).

日本語の概要は準備中です。原文の説明を表示しています。

napisani/proctmux112026年10月8日 更新

napisani のスキルをすべて見る

このスキルの問題を報告する