steep

package module
v0.0.0-alpha.11 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: MIT Imports: 40 Imported by: 0

README ΒΆ

banner

πŸ”— Table of Contents

✨ Features

steep is a test harness for terminal programs in Go. It runs your program in an emulated terminal, drives it with keys, text, mouse events, and resizes, and checks what it draws, all from go test.

[!WARNING] steep is still pre-1.0. Its API may change drastically until the v1.0.0 release, so expect breaking changes between versions, and pin the version you use.

  • βœ” Three harnesses, one API: run a Bubble Tea model, a single component, or any command on a pseudo-terminal, with the same inputs, waits, assertions, snapshots, and recordings.
  • βœ” Waits, not sleeps: wait on what's on the screen, a regular expression, or a message your model received, so tests don't depend on how fast the machine is.
  • βœ” Real input: keys, paste, clicks, and drags go through the terminal as bytes, encoded for whatever keyboard and mouse modes your program asked for. Locate clicks what's on the screen rather than a coordinate.
  • βœ” Snapshots: golden files of the screen's text, or of every cell and its style.
  • βœ” Terminal state: check the title, cursor, clipboard, progress, notifications, and more.
  • βœ” Recordings of failures: export a GIF, WebP, video, or .cast of any test, with no code changes, through STEEP_EXPORT_MODE, and step through it event by event in the inspector.
  • βœ” No cgo: the terminal emulator is compiled in, so there's nothing else to install.

βš™ Usage

Take a look at the official documentation for tutorials, guides, and reference, starting with your first test. The documentation is also available for LLMs and agents as llms.txt and llms-full.txt, and every page has a markdown version.

func TestMenu(t *testing.T) {
	steep.NewHarness(t, newModel()).
		Wait("Sencha").
		KeyDown().
		Assert("Matcha").
		Key("enter").
		Wait("Matcha is ready").
		AssertSnapshot()
}

Runnable examples are in _examples.

go get -u getsteep.dev/steep@latest

The reel CLI

Install reel on macOS or Linux with Homebrew:

brew install lrstanley/tap/reel

Or download it for Linux, macOS, or Windows (amd64 or arm64) from the releases page, with checksums in steep_checksums.txt, or build it with Go:

go install getsteep.dev/steep/cmd/reel@latest

See the CLI docs for the steps.

  • reel: records terminals and renders recordings, from Go. steep records through it.
  • reel CLI: the same without Go. It runs VHS tapes, tests with them, drives programs for scripts and agents, and renders .cast files.
  • bubbletint: hundreds of terminal color themes for Bubble Tea and Lip Gloss.
  • bloom: a layout, compositing, and animation engine for Bubble Tea programs. Its end-to-end tests and example recordings run on steep.

πŸ›  Development

This repository holds several Go modules. To work on them, install Task, then run this once after cloning:

task setup

It creates a gitignored go.work, so changes to reel apply to steep and the CLI straight away, the same as in CI. Without it, they build against the reel version they pin. Nothing runs this for you.

task --list shows the rest, including the documentation site (task docs:dev) and the inspector (task inspector:dev), which needs Go and Bun.


πŸ™‹β™‚ Support & Assistance

  • ❀ Please review the Code of Conduct for guidelines on ensuring everyone has the best experience interacting with the community.
  • πŸ™‹β™‚ Take a look at the support document on guidelines for tips on how to ask the right questions.
  • 🐞 For all features/bugs/issues/questions/etc, head over here.

🀝 Contributing

  • ❀ Please review the Code of Conduct for guidelines on ensuring everyone has the best experience interacting with the community.
  • πŸ“‹ Please review the contributing doc for submitting issues/a guide on submitting pull requests and helping out.
  • πŸ— For anything security related, please review this repositories security policy.

βš– License

MIT License

Copyright (c) 2026 Liam Stanley <liam@liam.sh>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Also located here

Documentation ΒΆ

Overview ΒΆ

Package steep provides test helpers for tea.Program and models (root and component).

Use NewHarness to run a root tea.Model in a test:

h := steep.NewHarness(t, model, steep.WithWindowSize(80, 24))
h.Type("hello").WaitSettle().Assert("hello")
h.QuitProgram()
h.WaitFinished()

Use NewComponentHarness for components that expose View() string and an Update method through the asynchronous tea.Program runtime.

Use NewExecHarness for a command, which need not use Bubble Tea, on a pseudo-terminal:

h := steep.NewExecHarness(t, exec.Command("./myapp"))
h.Wait("ready").Key("q")
if status := h.ExitStatus(); status != 0 {
	t.Errorf("exit status = %d", status)
}

Hooks ΒΆ

Harness actions invoke Hook callbacks from WithHooks before and after the action body. Hook errors fail the test immediately. Once an action is done, it is marked in the timeline as "category:n", with its event and count, such as "input:2" or "wait:1", and each failure the harness reports is marked as "failure:1" and so on (see Harness.Mark). Those categories are reserved; use the same form for a test's own marks, such as "login:done" or "step:3", so tools reading a recording can group marks by category (see reel.SplitLabel). WithAutoMarks turns the harness's marks off.

Recording and export ΒΆ

A harness runs its program in a reel.Session, whose reel.Timeline records everything the program wrote and the test did. Recordings are made from the timeline, so a test never slows down to be recorded; WithSlow is only for programs that need real time between actions, and reel.Delay, passed to Harness.Type and the like, for recordings that should show text being typed.

The timeline also holds reel.Annotation events, described by reel's annotation.schema.json, of what the test did and found: its configuration ("steep.start"), the program's start and end ("steep.ready", "steep.exit"), inputs ("steep.input"), terminal changes and program actions ("steep.terminal", "steep.program"), waits ("steep.wait.start", "steep.wait.end"), assertions and snapshot comparisons ("steep.assert", "steep.snapshot"), failures ("steep.failure"), and the result ("steep.result"). Text fields over 4 KiB are cut and marked truncated. Messages the model receives ("steep.message") are only recorded with WithRecordMessages, which leaves out the environment (tea.EnvMsg). WithSource adds the file, line, and function of the test's line that led to each annotation and mark, and the test's package and module to "steep.start".

By default nothing is written: the timeline stays in memory, for Harness.Timeline. WithExportOnFailure exports when the test failed, and WithExportAlways whether it passed or failed. Either exports once, at Harness.Close, which runs as a test cleanup, so a test that fails, calls FailNow, or panics still exports. A test killed by "go test -timeout" writes nothing.

WithRecording sets what an export writes as a reel.Profile. Without outputs it writes "{name}_{status}.gif" and "{name}_{status}.cast" under WithArtifacts, or testing.TB.ArtifactDir, which is only kept with "go test -artifacts"; without it, an export logs that its files are removed. Absolute paths are written as they are. Each export logs the paths it wrote. WithStills adds a PNG or text snapshot at the marks of the hook events it names.

steep adds two effects to those in the effects package: Logo, an animated intro, and Outro, a PASS or FAIL card. Effects run by priority (see reel.Prioritizer), so effects.Pause pauses on the screen the test failed on before the card shows:

steep.WithRecording(reel.Profile{
	Effects: []reel.Effect{
		steep.Logo(2 * time.Second),
		effects.Pause(),
		effects.Keys(time.Second),
		steep.Outro(2 * time.Second),
	},
}),
steep.WithExportOnFailure(),

The ".cast" output is the timeline itself, from which reel.ReadTimeline and reel.Export can write the other outputs again. A profile with effects.Redact or effects.RedactInputs must therefore set outputs without ".cast" (see reel.ErrRawOutput).

Environment ΒΆ

