package mujica import ( "fmt" "testing" "github.com/egoist/mygo/ui" "mujicaui/icons" ) func commandPaletteSample() []Command { return []Command{ {ID: "new", Label: "New document", Group: "File", Icon: icons.Must("file")}, {ID: "open", Label: "Open recent", Group: "File", Icon: icons.Must("folder")}, {ID: "theme", Label: "Toggle dark mode", Group: "View", Keywords: []string{"theme", "night"}, Icon: icons.Must("moon")}, {ID: "zoom", Label: "Zoom in", Group: "View", Icon: icons.Must("zoom-in")}, {ID: "save", Label: "Save all", Group: "File"}, } } type paletteApp struct { open bool ran []string } func (a *paletteApp) view(c *ui.Context) { Use(c, Settings{}) ui.Column(c).Fill().Children(func() { ui.Text(c, "Editor") if id, ok := CommandPalette(c, &a.open, commandPaletteSample(), CommandPaletteOptions{}).Chosen(); ok { a.ran = append(a.ran, id) } }) } func TestCommandPaletteFilter(t *testing.T) { ids := func(cs []Command) (out []string) { for _, c := range cs { out = append(out, c.ID) } return } got := ids(commandPaletteFilter(commandPaletteSample(), "")) // Groups keep first-appearance order: File's Save joins its group. want := []string{"new", "open", "save", "theme", "zoom"} if len(got) != len(want) { t.Fatalf("got %v", got) } for i := range want { if got[i] != want[i] { t.Fatalf("got %v want %v", got, want) } } if got := ids(commandPaletteFilter(commandPaletteSample(), "NIGHT")); len(got) != 1 || got[0] != "theme" { t.Fatalf("keyword search %v", got) } if got := ids(commandPaletteFilter(commandPaletteSample(), "file save")); len(got) != 1 || got[0] != "save" { t.Fatalf("multi-word search %v", got) } } func TestCommandPaletteShortcutSearchRun(t *testing.T) { a := &paletteApp{} tt := ui.NewTester(a.view, 800, 600) tt.Key(ui.Cmd, ui.KeyK) if !a.open || !tt.HasText("Zoom in") { t.Fatalf("Cmd+K did not open: %q", tt.Texts()) } tt.Type("zo") if tt.HasText("New document") || !tt.HasText("Zoom in") { t.Fatalf("search did not filter: %q", tt.Texts()) } tt.Key(0, ui.KeyEnter) if a.open || len(a.ran) != 1 || a.ran[0] != "zoom" { t.Fatalf("Enter: open %v ran %q", a.open, a.ran) } tt.Key(ui.Cmd, ui.KeyK) if !tt.HasText("New document") { t.Fatal("query not reset on reopen") } tt.Key(0, ui.KeyDown) tt.Key(0, ui.KeyDown) tt.Key(0, ui.KeyUp) tt.Key(0, ui.KeyUp) tt.Key(0, ui.KeyUp) // wraps to the last tt.Key(0, ui.KeyEnter) if len(a.ran) != 2 || a.ran[1] != "zoom" { t.Fatalf("arrows ran %q", a.ran) } } func TestCommandPaletteAnnouncesHighlight(t *testing.T) { a := &paletteApp{open: true} tt := ui.NewTester(a.view, 800, 600) tt.Key(0, ui.KeyDown) tt.Key(0, ui.KeyDown) got := tt.Announcements() if len(got) < 2 || got[len(got)-2] != "Open recent" || got[len(got)-1] != "Save all" { t.Fatalf("announcements %q", got) } } func TestCommandPaletteFilterRevealsHighlight(t *testing.T) { var cmds []Command for i := range 30 { cmds = append(cmds, Command{ID: fmt.Sprint(i), Label: fmt.Sprintf("Command %02d", i), Keywords: []string{"royal"}}) } open := true tt := ui.NewTester(func(c *ui.Context) { Use(c, Settings{}) CommandPalette(c, &open, cmds, CommandPaletteOptions{}) }, 800, 600) tt.Key(0, ui.KeyUp) // wraps to the last, scrolling to the bottom tt.Frame() if r, _ := tt.Find("Command 00"); r.H > 0 { t.Fatalf("list did not scroll to the end: first row at %+v", r) } tt.Type("royal") tt.Frame() if r, _ := tt.Find("Command 00"); r.H == 0 { t.Fatal("highlighted first match is off screen after filtering") } } func TestCommandPaletteEmptyClickEscape(t *testing.T) { a := &paletteApp{open: true} tt := ui.NewTester(a.view, 800, 600) tt.Type("qqq") if !tt.HasText("No matching commands") { t.Fatalf("no empty state: %q", tt.Texts()) } tt.Key(0, ui.KeyEnter) if !a.open || len(a.ran) != 0 { t.Fatal("Enter with no results ran something") } tt.Key(0, ui.KeyEscape) if a.open { t.Fatal("Escape did not close") } a.open = true tt.Frame() if err := tt.Click("Open recent"); err != nil { t.Fatal(err) } if a.open || len(a.ran) != 1 || a.ran[0] != "open" { t.Fatalf("click: open %v ran %q", a.open, a.ran) } } func TestCommandPaletteInvalid(t *testing.T) { for name, cmds := range map[string][]Command{ "id": {{Label: "A"}}, "dup": {{ID: "a", Label: "A"}, {ID: "a", Label: "B"}}, } { t.Run(name, func(t *testing.T) { defer func() { if recover() == nil { t.Fatal("no panic") } }() open := true ui.Render(func(c *ui.Context) { Use(c, Settings{}) CommandPalette(c, &open, cmds, CommandPaletteOptions{}) }, 300, 100, 1) }) } } func TestCommandPaletteSnapshot(t *testing.T) { open := true snapshot(t, "command_palette", 640, 460, func(c *ui.Context) { CommandPalette(c, &open, commandPaletteSample(), CommandPaletteOptions{}) }) }