1package layout_test
2
3import (
4 "context"
5 "testing"
6
7 "github.com/stretchr/testify/assert"
8 "github.com/stretchr/testify/mock"
9 "github.com/stretchr/testify/require"
10
11 "github.com/bnema/dumber/internal/domain/entity"
12 "github.com/bnema/dumber/internal/ui/layout"
13 "github.com/bnema/dumber/internal/ui/layout/mocks"
14)
15
16func setupStackedLeafMocks(t *testing.T, mockFactory *mocks.MockWidgetFactory) (*mocks.MockBoxWidget, *mocks.MockWidget) {
17 mockStackBox := mocks.NewMockBoxWidget(t)
18 mockFactory.EXPECT().NewBox(layout.OrientationVertical, 0).Return(mockStackBox).Once()
19 mockStackBox.EXPECT().SetHexpand(true).Once()
20 mockStackBox.EXPECT().SetVexpand(true).Once()
21
22 mockTitleBar, mockFavicon, mockLabel, mockContainer := setupPaneMocks(t, mockFactory, mockStackBox)
23 _ = mockFavicon
24 _ = mockLabel
25 mockTitleBar.EXPECT().SetVisible(false).Once() // Active pane hides its title bar
26 mockContainer.EXPECT().SetVisible(true).Once()
27 mockTitleBar.EXPECT().AddCssClass("active").Once()
28
29 return mockStackBox, mockContainer
30}
31
32func TestNewTreeRenderer_CreatesRenderer(t *testing.T) {
33 // Arrange
34 ctx := context.Background()
35 mockFactory := mocks.NewMockWidgetFactory(t)
36 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
37
38 // Act
39 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
40
41 // Assert
42 require.NotNil(t, renderer)
43 assert.Equal(t, 0, renderer.NodeCount())
44}
45
46func TestBuild_NilRoot_ReturnsError(t *testing.T) {
47 // Arrange
48 ctx := context.Background()
49 mockFactory := mocks.NewMockWidgetFactory(t)
50 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
51
52 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
53
54 // Act
55 widget, err := renderer.Build(ctx, nil)
56
57 // Assert
58 assert.Nil(t, widget)
59 assert.ErrorIs(t, err, layout.ErrNilRoot)
60}
61
62func TestBuild_SingleLeafPane(t *testing.T) {
63 // Arrange
64 ctx := context.Background()
65 mockFactory := mocks.NewMockWidgetFactory(t)
66 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
67 mockStackBox, mockContainer := setupStackedLeafMocks(t, mockFactory)
68
69 pane := entity.NewPane(entity.PaneID("pane-1"))
70 node := &entity.PaneNode{
71 ID: "node-1",
72 Pane: pane,
73 }
74
75 mockPaneViewFactory.EXPECT().CreatePaneView(node).Return(mockContainer).Once()
76
77 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
78
79 // Act
80 widget, err := renderer.Build(ctx, node)
81
82 // Assert
83 require.NoError(t, err)
84 assert.Equal(t, mockStackBox, widget)
85 assert.Equal(t, 1, renderer.NodeCount())
86
87 // Verify lookup works
88 assert.Equal(t, mockStackBox, renderer.Lookup("node-1"))
89}
90
91func TestBuild_HorizontalSplit(t *testing.T) {
92 // Arrange
93 ctx := context.Background()
94 mockFactory := mocks.NewMockWidgetFactory(t)
95 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
96 mockPaned := mocks.NewMockPanedWidget(t)
97 mockLeftStackBox, mockLeftContainer := setupStackedLeafMocks(t, mockFactory)
98 mockRightStackBox, mockRightContainer := setupStackedLeafMocks(t, mockFactory)
99
100 leftPane := entity.NewPane(entity.PaneID("pane-left"))
101 rightPane := entity.NewPane(entity.PaneID("pane-right"))
102
103 leftNode := &entity.PaneNode{ID: "node-left", Pane: leftPane}
104 rightNode := &entity.PaneNode{ID: "node-right", Pane: rightPane}
105
106 splitNode := &entity.PaneNode{
107 ID: "node-split",
108 SplitDir: entity.SplitHorizontal,
109 SplitRatio: 0.5,
110 Children: []*entity.PaneNode{leftNode, rightNode},
111 }
112
113 mockPaneViewFactory.EXPECT().CreatePaneView(leftNode).Return(mockLeftContainer).Once()
114 mockPaneViewFactory.EXPECT().CreatePaneView(rightNode).Return(mockRightContainer).Once()
115
116 // SplitView creation expectations
117 mockFactory.EXPECT().NewPaned(layout.OrientationHorizontal).Return(mockPaned).Once()
118 mockPaned.EXPECT().SetResizeStartChild(true).Once()
119 mockPaned.EXPECT().SetResizeEndChild(true).Once()
120 mockPaned.EXPECT().SetVisible(true).Once()
121 mockLeftStackBox.EXPECT().SetVisible(true).Once()
122 mockRightStackBox.EXPECT().SetVisible(true).Once()
123 mockPaned.EXPECT().SetStartChild(mockLeftStackBox).Once()
124 mockPaned.EXPECT().SetEndChild(mockRightStackBox).Once()
125 mockPaned.EXPECT().ConnectNotifyPosition(mock.Anything).Return(uint(0)).Once()
126 mockPaned.EXPECT().GetAllocatedWidth().Return(0).Once()
127 mockPaned.EXPECT().ConnectMap(mock.Anything).Return(uint(0)).Once()
128 mockPaned.EXPECT().AddTickCallback(mock.Anything).Return(uint(0)).Once()
129
130 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
131
132 // Act
133 widget, err := renderer.Build(ctx, splitNode)
134
135 // Assert
136 require.NoError(t, err)
137 assert.NotNil(t, widget)
138 assert.Equal(t, 3, renderer.NodeCount()) // split + 2 leaves
139}
140
141func TestBuild_VerticalSplit(t *testing.T) {
142 // Arrange
143 ctx := context.Background()
144 mockFactory := mocks.NewMockWidgetFactory(t)
145 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
146 mockPaned := mocks.NewMockPanedWidget(t)
147 mockTopStackBox, mockTopContainer := setupStackedLeafMocks(t, mockFactory)
148 mockBottomStackBox, mockBottomContainer := setupStackedLeafMocks(t, mockFactory)
149
150 topPane := entity.NewPane(entity.PaneID("pane-top"))
151 bottomPane := entity.NewPane(entity.PaneID("pane-bottom"))
152
153 topNode := &entity.PaneNode{ID: "node-top", Pane: topPane}
154 bottomNode := &entity.PaneNode{ID: "node-bottom", Pane: bottomPane}
155
156 splitNode := &entity.PaneNode{
157 ID: "node-split",
158 SplitDir: entity.SplitVertical,
159 SplitRatio: 0.3,
160 Children: []*entity.PaneNode{topNode, bottomNode},
161 }
162
163 mockPaneViewFactory.EXPECT().CreatePaneView(topNode).Return(mockTopContainer).Once()
164 mockPaneViewFactory.EXPECT().CreatePaneView(bottomNode).Return(mockBottomContainer).Once()
165
166 // SplitView creation expectations - should be vertical
167 mockFactory.EXPECT().NewPaned(layout.OrientationVertical).Return(mockPaned).Once()
168 mockPaned.EXPECT().SetResizeStartChild(true).Once()
169 mockPaned.EXPECT().SetResizeEndChild(true).Once()
170 mockPaned.EXPECT().SetVisible(true).Once()
171 mockTopStackBox.EXPECT().SetVisible(true).Once()
172 mockBottomStackBox.EXPECT().SetVisible(true).Once()
173 mockPaned.EXPECT().SetStartChild(mockTopStackBox).Once()
174 mockPaned.EXPECT().SetEndChild(mockBottomStackBox).Once()
175 mockPaned.EXPECT().ConnectNotifyPosition(mock.Anything).Return(uint(0)).Once()
176 mockPaned.EXPECT().GetAllocatedHeight().Return(0).Once()
177 mockPaned.EXPECT().ConnectMap(mock.Anything).Return(uint(0)).Once()
178 mockPaned.EXPECT().AddTickCallback(mock.Anything).Return(uint(0)).Once()
179
180 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
181
182 // Act
183 widget, err := renderer.Build(ctx, splitNode)
184
185 // Assert
186 require.NoError(t, err)
187 assert.NotNil(t, widget)
188}
189
190func TestBuild_NestedSplits(t *testing.T) {
191 // Arrange
192 ctx := context.Background()
193 mockFactory := mocks.NewMockWidgetFactory(t)
194 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
195 mockOuterPaned := mocks.NewMockPanedWidget(t)
196 mockInnerPaned := mocks.NewMockPanedWidget(t)
197 mockStackBox1, mockContainer1 := setupStackedLeafMocks(t, mockFactory)
198 mockStackBox2, mockContainer2 := setupStackedLeafMocks(t, mockFactory)
199 mockStackBox3, mockContainer3 := setupStackedLeafMocks(t, mockFactory)
200
201 // Create tree structure:
202 // outer (horizontal)
203 // / \
204 // inner (vertical) pane3
205 // / \
206 // pane1 pane2
207
208 pane1 := entity.NewPane(entity.PaneID("pane-1"))
209 pane2 := entity.NewPane(entity.PaneID("pane-2"))
210 pane3 := entity.NewPane(entity.PaneID("pane-3"))
211
212 node1 := &entity.PaneNode{ID: "node-1", Pane: pane1}
213 node2 := &entity.PaneNode{ID: "node-2", Pane: pane2}
214 node3 := &entity.PaneNode{ID: "node-3", Pane: pane3}
215
216 innerSplit := &entity.PaneNode{
217 ID: "inner-split",
218 SplitDir: entity.SplitVertical,
219 SplitRatio: 0.5,
220 Children: []*entity.PaneNode{node1, node2},
221 }
222
223 outerSplit := &entity.PaneNode{
224 ID: "outer-split",
225 SplitDir: entity.SplitHorizontal,
226 SplitRatio: 0.6,
227 Children: []*entity.PaneNode{innerSplit, node3},
228 }
229
230 // Setup expectations for leaf nodes
231 mockPaneViewFactory.EXPECT().CreatePaneView(node1).Return(mockContainer1).Once()
232 mockPaneViewFactory.EXPECT().CreatePaneView(node2).Return(mockContainer2).Once()
233 mockPaneViewFactory.EXPECT().CreatePaneView(node3).Return(mockContainer3).Once()
234
235 // Inner split (vertical) is created first
236 mockFactory.EXPECT().NewPaned(layout.OrientationVertical).Return(mockInnerPaned).Once()
237 mockInnerPaned.EXPECT().SetResizeStartChild(true).Once()
238 mockInnerPaned.EXPECT().SetResizeEndChild(true).Once()
239 mockInnerPaned.EXPECT().SetVisible(true).Once()
240 mockStackBox1.EXPECT().SetVisible(true).Once()
241 mockStackBox2.EXPECT().SetVisible(true).Once()
242 mockInnerPaned.EXPECT().SetStartChild(mockStackBox1).Once()
243 mockInnerPaned.EXPECT().SetEndChild(mockStackBox2).Once()
244 mockInnerPaned.EXPECT().ConnectNotifyPosition(mock.Anything).Return(uint(0)).Once()
245 mockInnerPaned.EXPECT().GetAllocatedHeight().Return(0).Once()
246 mockInnerPaned.EXPECT().ConnectMap(mock.Anything).Return(uint(0)).Once()
247 mockInnerPaned.EXPECT().AddTickCallback(mock.Anything).Return(uint(0)).Once()
248
249 // Outer split (horizontal) gets inner paned and pane3
250 mockFactory.EXPECT().NewPaned(layout.OrientationHorizontal).Return(mockOuterPaned).Once()
251 mockOuterPaned.EXPECT().SetResizeStartChild(true).Once()
252 mockOuterPaned.EXPECT().SetResizeEndChild(true).Once()
253 mockOuterPaned.EXPECT().SetVisible(true).Once()
254 mockInnerPaned.EXPECT().SetVisible(true).Once()
255 mockStackBox3.EXPECT().SetVisible(true).Once()
256 mockOuterPaned.EXPECT().SetStartChild(mockInnerPaned).Once()
257 mockOuterPaned.EXPECT().SetEndChild(mockStackBox3).Once()
258 mockOuterPaned.EXPECT().ConnectNotifyPosition(mock.Anything).Return(uint(0)).Once()
259 mockOuterPaned.EXPECT().GetAllocatedWidth().Return(0).Once()
260 mockOuterPaned.EXPECT().ConnectMap(mock.Anything).Return(uint(0)).Once()
261 mockOuterPaned.EXPECT().AddTickCallback(mock.Anything).Return(uint(0)).Once()
262
263 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
264
265 // Act
266 widget, err := renderer.Build(ctx, outerSplit)
267
268 // Assert
269 require.NoError(t, err)
270 assert.NotNil(t, widget)
271 assert.Equal(t, 5, renderer.NodeCount()) // 2 splits + 3 leaves
272}
273
274// Note: TestBuild_StackedPanes is omitted because StackedView has complex internal
275// widget creation that makes mocking unwieldy. StackedView is thoroughly tested
276// in stacked_test.go. TreeRenderer's stacked rendering is verified implicitly
277// through integration tests.
278
279func TestLookup_ExistingNode(t *testing.T) {
280 // Arrange
281 ctx := context.Background()
282 mockFactory := mocks.NewMockWidgetFactory(t)
283 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
284 mockStackBox, mockContainer := setupStackedLeafMocks(t, mockFactory)
285
286 pane := entity.NewPane(entity.PaneID("pane-1"))
287 node := &entity.PaneNode{ID: "test-node-id", Pane: pane}
288
289 mockPaneViewFactory.EXPECT().CreatePaneView(node).Return(mockContainer).Once()
290
291 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
292 _, _ = renderer.Build(ctx, node)
293
294 // Act
295 result := renderer.Lookup("test-node-id")
296
297 // Assert
298 assert.Equal(t, mockStackBox, result)
299}
300
301func TestLookup_NonExistentNode(t *testing.T) {
302 // Arrange
303 ctx := context.Background()
304 mockFactory := mocks.NewMockWidgetFactory(t)
305 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
306 _, mockContainer := setupStackedLeafMocks(t, mockFactory)
307
308 pane := entity.NewPane(entity.PaneID("pane-1"))
309 node := &entity.PaneNode{ID: "node-1", Pane: pane}
310
311 mockPaneViewFactory.EXPECT().CreatePaneView(node).Return(mockContainer).Once()
312
313 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
314 _, _ = renderer.Build(ctx, node)
315
316 // Act
317 result := renderer.Lookup("non-existent-id")
318
319 // Assert
320 assert.Nil(t, result)
321}
322
323func TestLookupNode_Existing(t *testing.T) {
324 // Arrange
325 ctx := context.Background()
326 mockFactory := mocks.NewMockWidgetFactory(t)
327 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
328 mockStackBox, mockContainer := setupStackedLeafMocks(t, mockFactory)
329
330 pane := entity.NewPane(entity.PaneID("pane-1"))
331 node := &entity.PaneNode{ID: "node-1", Pane: pane}
332
333 mockPaneViewFactory.EXPECT().CreatePaneView(node).Return(mockContainer).Once()
334
335 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
336 _, _ = renderer.Build(ctx, node)
337
338 // Act
339 widget, found := renderer.LookupNode("node-1")
340
341 // Assert
342 assert.True(t, found)
343 assert.Equal(t, mockStackBox, widget)
344}
345
346func TestLookupNode_NonExistent(t *testing.T) {
347 // Arrange
348 ctx := context.Background()
349 mockFactory := mocks.NewMockWidgetFactory(t)
350 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
351
352 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
353
354 // Act
355 widget, found := renderer.LookupNode("non-existent")
356
357 // Assert
358 assert.False(t, found)
359 assert.Nil(t, widget)
360}
361
362func TestClear_RemovesAllMappings(t *testing.T) {
363 // Arrange
364 ctx := context.Background()
365 mockFactory := mocks.NewMockWidgetFactory(t)
366 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
367 _, mockContainer := setupStackedLeafMocks(t, mockFactory)
368
369 pane := entity.NewPane(entity.PaneID("pane-1"))
370 node := &entity.PaneNode{ID: "node-1", Pane: pane}
371
372 mockPaneViewFactory.EXPECT().CreatePaneView(node).Return(mockContainer).Once()
373
374 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
375 _, _ = renderer.Build(ctx, node)
376
377 require.Equal(t, 1, renderer.NodeCount())
378
379 // Act
380 renderer.Clear()
381
382 // Assert
383 assert.Equal(t, 0, renderer.NodeCount())
384 assert.Nil(t, renderer.Lookup("node-1"))
385}
386
387func TestGetNodeIDs_ReturnsAllIDs(t *testing.T) {
388 // Arrange
389 mockFactory := mocks.NewMockWidgetFactory(t)
390 ctx := context.Background()
391 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
392 mockPaned := mocks.NewMockPanedWidget(t)
393 mockLeftStackBox, mockLeftContainer := setupStackedLeafMocks(t, mockFactory)
394 mockRightStackBox, mockRightContainer := setupStackedLeafMocks(t, mockFactory)
395
396 pane1 := entity.NewPane(entity.PaneID("pane-1"))
397 pane2 := entity.NewPane(entity.PaneID("pane-2"))
398
399 node1 := &entity.PaneNode{ID: "left-node", Pane: pane1}
400 node2 := &entity.PaneNode{ID: "right-node", Pane: pane2}
401
402 splitNode := &entity.PaneNode{
403 ID: "split-node",
404 SplitDir: entity.SplitHorizontal,
405 SplitRatio: 0.5,
406 Children: []*entity.PaneNode{node1, node2},
407 }
408
409 mockPaneViewFactory.EXPECT().CreatePaneView(node1).Return(mockLeftContainer).Once()
410 mockPaneViewFactory.EXPECT().CreatePaneView(node2).Return(mockRightContainer).Once()
411
412 mockFactory.EXPECT().NewPaned(layout.OrientationHorizontal).Return(mockPaned).Once()
413 mockPaned.EXPECT().SetResizeStartChild(true).Once()
414 mockPaned.EXPECT().SetResizeEndChild(true).Once()
415 mockPaned.EXPECT().SetVisible(true).Once()
416 mockLeftStackBox.EXPECT().SetVisible(true).Once()
417 mockRightStackBox.EXPECT().SetVisible(true).Once()
418 mockPaned.EXPECT().SetStartChild(mockLeftStackBox).Once()
419 mockPaned.EXPECT().SetEndChild(mockRightStackBox).Once()
420 mockPaned.EXPECT().ConnectNotifyPosition(mock.Anything).Return(uint(0)).Once()
421 mockPaned.EXPECT().GetAllocatedWidth().Return(0).Once()
422 mockPaned.EXPECT().ConnectMap(mock.Anything).Return(uint(0)).Once()
423 mockPaned.EXPECT().AddTickCallback(mock.Anything).Return(uint(0)).Once()
424
425 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
426 _, _ = renderer.Build(ctx, splitNode)
427
428 // Act
429 ids := renderer.GetNodeIDs()
430
431 // Assert
432 assert.Len(t, ids, 3)
433 assert.Contains(t, ids, "split-node")
434 assert.Contains(t, ids, "left-node")
435 assert.Contains(t, ids, "right-node")
436}
437
438func TestBuild_ClearsPreviousMappings(t *testing.T) {
439 // Arrange
440 ctx := context.Background()
441 mockFactory := mocks.NewMockWidgetFactory(t)
442 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
443 mockOldStackBox, mockOldContainer := setupStackedLeafMocks(t, mockFactory)
444 mockNewStackBox, mockNewContainer := setupStackedLeafMocks(t, mockFactory)
445
446 pane1 := entity.NewPane(entity.PaneID("pane-1"))
447 node1 := &entity.PaneNode{ID: "old-node", Pane: pane1}
448
449 pane2 := entity.NewPane(entity.PaneID("pane-2"))
450 node2 := &entity.PaneNode{ID: "new-node", Pane: pane2}
451
452 mockPaneViewFactory.EXPECT().CreatePaneView(node1).Return(mockOldContainer).Once()
453 mockPaneViewFactory.EXPECT().CreatePaneView(node2).Return(mockNewContainer).Once()
454
455 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
456
457 // First build
458 _, _ = renderer.Build(ctx, node1)
459 require.Equal(t, 1, renderer.NodeCount())
460 require.Equal(t, mockOldStackBox, renderer.Lookup("old-node"))
461
462 // Act - second build should clear old mappings
463 _, _ = renderer.Build(ctx, node2)
464
465 // Assert
466 assert.Equal(t, 1, renderer.NodeCount())
467 assert.Nil(t, renderer.Lookup("old-node"))
468 assert.Equal(t, mockNewStackBox, renderer.Lookup("new-node"))
469}
470
471func TestBuild_NilPaneViewFactory_ReturnsNilForLeaves(t *testing.T) {
472 // Arrange
473 ctx := context.Background()
474 mockFactory := mocks.NewMockWidgetFactory(t)
475
476 pane := entity.NewPane(entity.PaneID("pane-1"))
477 node := &entity.PaneNode{ID: "node-1", Pane: pane}
478
479 renderer := layout.NewTreeRenderer(ctx, mockFactory, nil)
480
481 // Act
482 widget, err := renderer.Build(ctx, node)
483
484 // Assert
485 require.NoError(t, err)
486 assert.Nil(t, widget) // nil because paneViewFactory is nil
487}
488
489func TestBuild_EmptyNodeID_NotStored(t *testing.T) {
490 // Arrange
491 ctx := context.Background()
492 mockFactory := mocks.NewMockWidgetFactory(t)
493 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
494 _, mockContainer := setupStackedLeafMocks(t, mockFactory)
495
496 pane := entity.NewPane(entity.PaneID("pane-1"))
497 node := &entity.PaneNode{ID: "", Pane: pane} // Empty ID
498
499 mockPaneViewFactory.EXPECT().CreatePaneView(node).Return(mockContainer).Once()
500
501 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
502
503 // Act
504 widget, err := renderer.Build(ctx, node)
505
506 // Assert
507 require.NoError(t, err)
508 assert.NotNil(t, widget)
509 assert.Equal(t, 0, renderer.NodeCount()) // Empty ID nodes aren't stored
510}
511
512func TestUpdateSplitRatio_ExistingNode(t *testing.T) {
513 // Arrange
514 mockFactory := mocks.NewMockWidgetFactory(t)
515 ctx := context.Background()
516 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
517 mockPaned := mocks.NewMockPanedWidget(t)
518 mockLeftStackBox, mockLeftContainer := setupStackedLeafMocks(t, mockFactory)
519 mockRightStackBox, mockRightContainer := setupStackedLeafMocks(t, mockFactory)
520
521 pane1 := entity.NewPane(entity.PaneID("pane-1"))
522 pane2 := entity.NewPane(entity.PaneID("pane-2"))
523
524 node1 := &entity.PaneNode{ID: "node-1", Pane: pane1}
525 node2 := &entity.PaneNode{ID: "node-2", Pane: pane2}
526
527 splitNode := &entity.PaneNode{
528 ID: "split-node",
529 SplitDir: entity.SplitHorizontal,
530 SplitRatio: 0.5,
531 Children: []*entity.PaneNode{node1, node2},
532 }
533
534 mockPaneViewFactory.EXPECT().CreatePaneView(node1).Return(mockLeftContainer).Once()
535 mockPaneViewFactory.EXPECT().CreatePaneView(node2).Return(mockRightContainer).Once()
536
537 mockFactory.EXPECT().NewPaned(layout.OrientationHorizontal).Return(mockPaned).Once()
538 mockPaned.EXPECT().SetResizeStartChild(true).Once()
539 mockPaned.EXPECT().SetResizeEndChild(true).Once()
540 mockPaned.EXPECT().SetVisible(true).Once()
541 mockLeftStackBox.EXPECT().SetVisible(true).Once()
542 mockRightStackBox.EXPECT().SetVisible(true).Once()
543 mockPaned.EXPECT().SetStartChild(mockLeftStackBox).Once()
544 mockPaned.EXPECT().SetEndChild(mockRightStackBox).Once()
545 mockPaned.EXPECT().ConnectNotifyPosition(mock.Anything).Return(uint(0)).Once()
546 mockPaned.EXPECT().GetAllocatedWidth().Return(0).Twice()
547 mockPaned.EXPECT().ConnectMap(mock.Anything).Return(uint(0)).Once()
548
549 mockPaned.EXPECT().AddTickCallback(mock.Anything).Return(uint(0)).Once()
550
551 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
552 _, _ = renderer.Build(ctx, splitNode)
553
554 // Act
555 err := renderer.UpdateSplitRatio("split-node", 0.7)
556
557 // Assert
558 assert.NoError(t, err)
559}
560
561func TestUpdateSplitRatio_NonExistentNode(t *testing.T) {
562 // Arrange
563 mockFactory := mocks.NewMockWidgetFactory(t)
564 ctx := context.Background()
565 mockPaneViewFactory := mocks.NewMockPaneViewFactory(t)
566
567 renderer := layout.NewTreeRenderer(ctx, mockFactory, mockPaneViewFactory)
568
569 // Act
570 err := renderer.UpdateSplitRatio("non-existent", 0.7)
571
572 // Assert
573 assert.ErrorIs(t, err, layout.ErrNodeNotFound)
574}