285 lines
7.6 KiB
Go
285 lines
7.6 KiB
Go
package mujica
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
)
|
|
|
|
type menubarApp struct {
|
|
wrap bool
|
|
zoom string
|
|
chosen []string
|
|
}
|
|
|
|
func (a *menubarApp) menus() []MenubarMenu {
|
|
return []MenubarMenu{
|
|
{Label: "File", Items: []MenubarItem{
|
|
{ID: "new", Label: "New", Shortcut: MenubarShortcut{ui.Cmd, ui.KeyN}},
|
|
{ID: "open", Label: "Open…", Shortcut: MenubarShortcut{ui.Cmd, ui.KeyO}},
|
|
{Label: "Export", Items: []MenubarItem{
|
|
{ID: "pdf", Label: "PDF"},
|
|
{ID: "png", Label: "Image", Disabled: true},
|
|
{ID: "svg", Label: "Vector"},
|
|
}},
|
|
{Kind: MenubarSeparator},
|
|
{ID: "quit", Label: "Quit"},
|
|
}},
|
|
{Label: "View", Items: []MenubarItem{
|
|
{ID: "wrap", Label: "Word wrap", Kind: MenubarCheck, Checked: a.wrap},
|
|
{Kind: MenubarSeparator},
|
|
{ID: "zoom-fit", Label: "Fit", Kind: MenubarRadio, Checked: a.zoom == "zoom-fit"},
|
|
{ID: "zoom-100", Label: "Actual size", Kind: MenubarRadio, Checked: a.zoom == "zoom-100"},
|
|
}},
|
|
{Label: "Help", Items: []MenubarItem{{ID: "about", Label: "About"}}},
|
|
}
|
|
}
|
|
|
|
func (a *menubarApp) view(c *ui.Context) {
|
|
Use(c, Settings{})
|
|
ui.Column(c).Fill().Children(func() {
|
|
if id, ok := Menubar(c, a.menus(), MenubarOptions{}).Chosen(); ok {
|
|
a.chosen = append(a.chosen, id)
|
|
switch id {
|
|
case "wrap":
|
|
a.wrap = !a.wrap
|
|
case "zoom-fit", "zoom-100":
|
|
a.zoom = id
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestMenubarPointer(t *testing.T) {
|
|
a := &menubarApp{zoom: "zoom-fit"}
|
|
tt := ui.NewTester(a.view, 700, 500)
|
|
if err := tt.Click("View"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := tt.Click("Word wrap"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !a.wrap || tt.HasText("Actual size") {
|
|
t.Fatalf("check item: wrap %v, menu still open %v", a.wrap, tt.HasText("Actual size"))
|
|
}
|
|
_ = tt.Click("View")
|
|
if _, ok := tt.Find("Word wrap, checked"); !ok {
|
|
t.Fatal("checked state not named")
|
|
}
|
|
if err := tt.Click("Actual size"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if a.zoom != "zoom-100" {
|
|
t.Fatalf("radio chose %q", a.zoom)
|
|
}
|
|
_ = tt.Click("File")
|
|
r, _ := tt.Find("Export")
|
|
tt.Move(r.X+r.W/2, r.Y+r.H/2)
|
|
tt.Move(r.X+r.W/2+1, r.Y+r.H/2)
|
|
if !tt.HasText("Vector") {
|
|
t.Fatalf("hover did not open submenu: %q", tt.Texts())
|
|
}
|
|
_ = tt.Click("Image")
|
|
if err := tt.Click("Vector"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := a.chosen; len(got) != 3 || got[2] != "svg" {
|
|
t.Fatalf("chosen %q", got)
|
|
}
|
|
_ = tt.Click("Help")
|
|
tt.ClickAt(600, 400)
|
|
if tt.HasText("About") {
|
|
t.Fatal("click outside did not close")
|
|
}
|
|
}
|
|
|
|
func TestMenubarHoverSwitchesMenus(t *testing.T) {
|
|
a := &menubarApp{}
|
|
tt := ui.NewTester(a.view, 700, 500)
|
|
_ = tt.Click("File")
|
|
r, _ := tt.Find("Help")
|
|
tt.Move(r.X+2, r.Y+2)
|
|
if tt.HasText("Quit") || !tt.HasText("About") {
|
|
t.Fatalf("hover did not switch: %q", tt.Texts())
|
|
}
|
|
}
|
|
|
|
func TestMenubarKeyboard(t *testing.T) {
|
|
a := &menubarApp{}
|
|
tt := ui.NewTester(a.view, 700, 500)
|
|
tt.Key(0, ui.KeyTab)
|
|
if !tt.Focused("File") {
|
|
t.Fatal("Tab did not reach the bar")
|
|
}
|
|
tt.Key(0, ui.KeyRight)
|
|
tt.Key(0, ui.KeyLeft)
|
|
if !tt.Focused("File") {
|
|
t.Fatal("arrows did not move between titles")
|
|
}
|
|
tt.Key(0, ui.KeyDown) // opens File on New
|
|
tt.Key(0, ui.KeyDown) // Open
|
|
tt.Key(0, ui.KeyDown) // Export
|
|
tt.Key(0, ui.KeyRight)
|
|
if !tt.HasText("Vector") {
|
|
t.Fatal("Right did not open the submenu")
|
|
}
|
|
tt.Key(0, ui.KeyDown) // skips disabled Image to Vector
|
|
tt.Key(0, ui.KeyEnter)
|
|
if len(a.chosen) != 1 || a.chosen[0] != "svg" {
|
|
t.Fatalf("chosen %q", a.chosen)
|
|
}
|
|
if tt.HasText("Quit") || !tt.Focused("File") {
|
|
t.Fatal("menu did not close back to its title")
|
|
}
|
|
tt.Key(0, ui.KeyDown)
|
|
tt.Key(0, ui.KeyUp) // wraps past the separator to Quit
|
|
tt.Key(0, ui.KeyRight)
|
|
if !tt.HasText("Word wrap") {
|
|
t.Fatal("Right on a plain item did not move to the next menu")
|
|
}
|
|
tt.Key(0, ui.KeySpace)
|
|
if !a.wrap {
|
|
t.Fatal("Space did not toggle the check item")
|
|
}
|
|
tt.Key(0, ui.KeyDown)
|
|
tt.Key(0, ui.KeyEscape)
|
|
if tt.HasText("Word wrap") {
|
|
t.Fatal("Escape did not close")
|
|
}
|
|
}
|
|
|
|
func TestMenubarKeysAfterClickInMenu(t *testing.T) {
|
|
a := &menubarApp{}
|
|
tt := ui.NewTester(a.view, 700, 500)
|
|
_ = tt.Click("File")
|
|
if err := tt.Click("Export"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !tt.Focused("File") {
|
|
t.Fatal("click into the menu left the title without focus")
|
|
}
|
|
tt.Key(0, ui.KeyDown) // PDF to Vector, skipping disabled Image
|
|
tt.Key(0, ui.KeyEnter)
|
|
if len(a.chosen) != 1 || a.chosen[0] != "svg" {
|
|
t.Fatalf("arrows after a click chose %q", a.chosen)
|
|
}
|
|
_ = tt.Click("File")
|
|
_ = tt.Click("Export")
|
|
_ = tt.Click("Image") // disabled: the press still keeps keys working
|
|
tt.Key(0, ui.KeyEscape)
|
|
if tt.HasText("Vector") || !tt.HasText("Quit") {
|
|
t.Fatalf("Escape did not close only the submenu: %q", tt.Texts())
|
|
}
|
|
tt.Key(0, ui.KeyEscape)
|
|
if tt.HasText("Quit") {
|
|
t.Fatal("second Escape did not close the menu")
|
|
}
|
|
}
|
|
|
|
func TestMenubarTabOutClosesMenu(t *testing.T) {
|
|
for _, back := range []bool{false, true} {
|
|
a := &menubarApp{}
|
|
tt := ui.NewTester(func(c *ui.Context) {
|
|
Use(c, Settings{})
|
|
ui.Column(c).Fill().Children(func() {
|
|
Button(c, "Before", ButtonOptions{})
|
|
Menubar(c, a.menus(), MenubarOptions{})
|
|
Button(c, "After", ButtonOptions{})
|
|
})
|
|
}, 700, 500)
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyDown)
|
|
if !tt.HasText("Quit") {
|
|
t.Fatal("menu did not open")
|
|
}
|
|
mods, want := ui.Modifiers(0), "After"
|
|
if back {
|
|
mods, want = ui.Shift, "Before"
|
|
}
|
|
tt.Key(mods, ui.KeyTab)
|
|
if tt.HasText("Quit") || !tt.Focused(want) {
|
|
t.Fatalf("shift=%v: menu open %v, focus on %s %v", back, tt.HasText("Quit"), want, tt.Focused(want))
|
|
}
|
|
tt.Key(0, ui.KeyEscape)
|
|
if !tt.Focused(want) {
|
|
t.Fatalf("shift=%v: Escape moved focus off %s", back, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMenubarLongMenuScrolls(t *testing.T) {
|
|
var items []MenubarItem
|
|
for i := range 30 {
|
|
items = append(items, MenubarItem{ID: fmt.Sprint(i), Label: fmt.Sprintf("Entry %02d", i)})
|
|
}
|
|
tt := ui.NewTester(func(c *ui.Context) {
|
|
Use(c, Settings{})
|
|
Menubar(c, []MenubarMenu{{Label: "Long", Items: items}}, MenubarOptions{})
|
|
}, 400, 300)
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyDown)
|
|
tt.Key(0, ui.KeyEnd)
|
|
tt.Frame()
|
|
r, ok := tt.Find("Entry 29")
|
|
if !ok || r.H == 0 || r.Y+r.H > 300 {
|
|
t.Fatalf("last item outside the window: %+v", r)
|
|
}
|
|
}
|
|
|
|
func TestMenubarAnnouncesHighlight(t *testing.T) {
|
|
a := &menubarApp{wrap: true}
|
|
tt := ui.NewTester(a.view, 700, 500)
|
|
tt.Key(0, ui.KeyTab)
|
|
tt.Key(0, ui.KeyRight)
|
|
tt.Key(0, ui.KeyDown) // View opens on Word wrap
|
|
tt.Key(0, ui.KeyDown) // Fit
|
|
got := tt.Announcements()
|
|
if len(got) < 2 || got[len(got)-2] != "Word wrap, checked" || got[len(got)-1] != "Fit" {
|
|
t.Fatalf("announcements %q", got)
|
|
}
|
|
}
|
|
|
|
func TestMenubarShortcutText(t *testing.T) {
|
|
got := menubarShortcutText(MenubarShortcut{ui.Ctrl | ui.Shift, ui.KeyF5})
|
|
want := "Ctrl+Shift+F5"
|
|
if runtime.GOOS == "darwin" {
|
|
want = "⌃⇧F5"
|
|
}
|
|
if got != want {
|
|
t.Fatalf("got %q want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestMenubarInvalid(t *testing.T) {
|
|
for name, menus := range map[string][]MenubarMenu{
|
|
"empty": nil,
|
|
"title": {{Items: []MenubarItem{{ID: "a", Label: "A"}}}},
|
|
"id": {{Label: "M", Items: []MenubarItem{{Label: "A"}}}},
|
|
"submenu": {{Label: "M", Items: []MenubarItem{{Label: "A", Kind: MenubarCheck, Items: []MenubarItem{}}}}},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
defer func() {
|
|
if recover() == nil {
|
|
t.Fatal("no panic")
|
|
}
|
|
}()
|
|
ui.Render(func(c *ui.Context) {
|
|
Use(c, Settings{})
|
|
Menubar(c, menus, MenubarOptions{})
|
|
}, 300, 100, 1)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMenubarSnapshot(t *testing.T) {
|
|
a := &menubarApp{wrap: true, zoom: "zoom-fit"}
|
|
snapshot(t, "menubar", 480, 100, func(c *ui.Context) {
|
|
ui.Column(c).Padding(16).Children(func() {
|
|
Menubar(c, a.menus(), MenubarOptions{})
|
|
})
|
|
})
|
|
}
|