1package modes
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9
10 "github.com/patriceckhart/zot/packages/tui"
11)
12
13func newPathChoiceTestInteractive(t *testing.T) *Interactive {
14 t.Helper()
15 tmp := t.TempDir()
16 for _, name := range []string{"foobar", "foobuz"} {
17 if err := os.WriteFile(filepath.Join(tmp, name), []byte("x"), 0o644); err != nil {
18 t.Fatal(err)
19 }
20 }
21
22 interactive := NewInteractive(InteractiveConfig{CWD: tmp})
23 interactive.ed.SetValue("./foo")
24 if !interactive.tryPathTabComplete() {
25 t.Fatal("first Tab was not consumed")
26 }
27 if !interactive.tryPathTabComplete() {
28 t.Fatal("second Tab was not consumed")
29 }
30 return interactive
31}
32
33func TestPathTabCompletionShowsChoicesAfterCommonPrefix(t *testing.T) {
34 interactive := newPathChoiceTestInteractive(t)
35 ed := interactive.ed
36 if got, want := ed.Value(), "./foob"; got != want {
37 t.Fatalf("first Tab completed to %q, want %q", got, want)
38 }
39
40 popup := interactive.pathSuggest
41 if !popup.Active(ed.Value()) {
42 t.Fatal("path choice popup is not active for the unchanged token")
43 }
44 rows := popup.Render(tui.Theme{}, 40)
45 rendered := stripANSIBytes(strings.Join(rows, "\n"))
46 for _, name := range []string{"foobar", "foobuz"} {
47 if !strings.Contains(rendered, name) {
48 t.Fatalf("popup omitted %q: %q", name, rendered)
49 }
50 }
51
52 if interactive.handleKey(context.Background(), tui.Key{Kind: tui.KeyDown}) {
53 t.Fatal("navigating the path popup unexpectedly exited interactive mode")
54 }
55 if interactive.handleKey(context.Background(), tui.Key{Kind: tui.KeyEnter}) {
56 t.Fatal("selecting a path choice unexpectedly exited interactive mode")
57 }
58 if got, want := ed.Value(), "./foobuz"; got != want {
59 t.Fatalf("selected path is %q, want %q", got, want)
60 }
61}
62
63func TestPathChoiceEscDoesNotCancelBusyTurn(t *testing.T) {
64 interactive := newPathChoiceTestInteractive(t)
65 interactive.busy = true
66 canceled := false
67 interactive.cancelTurn = func() { canceled = true }
68
69 interactive.handleKey(context.Background(), tui.Key{Kind: tui.KeyEsc})
70
71 if canceled {
72 t.Fatal("Esc canceled the active turn instead of dismissing the path popup")
73 }
74 if interactive.pathSuggest.Active(interactive.ed.Value()) {
75 t.Fatal("Esc left the path popup active")
76 }
77}
78
79func TestPathChoiceDoesNotReopenAfterInputCleared(t *testing.T) {
80 interactive := newPathChoiceTestInteractive(t)
81
82 interactive.handleKey(context.Background(), tui.Key{Kind: tui.KeyCtrlC})
83 for _, r := range "./foob" {
84 interactive.handleKey(context.Background(), tui.Key{Kind: tui.KeyRune, Rune: r})
85 }
86
87 if interactive.pathSuggest.Active(interactive.ed.Value()) {
88 t.Fatal("stale path popup reopened after Ctrl+C and retyping")
89 }
90}