recent_route_presentation_test.go RawHistoryFolder

1package daemon
2
3import (
4	"testing"
5
6	"github.com/bnema/vev/internal/protocol"
7	"github.com/bnema/vev/internal/usecase/command"
8	"github.com/bnema/vev/internal/usecase/palette"
9	"github.com/stretchr/testify/require"
10)
11
12func TestFormatRecentRoutePresentations(t *testing.T) {
13	tests := []struct {
14		name  string
15		input []recentRoutePresentation
16		want  []recentRouteDisplay
17	}{
18		{
19			name: "unique local name stays compact",
20			input: []recentRoutePresentation{{
21				name: "editor", kind: recentRouteLocal,
22			}},
23			want: []recentRouteDisplay{{
24				name: "editor", kind: recentRouteLocal,
25			}},
26		},
27		{
28			name: "remote name is host qualified",
29			input: []recentRoutePresentation{{
30				name: "logs", hostLabel: "edge", kind: recentRouteRemote,
31			}},
32			want: []recentRouteDisplay{{
33				name: "logs@edge", kind: recentRouteRemote,
34			}},
35		},
36		{
37			name: "duplicate local and remote names keep local compact",
38			input: []recentRoutePresentation{
39				{name: "logs", kind: recentRouteLocal},
40				{name: "logs", hostLabel: "edge", kind: recentRouteRemote},
41			},
42			want: []recentRouteDisplay{
43				{name: "logs", kind: recentRouteLocal},
44				{name: "logs@edge", kind: recentRouteRemote},
45			},
46		},
47		{
48			name: "duplicate local generations stay compact",
49			input: []recentRoutePresentation{
50				{name: "logs", kind: recentRouteLocal},
51				{name: "logs", kind: recentRouteLocal},
52			},
53			want: []recentRouteDisplay{
54				{name: "logs", kind: recentRouteLocal},
55				{name: "logs", kind: recentRouteLocal},
56			},
57		},
58		{
59			name: "missing host uses kind fallback",
60			input: []recentRoutePresentation{{
61				name: "logs", kind: recentRouteRemote,
62			}},
63			want: []recentRouteDisplay{{
64				name: "logs@remote", kind: recentRouteRemote,
65			}},
66		},
67		{
68			name: "authoritative markers survive formatting",
69			input: []recentRoutePresentation{{
70				name: "scratch", kind: recentRouteLocal, ephemeral: true, attention: true,
71			}},
72			want: []recentRouteDisplay{{
73				name: "scratch*", kind: recentRouteLocal, ephemeral: true, attention: true,
74			}},
75		},
76	}
77
78	for _, tt := range tests {
79		t.Run(tt.name, func(t *testing.T) {
80			input := append([]recentRoutePresentation(nil), tt.input...)
81			got := formatRecentRoutePresentations(input)
82
83			require.Equal(t, tt.want, got)
84			require.Equal(t, input, tt.input, "formatting must not mutate the source snapshot")
85		})
86	}
87}
88
89func TestRecentSessionHintsAndRankedRenderingShareCanonicalLabels(t *testing.T) {
90	snapshot := protocol.RecentRouteSnapshot{
91		Generation: 1,
92		Entries: []protocol.RecentRouteEntry{
93			{Key: 1, Generation: 1, Target: testRouteTarget("logs", 1), Name: "logs", Kind: protocol.RouteKindLocal, Attention: true, Visited: true},
94			testRouteEntry(2, 1, "logs", 2, protocol.RouteKindLocal),
95		},
96	}
97
98	hints := recentRouteHints(snapshot, nil)
99	require.Equal(t, []palette.RecentSessionHint{
100		{Rank: 1, Name: "logs", SnapshotGeneration: 1, Key: 1, Generation: 1},
101		{Rank: 2, Name: "logs", SnapshotGeneration: 1, Key: 2, Generation: 1},
102	}, hints.Recent)
103
104	hints = palette.ContextualHints{
105		Kind:         command.ContextHintRecentSessions,
106		SelectedRank: 9,
107		Recent: []palette.RecentSessionHint{
108			{Rank: 4, Name: "stale-label"},
109			{Rank: 9, Name: "stale-label"},
110		},
111	}
112	ranked := rankedRecentForHintsWithSnapshot(&hints, snapshot)
113
114	require.Equal(t, []rankedRecent{
115		{rank: 4, name: "logs", kind: recentRouteLocal, attention: true},
116		{rank: 9, name: "logs", kind: recentRouteLocal, selected: true},
117	}, ranked)
118}
119
120func TestFitRecentRouteEntriesKeepsWholeWideAndAttentionEntries(t *testing.T) {
121	tests := []struct {
122		name    string
123		entries []recentRouteDisplay
124		width   int
125		want    []string
126	}{
127		{
128			name: "wide rune consumes its full cell width",
129			entries: []recentRouteDisplay{
130				{name: "界"},
131				{name: "b"},
132			},
133			width: 12,
134			want:  []string{"界"},
135		},
136		{
137			name: "attention bell is part of the entry width",
138			entries: []recentRouteDisplay{
139				{name: "a", attention: true},
140				{name: "b"},
141			},
142			width: 12,
143			want:  []string{"a"},
144		},
145	}
146
147	for _, tt := range tests {
148		t.Run(tt.name, func(t *testing.T) {
149			got := fitMRU(tt.entries, tt.width, 0, "copy")
150			names := make([]string, len(got))
151			for i, entry := range got {
152				names[i] = entry.name
153			}
154			require.Equal(t, tt.want, names)
155		})
156	}
157}
158
159func TestRankedRecentKeepsCapturedRanksWhenFitting(t *testing.T) {
160	entries := []rankedRecent{
161		{rank: 3, name: "wide界"},
162		{rank: 7, name: "next"},
163	}
164
165	got := fitRankedRecent(entries, 17, 0, "")
166
167	require.Equal(t, []rankedRecent{{rank: 3, name: "wide界"}}, got)
168}

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