detached 4da4daf

test: update focus manager tests to match pure function refactor

commit
4da4dafb91e0fdeb693b0d9a857d1fdd323c8450
author
brice · · 2026-01-01 13:05 UTC
parent
2f5a503
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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 (
1111
1212 // TestNavigateWithinStack_UsesActiveStackIndex tests that navigation uses
1313 // 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.
1416 func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) {
1517 // Create a stack with 3 children
1618 pane0 := &entity.Pane{ID: "pane0"}
@@ -36,60 +38,53 @@ func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) {
3638 m := NewManager(nil)
3739
3840 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
4546 }{
4647 {
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",
5353 },
5454 {
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",
6160 },
6261 {
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: "",
6967 },
7068 {
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: "",
7774 },
7875 {
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",
8581 },
8682 {
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",
9388 },
9489 }
9590
@@ -98,16 +93,11 @@ func TestNavigateWithinStack_UsesActiveStackIndex(t *testing.T) {
9893 // Set up the active index
9994 stackNode.ActiveStackIndex = tt.activeIndex
10095
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
10397 canNav, paneID := m.navigateWithinStack(stackNode, stackNode.Children[tt.activeIndex], tt.direction)
10498
10599 assert.Equal(t, tt.wantCanNav, canNav)
106100 assert.Equal(t, tt.wantPaneID, paneID)
107-
108- if canNav {
109- assert.Equal(t, tt.wantNewIndex, stackNode.ActiveStackIndex)
110- }
111101 })
112102 }
113103 }
@@ -193,9 +183,10 @@ func TestNavigateWithinStack_InvalidDirection(t *testing.T) {
193183 assert.Empty(t, paneID)
194184 }
195185
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) {
199190 pane0 := &entity.Pane{ID: "pane0"}
200191 pane1 := &entity.Pane{ID: "pane1"}
201192 pane2 := &entity.Pane{ID: "pane2"}
@@ -213,22 +204,27 @@ func TestNavigateWithinStack_UpdatesActiveStackIndex(t *testing.T) {
213204
214205 m := NewManager(nil)
215206
216- // Navigate down twice
207+ // Navigate down from first - should return pane1
217208 stackNode.ActiveStackIndex = 0
218- canNav, _ := m.navigateWithinStack(stackNode, child0, usecase.NavDown)
209+ canNav, paneID := m.navigateWithinStack(stackNode, child0, usecase.NavDown)
219210 assert.True(t, canNav)
220- assert.Equal(t, 1, stackNode.ActiveStackIndex)
211+ assert.Equal(t, entity.PaneID("pane1"), paneID)
221212
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)
223216 assert.True(t, canNav)
224- assert.Equal(t, 2, stackNode.ActiveStackIndex)
217+ assert.Equal(t, entity.PaneID("pane2"), paneID)
225218
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)
228222 assert.True(t, canNav)
229- assert.Equal(t, 1, stackNode.ActiveStackIndex)
223+ assert.Equal(t, entity.PaneID("pane1"), paneID)
230224
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)
232228 assert.True(t, canNav)
233- assert.Equal(t, 0, stackNode.ActiveStackIndex)
229+ assert.Equal(t, entity.PaneID("pane0"), paneID)
234230 }
@@ -11,6 +11,8 @@ import (
11 11
12 // TestNavigateWithinStack_UsesActiveStackIndex tests that navigation uses12 // 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 children17 // 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 string41+ name string
40- activeIndex int42+ activeIndex int
41- direction usecase.NavigateDirection43+ direction usecase.NavigateDirection
42- wantCanNav bool44+ wantCanNav bool
43- wantPaneID entity.PaneID45+ 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 index93 // Set up the active index
99 stackNode.ActiveStackIndex = tt.activeIndex94 stackNode.ActiveStackIndex = tt.activeIndex
100 95
101- // Call the private function through reflection or test it indirectly96+ // 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 properly186+// 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 twice207+ // Navigate down from first - should return pane1
217 stackNode.ActiveStackIndex = 0208 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 twice219+ // 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 }

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