These variables override the options set in code unless WithIgnoreEnv is set, so any test can be recorded or tuned without changing it. Unparsable values are ignored, except in STEEP_EXPORT.

  • STEEP_TIMEOUT, STEEP_CHECK, STEEP_SETTLE, STEEP_SLOW: durations for WithTimeout, WithCheck, WithSettle, and WithSlow.
  • STEEP_TYPING_DELAY: a duration to pause after each key press of Harness.Type and Harness.Key (reel.WithTypingDelay), unless a call passes reel.Delay.
  • STEEP_LOG: on (or true, 1) adds the hook of WithLog.
  • STEEP_RECORD_MESSAGES: on or off (or true, 1, false, 0) for WithRecordMessages.
  • STEEP_SOURCE: on or off (or true, 1, false, 0) for WithSource.
  • STEEP_SNAPSHOT_DIR: the default snapshot directory (snapshot.WithDir); a directory passed to a snapshot call still wins.
  • STEEP_EXPORT_MODE: on (or always, true, 1) for WithExportAlways, failure (or on-failure) for WithExportOnFailure, off (or false, 0) for neither.
  • STEEP_EXPORT_DIR: the directory of WithArtifacts.
  • STEEP_EXPORT: replaces the outputs and stills with a comma-separated list of outputs (see reel.ParseFiles), each a path with optional query parameters, such as "{name}_{status}.gif?max-bytes=5MB" or "{name}_{mark}.snap?match=input:*". A path that holds {mark} with no marks or match parameter is a still at each hook mark of the events WithStills uses by default. An output that does not parse fails the test.
  • STEEP_EXPORT_FPS, STEEP_EXPORT_SPEED: replace reel.Profile.FPS and reel.Profile.Speed.
  • STEEP_RENDERER_SCALE, STEEP_RENDERER_FONT_FAMILY: appended to reel.Profile.Render.

For example:

STEEP_EXPORT_MODE=on STEEP_EXPORT='{name}.mp4,{name}_{mark}.png?match=wait:*' go test -artifacts ./...

Index ΒΆ

Constants ΒΆ

View Source
const (
	// DefaultTermWidth is the default terminal width (see [WithWindowSize]).
	DefaultTermWidth = 80
	// DefaultTermHeight is the default terminal height (see [WithWindowSize]).
	DefaultTermHeight = 24

	// DefaultTimeout is the default timeout used by [Harness] blocking waits.
	DefaultTimeout = 2 * time.Second
	// DefaultCheckInterval is the default [WithCheck] interval.
	DefaultCheckInterval = 10 * time.Millisecond
	// DefaultSettleTimeout is the default settle timeout used by [Harness] settle
	// waits. Settle timeouts are capped at 80% of the [WithTimeout] timeout.
	DefaultSettleTimeout = 100 * time.Millisecond
)
View Source
const MarkFailure = "failure"

MarkFailure is the category of the marks of the failures the harness reports, "failure:1" and so on (see Harness.Mark).

Variables ΒΆ

This section is empty.

Functions ΒΆ

func Assert ΒΆ

func Assert[T string | []string](tb testing.TB, view Viewable, content T, opts ...Option) bool

Assert reports an error unless content appears in output. content is a string or every string in a []string. It returns whether it did.

func AssertDimensions ΒΆ

func AssertDimensions(tb testing.TB, view Viewable, width, height int, opts ...Option) bool

AssertDimensions reports an error unless output has specified dimensions. It returns whether the output matched.

func AssertFor ΒΆ

func AssertFor(tb testing.TB, view Viewable, cond reel.Condition, opts ...Option) bool

AssertFor reports an error unless cond holds for the output, such as reel.Matches for a regular expression. It returns whether it held. The output has no cursor, so reel.OnCursorLine never holds; use Harness.AssertFor for that.

func AssertHasMessage ΒΆ

func AssertHasMessage[T uv.Event](tb testing.TB, log MessageCollector, opts ...Option) bool

AssertHasMessage asserts that at least one message with the same concrete type as T has been observed.

func AssertHeight ΒΆ

func AssertHeight(tb testing.TB, view Viewable, n int, opts ...Option) bool

AssertHeight reports an error unless output has n rows. Unlike charm.land/lipgloss/v2.Height, empty output has a height of 0. It returns whether the output matched.

func AssertNot ΒΆ

func AssertNot[T string | []string](tb testing.TB, view Viewable, content T, opts ...Option) bool

AssertNot reports an error if content appears in output. content is a string or any string in a []string. It returns whether none appeared.

func AssertWidth ΒΆ

func AssertWidth(tb testing.TB, view Viewable, n int, opts ...Option) bool

AssertWidth reports an error unless output has n columns. It returns whether the output matched.

func Dimensions ΒΆ

func Dimensions(tb testing.TB, out string, opts ...Option) (w, h int)

Dimensions returns the width and height of the output.

func FilterMessagesFunc ΒΆ

func FilterMessagesFunc[T uv.Event](tb testing.TB, messages iter.Seq[uv.Event], fn func(T) bool) iter.Seq[T]

FilterMessagesFunc filters an iterator of messages to only include messages of type T for which fn returns true.

func FilterMessagesType ΒΆ

func FilterMessagesType[T uv.Event](tb testing.TB, messages iter.Seq[uv.Event]) iter.Seq[T]

FilterMessagesType filters an iterator of messages to only include messages with the same concrete type as T.

func IgnoreMessagesReflect ΒΆ

func IgnoreMessagesReflect(tb testing.TB, messages iter.Seq[uv.Event], ignore ...reflect.Type) iter.Seq[uv.Event]

IgnoreMessagesReflect filters an iterator of messages to exclude messages with the same concrete type as any of the provided reflect.Types.

func Logo(d time.Duration) reel.Effect

Logo returns an effect that shows steep's animated logo before the first frame of a recording, with steam rising for d while the recording's title (the test name, for a Harness) fades in. Terminals too small for the logo get a plain "steep" card instead. A d of zero or less shows nothing. Playback speed does not scale it.

Its frames are reel.Frame.Isolated and have no cells, so later effects such as effects.Keys leave them alone. They count toward GIF frame and byte limits.

func Outro ΒΆ

func Outro(d time.Duration, lines ...effects.Line) reel.Effect

Outro returns an effect that shows a card for d after the last frame of a recording (see effects.OutroFunc). With lines, the card shows them. Without, it shows the *Result in reel.Context.Meta, which a Harness export sets: PASS or FAIL, the test name, and the first line of as many failures as fit. Without lines or a *Result, or with a d of zero or less, it shows nothing. Playback speed does not scale d.

For a failed test, the last frame before the card plays for at least 3s (effects.HoldLast), so the failing screen can be read. An effects.Pause badges that frame.

func Require ΒΆ

func Require[T string | []string](tb testing.TB, view Viewable, content T, opts ...Option)

Require fails the test immediately unless content appears in output. content is a string or every string in a []string.

func RequireDimensions ΒΆ

func RequireDimensions(tb testing.TB, view Viewable, width, height int, opts ...Option)

RequireDimensions fails the test immediately unless output has specified dimensions.

func RequireFor ΒΆ

func RequireFor(tb testing.TB, view Viewable, cond reel.Condition, opts ...Option)

RequireFor is AssertFor, failing the test immediately unless cond holds.

func RequireHasMessage ΒΆ

func RequireHasMessage[T uv.Event](tb testing.TB, log MessageCollector, opts ...Option)

RequireHasMessage requires that at least one message with the same concrete type as T has been observed.

func RequireHeight ΒΆ

func RequireHeight(tb testing.TB, view Viewable, n int, opts ...Option)

RequireHeight fails the test immediately unless output has n rows.

func RequireNot ΒΆ

func RequireNot[T string | []string](tb testing.TB, view Viewable, content T, opts ...Option)

RequireNot fails the test immediately if content appears in output. content is a string or any string in a []string.

func RequireWidth ΒΆ

func RequireWidth(tb testing.TB, view Viewable, n int, opts ...Option)

RequireWidth fails the test immediately unless output has n columns.

func WaitLiveMessage ΒΆ

func WaitLiveMessage[T uv.Event](tb testing.TB, log MessageCollector, opts ...Option) T

WaitLiveMessage waits until a message with the same concrete type as T is received after the call, then returns it.

func WaitLiveMessageWhere ΒΆ

func WaitLiveMessageWhere(tb testing.TB, log MessageCollector, fn func(uv.Event) (ok bool), opts ...Option) uv.Event

WaitLiveMessageWhere waits until fn returns true for a message received after the call, then returns it.

func WaitMessage ΒΆ

func WaitMessage[T uv.Event](tb testing.TB, log MessageCollector, opts ...Option) T

WaitMessage waits until at least one message with the same concrete type as T has been observed, then returns the first match.

func WaitMessageWhere ΒΆ

func WaitMessageWhere(tb testing.TB, log MessageCollector, fn func(uv.Event) (ok bool), opts ...Option) uv.Event

WaitMessageWhere waits until fn returns true for any received message, past or future, then returns it.

func WaitSettleMessages ΒΆ

func WaitSettleMessages(tb testing.TB, log MessageCollector, opts ...Option)

