Files
MujicaUI/overlay_test.go
T

202 lines
5.4 KiB
Go

package mujica
import (
"runtime"
"testing"
"github.com/egoist/mygo/ui"
)
type overlayApp struct {
open bool
opts OverlayOptions
behind int
panel ui.Rect
}
func (a *overlayApp) view(c *ui.Context) {
Use(c, Settings{})
ui.Row(c).Padding(20).Gap(8).Children(func() {
if Button(c, "Show", ButtonOptions{}).Clicked() {
a.open = true
}
if Button(c, "Behind", ButtonOptions{}).Clicked() {
a.behind++
}
})
if p := Overlay(c, &a.open, a.opts, func() {
ui.Text(c, "Layer")
if Button(c, "Done", ButtonOptions{}).Clicked() {
a.open = false
}
}); p != nil {
a.panel = p.Bounds()
}
}
func newOverlayApp(opts OverlayOptions) (*overlayApp, *ui.Tester) {
a := &overlayApp{opts: opts}
tt := ui.NewTester(a.view, 600, 400)
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
_ = tt.Click("Show")
tt.Frame()
return a, tt
}
func TestOverlayPlaces(t *testing.T) {
for _, modal := range []bool{false, true} {
for _, tc := range []struct {
place OverlayPlace
fx, fy float32
}{{OverlayCenter, 0.5, 0.5}, {OverlayTopLeft, 0, 0}, {OverlayBottomRight, 1, 1}, {OverlayTop, 0.5, 0}} {
a, _ := newOverlayApp(OverlayOptions{Place: tc.place, Modal: modal})
p := a.panel
wantX := 16 + (600-32-p.W)*tc.fx
wantY := 16 + (400-32-p.H)*tc.fy
if abs(p.X-wantX) > 1 || abs(p.Y-wantY) > 1 {
t.Errorf("modal %v place %d: panel %+v, want at (%v, %v)", modal, tc.place, p, wantX, wantY)
}
}
}
}
func abs(v float32) float32 {
if v < 0 {
return -v
}
return v
}
func TestOverlayModal(t *testing.T) {
a, tt := newOverlayApp(OverlayOptions{Modal: true, Dim: true})
if !tt.Focused("Done") {
t.Fatal("modal overlay did not take the focus")
}
_ = tt.Click("Behind")
if a.behind != 0 || a.open {
t.Fatalf("backdrop: behind %d open %v", a.behind, a.open)
}
if !tt.Focused("Show") {
t.Fatal("focus not restored")
}
}
func TestOverlayNonModal(t *testing.T) {
a, tt := newOverlayApp(OverlayOptions{Place: OverlayBottomRight})
_ = tt.Click("Behind")
if a.behind != 1 || !a.open {
t.Fatalf("behind %d open %v", a.behind, a.open)
}
tt.Key(0, ui.KeyEscape)
if a.open {
t.Fatal("Escape did not close")
}
}
func TestOverlayDimNeedsModal(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatal("no panic")
}
}()
open := true
ui.NewTester(func(c *ui.Context) {
Use(c, Settings{})
Overlay(c, &open, OverlayOptions{Dim: true}, func() {})
}, 200, 200)
}
// TestOverlayLifecycle opens and closes every overlay 100 times and checks
// that nothing grows: goroutines, heap objects or the elements left behind.
func TestOverlayLifecycle(t *testing.T) {
var open bool
views := map[string]func(c *ui.Context, b *ui.Element){
"Dialog": func(c *ui.Context, b *ui.Element) { Dialog(c, &open, DialogOptions{Title: "T"}, func() {}) },
"Alert": func(c *ui.Context, b *ui.Element) {
AlertDialog(c, &open, AlertDialogOptions{Title: "T", ConfirmText: "x"})
},
"Drawer": func(c *ui.Context, b *ui.Element) { Drawer(c, &open, DrawerOptions{Title: "T"}, nil) },
"Drawer2": func(c *ui.Context, b *ui.Element) { Drawer(c, &open, DrawerOptions{Title: "T", NonModal: true}, nil) },
"Popover": func(c *ui.Context, b *ui.Element) { Popover(c, b, &open, PopoverOptions{}, func() {}) },
"Confirm": func(c *ui.Context, b *ui.Element) { Popconfirm(c, b, &open, PopconfirmOptions{Title: "T"}) },
"Overlay": func(c *ui.Context, b *ui.Element) { Overlay(c, &open, OverlayOptions{Modal: true}, func() {}) },
"Menu": func(c *ui.Context, b *ui.Element) {
popupMenuOpen(c, b, &open, (&menuApp{}).build)
},
"Context": nil,
}
for name, view := range views {
tt := ui.NewTester(func(c *ui.Context) {
Use(c, Settings{})
row := ui.Row(c)
var b *ui.Element
row.Children(func() { b = Button(c, "Anchor", ButtonOptions{}) })
if view == nil {
ContextMenu(c, row, ContextMenuOptions{}, (&menuApp{}).build)
} else {
view(c, b)
}
}, 600, 400)
tt.SetPreferences(ui.Preferences{ReduceMotion: true, TextScale: 1})
cycle := func() {
open = true
if view == nil {
_ = tt.Click("Anchor")
tt.Key(ui.Shift, ui.KeyF10)
if !tt.HasText("Rename") {
t.Fatalf("%s: did not open", name)
}
}
tt.Frame()
if name != "Drawer2" {
tt.Key(0, ui.KeyEscape)
if view == nil {
open = false
}
} else {
open = false
tt.Frame()
}
if open || tt.HasText("Rename") {
t.Fatalf("%s: Escape did not close", name)
}
}
for range 20 {
cycle()
}
texts := len(tt.Texts())
before := heapObjects()
goroutines := runtime.NumGoroutine()
for range 100 {
cycle()
}
if got := len(tt.Texts()); got != texts {
t.Errorf("%s: texts %d -> %d", name, texts, got)
}
if g := runtime.NumGoroutine(); g > goroutines {
t.Errorf("%s: goroutines %d -> %d", name, goroutines, g)
}
if after := heapObjects(); after > before+2000 {
t.Errorf("%s: heap objects %d -> %d", name, before, after)
}
}
}
func heapObjects() uint64 {
runtime.GC()
var m runtime.MemStats
runtime.ReadMemStats(&m)
return m.HeapObjects
}
func TestOverlaySnapshot(t *testing.T) {
snapshot(t, "overlay", 480, 260, func(c *ui.Context) {
open := true
ui.Column(c).Padding(24).Children(func() { ui.Text(c, "Score of the second act") })
Overlay(c, &open, OverlayOptions{Place: OverlayBottomRight, Label: "Now playing"}, func() {
ui.Text(c, "Now playing").FontWeight(600)
ui.Text(c, "Symbol I: △").TextColor(Tokens(c).TextMuted)
})
})
}