Minimalist Go package aimed at creating Console User Interfaces. A community fork based on the amazing work of jroimartin For v0 to v1 mirgration help read: migrate-to-v1.md
This fork has many improvements over the original work from jroimartin.
For information about this org see: awesome-gocui/about.
Execute:
$ go get github.com/awesome-gocui/gocui
Execute:
$ go doc github.com/awesome-gocui/gocui
Or visit godoc.org to read it online.
See the _example folder for more examples
package main
import (
"fmt"
"log"
"github.com/awesome-gocui/gocui"
)
func main() {
g, err := gocui.NewGui(gocui.OutputNormal, true)
if err != nil {
log.Panicln(err)
}
defer g.Close()
g.SetManagerFunc(layout)
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err)
}
if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
log.Panicln(err)
}
}
func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2, 0); err != nil {
if !errors.Is(err, gocui.ErrUnknownView) {
return err
}
if _, err := g.SetCurrentView("hello"); err != nil {
return err
}
fmt.Fprintln(v, "Hello world!")
}
return nil
}
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
You can write simple tests for gocui
which let you simulate keyboard and then validate the output drawn to the screen.
gui
with OutputSimulator
set as the mode g, err := NewGui(OutputSimulator, true)
GetTestingScreen
to get a testingScreen
instance. SendKey
to simulate input and GetViewContent
to evaluate what is drawn.Warning: Timing plays a part here, key bindings don't fire synchronously and drawing isn't instant. Here we used
time.After
to pause,gomega
's asynchronous assertions are likely a better alternative for more complex tests.
Here is a simple example showing how this can be used to validate what a view shows and that a key binding is handled correctly:
func TestTestingScreenReturnsCorrectContent(t *testing.T) {
// Track what happened in the view, we'll assert on these
didCallCTRLC := false
expectedViewContent := "Hello world!"
viewName := "testView1"
// Create a view specifying the "OutputSimulator" mode
g, err := NewGui(OutputSimulator, true)
if err != nil {
log.Panicln(err)
}
g.SetManagerFunc(func(g *Gui) error {
maxX, maxY := g.Size()
if v, err := g.SetView(viewName, maxX/2-7, maxY/2, maxX/2+7, maxY/2+2, 0); err != nil {
if !errors.Is(err, ErrUnknownView) {
return err
}
if _, err := g.SetCurrentView(viewName); err != nil {
return err
}
// Have the view draw "Hello world!"
fmt.Fprintln(v, expectedViewContent)
}
return nil
})
// Create a key binding which sets "didCallCTRLC" when triggered
exampleBindingToTest := func(g *Gui, v *View) error {
didCallCTRLC = true
return nil
}
if err := g.SetKeybinding("", KeyCtrlC, ModNone, exampleBindingToTest); err != nil {
log.Panicln(err)
}
// Create a test screen and start gocui
testingScreen := g.GetTestingScreen()
cleanup := testingScreen.StartGui()
defer cleanup()
// Send a key to gocui
testingScreen.SendKey(KeyCtrlC)
// Wait for key to be processed
<-time.After(time.Millisecond * 50)
// Test that the keybinding fired and set "didCallCTRLC" to true
if !didCallCTRLC {
t.Error("Expect the simulator to invoke the key handler for CTRLC")
}
// Get the content from the testing screen
actualContent, err := testingScreen.GetViewContent(viewName)
if err != nil {
t.Error(err)
}
// Test that it contains the "Hello World!" we thought the view should draw
if strings.TrimSpace(actualContent) != expectedViewContent {
t.Error(fmt.Printf("Expected view content to be: %q got: %q", expectedViewContent, actualContent))
}
}
Note: Under the covers this is using the
tcell
SimulationScreen
.
Note: if your project is not listed here, let us know! :)