v0.33.0 62a09f5

navigation_webview_targeted_test.go RawHistoryFolder

1package coordinator
2
3import (
4	"context"
5	"testing"
6
7	"github.com/bnema/dumber/internal/application/port"
8	"github.com/bnema/dumber/internal/application/port/mocks"
9	"github.com/bnema/dumber/internal/application/usecase"
10	"github.com/bnema/dumber/internal/domain/entity"
11)
12
13type mockScrollableWebView struct {
14	*mocks.MockWebView
15	*mocks.MockPageScrollable
16}
17
18type mockDevToolsWebView struct {
19	*mocks.MockWebView
20	*mocks.MockDevToolsOpener
21}
22
23type mockPrinterWebView struct {
24	*mocks.MockWebView
25	*mocks.MockPrinter
26}
27
28// TestNavigationCoordinator_WebViewTargetedActionsUseProvidedWebView verifies that
29// the explicit WebView-targeted navigation methods call the correct methods on the
30// provided WebView rather than resolving through the content coordinator.
31func TestNavigationCoordinator_WebViewTargetedActionsUseProvidedWebView(t *testing.T) {
32	ctx := context.Background()
33
34	t.Run("NavigateWebView with nil webview returns error", func(t *testing.T) {
35		testNavigateWebViewNilWebView(t, ctx)
36	})
37	t.Run("NavigateWebView targets provided webview", func(t *testing.T) {
38		testNavigateWebViewTargetsProvidedWebView(t, ctx)
39	})
40	t.Run("ReloadWebView with nil webview returns error", func(t *testing.T) {
41		testReloadWebViewNilWebView(t, ctx)
42	})
43	t.Run("ReloadWebView bypassCache=false calls Reload once", func(t *testing.T) {
44		testReloadWebViewCallsReload(t, ctx)
45	})
46	t.Run("ReloadWebView bypassCache=true calls ReloadBypassCache once", func(t *testing.T) {
47		testReloadWebViewCallsReloadBypassCache(t, ctx)
48	})
49	t.Run("StopWebView with nil webview returns error", func(t *testing.T) {
50		testStopWebViewNilWebView(t, ctx)
51	})
52	t.Run("StopWebView calls Stop once", func(t *testing.T) {
53		testStopWebViewCallsStop(t, ctx)
54	})
55	t.Run("GoBackWebView with nil webview returns error", func(t *testing.T) {
56		testGoBackWebViewNilWebView(t, ctx)
57	})
58	t.Run("GoBackWebView calls GoBack once", func(t *testing.T) {
59		testGoBackWebViewCallsGoBack(t, ctx)
60	})
61	t.Run("GoForwardWebView with nil webview returns error", func(t *testing.T) {
62		testGoForwardWebViewNilWebView(t, ctx)
63	})
64	t.Run("GoForwardWebView calls GoForward once", func(t *testing.T) {
65		testGoForwardWebViewCallsGoForward(t, ctx)
66	})
67	t.Run("OpenDevToolsWebView with nil webview returns error", func(t *testing.T) {
68		testOpenDevToolsWebViewNilWebView(t, ctx)
69	})
70	t.Run("OpenDevToolsWebView unsupported capability returns error", func(t *testing.T) {
71		testOpenDevToolsWebViewUnsupportedCapability(t, ctx)
72	})
73	t.Run("OpenDevToolsWebView calls OpenDevTools once", func(t *testing.T) {
74		testOpenDevToolsWebViewTargetsProvidedWebView(t, ctx)
75	})
76	t.Run("PrintWebView with nil webview returns error", func(t *testing.T) {
77		testPrintWebViewNilWebView(t, ctx)
78	})
79	t.Run("PrintWebView unsupported capability returns error", func(t *testing.T) {
80		testPrintWebViewUnsupportedCapability(t, ctx)
81	})
82	t.Run("PrintWebView calls PrintPage once", func(t *testing.T) {
83		testPrintWebViewTargetsProvidedWebView(t, ctx)
84	})
85	t.Run("ScrollWebView with nil webview returns error", func(t *testing.T) {
86		testScrollWebViewNilWebView(t, ctx)
87	})
88	t.Run("ScrollWebView delegates each scroll command through usecase", func(t *testing.T) {
89		testScrollWebViewDelegatesEachCommand(t, ctx)
90	})
91	t.Run("ScrollWebView usecase not set returns error", func(t *testing.T) {
92		testScrollWebViewUsecaseNotSet(t, ctx)
93	})
94}
95
96func testNavigateWebViewNilWebView(t *testing.T, ctx context.Context) {
97	t.Helper()
98	c := &NavigationCoordinator{}
99	err := c.NavigateWebView(ctx, "https://example.com", entity.PaneID("p1"), nil)
100	if err == nil {
101		t.Fatal("expected error for nil webview, got nil")
102	}
103}
104
105func testNavigateWebViewTargetsProvidedWebView(t *testing.T, ctx context.Context) {
106	t.Helper()
107	wv := mocks.NewMockWebView(t)
108	wv.EXPECT().LoadURI(ctx, "https://example.com").Return(nil).Once()
109	wv.EXPECT().ID().Return(port.WebViewID(42)).Once()
110	c := &NavigationCoordinator{}
111	err := c.NavigateWebView(ctx, "https://example.com", entity.PaneID("p1"), wv)
112	if err != nil {
113		t.Fatalf("unexpected error: %v", err)
114	}
115}
116
117func testReloadWebViewNilWebView(t *testing.T, ctx context.Context) {
118	t.Helper()
119	c := &NavigationCoordinator{}
120	err := c.ReloadWebView(ctx, nil, false)
121	if err == nil {
122		t.Fatal("expected error for nil webview, got nil")
123	}
124}
125
126func testReloadWebViewCallsReload(t *testing.T, ctx context.Context) {
127	t.Helper()
128	wv := mocks.NewMockWebView(t)
129	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
130	wv.EXPECT().Reload(ctx).Return(nil).Once()
131	c := &NavigationCoordinator{}
132	err := c.ReloadWebView(ctx, wv, false)
133	if err != nil {
134		t.Fatalf("unexpected error: %v", err)
135	}
136}
137
138func testReloadWebViewCallsReloadBypassCache(t *testing.T, ctx context.Context) {
139	t.Helper()
140	wv := mocks.NewMockWebView(t)
141	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
142	wv.EXPECT().ReloadBypassCache(ctx).Return(nil).Once()
143	c := &NavigationCoordinator{}
144	err := c.ReloadWebView(ctx, wv, true)
145	if err != nil {
146		t.Fatalf("unexpected error: %v", err)
147	}
148}
149
150func testStopWebViewNilWebView(t *testing.T, ctx context.Context) {
151	t.Helper()
152	c := &NavigationCoordinator{}
153	err := c.StopWebView(ctx, nil)
154	if err == nil {
155		t.Fatal("expected error for nil webview, got nil")
156	}
157}
158
159func testStopWebViewCallsStop(t *testing.T, ctx context.Context) {
160	t.Helper()
161	wv := mocks.NewMockWebView(t)
162	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
163	wv.EXPECT().Stop(ctx).Return(nil).Once()
164	c := &NavigationCoordinator{}
165	err := c.StopWebView(ctx, wv)
166	if err != nil {
167		t.Fatalf("unexpected error: %v", err)
168	}
169}
170
171func testGoBackWebViewNilWebView(t *testing.T, ctx context.Context) {
172	t.Helper()
173	c := &NavigationCoordinator{}
174	err := c.GoBackWebView(ctx, nil)
175	if err == nil {
176		t.Fatal("expected error for nil webview, got nil")
177	}
178}
179
180func testGoBackWebViewCallsGoBack(t *testing.T, ctx context.Context) {
181	t.Helper()
182	wv := mocks.NewMockWebView(t)
183	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
184	wv.EXPECT().GoBack(ctx).Return(nil).Once()
185	c := &NavigationCoordinator{}
186	err := c.GoBackWebView(ctx, wv)
187	if err != nil {
188		t.Fatalf("unexpected error: %v", err)
189	}
190}
191
192func testGoForwardWebViewNilWebView(t *testing.T, ctx context.Context) {
193	t.Helper()
194	c := &NavigationCoordinator{}
195	err := c.GoForwardWebView(ctx, nil)
196	if err == nil {
197		t.Fatal("expected error for nil webview, got nil")
198	}
199}
200
201func testGoForwardWebViewCallsGoForward(t *testing.T, ctx context.Context) {
202	t.Helper()
203	wv := mocks.NewMockWebView(t)
204	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
205	wv.EXPECT().GoForward(ctx).Return(nil).Once()
206	c := &NavigationCoordinator{}
207	err := c.GoForwardWebView(ctx, wv)
208	if err != nil {
209		t.Fatalf("unexpected error: %v", err)
210	}
211}
212
213func testOpenDevToolsWebViewNilWebView(t *testing.T, ctx context.Context) {
214	t.Helper()
215	c := &NavigationCoordinator{}
216	err := c.OpenDevToolsWebView(ctx, nil)
217	if err == nil {
218		t.Fatal("expected error for nil webview, got nil")
219	}
220}
221
222func testOpenDevToolsWebViewUnsupportedCapability(t *testing.T, ctx context.Context) {
223	t.Helper()
224	wv := mocks.NewMockWebView(t)
225	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
226	c := &NavigationCoordinator{}
227	err := c.OpenDevToolsWebView(ctx, wv)
228	if err == nil {
229		t.Fatal("expected unsupported devtools error, got nil")
230	}
231	if got, want := err.Error(), "webview does not support devtools"; got != want {
232		t.Fatalf("error = %q, want %q", got, want)
233	}
234}
235
236func testOpenDevToolsWebViewTargetsProvidedWebView(t *testing.T, ctx context.Context) {
237	t.Helper()
238	base := mocks.NewMockWebView(t)
239	opener := mocks.NewMockDevToolsOpener(t)
240	wv := &mockDevToolsWebView{MockWebView: base, MockDevToolsOpener: opener}
241	base.EXPECT().ID().Return(port.WebViewID(1)).Once()
242	opener.EXPECT().OpenDevTools().Return().Once()
243	c := &NavigationCoordinator{}
244	err := c.OpenDevToolsWebView(ctx, wv)
245	if err != nil {
246		t.Fatalf("unexpected error: %v", err)
247	}
248}
249
250func testPrintWebViewNilWebView(t *testing.T, ctx context.Context) {
251	t.Helper()
252	c := &NavigationCoordinator{}
253	err := c.PrintWebView(ctx, nil)
254	if err == nil {
255		t.Fatal("expected error for nil webview, got nil")
256	}
257}
258
259func testPrintWebViewUnsupportedCapability(t *testing.T, ctx context.Context) {
260	t.Helper()
261	wv := mocks.NewMockWebView(t)
262	wv.EXPECT().ID().Return(port.WebViewID(1)).Once()
263	c := &NavigationCoordinator{}
264	err := c.PrintWebView(ctx, wv)
265	if err == nil {
266		t.Fatal("expected unsupported printing error, got nil")
267	}
268	if got, want := err.Error(), "webview does not support printing"; got != want {
269		t.Fatalf("error = %q, want %q", got, want)
270	}
271}
272
273func testPrintWebViewTargetsProvidedWebView(t *testing.T, ctx context.Context) {
274	t.Helper()
275	base := mocks.NewMockWebView(t)
276	printer := mocks.NewMockPrinter(t)
277	wv := &mockPrinterWebView{MockWebView: base, MockPrinter: printer}
278	base.EXPECT().ID().Return(port.WebViewID(1)).Once()
279	printer.EXPECT().PrintPage().Return().Once()
280	c := &NavigationCoordinator{}
281	err := c.PrintWebView(ctx, wv)
282	if err != nil {
283		t.Fatalf("unexpected error: %v", err)
284	}
285}
286
287func testScrollWebViewNilWebView(t *testing.T, ctx context.Context) {
288	t.Helper()
289	c := &NavigationCoordinator{}
290	c.SetPageScrollUseCase(usecase.NewPageScrollUseCase())
291	err := c.ScrollWebView(ctx, nil, usecase.PageScrollDown)
292	if err == nil {
293		t.Fatal("expected error for nil webview, got nil")
294	}
295}
296
297func testScrollWebViewDelegatesEachCommand(t *testing.T, ctx context.Context) {
298	t.Helper()
299	commands := []struct {
300		cmd usecase.PageScrollCommand
301		dx  int
302		dy  int
303	}{
304		{usecase.PageScrollLeft, -80, 0},
305		{usecase.PageScrollRight, 80, 0},
306		{usecase.PageScrollUp, 0, -80},
307		{usecase.PageScrollDown, 0, 80},
308		{usecase.PageScrollUpFast, 0, -320},
309		{usecase.PageScrollDownFast, 0, 320},
310	}
311
312	for _, tc := range commands {
313		t.Run(tc.cmd.String(), func(t *testing.T) {
314			base := mocks.NewMockWebView(t)
315			scroller := mocks.NewMockPageScrollable(t)
316			wv := &mockScrollableWebView{MockWebView: base, MockPageScrollable: scroller}
317			base.EXPECT().ID().Return(port.WebViewID(42)).Once()
318			req := port.PageScrollRequest{Command: port.PageScrollCommand(tc.cmd), FallbackDX: tc.dx, FallbackDY: tc.dy}
319			scroller.EXPECT().ScrollPage(ctx, req).Return(nil).Once()
320
321			c := &NavigationCoordinator{}
322			c.SetPageScrollUseCase(usecase.NewPageScrollUseCase())
323			err := c.ScrollWebView(ctx, wv, tc.cmd)
324			if err != nil {
325				t.Fatalf("unexpected error for %s: %v", tc.cmd, err)
326			}
327		})
328	}
329}
330
331func testScrollWebViewUsecaseNotSet(t *testing.T, ctx context.Context) {
332	t.Helper()
333	base := mocks.NewMockWebView(t)
334	wv := &mockScrollableWebView{MockWebView: base, MockPageScrollable: mocks.NewMockPageScrollable(t)}
335	base.EXPECT().ID().Return(port.WebViewID(1)).Once()
336	c := &NavigationCoordinator{}
337	err := c.ScrollWebView(ctx, wv, usecase.PageScrollDown)
338	if err == nil {
339		t.Fatal("expected error for missing usecase, got nil")
340	}
341	if got, want := err.Error(), "page scroll usecase not set"; got != want {
342		t.Fatalf("error = %q, want %q", got, want)
343	}
344}

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