btest

package
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package btest provides a virtual terminal screen and assertion helpers for testing Bubble Tea TUI applications. It uses go-te to emulate a terminal and parse ANSI escape sequences, enabling screen-based assertions on rendered View() output.

Package btest session record/replay.

A .tuisess file captures a deterministic sequence of input events against a tea.Model and the resulting screen after each step. Consumer apps can commit these files under `testdata/sessions/` as end-to-end regression fixtures; running Replay against the same model should yield identical screens at every step.

Format (JSON):

{
    "version": 1,
    "cols": 80,
    "lines": 24,
    "steps": [
        {"kind": "key",    "key":   "down"},
        {"kind": "screen", "screen": "..."},
        {"kind": "type",   "text":  "abc"},
        {"kind": "screen", "screen": "..."}
    ]
}

The "screen" steps are the expected screen strings captured right after the previous input step. Replay compares the live screen to the expected screen and fails the test on any divergence.

Index

Constants

View Source
const SessionFormatVersion = 2

SessionFormatVersion is the on-disk version marker. Bump whenever the step schema changes in a backwards-incompatible way.

History:

v1 – initial format (blit ≤ v0.7.1)
v2 – adds Name, RecordedAt, Command metadata fields (blit v0.11+)

Variables

View Source
var SnapshotDir = filepath.Join("testdata", "__snapshots__")

