package mujica import ( "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 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{}) }) }) }