1package usecase
2
3import (
4 "fmt"
5 "testing"
6
7 "github.com/bnema/dumber/internal/domain/entity"
8 "github.com/stretchr/testify/require"
9)
10
11func TestMovePaneToTab_MoveToExistingTab(t *testing.T) {
12 id := newTestIDGen()
13 uc := NewMovePaneToTabUseCase(id)
14
15 tabs := entity.NewTabList()
16
17 paneA := entity.NewPane(entity.PaneID("pA"))
18 tabA := entity.NewTab(entity.TabID("tA"), entity.WorkspaceID("wA"), paneA)
19 tabs.Add(tabA)
20
21 paneB := entity.NewPane(entity.PaneID("pB"))
22 tabB := entity.NewTab(entity.TabID("tB"), entity.WorkspaceID("wB"), paneB)
23 tabs.Add(tabB)
24 // Ensure tabB active pane is paneB.
25 tabB.Workspace.ActivePaneID = paneB.ID
26
27 out, err := uc.Execute(MovePaneToTabInput{
28 TabList: tabs,
29 SourceTabID: tabA.ID,
30 SourcePaneID: paneA.ID,
31 TargetTabID: tabB.ID,
32 })
33 require.NoError(t, err)
34 require.True(t, out.SourceTabClosed)
35 require.False(t, out.NewTabCreated)
36 require.Equal(t, tabB.ID, out.TargetTab.ID)
37 require.NotNil(t, out.MovedPaneNode)
38
39 require.Nil(t, tabs.Find(tabA.ID))
40 // Inserted as split in tabB.
41 require.NotNil(t, tabB.Workspace.Root)
42 require.True(t, tabB.Workspace.Root.IsSplit())
43 require.Equal(t, paneB.ID, tabB.Workspace.Root.Left().Pane.ID)
44 require.Equal(t, paneA.ID, tabB.Workspace.Root.Right().Pane.ID)
45 // Active pane becomes moved pane.
46 require.Equal(t, paneA.ID, tabB.Workspace.ActivePaneID)
47}
48
49func TestMovePaneToTab_MoveCreatesNewTabWhenOnlyOneTab(t *testing.T) {
50 id := newTestIDGen()
51 uc := NewMovePaneToTabUseCase(id)
52
53 tabs := entity.NewTabList()
54 paneA := entity.NewPane(entity.PaneID("pA"))
55 tabA := entity.NewTab(entity.TabID("tA"), entity.WorkspaceID("wA"), paneA)
56 tabs.Add(tabA)
57
58 out, err := uc.Execute(MovePaneToTabInput{
59 TabList: tabs,
60 SourceTabID: tabA.ID,
61 SourcePaneID: paneA.ID,
62 TargetTabID: "",
63 })
64 require.NoError(t, err)
65 require.True(t, out.NewTabCreated)
66 require.True(t, out.SourceTabClosed)
67 require.Equal(t, 1, tabs.Count())
68 require.Equal(t, paneA.ID, out.TargetTab.Workspace.ActivePaneID)
69 require.NotNil(t, out.TargetTab.Workspace.Root)
70 require.True(t, out.TargetTab.Workspace.Root.IsLeaf())
71 require.Equal(t, paneA.ID, out.TargetTab.Workspace.Root.Pane.ID)
72}
73
74func TestMovePaneToTab_CannotMoveToSameTab(t *testing.T) {
75 uc := NewMovePaneToTabUseCase(newTestIDGen())
76 abs := entity.NewTabList()
77 pane := entity.NewPane(entity.PaneID("pA"))
78 tab := entity.NewTab(entity.TabID("tA"), entity.WorkspaceID("wA"), pane)
79 abs.Add(tab)
80
81 _, err := uc.Execute(MovePaneToTabInput{
82 TabList: abs,
83 SourceTabID: tab.ID,
84 SourcePaneID: pane.ID,
85 TargetTabID: tab.ID,
86 })
87 require.Error(t, err)
88}
89
90func TestMovePaneToTab_SourcePaneNotFound(t *testing.T) {
91 uc := NewMovePaneToTabUseCase(newTestIDGen())
92 tabs := entity.NewTabList()
93 pane := entity.NewPane(entity.PaneID("pA"))
94 tab := entity.NewTab(entity.TabID("tA"), entity.WorkspaceID("wA"), pane)
95 tabs.Add(tab)
96
97 _, err := uc.Execute(MovePaneToTabInput{
98 TabList: tabs,
99 SourceTabID: tab.ID,
100 SourcePaneID: entity.PaneID("missing"),
101 TargetTabID: entity.TabID("tB"),
102 })
103 require.Error(t, err)
104}
105
106func TestMovePaneToTab_MoveFromSplitClosesSourceTabIfLastPane(t *testing.T) {
107 id := newTestIDGen()
108 uc := NewMovePaneToTabUseCase(id)
109
110 tabs := entity.NewTabList()
111
112 // Source workspace: split with two panes; move one, leaving one.
113 paneLeft := entity.NewPane(entity.PaneID("pL"))
114 paneRight := entity.NewPane(entity.PaneID("pR"))
115 source := entity.NewTab(entity.TabID("tA"), entity.WorkspaceID("wA"), paneLeft)
116 // Build a split root manually.
117 source.Workspace.Root = &entity.PaneNode{
118 ID: "root",
119 SplitDir: entity.SplitHorizontal,
120 SplitRatio: 0.5,
121 Children: []*entity.PaneNode{
122 {ID: string(paneLeft.ID), Pane: paneLeft},
123 {ID: string(paneRight.ID), Pane: paneRight},
124 },
125 }
126 source.Workspace.Root.Children[0].Parent = source.Workspace.Root
127 source.Workspace.Root.Children[1].Parent = source.Workspace.Root
128 source.Workspace.ActivePaneID = paneRight.ID
129 tabs.Add(source)
130
131 targetPane := entity.NewPane(entity.PaneID("pT"))
132 target := entity.NewTab(entity.TabID("tB"), entity.WorkspaceID("wB"), targetPane)
133 tabs.Add(target)
134
135 out, err := uc.Execute(MovePaneToTabInput{
136 TabList: tabs,
137 SourceTabID: source.ID,
138 SourcePaneID: paneRight.ID,
139 TargetTabID: target.ID,
140 })
141 require.NoError(t, err)
142 require.False(t, out.SourceTabClosed)
143 // Source should now be single leaf.
144 require.Equal(t, 1, source.Workspace.PaneCount())
145 require.NotNil(t, source.Workspace.Root)
146 require.True(t, source.Workspace.Root.IsLeaf())
147 require.Equal(t, paneLeft.ID, source.Workspace.Root.Pane.ID)
148}
149
150func TestMovePaneToTab_InsertsRightOfActiveEvenWhenActiveInStack(t *testing.T) {
151 id := newTestIDGen()
152 uc := NewMovePaneToTabUseCase(id)
153
154 tabs := entity.NewTabList()
155
156 paneA := entity.NewPane(entity.PaneID("pA"))
157 source := entity.NewTab(entity.TabID("tA"), entity.WorkspaceID("wA"), paneA)
158 tabs.Add(source)
159
160 // Target workspace: stacked container with two panes; active is second.
161 pane1 := entity.NewPane(entity.PaneID("p1"))
162 pane2 := entity.NewPane(entity.PaneID("p2"))
163 target := entity.NewTab(entity.TabID("tB"), entity.WorkspaceID("wB"), pane1)
164 stack := &entity.PaneNode{
165 ID: "stack",
166 IsStacked: true,
167 ActiveStackIndex: 1,
168 Children: []*entity.PaneNode{
169 {ID: string(pane1.ID), Pane: pane1},
170 {ID: string(pane2.ID), Pane: pane2},
171 },
172 }
173 stack.Children[0].Parent = stack
174 stack.Children[1].Parent = stack
175 target.Workspace.Root = stack
176 target.Workspace.ActivePaneID = pane2.ID
177 tabs.Add(target)
178
179 out, err := uc.Execute(MovePaneToTabInput{
180 TabList: tabs,
181 SourceTabID: source.ID,
182 SourcePaneID: paneA.ID,
183 TargetTabID: target.ID,
184 })
185 require.NoError(t, err)
186 require.NotNil(t, out.TargetTab.Workspace.Root)
187 require.True(t, out.TargetTab.Workspace.Root.IsSplit())
188 // Left child should be the stack container.
189 require.True(t, out.TargetTab.Workspace.Root.Left().IsStacked)
190 // Right child is moved pane.
191 require.Equal(t, paneA.ID, out.TargetTab.Workspace.Root.Right().Pane.ID)
192}
193
194func newTestIDGen() func() string {
195 counter := 0
196 return func() string {
197 counter++
198 return fmt.Sprintf("id%d", counter)
199 }
200}