202 lines
5.4 KiB
Go
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)
|
|
})
|
|
})
|
|
}
|