WaitSettleMessages waits until no messages have been observed for the settle timeout (WithSettle). Types from WithSettleIgnoreMsgs do not count.

See also Harness.WaitSettle.

Types ΒΆ

type AssertKind ΒΆ

type AssertKind string

AssertKind is what an assertion asserts.

const (
	// AssertKindContains asserts that text is on the screen
	// ([Harness.Assert] and [Harness.Require]).
	AssertKindContains AssertKind = "contains"
	// AssertKindNotContains asserts that text is not on the screen
	// ([Harness.AssertNot] and [Harness.RequireNot]).
	AssertKindNotContains AssertKind = "not_contains"
	// AssertKindCondition asserts a [reel.Condition] on the screen
	// ([Harness.AssertFor] and [Harness.RequireFor]).
	AssertKindCondition AssertKind = "condition"
	// AssertKindWidth asserts the width of the screen's text
	// ([Harness.AssertWidth] and [Harness.RequireWidth]).
	AssertKindWidth AssertKind = "width"
	// AssertKindHeight asserts the height of the screen's text
	// ([Harness.AssertHeight] and [Harness.RequireHeight]).
	AssertKindHeight AssertKind = "height"
	// AssertKindDimensions asserts the width and height of the screen's
	// text ([Harness.AssertDimensions] and [Harness.RequireDimensions]).
	AssertKindDimensions AssertKind = "dimensions"
	// AssertKindHasMessage asserts that the model received a message of a
	// type ([Harness.AssertHasMessage] and [Harness.RequireHasMessage]).
	AssertKindHasMessage AssertKind = "has_message"
)

type AssertMeta ΒΆ

type AssertMeta struct {
	Kind AssertKind `json:"assert"`
	// Required is true for a Require method, whose failure stops the test.
	Required bool `json:"required"`
}

AssertMeta is the metadata of HookAssert events.

func (AssertMeta) LogValue ΒΆ

func (m AssertMeta) LogValue() slog.Value

type CaptureKind ΒΆ

type CaptureKind string

CaptureKind is what a capture captures.

const (
	// CaptureKindText compares the screen's text with a ".snap" file
	// ([Harness.AssertSnapshot] and [Harness.RequireSnapshot]).
	CaptureKindText CaptureKind = "text"
	// CaptureKindJSON compares the screen's cells and the terminal's state
	// with a ".json" file ([Harness.AssertJSON] and [Harness.RequireJSON]).
	CaptureKindJSON CaptureKind = "json"
	// CaptureKindImage renders the screen as an image ([Harness.Image] and
	// [Harness.ImageInto]). It records no annotation.
	CaptureKindImage CaptureKind = "image"
)

type CaptureMeta ΒΆ

type CaptureMeta struct {
	Kind CaptureKind `json:"format"`
	// Required is true for a Require method, whose mismatch stops the test.
	Required bool `json:"required"`
}

CaptureMeta is the metadata of HookCapture events.

func (CaptureMeta) LogValue ΒΆ

func (m CaptureMeta) LogValue() slog.Value

type ColorProfile ΒΆ

type ColorProfile int

ColorProfile is the color support the harness terminal advertises to the program (see WithColor).

const (
	// ColorASCII advertises no color support (NO_COLOR=1). Text attributes
	// such as bold still render.
	ColorASCII ColorProfile = iota + 1
	// ColorANSI advertises the 16 ANSI colors (TERM=xterm).
	ColorANSI
	// ColorANSI256 advertises 256 colors (TERM=xterm-256color). This is what
	// the harness terminal reports without [WithColor].
	ColorANSI256
	// ColorTrueColor advertises 24-bit color (TERM=xterm-256color,
	// COLORTERM=truecolor).
	ColorTrueColor
)

func (ColorProfile) String ΒΆ

func (p ColorProfile) String() string

String returns the profile's name.

type Harness ΒΆ

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

Harness is a test harness for a tea.Program.

Its checks follow one naming convention:

Most methods return the harness for chaining. Waits on the screen are built on reel.Session.Wait. The package-level Assert*, Require*, and message Wait* functions of the same names check any Viewable or MessageCollector and return a result instead: whether an Assert* passed, or the message that satisfied a Wait*.

A harness from NewExecHarness runs a command rather than a tea.Program: methods that need the program or its model fail the test.

func NewComponentHarness ΒΆ

func NewComponentHarness[M any](tb testing.TB, model M, opts ...Option) *Harness

NewComponentHarness is NewHarness for a component model, which must have a View() string method and an Update method returning either a tea.Cmd or the updated model and a tea.Cmd. Init is optional.

func NewExecHarness ΒΆ

func NewExecHarness(tb testing.TB, cmd *exec.Cmd, opts ...Option) *Harness

NewExecHarness starts cmd on a pseudo-terminal (see reel.Exec) and returns a harness to drive and assert on it, for programs that cannot run in the test's process, such as a built binary or one that does not use Bubble Tea. It registers Harness.Close as a test cleanup, which kills cmd if it is still running. It fails the test if cmd cannot be started.

Unlike NewHarness, it does not wait for cmd to do anything, as a command has no first message to wait for. Wait for what it draws, such as with Harness.Wait.

The environment is cmd.Env, or the host's when nil, changed by WithEnvVars and WithColor. WithProgramOptions, WithRecordMessages, and WithSettleIgnoreMsgs have no effect. Methods that need the program or its model fail the test: Harness.SendProgram, Harness.QuitProgram, Harness.Mutate, Harness.FinalModel, and those that read messages, such as Harness.WaitMessage.

func NewHarness ΒΆ

func NewHarness(tb testing.TB, model tea.Model, opts ...Option) *Harness

NewHarness runs the root tea.Model in a tea.Program and returns a harness to drive and assert on it. It waits up to the WithTimeout timeout for the model's first message, and registers Harness.Close as a test cleanup.

func (*Harness) Assert ΒΆ

func (h *Harness) Assert[T string | []string](content T, opts ...Option) *Harness

Assert reports an error unless content appears in view output. content is a string or every string in a []string.

func (*Harness) AssertDimensions ΒΆ

func (h *Harness) AssertDimensions(width, height int, opts ...Option) *Harness

AssertDimensions reports an error unless view output has width columns and height rows.

See also Harness.Dimensions.

func (*Harness) AssertFor ΒΆ

func (h *Harness) AssertFor(cond reel.Condition, opts ...Option) *Harness

AssertFor reports an error unless cond holds for the screen, such as reel.Matches for a regular expression or reel.OnCursorLine for a condition on the cursor's line.

func (*Harness) AssertHasMessage ΒΆ

func (h *Harness) AssertHasMessage[T uv.Event](opts ...Option) bool

AssertHasMessage reports an error unless history contains a message of type T.

func (*Harness) AssertHeight ΒΆ

func (h *Harness) AssertHeight(height int, opts ...Option) *Harness

AssertHeight reports an error unless view output has height rows. It allows the test to continue.

func (*Harness) AssertJSON ΒΆ

func (h *Harness) AssertJSON(opts ...snapshot.Option) *Harness

AssertJSON compares the screen's cells and the terminal's state, as JSON, against a snapshot file.

func (*Harness) AssertNot ΒΆ

func (h *Harness) AssertNot[T string | []string](content T, opts ...Option) *Harness

AssertNot reports an error if content appears in view output. content is a string or any string in a []string.

func (*Harness) AssertSnapshot ΒΆ

func (h *Harness) AssertSnapshot(opts ...snapshot.Option) *Harness

AssertSnapshot compares Harness.View against a snapshot file. It allows the test to continue.

func (*Harness) AssertWidth ΒΆ

func (h *Harness) AssertWidth(width int, opts ...Option) *Harness

AssertWidth reports an error unless view output has width columns. It allows the test to continue.

func (*Harness) Bell ΒΆ

func (h *Harness) Bell() vt.Bell

Bell returns the latest bell (ansi.BEL). Its Count is zero before the first.

func (*Harness) BgColor ΒΆ

func (h *Harness) BgColor() color.Color

BgColor returns the terminal emulator's background color.

func (*Harness) Blur ΒΆ

func (h *Harness) Blur() *Harness

Blur sends the program a blur event if it enabled focus reporting.

func (*Harness) Bounds ΒΆ

func (h *Harness) Bounds() image.Rectangle

Bounds returns the terminal's current screen bounds.

