v0.31.0 bf0756b

autocomplete_test.go RawHistoryFolder

1package autocomplete
2
3import "testing"
4
5func TestComputeCompletionSuffix(t *testing.T) {
6	tests := []struct {
7		name       string
8		input      string
9		fullText   string
10		wantSuffix string
11		wantOK     bool
12	}{
13		{
14			name:       "empty input",
15			input:      "",
16			fullText:   "example.com",
17			wantSuffix: "",
18			wantOK:     false,
19		},
20		{
21			name:       "empty fullText",
22			input:      "exa",
23			fullText:   "",
24			wantSuffix: "",
25			wantOK:     false,
26		},
27		{
28			name:       "both empty",
29			input:      "",
30			fullText:   "",
31			wantSuffix: "",
32			wantOK:     false,
33		},
34		{
35			name:       "exact match returns false",
36			input:      "example.com",
37			fullText:   "example.com",
38			wantSuffix: "",
39			wantOK:     false,
40		},
41		{
42			name:       "prefix match",
43			input:      "exa",
44			fullText:   "example.com",
45			wantSuffix: "mple.com",
46			wantOK:     true,
47		},
48		{
49			name:       "case insensitive match",
50			input:      "EXA",
51			fullText:   "example.com",
52			wantSuffix: "mple.com",
53			wantOK:     true,
54		},
55		{
56			name:       "case insensitive preserves original case in suffix",
57			input:      "git",
58			fullText:   "GitHub.com",
59			wantSuffix: "Hub.com",
60			wantOK:     true,
61		},
62		{
63			name:       "no prefix match",
64			input:      "xyz",
65			fullText:   "example.com",
66			wantSuffix: "",
67			wantOK:     false,
68		},
69		{
70			name:       "input longer than fullText",
71			input:      "example.com/path",
72			fullText:   "example.com",
73			wantSuffix: "",
74			wantOK:     false,
75		},
76		{
77			name:       "single character prefix",
78			input:      "e",
79			fullText:   "example.com",
80			wantSuffix: "xample.com",
81			wantOK:     true,
82		},
83		{
84			name:       "bang shortcut prefix",
85			input:      "!g",
86			fullText:   "!github",
87			wantSuffix: "ithub",
88			wantOK:     true,
89		},
90	}
91
92	for _, tt := range tests {
93		t.Run(tt.name, func(t *testing.T) {
94			suffix, ok := ComputeCompletionSuffix(tt.input, tt.fullText)
95			if suffix != tt.wantSuffix {
96				t.Errorf("ComputeCompletionSuffix(%q, %q) suffix = %q, want %q",
97					tt.input, tt.fullText, suffix, tt.wantSuffix)
98			}
99			if ok != tt.wantOK {
100				t.Errorf("ComputeCompletionSuffix(%q, %q) ok = %v, want %v",
101					tt.input, tt.fullText, ok, tt.wantOK)
102			}
103		})
104	}
105}
106
107func TestStripProtocol(t *testing.T) {
108	tests := []struct {
109		name string
110		url  string
111		want string
112	}{
113		{
114			name: "https protocol",
115			url:  "https://example.com",
116			want: "example.com",
117		},
118		{
119			name: "http protocol",
120			url:  "http://example.com",
121			want: "example.com",
122		},
123		{
124			name: "no protocol",
125			url:  "example.com",
126			want: "example.com",
127		},
128		{
129			name: "https with path",
130			url:  "https://example.com/path/to/page",
131			want: "example.com/path/to/page",
132		},
133		{
134			name: "http with query string",
135			url:  "http://example.com?query=value",
136			want: "example.com?query=value",
137		},
138		{
139			name: "file protocol unchanged",
140			url:  "file:///path/to/file",
141			want: "file:///path/to/file",
142		},
143		{
144			name: "empty string",
145			url:  "",
146			want: "",
147		},
148	}
149
150	for _, tt := range tests {
151		t.Run(tt.name, func(t *testing.T) {
152			got := StripProtocol(tt.url)
153			if got != tt.want {
154				t.Errorf("StripProtocol(%q) = %q, want %q", tt.url, got, tt.want)
155			}
156		})
157	}
158}
159
160func TestComputeURLCompletionSuffix(t *testing.T) {
161	tests := []struct {
162		name           string
163		input          string
164		fullURL        string
165		wantSuffix     string
166		wantMatchedURL string
167		wantOK         bool
168	}{
169		{
170			name:           "empty input",
171			input:          "",
172			fullURL:        "https://example.com",
173			wantSuffix:     "",
174			wantMatchedURL: "",
175			wantOK:         false,
176		},
177		{
178			name:           "direct match with protocol",
179			input:          "https://exa",
180			fullURL:        "https://example.com",
181			wantSuffix:     "mple.com",
182			wantMatchedURL: "https://example.com",
183			wantOK:         true,
184		},
185		{
186			name:           "match without protocol in input",
187			input:          "exa",
188			fullURL:        "https://example.com",
189			wantSuffix:     "mple.com",
190			wantMatchedURL: "example.com",
191			wantOK:         true,
192		},
193		{
194			name:           "match with www prefix in URL strips www",
195			input:          "exa",
196			fullURL:        "https://www.example.com",
197			wantSuffix:     "mple.com",
198			wantMatchedURL: "example.com",
199			wantOK:         true,
200		},
201		{
202			name:           "match with www in input but not URL",
203			input:          "www.exa",
204			fullURL:        "https://example.com",
205			wantSuffix:     "mple.com",
206			wantMatchedURL: "example.com",
207			wantOK:         true,
208		},
209		{
210			name:           "no match",
211			input:          "xyz",
212			fullURL:        "https://example.com",
213			wantSuffix:     "",
214			wantMatchedURL: "",
215			wantOK:         false,
216		},
217		{
218			name:           "case insensitive URL match",
219			input:          "GITHUB",
220			fullURL:        "https://github.com/user/repo",
221			wantSuffix:     ".com/user/repo",
222			wantMatchedURL: "github.com/user/repo",
223			wantOK:         true,
224		},
225		{
226			name:           "http protocol stripped",
227			input:          "news",
228			fullURL:        "http://news.ycombinator.com",
229			wantSuffix:     ".ycombinator.com",
230			wantMatchedURL: "news.ycombinator.com",
231			wantOK:         true,
232		},
233		{
234			name:           "full URL match returns false",
235			input:          "example.com",
236			fullURL:        "https://example.com",
237			wantSuffix:     "",
238			wantMatchedURL: "",
239			wantOK:         false,
240		},
241		{
242			name:           "URL with path",
243			input:          "github.com/user",
244			fullURL:        "https://github.com/user/repo",
245			wantSuffix:     "/repo",
246			wantMatchedURL: "github.com/user/repo",
247			wantOK:         true,
248		},
249	}
250
251	for _, tt := range tests {
252		t.Run(tt.name, func(t *testing.T) {
253			suffix, matchedURL, ok := ComputeURLCompletionSuffix(tt.input, tt.fullURL)
254			if suffix != tt.wantSuffix {
255				t.Errorf("ComputeURLCompletionSuffix(%q, %q) suffix = %q, want %q",
256					tt.input, tt.fullURL, suffix, tt.wantSuffix)
257			}
258			if matchedURL != tt.wantMatchedURL {
259				t.Errorf("ComputeURLCompletionSuffix(%q, %q) matchedURL = %q, want %q",
260					tt.input, tt.fullURL, matchedURL, tt.wantMatchedURL)
261			}
262			if ok != tt.wantOK {
263				t.Errorf("ComputeURLCompletionSuffix(%q, %q) ok = %v, want %v",
264					tt.input, tt.fullURL, ok, tt.wantOK)
265			}
266		})
267	}
268}
269
270func TestBestURLCompletion_PrefersHostForHostLikeInput(t *testing.T) {
271	urls := []string{
272		"https://www.google.com/url?q=https://dashboard.stripe.com/auth",
273		"https://google.com",
274	}
275
276	suffix, matchedURL, ok := BestURLCompletion("goo", urls)
277	if !ok {
278		t.Fatalf("expected completion")
279	}
280	if matchedURL != "google.com" {
281		t.Fatalf("expected host completion, got %q", matchedURL)
282	}
283	if suffix != "gle.com" {
284		t.Fatalf("expected host suffix, got %q", suffix)
285	}
286}
287
288func TestBestURLCompletion_KeepsPathForPathLikeInput(t *testing.T) {
289	urls := []string{
290		"https://google.com/url?q=https://example.com",
291	}
292
293	suffix, matchedURL, ok := BestURLCompletion("google.com/u", urls)
294	if !ok {
295		t.Fatalf("expected completion")
296	}
297	if matchedURL != "google.com/url?q=https://example.com" {
298		t.Fatalf("unexpected matched URL: %q", matchedURL)
299	}
300	if suffix != "rl?q=https://example.com" {
301		t.Fatalf("unexpected suffix: %q", suffix)
302	}
303}
304
305func TestBestURLCompletion_SelectsAnyValidPrefixFromVisibleList(t *testing.T) {
306	urls := []string{
307		"https://example.org",
308		"https://google.com/maps",
309	}
310
311	suffix, matchedURL, ok := BestURLCompletion("goo", urls)
312	if !ok {
313		t.Fatalf("expected completion from second URL")
314	}
315	if matchedURL != "google.com" {
316		t.Fatalf("expected host completion from second URL, got %q", matchedURL)
317	}
318	if suffix != "gle.com" {
319		t.Fatalf("unexpected suffix: %q", suffix)
320	}
321}

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 8

Tags 42

↑↓ moveenter openesc close