v0.27.2 5960dd8

pane_view_test.go RawHistoryFolder

1package component_test
2
3import (
4	"testing"
5
6	"github.com/stretchr/testify/assert"
7	"github.com/stretchr/testify/mock"
8	"github.com/stretchr/testify/require"
9
10	"github.com/bnema/dumber/internal/domain/entity"
11	"github.com/bnema/dumber/internal/ui/component"
12	"github.com/bnema/dumber/internal/ui/layout"
13	"github.com/bnema/dumber/internal/ui/layout/mocks"
14)
15
16func TestNewPaneView_CreatesOverlay(t *testing.T) {
17	// Arrange
18	mockFactory := mocks.NewMockWidgetFactory(t)
19	mockOverlay := mocks.NewMockOverlayWidget(t)
20	mockBorderBox := mocks.NewMockBoxWidget(t)
21	mockWebView := mocks.NewMockWidget(t)
22
23	mockLoadingContainer := mocks.NewMockBoxWidget(t)
24	mockLoadingContent := mocks.NewMockBoxWidget(t)
25	mockLoadingSpinner := mocks.NewMockSpinnerWidget(t)
26	mockLoadingLogo := mocks.NewMockImageWidget(t)
27
28	paneID := entity.PaneID("pane-1")
29
30	mockFactory.EXPECT().NewOverlay().Return(mockOverlay).Once()
31	mockOverlay.EXPECT().SetHexpand(true).Once()
32	mockOverlay.EXPECT().SetVexpand(true).Once()
33	mockOverlay.EXPECT().SetVisible(true).Once()
34	mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background
35	mockOverlay.EXPECT().SetChild(mockWebView).Once()
36
37	setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo)
38
39	mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once()
40	mockBorderBox.EXPECT().SetCanFocus(false).Once()
41	mockBorderBox.EXPECT().SetCanTarget(false).Once()
42	mockBorderBox.EXPECT().AddCssClass("pane-border").Once()
43	mockBorderBox.EXPECT().SetHexpand(true).Once()
44	mockBorderBox.EXPECT().SetVexpand(true).Once()
45	mockOverlay.EXPECT().AddOverlay(mockBorderBox).Once()
46	mockOverlay.EXPECT().SetClipOverlay(mockBorderBox, false).Once()
47	mockOverlay.EXPECT().SetMeasureOverlay(mockBorderBox, false).Once()
48
49	// Act
50	pv := component.NewPaneView(mockFactory, paneID, mockWebView)
51
52	// Assert
53	require.NotNil(t, pv)
54	assert.Equal(t, paneID, pv.PaneID())
55	assert.Equal(t, mockWebView, pv.WebViewWidget())
56}
57
58func TestNewPaneView_NilWebView(t *testing.T) {
59	// Arrange
60	mockFactory := mocks.NewMockWidgetFactory(t)
61	mockOverlay := mocks.NewMockOverlayWidget(t)
62	mockBorderBox := mocks.NewMockBoxWidget(t)
63
64	mockLoadingContainer := mocks.NewMockBoxWidget(t)
65	mockLoadingContent := mocks.NewMockBoxWidget(t)
66	mockLoadingSpinner := mocks.NewMockSpinnerWidget(t)
67	mockLoadingLogo := mocks.NewMockImageWidget(t)
68
69	paneID := entity.PaneID("pane-1")
70
71	mockFactory.EXPECT().NewOverlay().Return(mockOverlay).Once()
72	mockOverlay.EXPECT().SetHexpand(true).Once()
73	mockOverlay.EXPECT().SetVexpand(true).Once()
74	mockOverlay.EXPECT().SetVisible(true).Once()
75	mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background
76	// SetChild should NOT be called when webview is nil
77
78	setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo)
79
80	mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once()
81	mockBorderBox.EXPECT().SetCanFocus(false).Once()
82	mockBorderBox.EXPECT().SetCanTarget(false).Once()
83	mockBorderBox.EXPECT().AddCssClass("pane-border").Once()
84	mockBorderBox.EXPECT().SetHexpand(true).Once()
85	mockBorderBox.EXPECT().SetVexpand(true).Once()
86	mockOverlay.EXPECT().AddOverlay(mockBorderBox).Once()
87	mockOverlay.EXPECT().SetClipOverlay(mockBorderBox, false).Once()
88	mockOverlay.EXPECT().SetMeasureOverlay(mockBorderBox, false).Once()
89
90	// Act
91	pv := component.NewPaneView(mockFactory, paneID, nil)
92
93	// Assert
94	require.NotNil(t, pv)
95	assert.Nil(t, pv.WebViewWidget())
96}
97
98func TestSetActive_True_AddsCSSClass(t *testing.T) {
99	// Arrange
100	mockFactory := mocks.NewMockWidgetFactory(t)
101	mockOverlay := mocks.NewMockOverlayWidget(t)
102	mockBorderBox := mocks.NewMockBoxWidget(t)
103	mockWebView := mocks.NewMockWidget(t)
104
105	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
106
107	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
108
109	// Expect CSS class to be added
110	mockBorderBox.EXPECT().AddCssClass("pane-active").Once()
111
112	// Act
113	pv.SetActive(true)
114
115	// Assert
116	assert.True(t, pv.IsActive())
117}
118
119func TestSetActive_False_RemovesCSSClass(t *testing.T) {
120	// Arrange
121	mockFactory := mocks.NewMockWidgetFactory(t)
122	mockOverlay := mocks.NewMockOverlayWidget(t)
123	mockBorderBox := mocks.NewMockBoxWidget(t)
124	mockWebView := mocks.NewMockWidget(t)
125
126	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
127
128	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
129
130	// First activate
131	mockBorderBox.EXPECT().AddCssClass("pane-active").Once()
132	pv.SetActive(true)
133
134	// Then deactivate
135	mockBorderBox.EXPECT().RemoveCssClass("pane-active").Once()
136
137	// Act
138	pv.SetActive(false)
139
140	// Assert
141	assert.False(t, pv.IsActive())
142}
143
144func TestSetActive_NoChangeWhenSameState(t *testing.T) {
145	// Arrange
146	mockFactory := mocks.NewMockWidgetFactory(t)
147	mockOverlay := mocks.NewMockOverlayWidget(t)
148	mockBorderBox := mocks.NewMockBoxWidget(t)
149	mockWebView := mocks.NewMockWidget(t)
150
151	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
152
153	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
154
155	// Act - setting false when already false should not call RemoveCssClass
156	pv.SetActive(false)
157
158	// Assert - no mock expectations for CSS changes
159	assert.False(t, pv.IsActive())
160}
161
162func TestPaneID_ReturnsPaneID(t *testing.T) {
163	// Arrange
164	mockFactory := mocks.NewMockWidgetFactory(t)
165	mockOverlay := mocks.NewMockOverlayWidget(t)
166	mockBorderBox := mocks.NewMockBoxWidget(t)
167	mockWebView := mocks.NewMockWidget(t)
168
169	paneID := entity.PaneID("test-pane-123")
170	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
171
172	pv := component.NewPaneView(mockFactory, paneID, mockWebView)
173
174	// Act
175	result := pv.PaneID()
176
177	// Assert
178	assert.Equal(t, paneID, result)
179}
180
181func TestWebViewWidget_ReturnsWebView(t *testing.T) {
182	// Arrange
183	mockFactory := mocks.NewMockWidgetFactory(t)
184	mockOverlay := mocks.NewMockOverlayWidget(t)
185	mockBorderBox := mocks.NewMockBoxWidget(t)
186	mockWebView := mocks.NewMockWidget(t)
187
188	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
189
190	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
191
192	// Act
193	result := pv.WebViewWidget()
194
195	// Assert
196	assert.Equal(t, mockWebView, result)
197}
198
199func TestSetWebViewWidget_ReplacesWidget(t *testing.T) {
200	// Arrange
201	mockFactory := mocks.NewMockWidgetFactory(t)
202	mockOverlay := mocks.NewMockOverlayWidget(t)
203	mockBorderBox := mocks.NewMockBoxWidget(t)
204	mockOldWebView := mocks.NewMockWidget(t)
205	mockNewWebView := mocks.NewMockWidget(t)
206
207	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockOldWebView)
208
209	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockOldWebView)
210
211	// Expect removal of old widget and addition of new
212	mockOverlay.EXPECT().SetChild(nil).Once()
213	mockNewWebView.EXPECT().GetParent().Return(nil).Once()
214	mockOverlay.EXPECT().SetChild(mockNewWebView).Once()
215
216	// Act
217	pv.SetWebViewWidget(mockNewWebView)
218
219	// Assert
220	assert.Equal(t, mockNewWebView, pv.WebViewWidget())
221}
222
223func TestSetWebViewWidget_FromNil(t *testing.T) {
224	// Arrange
225	mockFactory := mocks.NewMockWidgetFactory(t)
226	mockOverlay := mocks.NewMockOverlayWidget(t)
227	mockBorderBox := mocks.NewMockBoxWidget(t)
228	mockNewWebView := mocks.NewMockWidget(t)
229
230	setupPaneViewMocksNoWebView(t, mockFactory, mockOverlay, mockBorderBox)
231
232	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), nil)
233
234	// Expect only setting new child (no removal since old was nil)
235	mockNewWebView.EXPECT().GetParent().Return(nil).Once()
236	mockOverlay.EXPECT().SetChild(mockNewWebView).Once()
237
238	// Act
239	pv.SetWebViewWidget(mockNewWebView)
240
241	// Assert
242	assert.Equal(t, mockNewWebView, pv.WebViewWidget())
243}
244
245func TestGrabFocus_DelegatesToWebView(t *testing.T) {
246	// Arrange
247	mockFactory := mocks.NewMockWidgetFactory(t)
248	mockOverlay := mocks.NewMockOverlayWidget(t)
249	mockBorderBox := mocks.NewMockBoxWidget(t)
250	mockWebView := mocks.NewMockWidget(t)
251
252	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
253
254	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
255
256	mockWebView.EXPECT().GrabFocus().Return(true).Once()
257
258	// Act
259	result := pv.GrabFocus()
260
261	// Assert
262	assert.True(t, result)
263}
264
265func TestGrabFocus_NilWebView_ReturnsFalse(t *testing.T) {
266	// Arrange
267	mockFactory := mocks.NewMockWidgetFactory(t)
268	mockOverlay := mocks.NewMockOverlayWidget(t)
269	mockBorderBox := mocks.NewMockBoxWidget(t)
270
271	setupPaneViewMocksNoWebView(t, mockFactory, mockOverlay, mockBorderBox)
272
273	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), nil)
274
275	// Act
276	result := pv.GrabFocus()
277
278	// Assert
279	assert.False(t, result)
280}
281
282func TestHasFocus_DelegatesToWebView(t *testing.T) {
283	// Arrange
284	mockFactory := mocks.NewMockWidgetFactory(t)
285	mockOverlay := mocks.NewMockOverlayWidget(t)
286	mockBorderBox := mocks.NewMockBoxWidget(t)
287	mockWebView := mocks.NewMockWidget(t)
288
289	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
290
291	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
292
293	mockWebView.EXPECT().HasFocus().Return(true).Once()
294
295	// Act
296	result := pv.HasFocus()
297
298	// Assert
299	assert.True(t, result)
300}
301
302func TestHasFocus_NilWebView_ReturnsFalse(t *testing.T) {
303	// Arrange
304	mockFactory := mocks.NewMockWidgetFactory(t)
305	mockOverlay := mocks.NewMockOverlayWidget(t)
306	mockBorderBox := mocks.NewMockBoxWidget(t)
307
308	setupPaneViewMocksNoWebView(t, mockFactory, mockOverlay, mockBorderBox)
309
310	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), nil)
311
312	// Act
313	result := pv.HasFocus()
314
315	// Assert
316	assert.False(t, result)
317}
318
319func TestWidget_ReturnsOverlay(t *testing.T) {
320	// Arrange
321	mockFactory := mocks.NewMockWidgetFactory(t)
322	mockOverlay := mocks.NewMockOverlayWidget(t)
323	mockBorderBox := mocks.NewMockBoxWidget(t)
324	mockWebView := mocks.NewMockWidget(t)
325
326	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
327
328	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
329
330	// Act
331	result := pv.Widget()
332
333	// Assert
334	assert.Equal(t, mockOverlay, result)
335}
336
337func TestOverlay_ReturnsOverlayWidget(t *testing.T) {
338	// Arrange
339	mockFactory := mocks.NewMockWidgetFactory(t)
340	mockOverlay := mocks.NewMockOverlayWidget(t)
341	mockBorderBox := mocks.NewMockBoxWidget(t)
342	mockWebView := mocks.NewMockWidget(t)
343
344	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
345
346	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
347
348	// Act
349	result := pv.Overlay()
350
351	// Assert
352	assert.Equal(t, mockOverlay, result)
353}
354
355func TestShow_DelegatesToOverlay(t *testing.T) {
356	// Arrange
357	mockFactory := mocks.NewMockWidgetFactory(t)
358	mockOverlay := mocks.NewMockOverlayWidget(t)
359	mockBorderBox := mocks.NewMockBoxWidget(t)
360	mockWebView := mocks.NewMockWidget(t)
361
362	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
363
364	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
365
366	mockOverlay.EXPECT().Show().Once()
367
368	// Act
369	pv.Show()
370
371	// Assert - verified by mock expectations
372}
373
374func TestHide_DelegatesToOverlay(t *testing.T) {
375	// Arrange
376	mockFactory := mocks.NewMockWidgetFactory(t)
377	mockOverlay := mocks.NewMockOverlayWidget(t)
378	mockBorderBox := mocks.NewMockBoxWidget(t)
379	mockWebView := mocks.NewMockWidget(t)
380
381	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
382
383	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
384
385	mockOverlay.EXPECT().Hide().Once()
386
387	// Act
388	pv.Hide()
389
390	// Assert - verified by mock expectations
391}
392
393func TestSetVisible_DelegatesToOverlay(t *testing.T) {
394	// Arrange
395	mockFactory := mocks.NewMockWidgetFactory(t)
396	mockOverlay := mocks.NewMockOverlayWidget(t)
397	mockBorderBox := mocks.NewMockBoxWidget(t)
398	mockWebView := mocks.NewMockWidget(t)
399
400	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
401
402	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
403
404	mockOverlay.EXPECT().SetVisible(true).Once()
405
406	// Act
407	pv.SetVisible(true)
408
409	// Assert - verified by mock expectations
410}
411
412func TestIsVisible_DelegatesToOverlay(t *testing.T) {
413	// Arrange
414	mockFactory := mocks.NewMockWidgetFactory(t)
415	mockOverlay := mocks.NewMockOverlayWidget(t)
416	mockBorderBox := mocks.NewMockBoxWidget(t)
417	mockWebView := mocks.NewMockWidget(t)
418
419	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
420
421	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
422
423	mockOverlay.EXPECT().IsVisible().Return(true).Once()
424
425	// Act
426	result := pv.IsVisible()
427
428	// Assert
429	assert.True(t, result)
430}
431
432func TestAddCssClass_DelegatesToOverlay(t *testing.T) {
433	// Arrange
434	mockFactory := mocks.NewMockWidgetFactory(t)
435	mockOverlay := mocks.NewMockOverlayWidget(t)
436	mockBorderBox := mocks.NewMockBoxWidget(t)
437	mockWebView := mocks.NewMockWidget(t)
438
439	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
440
441	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
442
443	mockOverlay.EXPECT().AddCssClass("custom-class").Once()
444
445	// Act
446	pv.AddCssClass("custom-class")
447
448	// Assert - verified by mock expectations
449}
450
451func TestRemoveCssClass_DelegatesToOverlay(t *testing.T) {
452	// Arrange
453	mockFactory := mocks.NewMockWidgetFactory(t)
454	mockOverlay := mocks.NewMockOverlayWidget(t)
455	mockBorderBox := mocks.NewMockBoxWidget(t)
456	mockWebView := mocks.NewMockWidget(t)
457
458	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
459
460	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
461
462	mockOverlay.EXPECT().RemoveCssClass("custom-class").Once()
463
464	// Act
465	pv.RemoveCssClass("custom-class")
466
467	// Assert - verified by mock expectations
468}
469
470func TestSetOnFocusIn_SetsCallback(t *testing.T) {
471	// Arrange
472	mockFactory := mocks.NewMockWidgetFactory(t)
473	mockOverlay := mocks.NewMockOverlayWidget(t)
474	mockBorderBox := mocks.NewMockBoxWidget(t)
475	mockWebView := mocks.NewMockWidget(t)
476
477	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
478
479	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
480
481	callback := func(paneID entity.PaneID) {}
482
483	// Act
484	pv.SetOnFocusIn(callback)
485
486	// Assert - callback is set (we can't directly verify without triggering focus)
487	assert.NotNil(t, pv)
488}
489
490func TestSetOnFocusOut_SetsCallback(t *testing.T) {
491	// Arrange
492	mockFactory := mocks.NewMockWidgetFactory(t)
493	mockOverlay := mocks.NewMockOverlayWidget(t)
494	mockBorderBox := mocks.NewMockBoxWidget(t)
495	mockWebView := mocks.NewMockWidget(t)
496
497	setupPaneViewMocks(t, mockFactory, mockOverlay, mockBorderBox, mockWebView)
498
499	pv := component.NewPaneView(mockFactory, entity.PaneID("pane-1"), mockWebView)
500
501	callback := func(paneID entity.PaneID) {}
502
503	// Act
504	pv.SetOnFocusOut(callback)
505
506	// Assert - callback is set (we can't directly verify without triggering focus)
507	assert.NotNil(t, pv)
508}
509
510// Helper function to setup common mock expectations for PaneView creation
511func setupLoadingSkeletonMocks(
512	t *testing.T,
513	mockFactory *mocks.MockWidgetFactory,
514	mockOverlay *mocks.MockOverlayWidget,
515	mockLoadingContainer *mocks.MockBoxWidget,
516	mockLoadingContent *mocks.MockBoxWidget,
517	mockLoadingSpinner *mocks.MockSpinnerWidget,
518	mockLoadingLogo *mocks.MockImageWidget,
519) {
520	mockLoadingVersion := mocks.NewMockLabelWidget(t)
521
522	mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockLoadingContainer).Once()
523	mockLoadingContainer.EXPECT().SetHexpand(true).Maybe()
524	mockLoadingContainer.EXPECT().SetVexpand(true).Maybe()
525	mockLoadingContainer.EXPECT().SetHalign(mock.Anything).Maybe()
526	mockLoadingContainer.EXPECT().SetValign(mock.Anything).Maybe()
527	mockLoadingContainer.EXPECT().SetCanFocus(false).Maybe()
528	mockLoadingContainer.EXPECT().SetCanTarget(false).Maybe()
529	mockLoadingContainer.EXPECT().AddCssClass("loading-skeleton").Maybe()
530	mockLoadingContainer.EXPECT().SetVisible(true).Maybe()
531
532	mockFactory.EXPECT().NewBox(layout.OrientationVertical, 6).Return(mockLoadingContent).Once()
533	mockLoadingContent.EXPECT().SetHalign(mock.Anything).Maybe()
534	mockLoadingContent.EXPECT().SetValign(mock.Anything).Maybe()
535	mockLoadingContent.EXPECT().SetCanFocus(false).Maybe()
536	mockLoadingContent.EXPECT().SetCanTarget(false).Maybe()
537	mockLoadingContent.EXPECT().AddCssClass("loading-skeleton-content").Maybe()
538
539	mockFactory.EXPECT().NewImage().Return(mockLoadingLogo).Once()
540	mockLoadingLogo.EXPECT().SetHalign(mock.Anything).Maybe()
541	mockLoadingLogo.EXPECT().SetValign(mock.Anything).Maybe()
542	mockLoadingLogo.EXPECT().SetCanFocus(false).Maybe()
543	mockLoadingLogo.EXPECT().SetCanTarget(false).Maybe()
544	mockLoadingLogo.EXPECT().SetSizeRequest(256, 256).Maybe()
545	mockLoadingLogo.EXPECT().SetPixelSize(256).Maybe()
546	mockLoadingLogo.EXPECT().AddCssClass("loading-skeleton-logo").Maybe()
547	mockLoadingLogo.EXPECT().SetFromPaintable(mock.Anything).Maybe()
548
549	mockFactory.EXPECT().NewSpinner().Return(mockLoadingSpinner).Once()
550	mockLoadingSpinner.EXPECT().SetHalign(mock.Anything).Maybe()
551	mockLoadingSpinner.EXPECT().SetValign(mock.Anything).Maybe()
552	mockLoadingSpinner.EXPECT().SetCanFocus(false).Maybe()
553	mockLoadingSpinner.EXPECT().SetCanTarget(false).Maybe()
554	mockLoadingSpinner.EXPECT().SetSizeRequest(32, 32).Maybe()
555	mockLoadingSpinner.EXPECT().AddCssClass("loading-skeleton-spinner").Maybe()
556	mockLoadingSpinner.EXPECT().Start().Maybe()
557
558	mockFactory.EXPECT().NewLabel(mock.Anything).Return(mockLoadingVersion).Once()
559	mockLoadingVersion.EXPECT().SetHalign(mock.Anything).Maybe()
560	mockLoadingVersion.EXPECT().SetValign(mock.Anything).Maybe()
561	mockLoadingVersion.EXPECT().SetCanFocus(false).Maybe()
562	mockLoadingVersion.EXPECT().SetCanTarget(false).Maybe()
563	mockLoadingVersion.EXPECT().SetMaxWidthChars(mock.Anything).Maybe()
564	mockLoadingVersion.EXPECT().SetEllipsize(mock.Anything).Maybe()
565	mockLoadingVersion.EXPECT().AddCssClass("loading-skeleton-version").Maybe()
566
567	mockLoadingContent.EXPECT().Append(mockLoadingLogo).Maybe()
568	mockLoadingContent.EXPECT().Append(mockLoadingSpinner).Maybe()
569	mockLoadingContent.EXPECT().Append(mockLoadingVersion).Maybe()
570	mockLoadingContainer.EXPECT().Append(mockLoadingContent).Maybe()
571
572	mockOverlay.EXPECT().AddOverlay(mockLoadingContainer).Once()
573	mockOverlay.EXPECT().SetClipOverlay(mockLoadingContainer, false).Once()
574	mockOverlay.EXPECT().SetMeasureOverlay(mockLoadingContainer, false).Once()
575}
576
577// Helper function to setup common mock expectations for PaneView creation
578func setupPaneViewMocks(
579	t *testing.T,
580	mockFactory *mocks.MockWidgetFactory,
581	mockOverlay *mocks.MockOverlayWidget,
582	mockBorderBox *mocks.MockBoxWidget,
583	mockWebView *mocks.MockWidget,
584) {
585	mockLoadingContainer := mocks.NewMockBoxWidget(t)
586	mockLoadingContent := mocks.NewMockBoxWidget(t)
587	mockLoadingSpinner := mocks.NewMockSpinnerWidget(t)
588	mockLoadingLogo := mocks.NewMockImageWidget(t)
589
590	mockFactory.EXPECT().NewOverlay().Return(mockOverlay).Once()
591	mockOverlay.EXPECT().SetHexpand(true).Once()
592	mockOverlay.EXPECT().SetVexpand(true).Once()
593	mockOverlay.EXPECT().SetVisible(true).Once()
594	mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background
595	mockOverlay.EXPECT().SetChild(mockWebView).Once()
596
597	setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo)
598
599	mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once()
600	mockBorderBox.EXPECT().SetCanFocus(false).Once()
601	mockBorderBox.EXPECT().SetCanTarget(false).Once()
602	mockBorderBox.EXPECT().AddCssClass("pane-border").Once()
603	mockBorderBox.EXPECT().SetHexpand(true).Once()
604	mockBorderBox.EXPECT().SetVexpand(true).Once()
605	mockOverlay.EXPECT().AddOverlay(mockBorderBox).Once()
606	mockOverlay.EXPECT().SetClipOverlay(mockBorderBox, false).Once()
607	mockOverlay.EXPECT().SetMeasureOverlay(mockBorderBox, false).Once()
608}
609
610func setupPaneViewMocksNoWebView(
611	t *testing.T,
612	mockFactory *mocks.MockWidgetFactory,
613	mockOverlay *mocks.MockOverlayWidget,
614	mockBorderBox *mocks.MockBoxWidget,
615) {
616	mockLoadingContainer := mocks.NewMockBoxWidget(t)
617	mockLoadingContent := mocks.NewMockBoxWidget(t)
618	mockLoadingSpinner := mocks.NewMockSpinnerWidget(t)
619	mockLoadingLogo := mocks.NewMockImageWidget(t)
620
621	mockFactory.EXPECT().NewOverlay().Return(mockOverlay).Once()
622	mockOverlay.EXPECT().SetHexpand(true).Once()
623	mockOverlay.EXPECT().SetVexpand(true).Once()
624	mockOverlay.EXPECT().SetVisible(true).Once()
625	mockOverlay.EXPECT().AddCssClass("pane-overlay").Once() // Theme background
626
627	setupLoadingSkeletonMocks(t, mockFactory, mockOverlay, mockLoadingContainer, mockLoadingContent, mockLoadingSpinner, mockLoadingLogo)
628
629	mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockBorderBox).Once()
630	mockBorderBox.EXPECT().SetCanFocus(false).Once()
631	mockBorderBox.EXPECT().SetCanTarget(false).Once()
632	mockBorderBox.EXPECT().AddCssClass("pane-border").Once()
633	mockBorderBox.EXPECT().SetHexpand(true).Once()
634	mockBorderBox.EXPECT().SetVexpand(true).Once()
635	mockOverlay.EXPECT().AddOverlay(mockBorderBox).Once()
636	mockOverlay.EXPECT().SetClipOverlay(mockBorderBox, false).Once()
637	mockOverlay.EXPECT().SetMeasureOverlay(mockBorderBox, false).Once()
638}

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