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