func (*Harness) ClearScrollback ΒΆ

func (h *Harness) ClearScrollback() *Harness

ClearScrollback clears the terminal's scrollback (see reel.Session.ClearScrollback).

func (*Harness) Clipboard ΒΆ

func (h *Harness) Clipboard() vt.ClipboardWrite

Clipboard returns the program's latest clipboard write.

func (*Harness) Close ΒΆ

func (h *Harness) Close()

Close stops the program, exports the recording if the export policy says to, and releases the terminal. NewHarness registers it as a test cleanup, so calling it is optional. Calls after the first do nothing.

func (h *Harness) CursorBlink() bool

CursorBlink reports whether the cursor style is blinking.

func (*Harness) CursorColor ΒΆ

func (h *Harness) CursorColor() color.Color

CursorColor returns the terminal emulator's cursor color.

func (*Harness) CursorPosition ΒΆ

func (h *Harness) CursorPosition() image.Point

CursorPosition returns the terminal emulator cursor position, or (-1, -1) while the viewport is scrolled off it (see Harness.ScrollUp).

func (*Harness) CursorStyle ΒΆ

func (h *Harness) CursorStyle() vt.CursorShape

CursorStyle returns the current VT cursor shape.

func (*Harness) CursorVisible ΒΆ

func (h *Harness) CursorVisible() bool

CursorVisible reports whether the cursor is shown (DECTCEM).

func (*Harness) Dimensions ΒΆ

func (h *Harness) Dimensions() (width, height int)

Dimensions returns the terminal's width and height in cells.

func (*Harness) ExitStatus ΒΆ

func (h *Harness) ExitStatus(opts ...Option) int

ExitStatus waits for the command of NewExecHarness to finish, as Harness.WaitFinished does, and returns its exit status. On Unix, a command killed by a signal has status 128+signal, as shells report it.

func (*Harness) FgColor ΒΆ

func (h *Harness) FgColor() color.Color

FgColor returns the terminal emulator's foreground color.

func (*Harness) FilterMessagesFunc ΒΆ

func (h *Harness) FilterMessagesFunc[T uv.Event](fn func(T) bool) iter.Seq[T]

FilterMessagesFunc yields history messages of type T for which fn returns true.

func (*Harness) FilterMessagesType ΒΆ

func (h *Harness) FilterMessagesType[T uv.Event]() iter.Seq[T]

FilterMessagesType yields history messages whose concrete type is T.

func (*Harness) FinalModel ΒΆ

func (h *Harness) FinalModel(opts ...Option) tea.Model

FinalModel waits for the tea.Program to finish and returns the root model.

func (*Harness) FinalModelAs ΒΆ

func (h *Harness) FinalModelAs[M any](opts ...Option) M

FinalModelAs is Harness.FinalModel, with the model as M. It fails the test when the model is not M.

func (*Harness) Focus ΒΆ

func (h *Harness) Focus() *Harness

Focus sends the program a focus event if it enabled focus reporting.

func (*Harness) Height ΒΆ

func (h *Harness) Height() int

Height returns the terminal height in cells.

func (*Harness) Image ΒΆ

func (h *Harness) Image() image.Image

Image renders the screen as an image, with the Render options of the recording profile (see WithRecording and reel.Imager.Image).

The image is reused by the next call, so do not mutate or retain it; copy it, or use Harness.ImageInto.

func (*Harness) ImageInto ΒΆ

func (h *Harness) ImageInto(dst *image.NRGBA)

ImageInto renders the screen into dst, at dst.Rect.Min (see reel.Imager.ImageInto). dst must be at least as large as the image Harness.Image returns. Unlike that image, dst is the caller's, safe to keep after the call.

func (*Harness) IsAltScreen ΒΆ

func (h *Harness) IsAltScreen() bool

IsAltScreen returns true if the terminal is in alt screen mode.

func (*Harness) IsFocused ΒΆ

func (h *Harness) IsFocused() bool

IsFocused reports the last known terminal focus state.

func (*Harness) Key ΒΆ

func (h *Harness) Key(key string, opts ...reel.InputOption) *Harness

Key sends a single key press to the terminal, such as "ctrl+a", "enter", "shift+tab", or "space" (see reel.Session.Key). An unknown name fails the test. Like Harness.Type, it can be reordered against Harness.SendProgram. Pass reel.Delay to pause after the key press, or reel.Repeat to press it more than once.

func (*Harness) KeyBackspace ΒΆ

func (h *Harness) KeyBackspace(opts ...reel.InputOption) *Harness

KeyBackspace sends a backspace key press to the terminal emulator.

func (*Harness) KeyCtrlC ΒΆ

func (h *Harness) KeyCtrlC(opts ...reel.InputOption) *Harness

KeyCtrlC sends ctrl+c to the terminal emulator.

func (*Harness) KeyCtrlD ΒΆ

func (h *Harness) KeyCtrlD(opts ...reel.InputOption) *Harness

KeyCtrlD sends ctrl+d to the terminal emulator.

func (*Harness) KeyDelete ΒΆ

func (h *Harness) KeyDelete(opts ...reel.InputOption) *Harness

KeyDelete sends a delete (forward-delete) key press to the terminal emulator.

func (*Harness) KeyDown ΒΆ

func (h *Harness) KeyDown(opts ...reel.InputOption) *Harness

KeyDown sends a down-arrow key press to the terminal emulator.

func (*Harness) KeyEsc ΒΆ

func (h *Harness) KeyEsc(opts ...reel.InputOption) *Harness

KeyEsc sends an escape key press to the terminal emulator.

func (*Harness) KeyLeft ΒΆ

func (h *Harness) KeyLeft(opts ...reel.InputOption) *Harness

KeyLeft sends a left-arrow key press to the terminal emulator.

func (*Harness) KeyRight ΒΆ

func (h *Harness) KeyRight(opts ...reel.InputOption) *Harness

KeyRight sends a right-arrow key press to the terminal emulator.

func (*Harness) KeyUp ΒΆ

func (h *Harness) KeyUp(opts ...reel.InputOption) *Harness

KeyUp sends an up-arrow key press to the terminal emulator.

func (*Harness) LeftClick ΒΆ

func (h *Harness) LeftClick(x, y int, opts ...reel.InputOption) *Harness

LeftClick is Harness.MouseClick with the left mouse button.

func (*Harness) LeftDrag ΒΆ

func (h *Harness) LeftDrag(x1, y1, x2, y2 int, opts ...reel.InputOption) *Harness

LeftDrag is Harness.MouseDrag with the left mouse button.

func (*Harness) LiveMessages ΒΆ

func (h *Harness) LiveMessages(ctx context.Context) iter.Seq[uv.Event]

LiveMessages returns an iterator over messages the model receives after the call returns, until ctx is done. Like Harness.Messages, it buffers only 32 messages, and a loop that falls further behind misses the newer ones.

func (*Harness) Locate ΒΆ

func (h *Harness) Locate(cond reel.Condition, opts ...Option) Location

Locate waits until cond holds for the screen, as Harness.WaitFor does, and returns the cell where cond's first match starts, so a test can click what the program drew without knowing where it drew it:

h.Locate(reel.Contains("[ Save ]")).LeftClick()
h.Locate(reel.Contains("Name:")).Offset(6, 0).LeftClick()

The location is not updated if the program redraws afterward. A condition that holds without locating anything, such as reel.NotContains or reel.ScreenFunc, fails the test.

func (*Harness) Mark ΒΆ

func (h *Harness) Mark(label string) *Harness

Mark places a reel.Mark labeled label in the session's timeline, which reel.Timeline.Between can cut a recording at. The label must not be empty. Use the "category:value" form, such as "login:done" or "step:3", so marks can be grouped by the text before the first ":" (see reel.SplitLabel). The categories of the harness's own marks, the hook events' names and MarkFailure, are reserved: a label in one of them is a test error, and is not placed.

The harness marks every hook event once its action is done, before its After hooks run, labeled with the event's name and count, such as "input:1" or "wait:1". While exporting, input, terminal, and program actions are marked once the program has painted its response: when its output has been quiet for the WithCheck interval, or, if it writes nothing, after the WithSettle period (at least 50ms). For input and program actions, only output after the model has handled the action counts, so a still of one shows the response even when other output came first. Input the program does not hear of, such as a click without mouse tracking, takes that period too. A response made later, such as by a command, need not be painted yet. Any output counts for terminal actions, which the program may not hear of. A very large check interval slows those actions. Harness.Close places its mark as it starts, where the recording ends.

