1package focus
2
3import (
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7
8 "github.com/bnema/dumber/internal/application/usecase"
9 "github.com/bnema/dumber/internal/domain/entity"
10)
11
12// TestNavigateWithinStack_UsesActiveStackIndex tests that navigation uses
13// ActiveStackIndex from the domain model instead of pointer comparison.
14func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) {
15 // Create a stack with 3 children
16 pane0 := &entity.Pane{ID: "pane0"}
17 pane1 := &entity.Pane{ID: "pane1"}
18 pane2 := &entity.Pane{ID: "pane2"}
19
20 child0 := &entity.PaneNode{ID: "child0", Pane: pane0}
21 child1 := &entity.PaneNode{ID: "child1", Pane: pane1}
22 child2 := &entity.PaneNode{ID: "child2", Pane: pane2}
23
24 stackNode := &entity.PaneNode{
25 ID: "stack",
26 IsStacked: true,
27 Children: []*entity.PaneNode{child0, child1, child2},
28 ActiveStackIndex: 1, // Currently at middle pane
29 }
30
31 // Set parents
32 child0.Parent = stackNode
33 child1.Parent = stackNode
34 child2.Parent = stackNode
35
36 m := NewManager(nil)
37
38 tests := []struct {
39 name string
40 activeIndex int
41 direction usecase.NavigateDirection
42 wantCanNav bool
43 wantPaneID entity.PaneID
44 wantNewIndex int
45 }{
46 {
47 name: "navigate down from middle",
48 activeIndex: 1,
49 direction: usecase.NavDown,
50 wantCanNav: true,
51 wantPaneID: "pane2",
52 wantNewIndex: 2,
53 },
54 {
55 name: "navigate up from middle",
56 activeIndex: 1,
57 direction: usecase.NavUp,
58 wantCanNav: true,
59 wantPaneID: "pane0",
60 wantNewIndex: 0,
61 },
62 {
63 name: "navigate down from last - boundary",
64 activeIndex: 2,
65 direction: usecase.NavDown,
66 wantCanNav: false,
67 wantPaneID: "",
68 wantNewIndex: 2, // unchanged
69 },
70 {
71 name: "navigate up from first - boundary",
72 activeIndex: 0,
73 direction: usecase.NavUp,
74 wantCanNav: false,
75 wantPaneID: "",
76 wantNewIndex: 0, // unchanged
77 },
78 {
79 name: "navigate down from first",
80 activeIndex: 0,
81 direction: usecase.NavDown,
82 wantCanNav: true,
83 wantPaneID: "pane1",
84 wantNewIndex: 1,
85 },
86 {
87 name: "navigate up from last",
88 activeIndex: 2,
89 direction: usecase.NavUp,
90 wantCanNav: true,
91 wantPaneID: "pane1",
92 wantNewIndex: 1,
93 },
94 }
95
96 for _, tt := range tests {
97 t.Run(tt.name, func(t *testing.T) {
98 // Set up the active index
99 stackNode.ActiveStackIndex = tt.activeIndex
100
101 // Call the private function through reflection or test it indirectly
102 // Since navigateWithinStack is private, we test its behavior
103 canNav, paneID := m.navigateWithinStack(stackNode, stackNode.Children[tt.activeIndex], tt.direction)
104
105 assert.Equal(t, tt.wantCanNav, canNav)
106 assert.Equal(t, tt.wantPaneID, paneID)
107
108 if canNav {
109 assert.Equal(t, tt.wantNewIndex, stackNode.ActiveStackIndex)
110 }
111 })
112 }
113}
114
115// TestNavigateWithinStack_EmptyStack tests navigation in empty stack returns false.
116func TestNavigateWithinStack_EmptyStack(t *testing.T) {
117 stackNode := &entity.PaneNode{
118 ID: "stack",
119 IsStacked: true,
120 Children: []*entity.PaneNode{},
121 }
122
123 m := NewManager(nil)
124
125 canNav, paneID := m.navigateWithinStack(stackNode, nil, usecase.NavDown)
126
127 assert.False(t, canNav)
128 assert.Empty(t, paneID)
129}
130
131// TestNavigateWithinStack_NotStacked tests navigation on non-stacked node returns false.
132func TestNavigateWithinStack_NotStacked(t *testing.T) {
133 node := &entity.PaneNode{
134 ID: "leaf",
135 IsStacked: false,
136 }
137
138 m := NewManager(nil)
139
140 canNav, paneID := m.navigateWithinStack(node, nil, usecase.NavDown)
141
142 assert.False(t, canNav)
143 assert.Empty(t, paneID)
144}
145
146// TestNavigateWithinStack_InvalidActiveIndex tests navigation with invalid ActiveStackIndex
147// falls back to pointer search.
148func TestNavigateWithinStack_InvalidActiveIndexFallback(t *testing.T) {
149 pane0 := &entity.Pane{ID: "pane0"}
150 pane1 := &entity.Pane{ID: "pane1"}
151
152 child0 := &entity.PaneNode{ID: "child0", Pane: pane0}
153 child1 := &entity.PaneNode{ID: "child1", Pane: pane1}
154
155 stackNode := &entity.PaneNode{
156 ID: "stack",
157 IsStacked: true,
158 Children: []*entity.PaneNode{child0, child1},
159 ActiveStackIndex: -1, // Invalid index
160 }
161
162 child0.Parent = stackNode
163 child1.Parent = stackNode
164
165 m := NewManager(nil)
166
167 // With invalid ActiveStackIndex, should fall back to pointer search
168 // Pass child0 as current node
169 canNav, paneID := m.navigateWithinStack(stackNode, child0, usecase.NavDown)
170
171 assert.True(t, canNav)
172 assert.Equal(t, entity.PaneID("pane1"), paneID)
173}
174
175// TestNavigateWithinStack_InvalidDirection tests navigation with invalid direction returns false.
176func TestNavigateWithinStack_InvalidDirection(t *testing.T) {
177 pane0 := &entity.Pane{ID: "pane0"}
178 child0 := &entity.PaneNode{ID: "child0", Pane: pane0}
179
180 stackNode := &entity.PaneNode{
181 ID: "stack",
182 IsStacked: true,
183 Children: []*entity.PaneNode{child0},
184 ActiveStackIndex: 0,
185 }
186
187 m := NewManager(nil)
188
189 // NavLeft and NavRight should not work for stack navigation
190 canNav, paneID := m.navigateWithinStack(stackNode, child0, usecase.NavLeft)
191
192 assert.False(t, canNav)
193 assert.Empty(t, paneID)
194}
195
196// TestNavigateWithinStack_UpdatesActiveStackIndex tests that navigation properly
197// updates the ActiveStackIndex on the stack node.
198func TestNavigateWithinStack_UpdatesActiveStackIndex(t *testing.T) {
199 pane0 := &entity.Pane{ID: "pane0"}
200 pane1 := &entity.Pane{ID: "pane1"}
201 pane2 := &entity.Pane{ID: "pane2"}
202
203 child0 := &entity.PaneNode{ID: "child0", Pane: pane0}
204 child1 := &entity.PaneNode{ID: "child1", Pane: pane1}
205 child2 := &entity.PaneNode{ID: "child2", Pane: pane2}
206
207 stackNode := &entity.PaneNode{
208 ID: "stack",
209 IsStacked: true,
210 Children: []*entity.PaneNode{child0, child1, child2},
211 ActiveStackIndex: 0,
212 }
213
214 m := NewManager(nil)
215
216 // Navigate down twice
217 stackNode.ActiveStackIndex = 0
218 canNav, _ := m.navigateWithinStack(stackNode, child0, usecase.NavDown)
219 assert.True(t, canNav)
220 assert.Equal(t, 1, stackNode.ActiveStackIndex)
221
222 canNav, _ = m.navigateWithinStack(stackNode, child1, usecase.NavDown)
223 assert.True(t, canNav)
224 assert.Equal(t, 2, stackNode.ActiveStackIndex)
225
226 // Navigate up twice
227 canNav, _ = m.navigateWithinStack(stackNode, child2, usecase.NavUp)
228 assert.True(t, canNav)
229 assert.Equal(t, 1, stackNode.ActiveStackIndex)
230
231 canNav, _ = m.navigateWithinStack(stackNode, child1, usecase.NavUp)
232 assert.True(t, canNav)
233 assert.Equal(t, 0, stackNode.ActiveStackIndex)
234}