v0.2.99 4d92b3d

confirm_dialog_test.go RawHistoryFolder

1package modes
2
3import (
4	"context"
5	"encoding/json"
6	"runtime"
7	"strings"
8	"testing"
9	"time"
10
11	"github.com/patriceckhart/zot/packages/core"
12	"github.com/patriceckhart/zot/packages/tui"
13)
14
15func TestConfirmToolCallAttachesDiffBeforeDecision(t *testing.T) {
16	dialog := newConfirmDialog()
17	i := &Interactive{
18		toolCalls: map[string]*tui.ToolCallView{
19			"call-1": {ID: "call-1", Name: "edit"},
20		},
21		confirmDialog: dialog,
22		dirty:         make(chan struct{}, 1),
23	}
24	decision := make(chan core.ConfirmDecision, 1)
25	go func() {
26		decision <- i.ConfirmToolCall(core.ToolCallConfirmation{
27			ID:      "call-1",
28			Name:    "edit",
29			Summary: "sample.go",
30			Content: "-old\n+new\n",
31		})
32	}()
33
34	deadline := time.Now().Add(time.Second)
35	for !dialog.Active() && time.Now().Before(deadline) {
36		runtime.Gosched()
37	}
38	if !dialog.Active() {
39		t.Fatal("confirmation dialog did not open")
40	}
41	i.mu.Lock()
42	preview := i.toolCalls["call-1"].Preview
43	i.mu.Unlock()
44	if preview != "-old\n+new\n" {
45		t.Fatalf("tool preview = %q", preview)
46	}
47
48	dialog.HandleKey(tui.Key{Kind: tui.KeyEnter})
49	select {
50	case got := <-decision:
51		if !got.Allow {
52			t.Fatalf("confirmation decision = %+v", got)
53		}
54	case <-time.After(time.Second):
55		t.Fatal("confirmation did not return")
56	}
57}
58
59func TestConfirmDialogAllowsToolExpansion(t *testing.T) {
60	resp := make(chan core.ConfirmDecision, 1)
61	dialog := newConfirmDialog()
62	dialog.Enqueue(&confirmRequest{
63		toolName: "edit",
64		preview:  "large edit",
65		resp:     resp,
66	})
67	args, err := json.Marshal(map[string]any{
68		"path": "sample.ts",
69		"edits": []map[string]string{{
70			"oldText": "old",
71			"newText": strings.Repeat("const value = 1\n", 20),
72		}},
73	})
74	if err != nil {
75		t.Fatal(err)
76	}
77	view := &tui.View{
78		ToolCalls: []tui.ToolCallView{{
79			ID:         "call-1",
80			Name:       "edit",
81			RawJSONBuf: string(args),
82			LivePath:   "sample.ts",
83		}},
84	}
85	i := &Interactive{
86		view:          view,
87		confirmDialog: dialog,
88		dirty:         make(chan struct{}, 1),
89	}
90	if rendered := strings.Join(view.Build(80), "\n"); !strings.Contains(rendered, "ctrl+o to expand") {
91		t.Fatalf("test preview was not initially collapsed:\n%s", rendered)
92	}
93
94	i.handleKey(context.Background(), tui.Key{Kind: tui.KeyCtrlO})
95
96	if !i.view.ExpandAll {
97		t.Fatal("ctrl+o did not expand tool previews while confirmation was active")
98	}
99	if rendered := strings.Join(view.Build(80), "\n"); strings.Contains(rendered, "ctrl+o to expand") {
100		t.Fatalf("live edit preview remained collapsed after ctrl+o:\n%s", rendered)
101	}
102	if !dialog.Active() {
103		t.Fatal("ctrl+o closed the confirmation dialog")
104	}
105	select {
106	case decision := <-resp:
107		t.Fatalf("ctrl+o unexpectedly answered confirmation: %+v", decision)
108	default:
109	}
110
111	i.handleKey(context.Background(), tui.Key{Kind: tui.KeyCtrlO})
112	if i.view.ExpandAll {
113		t.Fatal("second ctrl+o did not collapse tool previews during confirmation")
114	}
115	if rendered := strings.Join(view.Build(80), "\n"); !strings.Contains(rendered, "ctrl+o to expand") {
116		t.Fatalf("live edit preview remained expanded after second ctrl+o:\n%s", rendered)
117	}
118	if !dialog.Active() {
119		t.Fatal("second ctrl+o closed the confirmation dialog")
120	}
121}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 477

↑↓ moveenter openesc close