The harness also marks each failure it reports as it reports it, "failure:1", "failure:2", and so on, numbered as in Result.Failures, such as for effects.PauseAtMarks to pause on a test's first failure. WithAutoMarks turns the harness's marks off.

func (*Harness) MessageHistory ΒΆ

func (h *Harness) MessageHistory() iter.Seq[uv.Event]

MessageHistory returns a copy of all messages the model received.

func (*Harness) Messages ΒΆ

func (h *Harness) Messages(ctx context.Context) iter.Seq[uv.Event]

Messages returns an iterator over all messages the model received, past and future, until ctx is done. Past messages are complete, but the iterator buffers only 32 new ones: a loop that falls further behind misses the newer messages, which Harness.MessageHistory still has.

func (*Harness) MiddleClick ΒΆ

func (h *Harness) MiddleClick(x, y int, opts ...reel.InputOption) *Harness

MiddleClick is Harness.MouseClick with the middle mouse button.

func (*Harness) ModeSetting ΒΆ

func (h *Harness) ModeSetting(mode ansi.Mode) ansi.ModeSetting

ModeSetting returns the setting of mode, or ansi.ModeNotRecognized for a mode the terminal does not track.

func (*Harness) Mouse ΒΆ

func (h *Harness) Mouse(action vt.MouseAction, button vt.MouseButton, x, y int) *Harness

Mouse sends a mouse event through the terminal. As with a real terminal, it is dropped unless the program enabled mouse tracking.

func (*Harness) MouseClick ΒΆ

func (h *Harness) MouseClick(button vt.MouseButton, x, y int, opts ...reel.InputOption) *Harness

MouseClick sends a press and release at (x, y) (see reel.Session.Click). Pass reel.Repeat for a double click. To click what is on the screen instead of a fixed cell, see Harness.Locate.

func (*Harness) MouseDrag ΒΆ

func (h *Harness) MouseDrag(button vt.MouseButton, x1, y1, x2, y2 int, opts ...reel.InputOption) *Harness

MouseDrag sends a press at (x1, y1), motion through each cell of the straight line to (x2, y2), and a release there (see reel.Session.Drag). Pass reel.Delay to pause after each step.

func (*Harness) Mutate ΒΆ

func (h *Harness) Mutate[M any](fn func(M) M, opts ...Option) *Harness

Mutate applies fn to the model on the program's goroutine, as part of its Update handling. Use it sparingly, for scenarios that are otherwise impossible to set up or would pollute the model with test-only state.

fn must take and return the model type passed to NewHarness or NewComponentHarness, such as func(Model) Model or func(*Model) *Model, or the test fails.

func (*Harness) Notifications ΒΆ

func (h *Harness) Notifications() []vt.Notification

Notifications returns desktop notifications from OSC 9 and OSC 777.

func (*Harness) Paste ΒΆ

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

Paste pastes text into the terminal, bracketed if the program enabled bracketed paste.

func (*Harness) ProgramStatuses ΒΆ

func (h *Harness) ProgramStatuses() []vt.ProgramStatus

ProgramStatuses returns the current OSC 7501 records (see vt.Terminal.ProgramStatuses). At most 256 are kept.

func (*Harness) Progress ΒΆ

func (h *Harness) Progress() vt.Progress

Progress returns the latest OSC 9;4 progress report.

func (*Harness) Prompt ΒΆ

func (h *Harness) Prompt() vt.Prompt

Prompt returns the latest OSC 133 semantic prompt step.

func (*Harness) QuitProgram ΒΆ

func (h *Harness) QuitProgram() *Harness

QuitProgram requests a graceful shutdown of the tea.Program.

func (*Harness) Require ΒΆ

func (h *Harness) Require[T string | []string](content T, opts ...Option) *Harness

Require fails the test immediately unless content appears in view output. content is a string or every string in a []string.

func (*Harness) RequireDimensions ΒΆ

func (h *Harness) RequireDimensions(width, height int, opts ...Option) *Harness

RequireDimensions fails the test immediately unless view output has width columns and height rows.

func (*Harness) RequireFor ΒΆ

func (h *Harness) RequireFor(cond reel.Condition, opts ...Option) *Harness

RequireFor is Harness.AssertFor, failing the test immediately unless cond holds.

func (*Harness) RequireHasMessage ΒΆ

func (h *Harness) RequireHasMessage[T uv.Event](opts ...Option) *Harness

RequireHasMessage fails the test immediately unless history contains a message of type T.

func (*Harness) RequireHeight ΒΆ

func (h *Harness) RequireHeight(height int, opts ...Option) *Harness

RequireHeight fails the test immediately unless view output has height rows.

func (*Harness) RequireJSON ΒΆ

func (h *Harness) RequireJSON(opts ...snapshot.Option) *Harness

RequireJSON is Harness.AssertJSON, but fails the test immediately if the snapshot does not match.

func (*Harness) RequireNot ΒΆ

func (h *Harness) RequireNot[T string | []string](content T, opts ...Option) *Harness

RequireNot fails the test immediately if content appears in view output. content is a string or any string in a []string.

func (*Harness) RequireSnapshot ΒΆ

func (h *Harness) RequireSnapshot(opts ...snapshot.Option) *Harness

RequireSnapshot compares Harness.View against a snapshot file. It fails the test immediately if the snapshot does not match.

func (*Harness) RequireWidth ΒΆ

func (h *Harness) RequireWidth(width int, opts ...Option) *Harness

RequireWidth fails the test immediately unless view output has width columns.

func (*Harness) Resize ΒΆ

func (h *Harness) Resize(width, height int) *Harness

Resize resizes the terminal, which sends the program a tea.WindowSizeMsg, or a command a SIGWINCH.

func (*Harness) ResizeRequest ΒΆ

func (h *Harness) ResizeRequest() (cols, rows int, ok bool)

ResizeRequest returns the last CSI 8 t window-resize request, with ok false until the program asks. The terminal is not resized.

func (*Harness) RightClick ΒΆ

func (h *Harness) RightClick(x, y int, opts ...reel.InputOption) *Harness

RightClick is Harness.MouseClick with the right mouse button.

func (*Harness) ScrollDown ΒΆ

func (h *Harness) ScrollDown(n int) *Harness

ScrollDown scrolls the terminal's viewport n rows toward the live screen. See Harness.ScrollUp.

func (*Harness) ScrollToBottom ΒΆ

func (h *Harness) ScrollToBottom() *Harness

ScrollToBottom scrolls the terminal's viewport back to the live screen. See Harness.ScrollUp.

func (*Harness) ScrollToTop ΒΆ

func (h *Harness) ScrollToTop() *Harness

ScrollToTop scrolls the terminal's viewport to the oldest scrollback row. See Harness.ScrollUp.

func (*Harness) ScrollUp ΒΆ

func (h *Harness) ScrollUp(n int) *Harness

ScrollUp scrolls the terminal's viewport n rows toward older scrollback, so Harness.View, snapshots, and a window-chrome scrollbar show the older rows. The program is not told. The viewport stays put as the program writes more, until Harness.ScrollDown or Harness.ScrollToBottom, or input from Harness.Type, Harness.Key, or Harness.Paste (see WithScrollOnInput).

func (*Harness) Scrollback ΒΆ

func (h *Harness) Scrollback() [][]vt.Cell

Scrollback returns a copy of the terminal's scrollback rows (oldest first). It is nil in alternate screen mode.

func (*Harness) ScrollbackCount ΒΆ

func (h *Harness) ScrollbackCount() int

ScrollbackCount returns the count of scrollback lines.

func (*Harness) ScrollbackOffset ΒΆ

func (h *Harness) ScrollbackOffset() int

ScrollbackOffset returns how many rows the terminal's viewport is scrolled up from the bottom, so zero while it shows the live screen.

func (*Harness) SendProgram ΒΆ

func (h *Harness) SendProgram(msg uv.Event) *Harness

SendProgram sends msg to the tea.Program.

Prefer driving input with Harness.Type, Harness.Key, Harness.Paste, or the mouse helpers, which go through the terminal as real input would.

func (*Harness) Session ΒΆ

func (h *Harness) Session() *reel.Session

Session returns the reel.Session the program runs in.

