multi_attachment_callers_test.go RawHistoryFolder
1package daemon
2
3import (
4 "encoding/json"
5 "testing"
6
7 "github.com/bnema/vev/internal/domain"
8 "github.com/bnema/vev/internal/domain/layout"
9 "github.com/stretchr/testify/require"
10)
11
12func TestDaemonActionTargetsUseEachInitiatingAttachmentView(t *testing.T) {
13 sess := registryTestSession()
14 first, second := registryTestAttachment(1), registryTestAttachment(2)
15 require.True(t, sess.registerAttachment(first))
16 require.True(t, sess.registerAttachment(second))
17 require.True(t, sess.selectAttachmentTab(second, domain.TabStableID(sess.tabs[1].stableID)))
18
19 firstTarget := resolveDaemonActionTargetForAttachment(sess, first)
20 secondTarget := resolveDaemonActionTargetForAttachment(sess, second)
21 require.Same(t, sess.tabs[0], firstTarget.tab)
22 require.Same(t, sess.tabs[1], secondTarget.tab)
23 require.Same(t, sess.tabs[0].focusedPane(), firstTarget.pane)
24 require.Same(t, sess.tabs[1].focusedPane(), secondTarget.pane)
25}
26
27func TestAttachmentsKeepDifferentPaneFocusForActionsRenderingAndMetadata(t *testing.T) {
28 sess, firstPane, secondPane := multiPaneAttachmentSession(t)
29 first, second := registryTestAttachment(1), registryTestAttachment(2)
30 require.True(t, sess.registerAttachment(first))
31 require.True(t, sess.registerAttachment(second))
32 require.True(t, sess.updateAttachmentView(second, func(view *attachmentView) {
33 view.paneID = domain.PaneStableID(secondPane.stableID)
34 }))
35
36 firstTarget := resolveDaemonActionTargetForAttachment(sess, first)
37 secondTarget := resolveDaemonActionTargetForAttachment(sess, second)
38 require.Same(t, firstPane, firstTarget.pane)
39 require.Same(t, secondPane, secondTarget.pane)
40
41 firstState, ok := captureLocalRenderState(sess, first, renderCaptureRequest{})
42 require.True(t, ok)
43 secondState, ok := captureLocalRenderState(sess, second, renderCaptureRequest{})
44 require.True(t, ok)
45 require.Equal(t, firstPane.id, firstState.layout.focus)
46 require.Equal(t, secondPane.id, secondState.layout.focus)
47
48 d := newTestDaemon(t, nil, stubClock{})
49 var rows []struct {
50 Pane string `json:"pane"`
51 Focused bool `json:"focused"`
52 }
53 output, err := (controlExec{d: d, sess: sess, tab: sess.tabs[0], target: daemonActionTarget{attachment: second}}).ListPanes(true)
54 require.NoError(t, err)
55 require.NoError(t, json.Unmarshal([]byte(output), &rows))
56 for _, row := range rows {
57 require.Equal(t, row.Pane == string(secondPane.id), row.Focused)
58 }
59}
60
61func TestAttachmentFocusActionUpdatesOnlyInitiatingPaneView(t *testing.T) {
62 sess, firstPane, secondPane := multiPaneAttachmentSession(t)
63 first, second := registryTestAttachment(1), registryTestAttachment(2)
64 require.True(t, sess.registerAttachment(first))
65 require.True(t, sess.registerAttachment(second))
66
67 d := newTestDaemon(t, nil, stubClock{})
68 require.NoError(t, d.focusDir(sess, second, layout.Right, nil))
69 require.Equal(t, domain.PaneStableID(firstPane.stableID), first.viewSnapshot().paneID)
70 require.Equal(t, domain.PaneStableID(secondPane.stableID), second.viewSnapshot().paneID)
71}
72
73func TestAttachmentFocusActionCommitsWhenLayoutAlreadyFocusesDestination(t *testing.T) {
74 sess, _, secondPane := multiPaneAttachmentSession(t)
75 ac := registryTestAttachment(1)
76 require.True(t, sess.registerAttachment(ac))
77
78 tb := sess.tabs[0]
79 tb.mu.Lock()
80 tb.tree.Focus = secondPane.id
81 tb.mu.Unlock()
82
83 d := newTestDaemon(t, nil, stubClock{})
84 require.NoError(t, d.focusDir(sess, ac, layout.Right, nil))
85 require.Equal(t, domain.PaneStableID(secondPane.stableID), ac.viewSnapshot().paneID)
86}
87
88func TestPaneRemovalRepairsEachAttachmentViewIndependently(t *testing.T) {
89 sess, firstPane, secondPane := multiPaneAttachmentSession(t)
90 first, second := registryTestAttachment(1), registryTestAttachment(2)
91 require.True(t, sess.registerAttachment(first))
92 require.True(t, sess.registerAttachment(second))
93 require.True(t, sess.updateAttachmentView(second, func(view *attachmentView) {
94 view.paneID = domain.PaneStableID(secondPane.stableID)
95 }))
96
97 require.NoError(t, sess.runMutation(func() error {
98 sess.mu.Lock()
99 defer sess.mu.Unlock()
100 tb := sess.tabs[0]
101 tb.mu.Lock()
102 closeErr := tb.tree.Close(firstPane.id)
103 if closeErr == nil {
104 delete(tb.panes, firstPane.id)
105 tb.bumpLayoutGenerationLocked()
106 }
107 tb.mu.Unlock()
108 if closeErr != nil {
109 return closeErr
110 }
111 sess.repairAttachmentViewsLocked()
112 return nil
113 }))
114
115 require.Equal(t, domain.PaneStableID(secondPane.stableID), first.viewSnapshot().paneID)
116 require.Equal(t, domain.PaneStableID(secondPane.stableID), second.viewSnapshot().paneID)
117}
118
119func multiPaneAttachmentSession(t *testing.T) (*session, *pane, *pane) {
120 t.Helper()
121 sess := registryTestSession()
122 tb := sess.tabs[0]
123 firstPane := tb.panes[layout.PaneID("pane-1")]
124 secondPane := newPaneWithStableID("pane-2", "stable-pane-b", nil, domain.Size{Cols: 80, Rows: 23})
125 tb.mu.Lock()
126 tb.panes[secondPane.id] = secondPane
127 tb.tree = &layout.Tree{
128 Root: &layout.Node{Kind: layout.Split, Dir: layout.Horizontal, Children: []*layout.Node{layout.NewLeaf(firstPane.id), layout.NewLeaf(secondPane.id)}},
129 Focus: firstPane.id,
130 }
131 tb.bumpLayoutGenerationLocked()
132 tb.mu.Unlock()
133 return sess, firstPane, secondPane
134}