omnibox_navigation_state_test.go RawHistoryFolder
1package component
2
3import (
4 "testing"
5
6 "github.com/bnema/puregotk/v4/gdk"
7)
8
9func TestShouldPreferTypedURLNavigation(t *testing.T) {
10 tests := []struct {
11 name string
12 input string
13 want bool
14 }{
15 {name: "domain", input: "x.com", want: true},
16 {name: "domain with path", input: "github.com/bnema", want: true},
17 {name: "http url", input: "http://example.com", want: true},
18 {name: "localhost", input: "localhost", want: true},
19 {name: "ipv4", input: "127.0.0.1", want: true},
20 {name: "ipv4 with port", input: "192.168.1.1:8080", want: true},
21 {name: "ipv6 bracketed", input: "[::1]", want: true},
22 {name: "http ipv6 bracketed", input: "http://[::1]", want: true},
23 {name: "search query", input: "hello world", want: false},
24 {name: "empty", input: "", want: false},
25 }
26
27 for _, tt := range tests {
28 t.Run(tt.name, func(t *testing.T) {
29 if got := shouldPreferTypedURLNavigation(tt.input); got != tt.want {
30 t.Fatalf("shouldPreferTypedURLNavigation(%q) = %v, want %v", tt.input, got, tt.want)
31 }
32 })
33 }
34}
35
36func TestResetSearchSessionState(t *testing.T) {
37 o := &Omnibox{}
38 o.lastQuery = "github"
39
40 o.resetSearchSessionState()
41
42 if o.lastQuery != "" {
43 t.Fatalf("lastQuery should be reset, got %q", o.lastQuery)
44 }
45}
46
47func TestIsDeletionKey(t *testing.T) {
48 tests := []struct {
49 name string
50 key uint
51 want bool
52 }{
53 {name: "backspace is deletion", key: uint(gdk.KEY_BackSpace), want: true},
54 {name: "delete is deletion", key: uint(gdk.KEY_Delete), want: true},
55 {name: "typing key is not deletion", key: uint(gdk.KEY_g), want: false},
56 {name: "arrow key is not deletion", key: uint(gdk.KEY_Left), want: false},
57 }
58
59 for _, tt := range tests {
60 t.Run(tt.name, func(t *testing.T) {
61 if got := isDeletionKey(tt.key); got != tt.want {
62 t.Fatalf("isDeletionKey(%d) = %v, want %v", tt.key, got, tt.want)
63 }
64 })
65 }
66}
67
68func TestEffectiveSearchQuery(t *testing.T) {
69 tests := []struct {
70 name string
71 entryText string
72 realInput string
73 hasGhost bool
74 want string
75 }{
76 {name: "uses typed input when ghost visible", entryText: "bnema.dev/dumber", realInput: "dumb", hasGhost: true, want: "dumb"},
77 {name: "falls back to entry when no ghost", entryText: "dumb", realInput: "dumb", hasGhost: false, want: "dumb"},
78 {name: "uses entry when real input unavailable", entryText: "dumb", realInput: "", hasGhost: true, want: "dumb"},
79 }
80
81 for _, tt := range tests {
82 t.Run(tt.name, func(t *testing.T) {
83 if got := effectiveSearchQuery(tt.entryText, tt.realInput, tt.hasGhost); got != tt.want {
84 t.Fatalf("effectiveSearchQuery(%q, %q, %v) = %q, want %q", tt.entryText, tt.realInput, tt.hasGhost, got, tt.want)
85 }
86 })
87 }
88}
89
90func TestResultsContainerState(t *testing.T) {
91 tests := []struct {
92 name string
93 rowCount int
94 wantVisible bool
95 wantExpand bool
96 wantListVisible bool
97 }{
98 {name: "negative rows stays hidden", rowCount: -1, wantVisible: false, wantExpand: false, wantListVisible: false},
99 {name: "no rows stays hidden", rowCount: 0, wantVisible: false, wantExpand: false, wantListVisible: false},
100 {name: "rows show and expand", rowCount: 3, wantVisible: true, wantExpand: true, wantListVisible: true},
101 }
102
103 for _, tt := range tests {
104 t.Run(tt.name, func(t *testing.T) {
105 gotVisible, gotExpand, gotListVisible := resultsContainerState(tt.rowCount)
106 if gotVisible != tt.wantVisible || gotExpand != tt.wantExpand || gotListVisible != tt.wantListVisible {
107 t.Fatalf(
108 "resultsContainerState(%d) = (%v, %v, %v), want (%v, %v, %v)",
109 tt.rowCount,
110 gotVisible,
111 gotExpand,
112 gotListVisible,
113 tt.wantVisible,
114 tt.wantExpand,
115 tt.wantListVisible,
116 )
117 }
118 })
119 }
120}
121
122func TestUpdateGhostFromSelectionUsesRealInputWhenGhostVisible(t *testing.T) {
123 entryText := "github.com/bnema/dumber"
124 realInput := "upl"
125
126 got := effectiveSearchQuery(entryText, realInput, true)
127 if got != realInput {
128 t.Fatalf("effectiveSearchQuery should prefer real input when ghost is visible, got %q want %q", got, realInput)
129 }
130}
131
132func TestResolveTargetURLForSelection(t *testing.T) {
133 suggestions := []Suggestion{
134 {URL: "https://github.com/bnema/dumber"},
135 {URL: "https://github.com/bnema/dumber/pulls"},
136 {URL: "https://github.com/bnema/dumber/issues"},
137 }
138 favorites := []Favorite{
139 {URL: "https://bnema.dev/dumber"},
140 {URL: "https://bnema.dev/dumber/docs"},
141 }
142
143 tests := []struct {
144 name string
145 mode ViewMode
146 index int
147 limit int
148 wantURL string
149 }{
150 {name: "history index", mode: ViewModeHistory, index: 1, limit: 10, wantURL: "https://github.com/bnema/dumber/pulls"},
151 {name: "favorites index", mode: ViewModeFavorites, index: 0, limit: 10, wantURL: "https://bnema.dev/dumber"},
152 {name: "invalid index", mode: ViewModeHistory, index: 99, limit: 10, wantURL: ""},
153 {name: "history index beyond visible limit is hidden", mode: ViewModeHistory, index: 2, limit: 2, wantURL: ""},
154 {name: "favorites index beyond visible limit is hidden", mode: ViewModeFavorites, index: 1, limit: 1, wantURL: ""},
155 }
156
157 for _, tt := range tests {
158 t.Run(tt.name, func(t *testing.T) {
159 got := resolveTargetURLForSelection(tt.mode, tt.index, tt.limit, suggestions, favorites)
160 if got != tt.wantURL {
161 t.Fatalf("resolveTargetURLForSelection(%s, %d) = %q, want %q", tt.mode, tt.index, got, tt.wantURL)
162 }
163 })
164 }
165}
166
167func TestSelectedTargetURL(t *testing.T) {
168 suggestions := []Suggestion{
169 {URL: "https://github.com/bnema/dumber"},
170 {URL: "https://github.com/bnema/dumber/pulls"},
171 }
172 favorites := []Favorite{
173 {URL: "https://bnema.dev/dumber"},
174 {URL: "https://bnema.dev/dumber/docs"},
175 }
176
177 tests := []struct {
178 name string
179 mode ViewMode
180 index int
181 limit int
182 wantURL string
183 wantBool bool
184 }{
185 {name: "negative index is not explicit selection", mode: ViewModeHistory, index: -1, limit: 10, wantURL: "", wantBool: false},
186 {name: "history selection is explicit", mode: ViewModeHistory, index: 0, limit: 10, wantURL: "https://github.com/bnema/dumber", wantBool: true},
187 {name: "favorites selection is explicit", mode: ViewModeFavorites, index: 0, limit: 10, wantURL: "https://bnema.dev/dumber", wantBool: true},
188 {name: "hidden history selection is ignored", mode: ViewModeHistory, index: 1, limit: 1, wantURL: "", wantBool: true},
189 {name: "hidden favorites selection is ignored", mode: ViewModeFavorites, index: 1, limit: 1, wantURL: "", wantBool: true},
190 }
191
192 for _, tt := range tests {
193 t.Run(tt.name, func(t *testing.T) {
194 gotURL, gotBool := selectedTargetURL(tt.mode, tt.index, tt.limit, suggestions, favorites)
195 if gotURL != tt.wantURL || gotBool != tt.wantBool {
196 t.Fatalf("selectedTargetURL(%s, %d) = (%q, %v), want (%q, %v)", tt.mode, tt.index, gotURL, gotBool, tt.wantURL, tt.wantBool)
197 }
198 })
199 }
200}
201
202func TestBangSuggestionTextAt(t *testing.T) {
203 bangSuggestions := []BangSuggestion{{Key: "g"}, {Key: "gh"}}
204
205 if got, ok := bangSuggestionTextAt(1, bangSuggestions); !ok || got != "!gh " {
206 t.Fatalf("bangSuggestionTextAt(1) = (%q, %v), want (%q, %v)", got, ok, "!gh ", true)
207 }
208 if got, ok := bangSuggestionTextAt(99, bangSuggestions); ok || got != "" {
209 t.Fatalf("bangSuggestionTextAt(out of range) = (%q, %v), want (%q, %v)", got, ok, "", false)
210 }
211}
212
213func TestVisibleGhostSuggestion(t *testing.T) {
214 suggestions := []Suggestion{
215 {URL: "https://github.com/bnema/dumber"},
216 {URL: "https://gitlab.com/team/project"},
217 }
218
219 tests := []struct {
220 name string
221 input string
222 selectedURL string
223 hasExplicitSelection bool
224 wantFull string
225 wantSuffix string
226 wantOK bool
227 }{
228 {
229 name: "top visible candidate drives ghost without selection",
230 input: "git",
231 wantFull: "github.com",
232 wantSuffix: "hub.com",
233 wantOK: true,
234 },
235 {
236 name: "explicit selection wins over top candidate",
237 input: "git",
238 selectedURL: "https://gitlab.com/team/project",
239 hasExplicitSelection: true,
240 wantFull: "gitlab.com/team/project",
241 wantSuffix: "lab.com/team/project",
242 wantOK: true,
243 },
244 {
245 name: "no visible prefix match means no ghost",
246 input: "xyz",
247 wantFull: "",
248 wantSuffix: "",
249 wantOK: false,
250 },
251 }
252
253 for _, tt := range tests {
254 t.Run(tt.name, func(t *testing.T) {
255 gotFull, gotSuffix, gotOK := visibleGhostSuggestion(
256 tt.input,
257 tt.selectedURL,
258 tt.hasExplicitSelection,
259 ViewModeHistory,
260 10,
261 suggestions,
262 nil,
263 )
264 if gotFull != tt.wantFull || gotSuffix != tt.wantSuffix || gotOK != tt.wantOK {
265 t.Fatalf(
266 "visibleGhostSuggestion(%q) = (%q, %q, %v), want (%q, %q, %v)",
267 tt.input,
268 gotFull,
269 gotSuffix,
270 gotOK,
271 tt.wantFull,
272 tt.wantSuffix,
273 tt.wantOK,
274 )
275 }
276 })
277 }
278}
279
280func TestVisibleGhostSuggestionRespectsVisibleLimit(t *testing.T) {
281 suggestions := []Suggestion{
282 {URL: "https://github.com/bnema/dumber"},
283 {URL: "https://gitlab.com/team/project"},
284 }
285
286 gotFull, gotSuffix, gotOK := visibleGhostSuggestion("gitl", "", false, ViewModeHistory, 1, suggestions, nil)
287 if gotFull != "" || gotSuffix != "" || gotOK {
288 t.Fatalf("visibleGhostSuggestion should ignore hidden suggestions, got (%q, %q, %v)", gotFull, gotSuffix, gotOK)
289 }
290}
291
292func TestShouldPromoteHoverSelection(t *testing.T) {
293 tests := []struct {
294 name string
295 realInput string
296 hasGhostText bool
297 hasNavigated bool
298 want bool
299 }{
300 {
301 name: "initial open allows hover selection",
302 want: true,
303 },
304 {
305 name: "typed input keeps hover from stealing authority",
306 realInput: "git",
307 want: false,
308 },
309 {
310 name: "visible ghost keeps hover from stealing authority",
311 hasGhostText: true,
312 want: false,
313 },
314 {
315 name: "keyboard navigation keeps hover from stealing authority",
316 hasNavigated: true,
317 want: false,
318 },
319 }
320
321 for _, tt := range tests {
322 t.Run(tt.name, func(t *testing.T) {
323 got := shouldPromoteHoverSelection(tt.realInput, tt.hasGhostText, tt.hasNavigated)
324 if got != tt.want {
325 t.Fatalf(
326 "shouldPromoteHoverSelection(%q, %v, %v) = %v, want %v",
327 tt.realInput,
328 tt.hasGhostText,
329 tt.hasNavigated,
330 got,
331 tt.want,
332 )
333 }
334 })
335 }
336}