func (*Harness) SetBgColor ΒΆ

func (h *Harness) SetBgColor(c color.Color) *Harness

SetBgColor sets the terminal emulator background color.

func (*Harness) SetClipboard ΒΆ

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

SetClipboard sets the host clipboard returned for OSC 52 reads.

func (*Harness) SetCursorColor ΒΆ

func (h *Harness) SetCursorColor(c color.Color) *Harness

SetCursorColor sets the terminal emulator cursor color.

func (*Harness) SetDefaultBgColor ΒΆ

func (h *Harness) SetDefaultBgColor(c color.Color) *Harness

SetDefaultBgColor sets the background color used when the program sets none.

func (*Harness) SetDefaultCursorColor ΒΆ

func (h *Harness) SetDefaultCursorColor(c color.Color) *Harness

SetDefaultCursorColor sets the cursor color used when the program sets none.

func (*Harness) SetDefaultFgColor ΒΆ

func (h *Harness) SetDefaultFgColor(c color.Color) *Harness

SetDefaultFgColor sets the foreground color used when the program sets none.

func (*Harness) SetFgColor ΒΆ

func (h *Harness) SetFgColor(c color.Color) *Harness

SetFgColor sets the terminal emulator foreground color.

func (*Harness) SetScrollbackSize ΒΆ

func (h *Harness) SetScrollbackSize(maxLines int) *Harness

SetScrollbackSize sets the most scrollback lines the terminal keeps (see reel.Session.SetScrollbackSize).

func (*Harness) Sleep ΒΆ

func (h *Harness) Sleep(d time.Duration, opts ...Option) *Harness

Sleep lets the program run for d, such as to hold on a screen in a recording or let an animation run. It fails the test if the context is canceled first.

Prefer waiting for the expected screen, such as with Harness.Wait, which does not depend on how fast the program runs.

func (*Harness) Timeline ΒΆ

func (h *Harness) Timeline() *reel.Timeline

Timeline returns the session's reel.Timeline, which grows while the program runs.

func (*Harness) Title ΒΆ

func (h *Harness) Title() string

Title returns the last known window title.

func (*Harness) Type ΒΆ

func (h *Harness) Type(s string, opts ...reel.InputOption) *Harness

Type sends s to the terminal as key presses (see reel.Session.Type). Use Harness.Key for modifier combinations such as ctrl+a.

Input goes through the terminal, so mixing it with Harness.SendProgram can deliver events to the program in a different order than the calls. Pass reel.Delay to pause after each key press of this call.

func (*Harness) View ΒΆ

func (h *Harness) View() string

View returns the screen's text, which the harness's waits, assertions, and snapshots compare. Empty cells are spaces, so every line is Harness.Width wide and there are Harness.Height lines.

func (*Harness) Wait ΒΆ

func (h *Harness) Wait[T string | []byte | []string](contents T, opts ...Option) *Harness

Wait waits until view output contains contents. contents is a string, a []byte sequence, or every string in a []string.

func (*Harness) WaitFinished ΒΆ

func (h *Harness) WaitFinished(opts ...Option)

WaitFinished waits for the program to finish. It fails the test if the program failed: it returned an error other than tea.ErrProgramKilled, or it panicked, which Bubble Tea reports as killed. A command's nonzero exit status is not an error (see Harness.ExitStatus).

See also Harness.FinalModel.

func (*Harness) WaitFor ΒΆ

func (h *Harness) WaitFor(cond reel.Condition, opts ...Option) *Harness

WaitFor waits until cond holds for the screen (see reel.Session.Wait), such as reel.Matches for a regular expression, reel.ScreenFunc for a condition of the test's own on the screen's text, or reel.OnCursorLine for one on the cursor's line. cond is checked whenever the screen changes.

func (*Harness) WaitLiveMessage ΒΆ

func (h *Harness) WaitLiveMessage[T uv.Event](opts ...Option) T

WaitLiveMessage waits until a new message with concrete type T arrives after this call, then returns the first match.

func (*Harness) WaitLiveMessageWhere ΒΆ

func (h *Harness) WaitLiveMessageWhere[T uv.Event](fn func(T) bool, opts ...Option) T

WaitLiveMessageWhere waits until fn returns true for a message of type T received after this call. T is inferred from fn. fn should be quick, as for Harness.WaitMessageWhere.

func (*Harness) WaitMessage ΒΆ

func (h *Harness) WaitMessage[T uv.Event](opts ...Option) T

WaitMessage waits until at least one message with concrete type T has been observed, then returns the first match.

func (*Harness) WaitMessageWhere ΒΆ

func (h *Harness) WaitMessageWhere[T uv.Event](fn func(T) bool, opts ...Option) T

WaitMessageWhere waits until fn returns true for a message of type T across history and later messages. T is inferred from fn. fn should be quick: a wait that falls 32 messages behind misses the newer ones (see Harness.Messages).

func (*Harness) WaitNot ΒΆ

func (h *Harness) WaitNot[T string | []byte | []string](contents T, opts ...Option) *Harness

WaitNot waits until view output contains none of contents. contents is a string, a []byte sequence, or any string in a []string.

func (*Harness) WaitSettle ΒΆ

func (h *Harness) WaitSettle(opts ...Option) *Harness

WaitSettle waits until Harness.View has not changed for the settle timeout (WithSettle) (see reel.Session.WaitSettle).

See also Harness.WaitSettleMessages.

func (*Harness) WaitSettleMessages ΒΆ

func (h *Harness) WaitSettleMessages(opts ...Option) *Harness

WaitSettleMessages waits until the model has received no messages for the settle timeout (WithSettle), ignoring types from WithSettleIgnoreMsgs.

See also Harness.WaitSettle.

func (*Harness) Width ΒΆ

func (h *Harness) Width() int

Width returns the terminal width in cells.

func (*Harness) WorkingDirectory ΒΆ

func (h *Harness) WorkingDirectory() string

WorkingDirectory returns the last OSC 7 working directory.

type Hook ΒΆ

type Hook func(event HookEvent, stage HookStage, metadata any) error

Hook observes harness actions (see WithHooks). Errors fail the test immediately.

metadata is nil, or the action's InputMeta, WaitMeta, AssertMeta, or CaptureMeta, which marshal to JSON as the matching fields of the annotation the harness records for the action.

func LoggerHook ΒΆ

func LoggerHook(tb testing.TB, logger *slog.Logger) Hook

LoggerHook returns a hook that logs events to logger, or with testing.TB.Log when logger is nil.

type HookEvent ΒΆ

type HookEvent int

HookEvent identifies a harness action for hooks and mark labels (see Harness.Mark).

const (
	// HookInput is a keyboard, paste, or mouse action, with [InputMeta].
	HookInput HookEvent = iota
	// HookTerminal is a change to the terminal, such as a resize.
	HookTerminal
	// HookProgram is an action on the program, such as a message sent to it.
	HookProgram
	// HookWait is a wait, or a sleep, with [WaitMeta].
	HookWait
	// HookReady is the program's start.
	HookReady
	// HookClose is the program's end, and the harness's. The wait of
	// [Harness.WaitFinished] has [WaitMeta].
	HookClose
	// HookAssert is an assertion, with [AssertMeta].
	HookAssert
	// HookCapture is a snapshot comparison or image capture, with
	// [CaptureMeta].
	HookCapture
)

func (HookEvent) String ΒΆ

func (e HookEvent) String() string

String returns the event name used in mark labels and still file names.

type HookStage ΒΆ

type HookStage int

HookStage is whether a hook runs before or after the harness action.

const (
	HookStageBefore HookStage = iota
	HookStageAfter
)

func (HookStage) String ΒΆ

func (s HookStage) String() string

type InputKind ΒΆ

type InputKind string

InputKind is the kind of an input action.

const (
	// InputKindType is text typed as key presses ([Harness.Type]).
	InputKindType InputKind = "type"
	// InputKindKey is a key press ([Harness.Key]).
	InputKindKey InputKind = "key"
	// InputKindPaste is a paste ([Harness.Paste]).
	InputKindPaste InputKind = "paste"
	// InputKindMouse is a single mouse event ([Harness.Mouse]).
	InputKindMouse InputKind = "mouse"
	// InputKindClick is a press and release of a mouse button
	// ([Harness.MouseClick]).
	InputKindClick InputKind = "click"
	// InputKindDrag is a press of a mouse button, motion, and its release
	// ([Harness.MouseDrag]).
	InputKindDrag InputKind = "drag"
)

