main ece7682

ui_action_navigation_test.go RawHistoryFolder

1package daemon
2
3import (
4	"testing"
5
6	"github.com/bnema/vev/internal/adapters/sessionwire"
7	"github.com/bnema/vev/internal/protocol"
8	"github.com/stretchr/testify/require"
9)
10
11func TestUIActionNavigationUsesAdmittedCause(t *testing.T) {
12	for _, actionID := range []uint64{0, 17} {
13		for _, message := range []protocol.ServerMessage{
14			protocol.AttachTarget{CauseActionID: 999, Session: "destination", Intent: protocol.IntentAttach},
15			protocol.RouteNavigationAction{CauseActionID: 999, SnapshotGeneration: 1, Key: 2, Generation: 3},
16			protocol.RouteCreateSessionAction{CauseActionID: 999, RequestID: 1, SnapshotGeneration: 2, Key: 3, Generation: 4, SessionName: "new"},
17		} {
18			t.Run(testNavigationName(message, actionID), func(t *testing.T) {
19				_, sess, ac, sends := newManualSessionWithPTYs(t, nil)
20				effect, ok := ac.beginAttachmentEffect(captureAttachmentCapability(sess, ac, ac.transport()))
21				require.True(t, ok)
22				t.Cleanup(effect.End)
23				effect.uiActionID = actionID
24				require.NoError(t, effect.sendControl(message))
25				frame := awaitTestValue(t, sends, "navigation was not sent")
26				require.Equal(t, actionID, testNavigationCause(t, frame.Payload))
27				original, err := testServerEnvelope(message)
28				require.NoError(t, err)
29				require.Equal(t, uint64(999), testNavigationCause(t, original.Payload), "sending must not mutate reusable navigation templates")
30			})
31		}
32	}
33}
34
35func testNavigationName(message protocol.ServerMessage, actionID uint64) string {
36	name := "human/"
37	if actionID != 0 {
38		name = "automation/"
39	}
40	switch message.(type) {
41	case protocol.AttachTarget:
42		return name + "attach"
43	case protocol.RouteNavigationAction:
44		return name + "recent"
45	default:
46		return name + "create"
47	}
48}
49
50func testNavigationCause(t *testing.T, payload []byte) uint64 {
51	t.Helper()
52	message, err := sessionwire.DecodeServerEnvelope(payload)
53	require.NoError(t, err)
54	switch message := message.(type) {
55	case protocol.AttachTarget:
56		return message.CauseActionID
57	case protocol.RouteNavigationAction:
58		return message.CauseActionID
59	case protocol.RouteCreateSessionAction:
60		return message.CauseActionID
61	default:
62		t.Fatalf("unexpected navigation message %T", message)
63		return 0
64	}
65}
66
67func TestUIActionInputPaletteSubmissionCarriesItsOwnCause(t *testing.T) {
68	d, sess, ac, sends := newManualSessionWithPTYs(t, nil)
69	rc := d.attachCoordinator(sess, nil, ac, true)
70	t.Cleanup(func() { rc.beginSessionTeardown().finish(); rc.waitForTimerWorkers() })
71	ac.setRouteSnapshot(protocol.RecentRouteSnapshot{
72		Generation: 9, Active: protocol.RouteRef{Key: 1, Generation: 1},
73		ActiveEntry: testRouteEntry(1, 1, sess.name, 1, protocol.RouteKindLocal),
74		Entries:     []protocol.RecentRouteEntry{{Key: 8, Generation: 4, Target: testRouteTarget("logs", 8), Name: "logs", HostLabel: "edge", Kind: protocol.RouteKindRemote}},
75	})
76	for _, input := range []protocol.Input{
77		{ActionID: 40, Data: []byte("\x1b ")},
78		{ActionID: 41, Data: []byte("logs@edge\r")},
79	} {
80		require.False(t, d.handleAttachmentClientMessage(captureAttachmentCapability(sess, ac, ac.transport()), input))
81	}
82	frame := awaitFrame(t, sends, "RouteNavigationAction")
83	require.Equal(t, uint64(41), testNavigationCause(t, frame.Payload), "submission owns navigation, not the earlier palette-opening action")
84}

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