1package dispatcher
2
3import (
4 "context"
5 "fmt"
6 "testing"
7
8 "github.com/bnema/dumber/internal/domain/entity"
9 "github.com/bnema/dumber/internal/ui/coordinator"
10 "github.com/bnema/dumber/internal/ui/input"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13)
14
15func TestKeyboardDispatcher_TabActionsUseInjectedKeyboardActions(t *testing.T) {
16 ctx := context.Background()
17 called := make([]string, 0)
18 var switchedIndex int
19 d := NewKeyboardDispatcher(
20 ctx,
21 &coordinator.WorkspaceCoordinator{},
22 &coordinator.NavigationCoordinator{},
23 nil,
24 nil,
25 KeyboardActions{
26 NewTab: func(context.Context) error {
27 called = append(called, "new")
28 return nil
29 },
30 CloseTab: func(context.Context) error {
31 called = append(called, "close")
32 return nil
33 },
34 NextTab: func(context.Context) error {
35 called = append(called, "next")
36 return nil
37 },
38 PreviousTab: func(context.Context) error {
39 called = append(called, "previous")
40 return nil
41 },
42 SwitchLastTab: func(context.Context) error {
43 called = append(called, "last")
44 return nil
45 },
46 SwitchTabIndex: func(_ context.Context, index int) error {
47 called = append(called, "index")
48 switchedIndex = index
49 return nil
50 },
51 },
52 func(context.Context) entity.PaneID { return "" },
53 )
54
55 require.NoError(t, d.Dispatch(ctx, input.ActionNewTab))
56 require.NoError(t, d.Dispatch(ctx, input.ActionCloseTab))
57 require.NoError(t, d.Dispatch(ctx, input.ActionNextTab))
58 require.NoError(t, d.Dispatch(ctx, input.ActionPreviousTab))
59 require.NoError(t, d.Dispatch(ctx, input.ActionSwitchLastTab))
60 require.NoError(t, d.Dispatch(ctx, input.ActionSwitchTabIndex5))
61
62 assert.Equal(t, []string{"new", "close", "next", "previous", "last", "index"}, called)
63 assert.Equal(t, 4, switchedIndex)
64}
65
66func TestKeyboardDispatcher_ToggleHistorySidebarCallsCallback(t *testing.T) {
67 ctx := context.Background()
68 d := NewKeyboardDispatcher(ctx, &coordinator.WorkspaceCoordinator{}, &coordinator.NavigationCoordinator{}, nil, nil, KeyboardActions{}, func(context.Context) entity.PaneID { return "" })
69
70 var called bool
71 d.SetOnToggleHistorySidebar(func(context.Context) error {
72 called = true
73 return nil
74 })
75
76 err := d.Dispatch(ctx, input.ActionToggleHistorySystemView)
77 require.NoError(t, err)
78 assert.True(t, called, "onToggleHistorySidebar should have been called")
79}
80
81func TestKeyboardDispatcher_ToggleHistorySystemViewReturnsErrorWhenHandlerMissing(t *testing.T) {
82 ctx := context.Background()
83 d := NewKeyboardDispatcher(ctx, &coordinator.WorkspaceCoordinator{}, &coordinator.NavigationCoordinator{}, nil, nil, KeyboardActions{}, func(context.Context) entity.PaneID { return "" })
84
85 err := d.Dispatch(ctx, input.ActionToggleHistorySystemView)
86 require.Error(t, err)
87 assert.ErrorContains(t, err, "history sidebar unavailable")
88}
89
90func TestKeyboardDispatcher_ToggleHistorySidebarErrorPropagation(t *testing.T) {
91 ctx := context.Background()
92 d := NewKeyboardDispatcher(ctx, &coordinator.WorkspaceCoordinator{}, &coordinator.NavigationCoordinator{}, nil, nil, KeyboardActions{}, func(context.Context) entity.PaneID { return "" })
93
94 wantErr := fmt.Errorf("sidebar toggle failed")
95 d.SetOnToggleHistorySidebar(func(context.Context) error {
96 return wantErr
97 })
98
99 err := d.Dispatch(ctx, input.ActionToggleHistorySystemView)
100 require.Error(t, err)
101 assert.ErrorIs(t, err, wantErr, "onToggleHistorySidebar error should propagate")
102}
103
104func TestKeyboardDispatcher_ToggleHistorySidebarSetThenUnsetReturnsError(t *testing.T) {
105 ctx := context.Background()
106 d := NewKeyboardDispatcher(ctx, &coordinator.WorkspaceCoordinator{}, &coordinator.NavigationCoordinator{}, nil, nil, KeyboardActions{}, func(context.Context) entity.PaneID { return "" })
107
108 d.SetOnToggleHistorySidebar(func(context.Context) error {
109 return nil
110 })
111 d.SetOnToggleHistorySidebar(nil)
112
113 err := d.Dispatch(ctx, input.ActionToggleHistorySystemView)
114 require.Error(t, err)
115 assert.ErrorContains(t, err, "history sidebar unavailable")
116}
117
118func TestKeyboardDispatcher_PassesActivePaneIDToShellCallbacks(t *testing.T) {
119 ctx := context.Background()
120 activePaneID := entity.PaneID("pane-1")
121 d := NewKeyboardDispatcher(ctx, &coordinator.WorkspaceCoordinator{}, &coordinator.NavigationCoordinator{}, nil, nil, KeyboardActions{}, func(context.Context) entity.PaneID {
122 return activePaneID
123 })
124
125 tests := []struct {
126 name string
127 set func(func(context.Context, entity.PaneID) error)
128 invoke func(context.Context) error
129 }{
130 {
131 name: "session open",
132 set: d.SetOnSessionOpen,
133 invoke: d.handleSessionOpen,
134 },
135 {
136 name: "move pane to tab",
137 set: d.SetOnMovePaneToTab,
138 invoke: d.handleMovePaneToTab,
139 },
140 {
141 name: "move pane to next tab",
142 set: d.SetOnMovePaneToNextTab,
143 invoke: d.handleMovePaneToNextTab,
144 },
145 {
146 name: "eject pane to window",
147 set: d.SetOnEjectPaneToWindow,
148 invoke: d.handleEjectPaneToWindow,
149 },
150 }
151
152 for _, tc := range tests {
153 t.Run(tc.name, func(t *testing.T) {
154 var gotPaneID entity.PaneID
155 tc.set(func(_ context.Context, paneID entity.PaneID) error {
156 gotPaneID = paneID
157 return nil
158 })
159
160 require.NoError(t, tc.invoke(ctx))
161 assert.Equal(t, activePaneID, gotPaneID)
162 })
163 }
164}