type InputMeta ΒΆ

type InputMeta struct {
	Kind InputKind `json:"action"`
	// Text is the text of [InputKindType] and [InputKindPaste].
	Text string `json:"text,omitzero"`
	// Key is the key of [InputKindKey], such as "enter" or "ctrl+c".
	Key string `json:"key,omitzero"`
	// Mouse is the mouse event of [InputKindMouse]: "press", "release", or
	// "motion".
	Mouse string `json:"mouse,omitzero"`
	// Button is the mouse button of [InputKindMouse], [InputKindClick], and
	// [InputKindDrag]: "left", "right", "middle", or "none" for motion
	// without one.
	Button string `json:"button,omitzero"`
}

InputMeta is the metadata of HookInput events.

func (InputMeta) LogValue ΒΆ

func (m InputMeta) LogValue() slog.Value

type Location ΒΆ

type Location struct {
	// X and Y are the cell's column and row, from 0.
	X, Y int
	// contains filtered or unexported fields
}

Location is a cell of the screen, returned by Harness.Locate.

func (Location) LeftClick ΒΆ

func (l Location) LeftClick(opts ...reel.InputOption) *Harness

LeftClick is Location.MouseClick with the left mouse button.

func (Location) MiddleClick ΒΆ

func (l Location) MiddleClick(opts ...reel.InputOption) *Harness

MiddleClick is Location.MouseClick with the middle mouse button.

func (Location) MouseClick ΒΆ

func (l Location) MouseClick(button vt.MouseButton, opts ...reel.InputOption) *Harness

MouseClick is Harness.MouseClick at l. It fails the test if l is off the screen, where the program would only see a release at the screen's edge.

func (Location) Offset ΒΆ

func (l Location) Offset(x, y int) Location

Offset returns the location x columns to the right and y rows below l. Negative values go left and up.

func (Location) RightClick ΒΆ

func (l Location) RightClick(opts ...reel.InputOption) *Harness

RightClick is Location.MouseClick with the right mouse button.

type MessageCollector ΒΆ

type MessageCollector interface {
	MessageHistory() iter.Seq[uv.Event]
	Messages(ctx context.Context) iter.Seq[uv.Event]
	LiveMessages(ctx context.Context) iter.Seq[uv.Event]
}

MessageCollector exposes messages observed by a test harness. Harness implements this interface.

type Option ΒΆ

type Option func(*options)

Option configures a Harness or assertion helper. Options given to NewHarness are defaults for every Harness method that accepts options, and per-call options override them.

func WithANSI ΒΆ

func WithANSI(enable bool) Option

WithANSI determines whether ANSI sequences are kept (the default) in the output that assertions and Harness.AssertSnapshot compare. It does not change JSON snapshots, images, or recordings, or the screen text waits check, which has none.

func WithArtifacts ΒΆ

func WithArtifacts(dir string) Option

WithArtifacts sets the directory relative output paths are written under, created when needed. A relative dir is relative to the test's package directory. Without it, files go to testing.TB.ArtifactDir, which is only kept with "go test -artifacts"; without that flag, an export logs that its files are removed. Absolute output paths ignore dir.

Environment: STEEP_EXPORT_DIR (overrides this option unless WithIgnoreEnv is set).

func WithAutoMarks ΒΆ

func WithAutoMarks(enable bool, categories ...string) Option

WithAutoMarks turns the marks the harness places itself (see Harness.Mark) on or off: those of categories, or all of them without categories. The categories are the hook events' names, such as HookInput.String() for "input:1", and MarkFailure. Calls apply in order, so WithAutoMarks(false) then WithAutoMarks(true, MarkFailure) keeps only the marks of failures. All are on by default. It only applies to NewHarness; an unknown category stops the test.

The rest of the recording is unchanged: annotations such as "steep.input" are still recorded, and the recording still ends where Harness.Close starts. A category that an export output's Marks or MarkMatch matches up to the first ":", such as "wait" for "wait:*", "*" for every category, or the events of WithStills, is marked anyway, so its stills are written (a pattern such as "*1" that only matches across the ":" is not). Anything else that looks for a mark that is off, such as effects.PauseAtMarks on "failure:1", finds none; WithRecording says how an export reports that.

func WithCheck ΒΆ

func WithCheck(interval time.Duration) Option

WithCheck configures how often Harness.WaitSettleMessages re-checks for messages. Waits on the screen do not poll: they check it whenever it changes. Defaults to DefaultCheckInterval. While exporting, it is also how long the program's output must stay quiet before an action is marked (see Harness.Mark).

Environment: STEEP_CHECK (overrides this option unless WithIgnoreEnv is set).

func WithColor ΒΆ

func WithColor(p ColorProfile) Option

WithColor sets the color support the harness terminal advertises to the program, through TERM, COLORTERM, and NO_COLOR and tea.WithColorProfile. Without it the terminal reports ColorANSI256, so Bubble Tea downsamples 24-bit colors; use ColorTrueColor for gradients and blends.

It overrides the color variables of WithEnvVars, whatever the order. A tea.WithColorProfile passed through WithProgramOptions wins over it.

For NewExecHarness, it also overrides those of cmd's environment, and without it the command's color support is as reel.Exec sets it.

func WithContext ΒΆ

func WithContext(ctx context.Context) Option

WithContext sets the parent context for blocking waits and the running program. A Harness defaults to the test's context, canceled on interrupt; other helpers default to context.Background.

func WithEnvVars ΒΆ

func WithEnvVars(vars ...string) Option

WithEnvVars sets environment variables for the tea.Program, in the form of "key=value". Without this option the program sees only the defaults:

  • TTY_FORCE=1
  • TERM=xterm-256color (see WithColor)

This option replaces those defaults with the test process environment plus vars, so pass TTY_FORCE and TERM again if the program needs them. It panics if an entry is not in "key=value" form.

The program sees the whole test process environment, which in CI may hold tokens and credentials. WithRecordMessages does not record it, but the program can still read and draw it.

For NewExecHarness, vars and the test process environment replace those of the same name in cmd's environment.

func WithExportAlways ΒΆ

func WithExportAlways() Option

WithExportAlways exports the recording (see WithRecording) at Harness.Close, whether the test passed or failed, except for a skipped test. See WithExportOnFailure for caveats and the environment override.

func WithExportOnFailure ΒΆ

func WithExportOnFailure() Option

WithExportOnFailure exports the recording (see WithRecording) at Harness.Close if the test has failed by then. Without an export policy, nothing is written.

Tests that panic or call testing.TB.FailNow still export. A test killed by "go test -timeout", or a panic on another goroutine, writes nothing.

Environment: STEEP_EXPORT_MODE (see the package documentation).

func WithHooks ΒΆ

func WithHooks(hooks ...Hook) Option

WithHooks appends hooks (see Hook). Calls accumulate.

func WithIgnoreEnv ΒΆ

func WithIgnoreEnv(ignore bool) Option

WithIgnoreEnv disables STEEP_* environment overrides. By default they are applied after every other option, so they override options set in code.

func WithLog ΒΆ

func WithLog(tb testing.TB) Option

WithLog logs every hooked action to tb, as WithHooks with a LoggerHook of a nil logger.

Environment: STEEP_LOG (on, true, or 1) adds the same hook unless WithIgnoreEnv is set.

func WithProgramOptions ΒΆ

func WithProgramOptions(opts ...tea.ProgramOption) Option

WithProgramOptions appends tea.ProgramOption values for NewHarness and NewComponentHarness, and has no effect on NewExecHarness. Environment, context, input, output, tea.WithoutSignals, and tea.WithWindowSize are always set by the harness and cannot be replaced here.

func WithRecordMessages ΒΆ

func WithRecordMessages(enable bool) Option

WithRecordMessages records each message the model receives as a "steep.message" annotation of its type and %+v value. It is off by default, since programs that tick or animate receive many messages, each of which grows the timeline and ".cast" file.

The tea.EnvMsg Bubble Tea sends the model is never recorded, since it holds the program's whole environment (see WithEnvVars). Other messages are recorded as they are, including any secret they hold, as is the text the program draws, which the effects package's Redact hides.

Environment: STEEP_RECORD_MESSAGES (on or off) overrides this option unless WithIgnoreEnv is set.

func WithRecording ΒΆ

