test: update focus manager tests to match pure function refactor
- commit
- 4da4dafb91e0fdeb693b0d9a857d1fdd323c8450
- author
- brice · · 2026-01-01 13:05 UTC
- parent
- 2f5a503
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +57 -61
M internal/ui/focus/manager_test.go +57 -61
| @@ -11,6 +11,8 @@ import ( | |||
| 11 | 11 | ||
| 12 | 12 | // TestNavigateWithinStack_UsesActiveStackIndex tests that navigation uses | |
| 13 | 13 | // ActiveStackIndex from the domain model instead of pointer comparison. | |
| 14 | + | // Note: navigateWithinStack is a pure function that returns the target pane ID | |
| 15 | + | // without modifying state. The actual state mutation happens in ApplyFocusChange. | |
| 14 | 16 | func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | |
| 15 | 17 | // Create a stack with 3 children | |
| 16 | 18 | pane0 := &entity.Pane{ID: "pane0"} | |
| @@ -36,60 +38,53 @@ func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | |||
| 36 | 38 | m := NewManager(nil) | |
| 37 | 39 | ||
| 38 | 40 | tests := []struct { | |
| 39 | - | name string | |
| 40 | - | activeIndex int | |
| 41 | - | direction usecase.NavigateDirection | |
| 42 | - | wantCanNav bool | |
| 43 | - | wantPaneID entity.PaneID | |
| 44 | - | wantNewIndex int | |
| 41 | + | name string | |
| 42 | + | activeIndex int | |
| 43 | + | direction usecase.NavigateDirection | |
| 44 | + | wantCanNav bool | |
| 45 | + | wantPaneID entity.PaneID | |
| 45 | 46 | }{ | |
| 46 | 47 | { | |
| 47 | - | name: "navigate down from middle", | |
| 48 | - | activeIndex: 1, | |
| 49 | - | direction: usecase.NavDown, | |
| 50 | - | wantCanNav: true, | |
| 51 | - | wantPaneID: "pane2", | |
| 52 | - | wantNewIndex: 2, | |
| 48 | + | name: "navigate down from middle", | |
| 49 | + | activeIndex: 1, | |
| 50 | + | direction: usecase.NavDown, | |
| 51 | + | wantCanNav: true, | |
| 52 | + | wantPaneID: "pane2", | |
| 53 | 53 | }, | |
| 54 | 54 | { | |
| 55 | - | name: "navigate up from middle", | |
| 56 | - | activeIndex: 1, | |
| 57 | - | direction: usecase.NavUp, | |
| 58 | - | wantCanNav: true, | |
| 59 | - | wantPaneID: "pane0", | |
| 60 | - | wantNewIndex: 0, | |
| 55 | + | name: "navigate up from middle", | |
| 56 | + | activeIndex: 1, | |
| 57 | + | direction: usecase.NavUp, | |
| 58 | + | wantCanNav: true, | |
| 59 | + | wantPaneID: "pane0", | |
| 61 | 60 | }, | |
| 62 | 61 | { | |
| 63 | - | name: "navigate down from last - boundary", | |
| 64 | - | activeIndex: 2, | |
| 65 | - | direction: usecase.NavDown, | |
| 66 | - | wantCanNav: false, | |
| 67 | - | wantPaneID: "", | |
| 68 | - | wantNewIndex: 2, // unchanged | |
| 62 | + | name: "navigate down from last - boundary", | |
| 63 | + | activeIndex: 2, | |
| 64 | + | direction: usecase.NavDown, | |
| 65 | + | wantCanNav: false, | |
| 66 | + | wantPaneID: "", | |
| 69 | 67 | }, | |
| 70 | 68 | { | |
| 71 | - | name: "navigate up from first - boundary", | |
| 72 | - | activeIndex: 0, | |
| 73 | - | direction: usecase.NavUp, | |
| 74 | - | wantCanNav: false, | |
| 75 | - | wantPaneID: "", | |
| 76 | - | wantNewIndex: 0, // unchanged | |
| 69 | + | name: "navigate up from first - boundary", | |
| 70 | + | activeIndex: 0, | |
| 71 | + | direction: usecase.NavUp, | |
| 72 | + | wantCanNav: false, | |
| 73 | + | wantPaneID: "", | |
| 77 | 74 | }, | |
| 78 | 75 | { | |
| 79 | - | name: "navigate down from first", | |
| 80 | - | activeIndex: 0, | |
| 81 | - | direction: usecase.NavDown, | |
| 82 | - | wantCanNav: true, | |
| 83 | - | wantPaneID: "pane1", | |
| 84 | - | wantNewIndex: 1, | |
| 76 | + | name: "navigate down from first", | |
| 77 | + | activeIndex: 0, | |
| 78 | + | direction: usecase.NavDown, | |
| 79 | + | wantCanNav: true, | |
| 80 | + | wantPaneID: "pane1", | |
| 85 | 81 | }, | |
| 86 | 82 | { | |
| 87 | - | name: "navigate up from last", | |
| 88 | - | activeIndex: 2, | |
| 89 | - | direction: usecase.NavUp, | |
| 90 | - | wantCanNav: true, | |
| 91 | - | wantPaneID: "pane1", | |
| 92 | - | wantNewIndex: 1, | |
| 83 | + | name: "navigate up from last", | |
| 84 | + | activeIndex: 2, | |
| 85 | + | direction: usecase.NavUp, | |
| 86 | + | wantCanNav: true, | |
| 87 | + | wantPaneID: "pane1", | |
| 93 | 88 | }, | |
| 94 | 89 | } | |
| 95 | 90 | ||
| @@ -98,16 +93,11 @@ func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | |||
| 98 | 93 | // Set up the active index | |
| 99 | 94 | stackNode.ActiveStackIndex = tt.activeIndex | |
| 100 | 95 | ||
| 101 | - | // Call the private function through reflection or test it indirectly | |
| 102 | - | // Since navigateWithinStack is private, we test its behavior | |
| 96 | + | // navigateWithinStack is a pure function - it does not modify state | |
| 103 | 97 | canNav, paneID := m.navigateWithinStack(stackNode, stackNode.Children[tt.activeIndex], tt.direction) | |
| 104 | 98 | ||
| 105 | 99 | assert.Equal(t, tt.wantCanNav, canNav) | |
| 106 | 100 | assert.Equal(t, tt.wantPaneID, paneID) | |
| 107 | - | ||
| 108 | - | if canNav { | |
| 109 | - | assert.Equal(t, tt.wantNewIndex, stackNode.ActiveStackIndex) | |
| 110 | - | } | |
| 111 | 101 | }) | |
| 112 | 102 | } | |
| 113 | 103 | } | |
| @@ -193,9 +183,10 @@ func TestNavigateWithinStack_InvalidDirection(t *testing.T) { | |||
| 193 | 183 | assert.Empty(t, paneID) | |
| 194 | 184 | } | |
| 195 | 185 | ||
| 196 | - | // TestNavigateWithinStack_UpdatesActiveStackIndex tests that navigation properly | |
| 197 | - | // updates the ActiveStackIndex on the stack node. | |
| 198 | - | func TestNavigateWithinStack_UpdatesActiveStackIndex(t *testing.T) { | |
| 186 | + | // TestNavigateWithinStack_ReturnsCorrectPaneID tests that navigation returns | |
| 187 | + | // the correct target pane ID for sequential navigation. | |
| 188 | + | // Note: navigateWithinStack is now a pure function - state updates happen in ApplyFocusChange. | |
| 189 | + | func TestNavigateWithinStack_ReturnsCorrectPaneID(t *testing.T) { | |
| 199 | 190 | pane0 := &entity.Pane{ID: "pane0"} | |
| 200 | 191 | pane1 := &entity.Pane{ID: "pane1"} | |
| 201 | 192 | pane2 := &entity.Pane{ID: "pane2"} | |
| @@ -213,22 +204,27 @@ func TestNavigateWithinStack_UpdatesActiveStackIndex(t *testing.T) { | |||
| 213 | 204 | ||
| 214 | 205 | m := NewManager(nil) | |
| 215 | 206 | ||
| 216 | - | // Navigate down twice | |
| 207 | + | // Navigate down from first - should return pane1 | |
| 217 | 208 | stackNode.ActiveStackIndex = 0 | |
| 218 | - | canNav, _ := m.navigateWithinStack(stackNode, child0, usecase.NavDown) | |
| 209 | + | canNav, paneID := m.navigateWithinStack(stackNode, child0, usecase.NavDown) | |
| 219 | 210 | assert.True(t, canNav) | |
| 220 | - | assert.Equal(t, 1, stackNode.ActiveStackIndex) | |
| 211 | + | assert.Equal(t, entity.PaneID("pane1"), paneID) | |
| 221 | 212 | ||
| 222 | - | canNav, _ = m.navigateWithinStack(stackNode, child1, usecase.NavDown) | |
| 213 | + | // Navigate down from middle - should return pane2 | |
| 214 | + | stackNode.ActiveStackIndex = 1 | |
| 215 | + | canNav, paneID = m.navigateWithinStack(stackNode, child1, usecase.NavDown) | |
| 223 | 216 | assert.True(t, canNav) | |
| 224 | - | assert.Equal(t, 2, stackNode.ActiveStackIndex) | |
| 217 | + | assert.Equal(t, entity.PaneID("pane2"), paneID) | |
| 225 | 218 | ||
| 226 | - | // Navigate up twice | |
| 227 | - | canNav, _ = m.navigateWithinStack(stackNode, child2, usecase.NavUp) | |
| 219 | + | // Navigate up from last - should return pane1 | |
| 220 | + | stackNode.ActiveStackIndex = 2 | |
| 221 | + | canNav, paneID = m.navigateWithinStack(stackNode, child2, usecase.NavUp) | |
| 228 | 222 | assert.True(t, canNav) | |
| 229 | - | assert.Equal(t, 1, stackNode.ActiveStackIndex) | |
| 223 | + | assert.Equal(t, entity.PaneID("pane1"), paneID) | |
| 230 | 224 | ||
| 231 | - | canNav, _ = m.navigateWithinStack(stackNode, child1, usecase.NavUp) | |
| 225 | + | // Navigate up from middle - should return pane0 | |
| 226 | + | stackNode.ActiveStackIndex = 1 | |
| 227 | + | canNav, paneID = m.navigateWithinStack(stackNode, child1, usecase.NavUp) | |
| 232 | 228 | assert.True(t, canNav) | |
| 233 | - | assert.Equal(t, 0, stackNode.ActiveStackIndex) | |
| 229 | + | assert.Equal(t, entity.PaneID("pane0"), paneID) | |
| 234 | 230 | } | |
| @@ -11,6 +11,8 @@ import ( | |||||
| 11 | 11 | ||||
| 12 | // TestNavigateWithinStack_UsesActiveStackIndex tests that navigation uses | 12 | // TestNavigateWithinStack_UsesActiveStackIndex tests that navigation uses | ||
| 13 | // ActiveStackIndex from the domain model instead of pointer comparison. | 13 | // ActiveStackIndex from the domain model instead of pointer comparison. | ||
| 14 | + | // Note: navigateWithinStack is a pure function that returns the target pane ID | |||
| 15 | + | // without modifying state. The actual state mutation happens in ApplyFocusChange. | |||
| 14 | func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | 16 | func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | ||
| 15 | // Create a stack with 3 children | 17 | // Create a stack with 3 children | ||
| 16 | pane0 := &entity.Pane{ID: "pane0"} | 18 | pane0 := &entity.Pane{ID: "pane0"} | ||
| @@ -36,60 +38,53 @@ func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | |||||
| 36 | m := NewManager(nil) | 38 | m := NewManager(nil) | ||
| 37 | 39 | ||||
| 38 | tests := []struct { | 40 | tests := []struct { | ||
| 39 | - | name string | 41 | + | name string |
| 40 | - | activeIndex int | 42 | + | activeIndex int |
| 41 | - | direction usecase.NavigateDirection | 43 | + | direction usecase.NavigateDirection |
| 42 | - | wantCanNav bool | 44 | + | wantCanNav bool |
| 43 | - | wantPaneID entity.PaneID | 45 | + | wantPaneID entity.PaneID |
| 44 | - | wantNewIndex int | |||
| 45 | }{ | 46 | }{ | ||
| 46 | { | 47 | { | ||
| 47 | - | name: "navigate down from middle", | 48 | + | name: "navigate down from middle", |
| 48 | - | activeIndex: 1, | 49 | + | activeIndex: 1, |
| 49 | - | direction: usecase.NavDown, | 50 | + | direction: usecase.NavDown, |
| 50 | - | wantCanNav: true, | 51 | + | wantCanNav: true, |
| 51 | - | wantPaneID: "pane2", | 52 | + | wantPaneID: "pane2", |
| 52 | - | wantNewIndex: 2, | |||
| 53 | }, | 53 | }, | ||
| 54 | { | 54 | { | ||
| 55 | - | name: "navigate up from middle", | 55 | + | name: "navigate up from middle", |
| 56 | - | activeIndex: 1, | 56 | + | activeIndex: 1, |
| 57 | - | direction: usecase.NavUp, | 57 | + | direction: usecase.NavUp, |
| 58 | - | wantCanNav: true, | 58 | + | wantCanNav: true, |
| 59 | - | wantPaneID: "pane0", | 59 | + | wantPaneID: "pane0", |
| 60 | - | wantNewIndex: 0, | |||
| 61 | }, | 60 | }, | ||
| 62 | { | 61 | { | ||
| 63 | - | name: "navigate down from last - boundary", | 62 | + | name: "navigate down from last - boundary", |
| 64 | - | activeIndex: 2, | 63 | + | activeIndex: 2, |
| 65 | - | direction: usecase.NavDown, | 64 | + | direction: usecase.NavDown, |
| 66 | - | wantCanNav: false, | 65 | + | wantCanNav: false, |
| 67 | - | wantPaneID: "", | 66 | + | wantPaneID: "", |
| 68 | - | wantNewIndex: 2, // unchanged | |||
| 69 | }, | 67 | }, | ||
| 70 | { | 68 | { | ||
| 71 | - | name: "navigate up from first - boundary", | 69 | + | name: "navigate up from first - boundary", |
| 72 | - | activeIndex: 0, | 70 | + | activeIndex: 0, |
| 73 | - | direction: usecase.NavUp, | 71 | + | direction: usecase.NavUp, |
| 74 | - | wantCanNav: false, | 72 | + | wantCanNav: false, |
| 75 | - | wantPaneID: "", | 73 | + | wantPaneID: "", |
| 76 | - | wantNewIndex: 0, // unchanged | |||
| 77 | }, | 74 | }, | ||
| 78 | { | 75 | { | ||
| 79 | - | name: "navigate down from first", | 76 | + | name: "navigate down from first", |
| 80 | - | activeIndex: 0, | 77 | + | activeIndex: 0, |
| 81 | - | direction: usecase.NavDown, | 78 | + | direction: usecase.NavDown, |
| 82 | - | wantCanNav: true, | 79 | + | wantCanNav: true, |
| 83 | - | wantPaneID: "pane1", | 80 | + | wantPaneID: "pane1", |
| 84 | - | wantNewIndex: 1, | |||
| 85 | }, | 81 | }, | ||
| 86 | { | 82 | { | ||
| 87 | - | name: "navigate up from last", | 83 | + | name: "navigate up from last", |
| 88 | - | activeIndex: 2, | 84 | + | activeIndex: 2, |
| 89 | - | direction: usecase.NavUp, | 85 | + | direction: usecase.NavUp, |
| 90 | - | wantCanNav: true, | 86 | + | wantCanNav: true, |
| 91 | - | wantPaneID: "pane1", | 87 | + | wantPaneID: "pane1", |
| 92 | - | wantNewIndex: 1, | |||
| 93 | }, | 88 | }, | ||
| 94 | } | 89 | } | ||
| 95 | 90 | ||||
| @@ -98,16 +93,11 @@ func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) { | |||||
| 98 | // Set up the active index | 93 | // Set up the active index | ||
| 99 | stackNode.ActiveStackIndex = tt.activeIndex | 94 | stackNode.ActiveStackIndex = tt.activeIndex | ||
| 100 | 95 | ||||
| 101 | - | // Call the private function through reflection or test it indirectly | 96 | + | // navigateWithinStack is a pure function - it does not modify state |
| 102 | - | // Since navigateWithinStack is private, we test its behavior | |||
| 103 | canNav, paneID := m.navigateWithinStack(stackNode, stackNode.Children[tt.activeIndex], tt.direction) | 97 | canNav, paneID := m.navigateWithinStack(stackNode, stackNode.Children[tt.activeIndex], tt.direction) | ||
| 104 | 98 | ||||
| 105 | assert.Equal(t, tt.wantCanNav, canNav) | 99 | assert.Equal(t, tt.wantCanNav, canNav) | ||
| 106 | assert.Equal(t, tt.wantPaneID, paneID) | 100 | assert.Equal(t, tt.wantPaneID, paneID) | ||
| 107 | - | ||||
| 108 | - | if canNav { | |||
| 109 | - | assert.Equal(t, tt.wantNewIndex, stackNode.ActiveStackIndex) | |||
| 110 | - | } | |||
| 111 | }) | 101 | }) | ||
| 112 | } | 102 | } | ||
| 113 | } | 103 | } | ||
| @@ -193,9 +183,10 @@ func TestNavigateWithinStack_InvalidDirection(t *testing.T) { | |||||
| 193 | assert.Empty(t, paneID) | 183 | assert.Empty(t, paneID) | ||
| 194 | } | 184 | } | ||
| 195 | 185 | ||||
| 196 | - | // TestNavigateWithinStack_UpdatesActiveStackIndex tests that navigation properly | 186 | + | // TestNavigateWithinStack_ReturnsCorrectPaneID tests that navigation returns |
| 197 | - | // updates the ActiveStackIndex on the stack node. | 187 | + | // the correct target pane ID for sequential navigation. |
| 198 | - | func TestNavigateWithinStack_UpdatesActiveStackIndex(t *testing.T) { | 188 | + | // Note: navigateWithinStack is now a pure function - state updates happen in ApplyFocusChange. |
| 189 | + | func TestNavigateWithinStack_ReturnsCorrectPaneID(t *testing.T) { | |||
| 199 | pane0 := &entity.Pane{ID: "pane0"} | 190 | pane0 := &entity.Pane{ID: "pane0"} | ||
| 200 | pane1 := &entity.Pane{ID: "pane1"} | 191 | pane1 := &entity.Pane{ID: "pane1"} | ||
| 201 | pane2 := &entity.Pane{ID: "pane2"} | 192 | pane2 := &entity.Pane{ID: "pane2"} | ||
| @@ -213,22 +204,27 @@ func TestNavigateWithinStack_UpdatesActiveStackIndex(t *testing.T) { | |||||
| 213 | 204 | ||||
| 214 | m := NewManager(nil) | 205 | m := NewManager(nil) | ||
| 215 | 206 | ||||
| 216 | - | // Navigate down twice | 207 | + | // Navigate down from first - should return pane1 |
| 217 | stackNode.ActiveStackIndex = 0 | 208 | stackNode.ActiveStackIndex = 0 | ||
| 218 | - | canNav, _ := m.navigateWithinStack(stackNode, child0, usecase.NavDown) | 209 | + | canNav, paneID := m.navigateWithinStack(stackNode, child0, usecase.NavDown) |
| 219 | assert.True(t, canNav) | 210 | assert.True(t, canNav) | ||
| 220 | - | assert.Equal(t, 1, stackNode.ActiveStackIndex) | 211 | + | assert.Equal(t, entity.PaneID("pane1"), paneID) |
| 221 | 212 | ||||
| 222 | - | canNav, _ = m.navigateWithinStack(stackNode, child1, usecase.NavDown) | 213 | + | // Navigate down from middle - should return pane2 |
| 214 | + | stackNode.ActiveStackIndex = 1 | |||
| 215 | + | canNav, paneID = m.navigateWithinStack(stackNode, child1, usecase.NavDown) | |||
| 223 | assert.True(t, canNav) | 216 | assert.True(t, canNav) | ||
| 224 | - | assert.Equal(t, 2, stackNode.ActiveStackIndex) | 217 | + | assert.Equal(t, entity.PaneID("pane2"), paneID) |
| 225 | 218 | ||||
| 226 | - | // Navigate up twice | 219 | + | // Navigate up from last - should return pane1 |
| 227 | - | canNav, _ = m.navigateWithinStack(stackNode, child2, usecase.NavUp) | 220 | + | stackNode.ActiveStackIndex = 2 |
| 221 | + | canNav, paneID = m.navigateWithinStack(stackNode, child2, usecase.NavUp) | |||
| 228 | assert.True(t, canNav) | 222 | assert.True(t, canNav) | ||
| 229 | - | assert.Equal(t, 1, stackNode.ActiveStackIndex) | 223 | + | assert.Equal(t, entity.PaneID("pane1"), paneID) |
| 230 | 224 | ||||
| 231 | - | canNav, _ = m.navigateWithinStack(stackNode, child1, usecase.NavUp) | 225 | + | // Navigate up from middle - should return pane0 |
| 226 | + | stackNode.ActiveStackIndex = 1 | |||
| 227 | + | canNav, paneID = m.navigateWithinStack(stackNode, child1, usecase.NavUp) | |||
| 232 | assert.True(t, canNav) | 228 | assert.True(t, canNav) | ||
| 233 | - | assert.Equal(t, 0, stackNode.ActiveStackIndex) | 229 | + | assert.Equal(t, entity.PaneID("pane0"), paneID) |
| 234 | } | 230 | } | ||