SnapshotDir is the subdirectory (relative to the test file's package) where snapshot files are written. Kept exported so users can override it for a specific package if they dislike the default.

Functions

func AssertBgAt

func AssertBgAt(t testing.TB, s *Screen, row, col int, color string)

AssertBgAt fails if the background color at (row, col) doesn't match.

func AssertBoldAt

func AssertBoldAt(t testing.TB, s *Screen, row, col int)

AssertBoldAt fails the test if the cell at (row, col) is not bold.

func AssertColumnContains

func AssertColumnContains(t testing.TB, s *Screen, col, startRow, endRow int, text string)

AssertColumnContains asserts any row in column range [startRow,endRow] contains text.

func AssertColumnCount

func AssertColumnCount(t testing.TB, s *Screen, col, startRow, endRow int, text string, want int)

AssertColumnCount asserts how many non-empty rows contain text in the given column range.

func AssertContains

func AssertContains(t testing.TB, s *Screen, text string)

AssertContains fails the test if the screen doesn't contain the text.

func AssertContainsAt

func AssertContainsAt(t testing.TB, s *Screen, row, col int, text string)

AssertContainsAt fails the test if text doesn't appear at (row, col).

func AssertContainsCount

func AssertContainsCount(t testing.TB, s *Screen, text string, n int)

AssertContainsCount fails if text doesn't appear exactly n times on screen.

func AssertCursorAt

func AssertCursorAt(t testing.TB, s *Screen, row, col int)

AssertCursorAt fails the test if the cursor isn't at (row, col).

func AssertCursorRowContains

func AssertCursorRowContains(t testing.TB, s *Screen, text string)

AssertCursorRowContains asserts the row under the cursor contains text.

func AssertEmpty

func AssertEmpty(t testing.TB, s *Screen)

AssertEmpty fails if the screen has any visible content.

func AssertFgAt

func AssertFgAt(t testing.TB, s *Screen, row, col int, color string)

AssertFgAt fails if the foreground color at (row, col) doesn't match.

func AssertGolden

func AssertGolden(t testing.TB, s *Screen, name string)

AssertGolden compares the screen content against a golden file. If the file doesn't exist or -update flag is set, it creates/updates the golden file. Golden files are stored in testdata/ relative to the test file.

func AssertItalicAt

func AssertItalicAt(t testing.TB, s *Screen, row, col int)

AssertItalicAt fails if the cell at (row, col) is not italic.

func AssertKeybind

func AssertKeybind(t testing.TB, s *Screen, key, description string)

AssertKeybind asserts the screen's footer/help line contains the given key label. It searches the whole screen for a "key description" pattern anywhere.

func AssertMatches

func AssertMatches(t testing.TB, s *Screen, pattern string)

AssertMatches fails if the screen content doesn't match the regular expression.

func AssertNoANSI

func AssertNoANSI(t testing.TB, s *Screen)

AssertNoANSI asserts the screen's rendered text contains no raw ANSI escape sequences. Raw ANSI in the decoded virtual screen indicates a broken writer that emitted literal bytes instead of styled runs.

func AssertNotContains

func AssertNotContains(t testing.TB, s *Screen, text string)

AssertNotContains fails the test if the screen contains the text.

func AssertNotEmpty

func AssertNotEmpty(t testing.TB, s *Screen)

AssertNotEmpty fails if the screen has no visible content.

func AssertRegionBg

func AssertRegionBg(t testing.TB, s *Screen, row, col, width, height int, color string)

AssertRegionBg asserts every non-space cell in a region has the given bg color.

func AssertRegionBold

func AssertRegionBold(t testing.TB, s *Screen, row, col, width, height int)

AssertRegionBold asserts every non-space cell in a region is bold.

func AssertRegionContains

func AssertRegionContains(t testing.TB, s *Screen, row, col, width, height int, text string)

AssertRegionContains fails if the region doesn't contain the text.

func AssertRegionFg

func AssertRegionFg(t testing.TB, s *Screen, row, col, width, height int, color string)

AssertRegionFg asserts every non-space cell in a region has the given fg color.

func AssertRegionNotContains

func AssertRegionNotContains(t testing.TB, s *Screen, row, col, width, height int, text string)

AssertRegionNotContains fails if the region contains the text.

func AssertReverseAt

func AssertReverseAt(t testing.TB, s *Screen, row, col int)

AssertReverseAt fails if the cell at (row, col) is not reversed.

func AssertRowContains

func AssertRowContains(t testing.TB, s *Screen, row int, text string)

AssertRowContains fails if the given row doesn't contain the text.

func AssertRowCount

func AssertRowCount(t testing.TB, s *Screen, want int)

AssertRowCount fails if the number of non-empty rows doesn't match.

func AssertRowEquals

func AssertRowEquals(t testing.TB, s *Screen, row int, text string)

AssertRowEquals fails if the given row doesn't exactly equal text (trimmed).

func AssertRowMatches

func AssertRowMatches(t testing.TB, s *Screen, row int, pattern string)

AssertRowMatches fails if the given row doesn't match the regular expression.

func AssertRowNotContains

func AssertRowNotContains(t testing.TB, s *Screen, row int, text string)

AssertRowNotContains fails if the given row contains the text.

func AssertScreenEquals

func AssertScreenEquals(t testing.TB, s *Screen, expected string)

AssertScreenEquals fails if the full screen text doesn't exactly match expected. On failure it also persists a FailureCapture so `blit diff` can replay it.

func AssertScreenMatches

func AssertScreenMatches(t testing.TB, s *Screen, pattern string)

AssertScreenMatches is a regexp variant of AssertScreenEquals for fuzzier matches.

func AssertScreensEqual

func AssertScreensEqual(t testing.TB, a, b *Screen)

AssertScreensEqual fails if two screens don't have identical text content. On failure it also persists a FailureCapture so `blit diff` can replay it.

func AssertScreensNotEqual

func AssertScreensNotEqual(t testing.TB, a, b *Screen)

AssertScreensNotEqual fails if two screens have identical text content.

func AssertSnapshot

func AssertSnapshot(t testing.TB, scr *Screen, name string)

AssertSnapshot compares scr's current screen contents against a previously stored snapshot named <name>.snap under SnapshotDir. On first run (or when -btest.update is passed), the snapshot is (re)generated. Line endings are normalized to \n so snapshots round-trip across OSes.

Example:

scr := tm.Screen()
btest.AssertSnapshot(t, scr, "login-form")

func AssertStyleAt

func AssertStyleAt(t testing.TB, s *Screen, row, col int, style CellStyle)

AssertStyleAt fails the test if the cell at (row, col) doesn't match the style.

func AssertTextAt

func AssertTextAt(t testing.TB, s *Screen, row, col int, text string)

AssertTextAt fails if the text at (row, col) through (row, col+len) doesn't match.

func AssertUnderlineAt

func AssertUnderlineAt(t testing.TB, s *Screen, row, col int)

AssertUnderlineAt fails if the cell at (row, col) is not underlined.

func HTMLReporter

func HTMLReporter(t testing.TB, report *Report, path string)

HTMLReporter registers a t.Cleanup hook that writes the report as HTML to path when the test finishes.

func JUnitReporter

func JUnitReporter(t testing.TB, report *Report, path string)

JUnitReporter registers a t.Cleanup hook that writes the report as JUnit XML to path when the test (or parent TestMain) finishes. The caller owns the Report and is responsible for populating Results before teardown.

func KeyMsgForTesting

func KeyMsgForTesting(key string) tea.KeyMsg

KeyMsgForTesting converts a key name to a tea.KeyMsg for use in unit tests that call Component.Update directly without a full TestModel.

func KeyNames

func KeyNames() []string

KeyNames returns the list of all recognized key names for documentation.

func ListFailureCaptures

func ListFailureCaptures() ([]string, error)

ListFailureCaptures returns test names for all persisted failure captures.

func PendingGoldenPath

func PendingGoldenPath(goldenPath string) string

PendingGoldenPath returns the path of the pending-review file for a golden. The pending file is <goldenPath>.new and is written by AssertGolden on mismatch so that `blit review` can enumerate it.

func Replay

func Replay(t testing.TB, model tea.Model, path string)

Replay drives the given model through a recorded session, asserting that each screen step matches the live screen. It fails the test on any divergence. Cols/lines from the session override the model's initial size.

func SaveFailureCapture

func SaveFailureCapture(t testing.TB, fc FailureCapture)

SaveFailureCapture persists fc to .blit/failures/<testname>.json. If fc.TestName is empty and t implements Name(), the test name is filled in. Errors are logged via t but never fatal — failure capture is best-effort.

func SnapshotApp added in v0.1.2

func SnapshotApp(t testing.TB, app interface{ Model() tea.Model }, cols, lines int, name string)

SnapshotApp is a one-liner that creates a harness from a full application, takes a named golden snapshot, and cleans up. Use this when you only need to verify the initial render of the real app layout:

btest.SnapshotApp(t, myApp, 80, 24, "initial")

For interactive tests (key presses, resizes, multiple snapshots), use NewAppHarness instead.

func SnapshotPath

func SnapshotPath(name string) string

SnapshotPath returns the full on-disk path for the named snapshot. Exposed so tests can assert on where a snapshot landed.

func UntilContains

func UntilContains(text string) screenPredicate

UntilContains returns a predicate that is satisfied when the screen contains text.

func UntilNotContains

func UntilNotContains(text string) screenPredicate

UntilNotContains returns a predicate satisfied when the screen no longer contains text.

func UntilRowContains

func UntilRowContains(row int, text string) screenPredicate

UntilRowContains returns a predicate satisfied when the given row contains text.

Types

type CellDiff

type CellDiff struct {
	Row           int
	Col           int
	ExpectedText  string
	ActualText    string
	ExpectedStyle CellStyle
	ActualStyle   CellStyle
	Kind          CellKind
}

CellDiff holds the comparison result for a single terminal cell.

func ScreenCellDiff

func ScreenCellDiff(expected, actual *Screen) []CellDiff

ScreenCellDiff performs a per-cell comparison between two screens, returning one CellDiff per cell that differs (text or style).

type CellKind

type CellKind int

CellKind classifies the type of difference at a single cell position.

const (
	// CellMatch means expected and actual are identical (text + style).
	CellMatch CellKind = iota
	// CellTextDiffer means the character content differs.
	CellTextDiffer
	// CellStyleDiffer means the character content matches but styles differ.
	CellStyleDiffer
)

type CellStyle

type CellStyle struct {
	Fg        string // foreground color (empty = default)
	Bg        string // background color (empty = default)
	Bold      bool
	Italic    bool
	Underline bool
	Reverse   bool
}

CellStyle represents the visual attributes of a terminal cell.

type Clock

type Clock interface {
	// Now returns the current time as seen by this clock.
	Now() time.Time
	// Sleep blocks until the clock has advanced by d.
	// FakeClock implementations return immediately but still honor Advance.
	Sleep(d time.Duration)
}

Clock is an abstraction over time.Now used by Poller-like components so tests can drive time deterministically. The real clock uses time.Now and time.Sleep; FakeClock lets tests advance time manually.

type Diff

type Diff struct {
	Lines []DiffLine
}

Diff represents a line-by-line comparison between two screen states.

func ScreenDiff

func ScreenDiff(a, b *Screen) Diff

ScreenDiff compares two screens and returns a Diff showing changed lines.

func (Diff) ChangedLines

func (d Diff) ChangedLines() []DiffLine

ChangedLines returns only the lines that differ.

func (Diff) HasChanges

func (d Diff) HasChanges() bool

HasChanges reports whether any lines differ.

func (Diff) String

func (d Diff) String() string

String returns a human-readable unified-style diff.

type DiffLine

type DiffLine struct {
	Row      int
	Expected string
	Actual   string
	Changed  bool
}

DiffLine represents a single line difference.

type DiffMode

type DiffMode int

DiffMode controls how the DiffViewer renders the comparison.

const (
	// DiffModeSideBySide shows expected on the left, actual on the right.
	DiffModeSideBySide DiffMode = iota
	// DiffModeUnified shows a unified diff with +/- prefix lines.
	DiffModeUnified
	// DiffModeCellsOnly lists only the differing cells.
	DiffModeCellsOnly
)

type DiffViewer

type DiffViewer struct {
	// contains filtered or unexported fields
}

DiffViewer is a blit Component that renders a side-by-side (or unified / cells-only) comparison of two screens captured from a failing btest assertion. It implements the blit Component interface so it can be embedded in any App layout.

Keybindings:

s  — side-by-side mode
u  — unified mode
d  — cells-only mode
q  — signal back-to-runner (emits DiffViewerBackMsg)

func NewDiffViewer

func NewDiffViewer(fc *FailureCapture) *DiffViewer

NewDiffViewer constructs a DiffViewer from a persisted FailureCapture.

func (*DiffViewer) Focused

func (dv *DiffViewer) Focused() bool

Focused implements blit.Component.

func (*DiffViewer) Init

func (dv *DiffViewer) Init() tea.Cmd

Init implements blit.Component.

func (*DiffViewer) KeyBindings

func (dv *DiffViewer) KeyBindings() []interface{}

KeyBindings implements blit.Component.

func (*DiffViewer) Mode

func (dv *DiffViewer) Mode() DiffMode

Mode returns the current display mode.

func (*DiffViewer) SetFocused

func (dv *DiffViewer) SetFocused(f bool)

SetFocused implements blit.Component.

func (*DiffViewer) SetMode

func (dv *DiffViewer) SetMode(m DiffMode)

SetMode sets the display mode directly (used by the CLI one-shot renderer).

func (*DiffViewer) SetSize

func (dv *DiffViewer) SetSize(w, h int)

SetSize implements blit.Component.

func (*DiffViewer) Update

func (dv *DiffViewer) Update(msg tea.Msg, _ interface{}) (*DiffViewer, tea.Cmd)

Update implements blit.Component. ctx is the ambient blit.Context but the DiffViewer only needs key messages, so it accepts tea.Msg directly and ignores the ctx value (kept as interface{} to avoid importing blit from inside the btest sub-package, which would create a cycle).

func (*DiffViewer) View

func (dv *DiffViewer) View() string

View implements blit.Component.

type DiffViewerBackMsg

type DiffViewerBackMsg struct{}

DiffViewerBackMsg is sent when the user presses q to return to the runner.

type FailureCapture

type FailureCapture struct {
	TestName string      `json:"test_name"`
	Kind     FailureKind `json:"kind"`
	// For FailureScreenEqual: both fields populated.
	ExpectedScreen string `json:"expected_screen,omitempty"`
	ActualScreen   string `json:"actual_screen,omitempty"`
	// For FailureGolden: golden file path + expected/actual bytes.
	GoldenPath     string `json:"golden_path,omitempty"`
	GoldenExpected string `json:"golden_expected,omitempty"`
	GoldenActual   string `json:"golden_actual,omitempty"`
}

FailureCapture holds the screens (or golden bytes) captured when an assertion fails. It is persisted to .blit/failures/<testname>.json so that `blit diff <testname>` can replay the diff offline.

func LoadFailureCapture

func LoadFailureCapture(testName string) (*FailureCapture, error)

LoadFailureCapture reads the persisted capture for a test name.

type FailureKind

type FailureKind string

FailureKind identifies what kind of assertion failed.

const (
	// FailureScreenEqual is produced by screen-equality assertions.
	FailureScreenEqual FailureKind = "screen_equal"
	// FailureGolden is produced by AssertGolden when the file differs.
	FailureGolden FailureKind = "golden"
)

type FakeClock

type FakeClock struct {
	// contains filtered or unexported fields
}

FakeClock is a deterministic Clock for tests. Create one with NewFakeClock and advance it with Advance. Now and Sleep are safe for concurrent use.

func NewFakeClock

func NewFakeClock(t time.Time) *FakeClock

NewFakeClock returns a FakeClock anchored at the given time. If t is the zero value, it is anchored at a fixed epoch (2026-01-01 UTC) so tests are reproducible across machines.

func (*FakeClock) Advance

func (f *FakeClock) Advance(d time.Duration)

Advance moves the fake clock forward by d. Negative durations are ignored.

func (*FakeClock) Now

func (f *FakeClock) Now() time.Time

Now returns the current fake time.

func (*FakeClock) Set

func (f *FakeClock) Set(t time.Time)

Set moves the fake clock to an absolute time.

func (*FakeClock) Sleep

func (f *FakeClock) Sleep(d time.Duration)

Sleep advances the fake clock by d and returns immediately. It does not block, so tests remain fast. Use Advance for clarity when the intent is "time passes" rather than "goroutine sleeps".

type Harness

type Harness struct {
	// contains filtered or unexported fields
}

Harness is a fluent wrapper around TestModel for concise, chainable test scripts. Each method returns *Harness so calls can be chained:

btest.NewHarness(t, model, 80, 24).
    Keys("down", "down", "enter").
    Expect("Loaded").
    ExpectRow(2, "selected").
    Done()

func NewAppHarness added in v0.1.2

func NewAppHarness(t testing.TB, app interface{ Model() tea.Model }, cols, lines int) *Harness

NewAppHarness creates a test harness from a full application, using the real component tree (Tabs, DualPane, StatusBar, overlays, etc.). This ensures golden snapshots cover the actual production layout, not a simplified reconstruction. app must implement Model() tea.Model (e.g., *blit.App).

h := btest.NewAppHarness(t, myApp, 80, 24)
defer h.Done()
h.Send(someMsg).Expect("loaded").Snapshot("initial")

func NewHarness

func NewHarness(t testing.TB, model tea.Model, cols, lines int) *Harness

NewHarness creates a new test harness around the given model.

func (*Harness) Advance

func (h *Harness) Advance(d time.Duration) *Harness

Advance is a placeholder for time-based drivers. It accepts a duration so call sites read naturally; the underlying TestModel does not yet integrate a FakeClock directly, but tests that own the FakeClock can chain .Advance(d) purely for documentation.

func (*Harness) Click

func (h *Harness) Click(x, y int) *Harness

Click dispatches a left-click at (x, y).

func (*Harness) Done

func (h *Harness) Done()

Done runs registered teardown callbacks in LIFO order. Safe to call multiple times; subsequent calls are no-ops.

func (*Harness) Expect

func (h *Harness) Expect(text string) *Harness

Expect asserts that the current screen contains text anywhere. Fails the test with a helpful diff if not.

func (*Harness) ExpectNot

func (h *Harness) ExpectNot(text string) *Harness

ExpectNot asserts that text is NOT present on the current screen.

func (*Harness) ExpectRow

func (h *Harness) ExpectRow(row int, text string) *Harness

ExpectRow asserts that the given row contains text.

func (*Harness) Keys

func (h *Harness) Keys(keys ...string) *Harness

Keys sends a sequence of named keys (down, enter, ctrl+c, a…).

func (*Harness) OnSetup

func (h *Harness) OnSetup(fn func()) *Harness

OnSetup registers a function to run once when the first action fires. Use this for things like seeding state that depends on Harness being wired up but shouldn't run in NewHarness.

func (*Harness) OnTeardown

func (h *Harness) OnTeardown(fn func()) *Harness

OnTeardown registers a function to run when Done() is called. Registered functions run in reverse order (last-registered first) so they match a deferred-cleanup mental model.

func (*Harness) Resize

func (h *Harness) Resize(cols, lines int) *Harness

Resize updates the simulated terminal size.

func (*Harness) Screen

func (h *Harness) Screen() *Screen

Screen returns the current screen for ad-hoc assertions outside the fluent API.

func (*Harness) Send

func (h *Harness) Send(msg tea.Msg) *Harness

Send dispatches an arbitrary tea.Msg.

func (*Harness) Snapshot

func (h *Harness) Snapshot(name string) *Harness

Snapshot stores or compares a screen snapshot under the given name.

func (*Harness) TestModel

func (h *Harness) TestModel() *TestModel

TestModel returns the underlying TestModel for calls not covered by the fluent API. Escape hatch — prefer to add a method on Harness if possible.

func (*Harness) Type

func (h *Harness) Type(text string) *Harness

Type sends each character in text as an individual key event.

type IndexedRow

type IndexedRow struct {
	Index int
	Text  string
}

IndexedRow pairs a row index with its text content.

type PendingGolden

type PendingGolden struct {
	// GoldenPath is the path to the accepted golden file (may not exist yet
	// if the golden was never written).
	GoldenPath string
	// NewPath is the .golden.new file written by AssertGolden on mismatch.
	NewPath string
	// Expected is the current accepted content (empty if golden doesn't exist).
	Expected string
	// Actual is the candidate content from the .golden.new file.
	Actual string
}

PendingGolden describes a single pending snapshot review item: the canonical .golden path plus the candidate .golden.new content.

func FindPendingGoldens

func FindPendingGoldens(root string) ([]PendingGolden, error)

FindPendingGoldens walks root recursively and returns all .golden.new files as PendingGolden items. root is typically "." (the package under test).

func (PendingGolden) Accept

func (p PendingGolden) Accept() error

Accept writes Actual atomically to GoldenPath and removes NewPath. It uses a temp-file + rename so the update is atomic.

func (PendingGolden) Reject

func (p PendingGolden) Reject() error

Reject removes the .golden.new file without touching the accepted golden.

func (PendingGolden) TestName

func (p PendingGolden) TestName() string

TestName returns a short human-readable label derived from the golden path. It strips the leading testdata/ prefix and the .golden suffix.

type RealClock

type RealClock struct{}

RealClock is a Clock backed by the real time package. Safe for concurrent use.

func (RealClock) Now

func (RealClock) Now() time.Time

Now returns time.Now.

func (RealClock) Sleep

func (RealClock) Sleep(d time.Duration)

Sleep calls time.Sleep.

type Region

type Region struct {
	// contains filtered or unexported fields
}

Region is a rectangular sub-area of a Screen for scoped assertions.

func (*Region) AllRows

func (r *Region) AllRows() []string

AllRows returns all row strings in the region.

func (*Region) Contains

func (r *Region) Contains(text string) bool

Contains reports whether the region contains the given text on any row.

func (*Region) CountOccurrences

func (r *Region) CountOccurrences(text string) int

CountOccurrences returns how many times text appears in the region.

func (*Region) FindText

func (r *Region) FindText(text string) (row, col int)

FindText returns the (relativeRow, relativeCol) of the first occurrence of text within the region. Returns (-1, -1) if not found.

func (*Region) IsEmpty

func (r *Region) IsEmpty() bool

IsEmpty reports whether the region has no visible text.

func (*Region) Row

func (r *Region) Row(row int) string

Row returns the text content of a row within the region (0-indexed relative to the region's top-left corner), trimmed of trailing spaces.

func (*Region) RowCount

func (r *Region) RowCount() int

RowCount returns the number of non-empty rows in the region.

func (*Region) String

func (r *Region) String() string

String returns a plain text representation of the region.

func (*Region) StyleAt

func (r *Region) StyleAt(row, col int) CellStyle

StyleAt returns the cell style at the given region-relative coordinates.

type Report

type Report struct {
	Suite     string
	StartedAt time.Time
	Results   []TestResult
}

Report is a collection of test results plus suite metadata.

func (*Report) TotalDuration

func (r *Report) TotalDuration() time.Duration

TotalDuration sums the duration of all results.

func (*Report) Totals

func (r *Report) Totals() (total, passed, failed, skipped int)

Totals returns (total, passed, failed, skipped).

func (*Report) WriteHTML

func (r *Report) WriteHTML(path string) error

WriteHTML writes the report as a self-contained HTML page.

func (*Report) WriteJUnit

func (r *Report) WriteJUnit(path string) error

WriteJUnit writes the report as a JUnit XML file at path. Parent directories are created as needed.

type Screen

type Screen struct {
	// contains filtered or unexported fields
}

Screen wraps go-te to provide a virtual terminal for testing TUI output.

func NewScreen

func NewScreen(cols, lines int) *Screen

NewScreen creates a virtual terminal screen of the given dimensions.

func (*Screen) AllRows

func (s *Screen) AllRows() []string

AllRows returns all row strings (including empty ones).

func (*Screen) Column

func (s *Screen) Column(col, startRow, endRow int) string

Column extracts a vertical column of text from startRow to endRow (exclusive).

func (*Screen) Contains

func (s *Screen) Contains(text string) bool

Contains reports whether the screen contains the given text anywhere.

func (*Screen) ContainsAt

func (s *Screen) ContainsAt(row, col int, text string) bool

ContainsAt reports whether the given text appears starting at (row, col).

func (*Screen) CountOccurrences

func (s *Screen) CountOccurrences(text string) int

CountOccurrences returns how many times text appears on the screen.

func (*Screen) CursorPos

func (s *Screen) CursorPos() (row, col int)

CursorPos returns the current cursor position.

func (*Screen) FindAllText

func (s *Screen) FindAllText(text string) [][2]int

FindAllText returns all (row, col) positions where text appears.

func (*Screen) FindRegexp

func (s *Screen) FindRegexp(pattern string) (row, col int)

FindRegexp returns the (row, col) of the first regexp match. Returns (-1, -1) if not found.

func (*Screen) FindText

func (s *Screen) FindText(text string) (row, col int)

FindText returns the (row, col) of the first occurrence of text on the screen. Returns (-1, -1) if not found.

func (*Screen) IsEmpty

func (s *Screen) IsEmpty() bool

IsEmpty reports whether the screen has no visible text content.

func (*Screen) MatchesRegexp

func (s *Screen) MatchesRegexp(pattern string) bool

MatchesRegexp reports whether the screen content matches the regular expression.

func (*Screen) NonEmptyRows

func (s *Screen) NonEmptyRows() []IndexedRow

NonEmptyRows returns only the non-empty rows with their row indices.

func (*Screen) Region

func (s *Screen) Region(row, col, width, height int) *Region

Region returns a sub-screen for bounded assertions.

func (*Screen) Render

func (s *Screen) Render(output string)

Render feeds View() output (with ANSI codes) into the virtual terminal. It translates bare \n to \r\n (mimicking the terminal's ONLCR flag) so that Bubble Tea View() output renders correctly.

func (*Screen) Row

func (s *Screen) Row(row int) string

Row returns the full text content of a row (trimmed of trailing spaces).

func (*Screen) RowCount

func (s *Screen) RowCount() int

RowCount returns the number of non-empty rows on screen.

func (*Screen) Size

func (s *Screen) Size() (cols, lines int)

Size returns the screen dimensions as (cols, lines).

func (*Screen) String

func (s *Screen) String() string

String returns a plain text representation of the entire screen (for debugging/golden files).

func (*Screen) StyleAt

func (s *Screen) StyleAt(row, col int) CellStyle

StyleAt returns the style attributes of the cell at (row, col).

func (*Screen) TextAt

func (s *Screen) TextAt(row, startCol, endCol int) string

TextAt returns the text content at the given row, from startCol to endCol. Row and col are 0-indexed. endCol is exclusive.

type Session

type Session struct {
	Version    int           `json:"version"`
	Cols       int           `json:"cols"`
	Lines      int           `json:"lines"`
	Name       string        `json:"name,omitempty"`        // v2+: logical session name
	RecordedAt string        `json:"recorded_at,omitempty"` // v2+: RFC3339 timestamp
	Command    []string      `json:"command,omitempty"`     // v2+: recorded command args
	Steps      []SessionStep `json:"steps"`
}

Session is the on-disk representation of a .tuisess file.

func LoadSession

func LoadSession(path string) (*Session, error)

LoadSession reads a .tuisess file. Versions 1 and 2 are both accepted; v1 files are loaded without the metadata fields introduced in v2.

type SessionRecorder

type SessionRecorder struct {
	// contains filtered or unexported fields
}

SessionRecorder captures input + screen steps against a live TestModel.

func NewSessionRecorder

func NewSessionRecorder(tm *TestModel) *SessionRecorder

NewSessionRecorder returns a recorder bound to an existing TestModel.

func (*SessionRecorder) Key

func (r *SessionRecorder) Key(key string) *SessionRecorder

Key sends a named key and records the key + resulting screen.

func (*SessionRecorder) Resize

func (r *SessionRecorder) Resize(cols, lines int) *SessionRecorder

Resize updates the simulated terminal size and records the step.

func (*SessionRecorder) Save

func (r *SessionRecorder) Save(path string) error

Save writes the recorded session to the given path as a .tuisess file. Missing parent directories are created. If path has no extension it is given ".tuisess".

func (*SessionRecorder) Type

func (r *SessionRecorder) Type(text string) *SessionRecorder

Type sends text one char at a time and records the aggregated step.

type SessionStep

type SessionStep struct {
	Kind   string `json:"kind"`             // "key", "type", "resize", "screen"
	Key    string `json:"key,omitempty"`    // for kind=key
	Text   string `json:"text,omitempty"`   // for kind=type
	Cols   int    `json:"cols,omitempty"`   // for kind=resize
	Lines  int    `json:"lines,omitempty"`  // for kind=resize
	Screen string `json:"screen,omitempty"` // for kind=screen (expected post-state)
}

SessionStep is a single input or screen assertion.

type Stopwatch

type Stopwatch struct {
	// contains filtered or unexported fields
}

Stopwatch measures elapsed time for performance assertions in tests.

func StartStopwatch

func StartStopwatch() Stopwatch

StartStopwatch begins a new timing measurement.

func (Stopwatch) AssertUnder

func (sw Stopwatch) AssertUnder(t testing.TB, d time.Duration)

AssertUnder fails the test if the elapsed time exceeds the given duration.

func (Stopwatch) Elapsed

func (sw Stopwatch) Elapsed() time.Duration

Elapsed returns the duration since the stopwatch was started.

type TestModel

type TestModel struct {
	// contains filtered or unexported fields
}

TestModel wraps a tea.Model for easy testing.

func NewTestModel

func NewTestModel(t testing.TB, model tea.Model, cols, lines int) *TestModel

NewTestModel creates a test wrapper around a Bubble Tea model. Calls Init() and processes any returned commands that produce messages.

func (*TestModel) Cols

func (tm *TestModel) Cols() int

Cols returns the current terminal width.

func (*TestModel) Lines

func (tm *TestModel) Lines() int

Lines returns the current terminal height.

func (*TestModel) Model

func (tm *TestModel) Model() tea.Model

Model returns the current underlying tea.Model.

func (*TestModel) RequireScreen

func (tm *TestModel) RequireScreen(fn func(t testing.TB, s *Screen))

RequireScreen renders the model and runs assertions against the screen in one call. The callback receives the screen and can use assert helpers on it.

func (*TestModel) Screen

func (tm *TestModel) Screen() *Screen

Screen renders the model's View() and returns the parsed screen.

func (*TestModel) SendKey

func (tm *TestModel) SendKey(key string)

SendKey sends a key event to the model. Supports special key names like "enter", "tab", "up", "down", "left", "right", "esc", "backspace", "space", and single characters.

func (*TestModel) SendKeys

func (tm *TestModel) SendKeys(keys ...string)

SendKeys sends a sequence of named keys. Each key is processed individually. Example: tm.SendKeys("down", "down", "enter")

func (*TestModel) SendMouse

func (tm *TestModel) SendMouse(x, y int, button tea.MouseButton)

SendMouse sends a mouse event to the model.

func (*TestModel) SendMsg

func (tm *TestModel) SendMsg(msg tea.Msg)

SendMsg sends an arbitrary tea.Msg to the model and processes any resulting command.

func (*TestModel) SendResize

func (tm *TestModel) SendResize(cols, lines int)

SendResize sends a window resize event.

func (*TestModel) SendTick

func (tm *TestModel) SendTick()

SendTick sends a generic tick message. Useful for testing time-based components like pollers, spinners, and animations.

func (*TestModel) Type

func (tm *TestModel) Type(text string)

Type sends a sequence of key events (one per character).

func (*TestModel) WaitFor

func (tm *TestModel) WaitFor(predicate func(*Screen) bool, maxTicks int) bool

WaitFor repeatedly sends tick messages until the predicate returns true or maxTicks is reached. Returns true if the predicate was satisfied. This is useful for waiting on async state changes driven by tea.Cmd chains.

type TestResult

type TestResult struct {
	Name     string
	Package  string
	Duration time.Duration
	Passed   bool
	Failure  string
	Skipped  bool
	Before   string // optional screen before failure
	After    string // optional screen after failure
}

TestResult describes one test outcome used by the reporters.

type TickMsgPlaceholder

type TickMsgPlaceholder struct {
	At time.Time
}

TickMsgPlaceholder is a btest-local tick shape used by Harness.Advance so we don't depend on an external TickMsg definition here. Consumers that care about tick semantics should use SendMsg directly.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL