v0.3.84 97ae0a2

clipboard_images_test.go RawHistoryFolder

1package modes
2
3import (
4	"os"
5	"path/filepath"
6	"strings"
7	"testing"
8
9	"github.com/patriceckhart/zot/packages/provider"
10)
11
12func TestReadClipboardImageAndCleanupRemovesTemporaryFile(t *testing.T) {
13	path := filepath.Join(t.TempDir(), "clipboard.png")
14	if err := os.WriteFile(path, []byte("png"), 0o600); err != nil {
15		t.Fatal(err)
16	}
17
18	data, ok, err := readClipboardImageAndCleanup(func() (string, []byte, bool, error) {
19		return path, []byte("png"), true, nil
20	})
21	if err != nil {
22		t.Fatal(err)
23	}
24	if !ok || string(data) != "png" {
25		t.Fatalf("ok = %v, data = %q", ok, data)
26	}
27	if _, err := os.Stat(path); !os.IsNotExist(err) {
28		t.Fatalf("temporary image still exists: %v", err)
29	}
30}
31
32func testClipboardImage(marker string, data string) clipboardImageAttachment {
33	return clipboardImageAttachment{
34		Marker: marker,
35		Image:  provider.ImageBlock{MimeType: "image/png", Data: []byte(data)},
36	}
37}
38
39func TestPreparePromptWithClipboardImagesStripsPresentMarker(t *testing.T) {
40	pending := []clipboardImageAttachment{testClipboardImage("[clipboard image #1]", "png-1")}
41
42	text, images := preparePromptWithClipboardImages("describe this [clipboard image #1] please", pending)
43
44	if text != "describe this please" {
45		t.Fatalf("text = %q, want %q", text, "describe this please")
46	}
47	if len(images) != 1 {
48		t.Fatalf("len(images) = %d, want 1", len(images))
49	}
50	if string(images[0].Data) != "png-1" {
51		t.Fatalf("image data = %q, want png-1", string(images[0].Data))
52	}
53}
54
55func TestPreparePromptWithClipboardImagesAllowsImageOnlyPrompt(t *testing.T) {
56	pending := []clipboardImageAttachment{testClipboardImage("[clipboard image #1]", "png-1")}
57
58	text, images := preparePromptWithClipboardImages("[clipboard image #1]", pending)
59
60	if text != "" {
61		t.Fatalf("text = %q, want empty", text)
62	}
63	if len(images) != 1 {
64		t.Fatalf("len(images) = %d, want 1", len(images))
65	}
66}
67
68func TestPreparePromptWithClipboardImagesIgnoresDeletedMarker(t *testing.T) {
69	pending := []clipboardImageAttachment{testClipboardImage("[clipboard image #1]", "png-1")}
70
71	text, images := preparePromptWithClipboardImages("describe this from memory", pending)
72
73	if text != "describe this from memory" {
74		t.Fatalf("text = %q, want unchanged", text)
75	}
76	if len(images) != 0 {
77		t.Fatalf("len(images) = %d, want 0", len(images))
78	}
79}
80
81func TestPreparePromptWithClipboardImagesHandlesMultipleImagesInPasteOrder(t *testing.T) {
82	pending := []clipboardImageAttachment{
83		testClipboardImage("[clipboard image #1]", "png-1"),
84		testClipboardImage("[clipboard image #2]", "png-2"),
85	}
86
87	text, images := preparePromptWithClipboardImages("compare [clipboard image #2] with [clipboard image #1]", pending)
88
89	if text != "compare with" {
90		t.Fatalf("text = %q, want %q", text, "compare with")
91	}
92	if len(images) != 2 {
93		t.Fatalf("len(images) = %d, want 2", len(images))
94	}
95	if string(images[0].Data) != "png-1" || string(images[1].Data) != "png-2" {
96		t.Fatalf("images not attached in paste order: %q, %q", string(images[0].Data), string(images[1].Data))
97	}
98}
99
100func TestPreparePromptWithClipboardImagesDuplicateMarkerAttachesOnce(t *testing.T) {
101	pending := []clipboardImageAttachment{testClipboardImage("[clipboard image #1]", "png-1")}
102
103	text, images := preparePromptWithClipboardImages("[clipboard image #1] and again [clipboard image #1]", pending)
104
105	if strings.Contains(text, "[clipboard image #1]") {
106		t.Fatalf("text still contains marker: %q", text)
107	}
108	if text != "and again" {
109		t.Fatalf("text = %q, want %q", text, "and again")
110	}
111	if len(images) != 1 {
112		t.Fatalf("len(images) = %d, want 1", len(images))
113	}
114}
115
116func TestPreparePromptWithClipboardImagesNoPendingPreservesWhitespace(t *testing.T) {
117	input := "please review:\n\nfunc main() {\n\tfmt.Println(\"hi\")\n}\n"
118
119	text, images := preparePromptWithClipboardImages(input, nil)
120
121	if text != input {
122		t.Fatalf("text changed without pending images:\n got %q\nwant %q", text, input)
123	}
124	if images != nil {
125		t.Fatalf("images = %#v, want nil", images)
126	}
127}
128
129func TestPreparePromptWithClipboardImagesPreservesMultilinePrompt(t *testing.T) {
130	pending := []clipboardImageAttachment{testClipboardImage("[clipboard image #1]", "png-1")}
131	input := "compare this image [clipboard image #1]\n\nwith this code:\n\treturn 1"
132	want := "compare this image\n\nwith this code:\n\treturn 1"
133
134	text, images := preparePromptWithClipboardImages(input, pending)
135
136	if text != want {
137		t.Fatalf("text = %q, want %q", text, want)
138	}
139	if len(images) != 1 {
140		t.Fatalf("len(images) = %d, want 1", len(images))
141	}
142}

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