Testing
Test bloom layouts with Render and Solve in plain unit tests, and test whole programs end to end with steep.
Because a bloom view is a function of your model, most of it can be tested without running a terminal.
Test what a layout draws
bloom.Render draws a tree at a size and returns a string.
Compare it with a golden file, or check for the text you expect:
func TestSidebarCollapses(t *testing.T) {
m := newModel()
m.width, m.height = 50, 10
got := ansi.Strip(bloom.Render(m.layout(), m.width, m.height))
if strings.Contains(got, "Groceries") {
t.Errorf("sidebar should collapse at 50 columns:\n%s", got)
}
}Render returns styled text; ansi.Strip from github.com/charmbracelet/x/ansi removes the styles when you only
care about the text. Render has no animation state, so it always draws the final layout, with transitions at
whatever progress you give them.
This works best when your model has a method that returns the layout, separate from View, which only adds the
tea.View settings.
Test where things go
bloom.Solve returns the geometry without drawing. Use it to
check sizes and positions, which is less brittle than comparing drawn output:
func TestReaderMinimum(t *testing.T) {
frame := bloom.Solve(newModel().layout(), 60, 20)
r, ok := frame.Rect(paneReader)
if !ok {
t.Fatal("reader not in the frame")
}
if r.Dx() < 40 {
t.Errorf("reader is %d columns wide, want at least 40", r.Dx())
}
}Test a whole program
For behavior that depends on the event loop, such as clicks, focus, timing, and animation, run the program in an emulated terminal. steep does that: it drives the program with keys, text, mouse events, and resizes, waits for what it draws, and compares snapshots. bloom's own examples are tested this way:
func TestNotes(t *testing.T) {
h := steep.NewHarness(t, newModel(), steep.WithWindowSize(80, 12))
h.Wait("Groceries")
h.LeftClick(3, 2).Wait("A terminal garden")
h.Key("d").Wait("Delete \"Ideas\"?")
h.AssertSnapshot()
h.Key("ctrl+c")
h.WaitFinished()
}Clicks go through the same path as a real terminal's, so this tests your IDs and mouse handling too.
Make animations deterministic
Animations run on the wall clock, which makes timing-sensitive tests flaky. Give values a clock you control with
anim.Clock, and the root one with
bloom.WithClock for layout animation. Or wait for the
end state instead: tests usually care that a dialog opened, not how it looked halfway.
Last updated on