func WithRecording(p reel.Profile) Option

WithRecording sets what an export writes as a reel.Profile. It does not export by itself (see WithExportAlways and WithExportOnFailure). Calls merge with reel.Profile.Merge. The profile's Render options also draw Harness.Image and Harness.ImageInto.

Besides reel's path variables and the profile's Vars, output paths may use {name}, the test's name with everything but letters and digits replaced by "_", and {status}, "passed" or "failed". Vars of the same names override them. Relative paths are under WithArtifacts. Nil Outputs write "{name}_{status}.gif" and "{name}_{status}.cast".

At export, reel.Context.Meta is the test's *Result. A mark the profile looks for that the test never placed does not stop the export (see reel.Export): for a failed test it is logged, and for a passing test it is a test error, except for "failure:n" marks, so a profile can pause on "failure:1" whatever the result. The recording ends where Harness.Close starts, so the program's repaint as it exits is left out, even when the close mark is off (see WithAutoMarks).

The STEEP_EXPORT* and STEEP_RENDERER_* variables patch the profile (see the package documentation).

func WithScrollOnInput ΒΆ

func WithScrollOnInput(enable bool) Option

WithScrollOnInput determines whether Harness.Type, Harness.Key (and the helpers built on it), and Harness.Paste first scroll a viewport scrolled with Harness.ScrollUp and friends back to the bottom, as a real terminal does on a key press. Enabled by default. Mouse events and Harness.SendProgram never scroll.

func WithSettle ΒΆ

func WithSettle(timeout time.Duration) Option

WithSettle configures the quiet period settle waits require. Defaults to DefaultSettleTimeout, and is capped at 80% of WithTimeout. While exporting, it also paces action marks (see Harness.Mark).

Environment: STEEP_SETTLE (overrides this option unless WithIgnoreEnv is set).

func WithSettleIgnoreMsgs ΒΆ

func WithSettleIgnoreMsgs(types ...any) Option

WithSettleIgnoreMsgs marks message types that do not reset the quiet period used by Harness.WaitSettleMessages. Pass a value of each type to ignore, such as a zero value of a periodic tick type; types must match exactly. Calls accumulate.

func WithSlow ΒΆ

func WithSlow(d time.Duration) Option

WithSlow makes the harness sleep for d after every hooked action, before its After hooks run, giving the program real time between actions for animations, ticks, and timers. Recordings do not need it: they play back the timeline as it happened, at reel.Profile.Speed.

Environment: STEEP_SLOW (overrides this option unless WithIgnoreEnv is set).

func WithSource ΒΆ

func WithSource(enable bool) Option

WithSource records where in the test each annotation and mark of the recording came from (see reel.WithSource): a "source" member of each annotation, and a "reel.mark" annotation after each mark, of the file, line, and function of the test's line that led to it, past any of the test's helpers. "steep.start" also gets the test's package, its module, and its directory in the module, and file paths are relative to the module's root, so they hold no home or build directories. Annotations and marks no line of the test led to, such as messages the model receives, or those Harness.Close places as a test cleanup, get none. Off by default; while off, no stack is walked.

Environment: STEEP_SOURCE (on or off) overrides this option unless WithIgnoreEnv is set.

func WithStills ΒΆ

func WithStills(kinds StillKind, events ...HookEvent) Option

WithStills writes stills of kinds at the marks of events (see Harness.Mark), one file per mark: "{name}_{status}_{mark}.png" for StillPNG and "{name}_{status}_{mark}.snap" for StillSnapshot, such as "TestLogin_failed_input-2.png" for the mark "input:2" (the ":" is not allowed in Windows file names; see reel.File.Marks). Without events, it uses input, terminal, program, wait, ready, and close. Calls accumulate.

Each is a reel.File whose reel.File.MarkMatch matches the events' marks, such as "input:[0-9]*", so the harness marks those events even if WithAutoMarks turns their marks off. Stills are only written when the recording is exported. For a single PNG of the last frame, add a ".png" output to the profile instead.

func WithTimeout ΒΆ

func WithTimeout(timeout time.Duration) Option

WithTimeout configures the maximum time blocking waits (startup, waits, settle, mutation) may run before failing the test. Defaults to DefaultTimeout.

Environment: STEEP_TIMEOUT (overrides this option unless WithIgnoreEnv is set).

func WithWindowSize ΒΆ

func WithWindowSize(width, height int) Option

WithWindowSize configures the starting terminal size.

See also DefaultTermWidth and DefaultTermHeight.

type Result ΒΆ

type Result struct {
	// Name is the test's name, [testing.TB.Name].
	Name string
	// Failed is whether the test had failed, or was panicking, by the time
	// [Harness.Close] ran.
	Failed bool
	// Failures are the failure messages the harness reported, oldest first,
	// followed by "test panicked" for a test that panicked. The harness marks
	// each but "test panicked" as "failure:n" (see [Harness.Mark]). Failures
	// the test reports through its own [testing.TB] are not included.
	Failures []string
	// Duration is how long the harness ran, from [NewHarness] to
	// [Harness.Close].
	Duration time.Duration
}

Result is a test's result, which an export passes to effects as reel.Context.Meta, such as for Outro.

type StillKind ΒΆ

type StillKind uint8

StillKind is a kind of still WithStills writes at a hook event's mark. Kinds combine with |.

const (
	// StillPNG is a PNG of the terminal at the mark, drawn with the profile's
	// Render options and only its [reel.StillEffect] effects.
	StillPNG StillKind = 1 << iota
	// StillSnapshot is a ".snap" file of the screen's text at the mark, as
	// [Harness.View] showed it, from the recording's replay.
	StillSnapshot
)

type Viewable ΒΆ

type Viewable func() string

Viewable returns the output the package-level assertions check, such as Harness.View.

type WaitKind ΒΆ

type WaitKind string

WaitKind is what a wait waits for.

const (
	// WaitKindText waits for text to be on the screen ([Harness.Wait]).
	WaitKindText WaitKind = "text"
	// WaitKindNotText waits for text not to be on the screen
	// ([Harness.WaitNot]).
	WaitKindNotText WaitKind = "not_text"
	// WaitKindCondition waits for a [reel.Condition] on the screen
	// ([Harness.WaitFor]).
	WaitKindCondition WaitKind = "condition"
	// WaitKindSettle waits for the screen to stay unchanged
	// ([Harness.WaitSettle]).
	WaitKindSettle WaitKind = "settle"
	// WaitKindSettleMessages waits for the model to receive no messages
	// ([Harness.WaitSettleMessages]).
	WaitKindSettleMessages WaitKind = "settle_messages"
	// WaitKindMessage waits for the model to receive a message of a type
	// ([Harness.WaitMessage] and the like).
	WaitKindMessage WaitKind = "message"
	// WaitKindFinished waits for the program to end
	// ([Harness.WaitFinished]).
	WaitKindFinished WaitKind = "finished"
	// WaitKindSleep waits for time to pass ([Harness.Sleep]).
	WaitKindSleep WaitKind = "sleep"
)

type WaitMeta ΒΆ

type WaitMeta struct {
	Kind WaitKind `json:"wait"`
	// Live is true for a [WaitKindMessage] wait that only counts messages
	// received after it starts, such as [Harness.WaitLiveMessage].
	Live bool `json:"live,omitzero"`
}

WaitMeta is the metadata of HookWait events, and of the HookClose event of Harness.WaitFinished.

func (WaitMeta) LogValue ΒΆ

func (m WaitMeta) LogValue() slog.Value

Directories ΒΆ

Path Synopsis
cmd
reel module
internal
broker
Package broker provides a generic in-memory event broker.
Package broker provides a generic in-memory event broker.
typeobserver
Package typeobserver counts the types of values it observes, and when each was first and last seen, for failure messages.
Package typeobserver counts the types of values it observes, and when each was first and last seen, for failure messages.
xansi
Package xansi normalizes terminal output for stable snapshots and assertions: stripping ANSI, spinner glyphs, and private-use code points, normalizing newlines, and escaping ESC for readable diffs.
Package xansi normalizes terminal output for stable snapshots and assertions: stripping ANSI, spinner glyphs, and private-use code points, normalizing newlines, and escaping ESC for readable diffs.
reel module
Package snapshot provides snapshot assertions for tests.
Package snapshot provides snapshot assertions for tests.

Jump to

Keyboard shortcuts

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