v0.2.0 80c61e3

route_position_test.go RawHistoryFolder

1package daemon
2
3import (
4	"context"
5	"testing"
6
7	"github.com/stretchr/testify/require"
8
9	"github.com/bnema/vev/internal/domain"
10	"github.com/bnema/vev/internal/ports"
11)
12
13func TestRouteRetainsRemoteOriginWithoutDiscoveryTarget(t *testing.T) {
14	d := newTestDaemon(t, nil, stubClock{})
15	sess := addControlSession(d, "work", "tab-1", "pane-1")
16	sess.incarnation = domain.SessionLifecycleID{1}
17	tr := &closeTrackingTransport{}
18	target := ports.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name}
19	hello := ports.Hello{
20		Version: ports.ProtocolVersion, Intent: ports.IntentAttach, Name: sess.name,
21		Size: defaultSize, ExactTarget: &target, RemoteOrigin: "remote",
22		EnvironmentPolicy: ports.EnvironmentPolicyClientOwned,
23	}
24
25	_, ac, err := d.routeWithContext(context.Background(), hello, tr)
26	require.NoError(t, err)
27	require.Equal(t, "remote", ac.remoteOrigin)
28	d.clientGone(sess, ac, tr, false)
29}
30
31func TestRouteAppliesPreferredTabPerAttachment(t *testing.T) {
32	for _, test := range []struct {
33		name      string
34		preferred domain.TabStableID
35		want      domain.TabStableID
36	}{
37		{name: "existing preferred tab", preferred: "tab-2", want: "tab-2"},
38		{name: "missing preferred tab falls back", preferred: "missing", want: "tab-1"},
39	} {
40		t.Run(test.name, func(t *testing.T) {
41			d := newTestDaemon(t, nil, stubClock{})
42			sess := addControlSession(d, "work", "tab-1", "pane-1")
43			sess.incarnation = domain.SessionLifecycleID{1}
44			second := newTabWithStableID("tab-2", "pane-2", newQuietPTY(), domain.Size{Cols: 80, Rows: 22})
45			sess.mu.Lock()
46			sess.tabs = append(sess.tabs, second)
47			sess.mu.Unlock()
48			tr := &closeTrackingTransport{}
49			target := ports.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name}
50			hello := ports.Hello{
51				Version: ports.ProtocolVersion, Intent: ports.IntentAttach, Name: sess.name,
52				Size: defaultSize, ExactTarget: &target, PreferredTabID: test.preferred,
53				EnvironmentPolicy: ports.EnvironmentPolicyClientOwned,
54			}
55
56			_, ac, err := d.routeWithContext(context.Background(), hello, tr)
57			require.NoError(t, err)
58			sess.repairAttachmentView(ac)
59			require.Equal(t, test.want, ac.viewSnapshot().tabID)
60			d.clientGone(sess, ac, tr, false)
61		})
62	}
63}
64
65func TestResumeAppliesPreferredTabToParkedAttachment(t *testing.T) {
66	pty, release := newBlockingPTY(t)
67	defer release()
68	d := newTestDaemon(t, newFactory(t, pty), stubClock{})
69	oldTransport := &closeTrackingTransport{}
70	sess, ac, err := d.route(helloResumeCapable(ports.IntentNew, "work", 0), oldTransport)
71	require.NoError(t, err)
72	second := newTabWithStableID("tab-2", "pane-2", newQuietPTY(), domain.Size{Cols: 80, Rows: 22})
73	sess.mu.Lock()
74	sess.tabs = append(sess.tabs, second)
75	sess.mu.Unlock()
76	sess.repairAttachmentView(ac)
77	token := ac.resumeToken
78	d.clientGone(sess, ac, oldTransport, false)
79
80	hello := helloResumeCapable(ports.IntentResume, "work", token)
81	hello.PreferredTabID = "tab-2"
82	newTransport := &closeTrackingTransport{}
83	resumedSession, resumed, err := d.route(hello, newTransport)
84	require.NoError(t, err)
85	require.Same(t, ac, resumed)
86	require.Equal(t, domain.TabStableID("tab-2"), resumed.viewSnapshot().tabID)
87	d.clientGone(resumedSession, resumed, newTransport, false)
88}
89
90func TestPaintPublishesChangedAttachmentRoutePosition(t *testing.T) {
91	d := newTestDaemon(t, nil, stubClock{})
92	sess := addControlSession(d, "work", "tab-1", "pane-1")
93	sess.incarnation = domain.SessionLifecycleID{1}
94	second := newTabWithStableID("tab-2", "pane-2", newQuietPTY(), domain.Size{Cols: 80, Rows: 22})
95	sess.mu.Lock()
96	sess.tabs = append(sess.tabs, second)
97	sess.mu.Unlock()
98	tr := &closeTrackingTransport{}
99	target := ports.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name}
100	hello := ports.Hello{
101		Version: ports.ProtocolVersion, Intent: ports.IntentAttach, Name: sess.name,
102		Size: defaultSize, ExactTarget: &target, EnvironmentPolicy: ports.EnvironmentPolicyClientOwned,
103	}
104	_, ac, err := d.routeWithContext(context.Background(), hello, tr)
105	require.NoError(t, err)
106	rc := sess.renderCoordinator()
107	require.True(t, rc.markAttachmentReady(rc.attachmentLease(ac)))
108	sess.repairAttachmentView(ac)
109
110	require.True(t, d.firstPaintForTransition(sess.attachmentToken(ac, tr)))
111	sess.selectAttachmentTab(ac, "tab-2")
112	d.paint(sess, ac, false, nil)
113
114	var positions []ports.RoutePosition
115	for _, frame := range tr.Sends() {
116		if frame.Type != ports.MsgRoutePosition {
117			continue
118		}
119		position, err := ports.UnmarshalRoutePosition(frame.Payload)
120		require.NoError(t, err)
121		positions = append(positions, position)
122	}
123	require.Equal(t, []ports.RoutePosition{
124		{Target: target, ActiveTabID: "tab-1"},
125		{Target: target, ActiveTabID: "tab-2"},
126	}, positions)
127	d.clientGone(sess, ac, tr, false)
128}

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 12

Tags 11

↑↓ moveenter openesc close