v0.26.1 75cd777

normalize_test.go RawHistoryFolder

1package url
2
3import (
4	"os"
5	"path/filepath"
6	"testing"
7)
8
9func TestNormalize(t *testing.T) {
10	tests := []struct {
11		name  string
12		input string
13		want  string
14	}{
15		{
16			name:  "empty string",
17			input: "",
18			want:  "",
19		},
20		{
21			name:  "http scheme unchanged",
22			input: "http://example.com",
23			want:  "http://example.com",
24		},
25		{
26			name:  "https scheme unchanged",
27			input: "https://example.com",
28			want:  "https://example.com",
29		},
30		{
31			name:  "file scheme unchanged",
32			input: "file:///path/to/file.html",
33			want:  "file:///path/to/file.html",
34		},
35		{
36			name:  "dumb scheme unchanged",
37			input: "dumb://home",
38			want:  "dumb://home",
39		},
40		{
41			name:  "about scheme unchanged",
42			input: "about:blank",
43			want:  "about:blank",
44		},
45		{
46			name:  "domain gets https",
47			input: "example.com",
48			want:  "https://example.com",
49		},
50		{
51			name:  "domain with path gets https",
52			input: "example.com/path",
53			want:  "https://example.com/path",
54		},
55		{
56			name:  "search query unchanged",
57			input: "hello world",
58			want:  "hello world",
59		},
60		{
61			name:  "single word unchanged",
62			input: "hello",
63			want:  "hello",
64		},
65		{
66			name:  "localhost",
67			input: "localhost",
68			want:  "http://localhost",
69		},
70		{
71			name:  "localhost with port",
72			input: "localhost:5173",
73			want:  "http://localhost:5173",
74		},
75		{
76			name:  "localhost with alt port",
77			input: "localhost:8080",
78			want:  "http://localhost:8080",
79		},
80		{
81			name:  "localhost with path",
82			input: "localhost/api/v1",
83			want:  "http://localhost/api/v1",
84		},
85		{
86			name:  "localhost.com is a domain not localhost",
87			input: "localhost.com",
88			want:  "https://localhost.com",
89		},
90		{
91			name:  "localhostevil.com is a domain not localhost",
92			input: "localhostevil.com",
93			want:  "https://localhostevil.com",
94		},
95	}
96
97	for _, tt := range tests {
98		t.Run(tt.name, func(t *testing.T) {
99			got := Normalize(tt.input)
100			if got != tt.want {
101				t.Errorf("Normalize(%q) = %q, want %q", tt.input, got, tt.want)
102			}
103		})
104	}
105}
106
107func TestNormalize_LocalFiles(t *testing.T) {
108	// Create a temporary file for testing
109	tmpDir := t.TempDir()
110	tmpFile := filepath.Join(tmpDir, "test.html")
111	if err := os.WriteFile(tmpFile, []byte("<html></html>"), 0644); err != nil {
112		t.Fatalf("failed to create temp file: %v", err)
113	}
114
115	// Create a subdirectory with a file
116	subDir := filepath.Join(tmpDir, "subdir")
117	if err := os.Mkdir(subDir, 0755); err != nil {
118		t.Fatalf("failed to create subdir: %v", err)
119	}
120	subFile := filepath.Join(subDir, "page.html")
121	if err := os.WriteFile(subFile, []byte("<html></html>"), 0644); err != nil {
122		t.Fatalf("failed to create subdir file: %v", err)
123	}
124
125	tests := []struct {
126		name  string
127		input string
128		want  string
129	}{
130		{
131			name:  "absolute path to existing file",
132			input: tmpFile,
133			want:  "file://" + tmpFile,
134		},
135		{
136			name:  "absolute path to existing directory",
137			input: tmpDir,
138			want:  "file://" + tmpDir,
139		},
140		{
141			name:  "non-existent absolute path returned unchanged",
142			input: "/nonexistent/file.html",
143			want:  "/nonexistent/file.html",
144		},
145		{
146			name:  "non-existent relative path with dot prefix returned unchanged",
147			input: "./nonexistent/file.html",
148			want:  "./nonexistent/file.html",
149		},
150		{
151			name:  "non-existent home path returned unchanged",
152			input: "~/nonexistent/file.html",
153			want:  "~/nonexistent/file.html",
154		},
155	}
156
157	for _, tt := range tests {
158		t.Run(tt.name, func(t *testing.T) {
159			got := Normalize(tt.input)
160			if got != tt.want {
161				t.Errorf("Normalize(%q) = %q, want %q", tt.input, got, tt.want)
162			}
163		})
164	}
165}
166
167func TestNormalize_RelativePaths(t *testing.T) {
168	// Create a temp file in current directory
169	tmpFile, err := os.CreateTemp(".", "test-*.html")
170	if err != nil {
171		t.Fatalf("failed to create temp file: %v", err)
172	}
173	tmpFile.Close()
174	defer os.Remove(tmpFile.Name())
175
176	// Get absolute path for comparison
177	absPath, err := filepath.Abs(tmpFile.Name())
178	if err != nil {
179		t.Fatalf("failed to get abs path: %v", err)
180	}
181
182	t.Run("relative path to existing file", func(t *testing.T) {
183		got := Normalize(tmpFile.Name())
184		want := "file://" + absPath
185		if got != want {
186			t.Errorf("Normalize(%q) = %q, want %q", tmpFile.Name(), got, want)
187		}
188	})
189}
190
191func TestExpandHome(t *testing.T) {
192	home, err := os.UserHomeDir()
193	if err != nil {
194		t.Skip("cannot get home directory")
195	}
196
197	tests := []struct {
198		name  string
199		input string
200		want  string
201	}{
202		{
203			name:  "tilde expansion",
204			input: "~/Documents",
205			want:  filepath.Join(home, "Documents"),
206		},
207		{
208			name:  "no tilde unchanged",
209			input: "/absolute/path",
210			want:  "/absolute/path",
211		},
212		{
213			name:  "relative path unchanged",
214			input: "./relative/path",
215			want:  "./relative/path",
216		},
217		{
218			name:  "tilde in middle unchanged",
219			input: "/path/~/file",
220			want:  "/path/~/file",
221		},
222	}
223
224	for _, tt := range tests {
225		t.Run(tt.name, func(t *testing.T) {
226			got := expandHome(tt.input)
227			if got != tt.want {
228				t.Errorf("expandHome(%q) = %q, want %q", tt.input, got, tt.want)
229			}
230		})
231	}
232}
233
234func TestLooksLikeURL(t *testing.T) {
235	tests := []struct {
236		name  string
237		input string
238		want  bool
239	}{
240		{
241			name:  "empty string",
242			input: "",
243			want:  false,
244		},
245		{
246			name:  "http URL",
247			input: "http://example.com",
248			want:  true,
249		},
250		{
251			name:  "https URL",
252			input: "https://example.com",
253			want:  true,
254		},
255		{
256			name:  "file URL",
257			input: "file:///path/to/file",
258			want:  true,
259		},
260		{
261			name:  "dumb URL",
262			input: "dumb://home",
263			want:  true,
264		},
265		{
266			name:  "about URL",
267			input: "about:blank",
268			want:  true,
269		},
270		{
271			name:  "domain",
272			input: "example.com",
273			want:  true,
274		},
275		{
276			name:  "domain with path",
277			input: "example.com/path",
278			want:  true,
279		},
280		{
281			name:  "search query",
282			input: "hello world",
283			want:  false,
284		},
285		{
286			name:  "single word",
287			input: "hello",
288			want:  false,
289		},
290		{
291			name:  "localhost",
292			input: "localhost",
293			want:  true,
294		},
295		{
296			name:  "localhost with port",
297			input: "localhost:5173",
298			want:  true,
299		},
300		{
301			name:  "localhost with alt port",
302			input: "localhost:8080",
303			want:  true,
304		},
305		{
306			name:  "localhost with path",
307			input: "localhost/api/v1",
308			want:  true,
309		},
310		{
311			name:  "localhost.com is a domain not localhost",
312			input: "localhost.com",
313			want:  true,
314		},
315		{
316			name:  "localhostevil.com is a domain not localhost",
317			input: "localhostevil.com",
318			want:  true,
319		},
320	}
321
322	for _, tt := range tests {
323		t.Run(tt.name, func(t *testing.T) {
324			got := LooksLikeURL(tt.input)
325			if got != tt.want {
326				t.Errorf("LooksLikeURL(%q) = %v, want %v", tt.input, got, tt.want)
327			}
328		})
329	}
330}

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