v0.2.0 80c61e3

recent_route_presentation_test.go RawHistoryFolder

1package daemon
2
3import (
4	"testing"
5
6	"github.com/bnema/vev/internal/ports"
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 qualify every entry",
38			input: []recentRoutePresentation{
39				{name: "logs", kind: recentRouteLocal},
40				{name: "logs", hostLabel: "edge", kind: recentRouteRemote},
41			},
42			want: []recentRouteDisplay{
43				{name: "logs@local", kind: recentRouteLocal},
44				{name: "logs@edge", kind: recentRouteRemote},
45			},
46		},
47		{
48			name: "missing host uses kind fallback",
49			input: []recentRoutePresentation{{
50				name: "logs", kind: recentRouteRemote,
51			}},
52			want: []recentRouteDisplay{{
53				name: "logs@remote", kind: recentRouteRemote,
54			}},
55		},
56		{
57			name: "authoritative markers survive formatting",
58			input: []recentRoutePresentation{{
59				name: "scratch", kind: recentRouteLocal, ephemeral: true, attention: true,
60			}},
61			want: []recentRouteDisplay{{
62				name: "scratch*", kind: recentRouteLocal, ephemeral: true, attention: true,
63			}},
64		},
65	}
66
67	for _, tt := range tests {
68		t.Run(tt.name, func(t *testing.T) {
69			input := append([]recentRoutePresentation(nil), tt.input...)
70			got := formatRecentRoutePresentations(input)
71
72			require.Equal(t, tt.want, got)
73			require.Equal(t, input, tt.input, "formatting must not mutate the source snapshot")
74		})
75	}
76}
77
78func TestRecentSessionHintsAndRankedRenderingShareCanonicalLabels(t *testing.T) {
79	snapshot := ports.RecentRouteSnapshot{
80		Generation: 1,
81		Entries: []ports.RecentRouteEntry{
82			{Key: 1, Generation: 1, Name: "logs", Kind: ports.RouteKindLocal, Attention: true},
83			{Key: 2, Generation: 1, Name: "logs", Kind: ports.RouteKindLocal},
84		},
85	}
86
87	hints := recentRouteHints(snapshot, nil)
88	require.Equal(t, []palette.RecentSessionHint{
89		{Rank: 1, Name: "logs@local", SnapshotGeneration: 1, Key: 1, Generation: 1},
90		{Rank: 2, Name: "logs@local", SnapshotGeneration: 1, Key: 2, Generation: 1},
91	}, hints.Recent)
92
93	hints = palette.ContextualHints{
94		Kind:         command.ContextHintRecentSessions,
95		SelectedRank: 9,
96		Recent: []palette.RecentSessionHint{
97			{Rank: 4, Name: "stale-label"},
98			{Rank: 9, Name: "stale-label"},
99		},
100	}
101	ranked := rankedRecentForHintsWithSnapshot(&hints, snapshot)
102
103	require.Equal(t, []rankedRecent{
104		{rank: 4, name: "logs@local", kind: recentRouteLocal, attention: true},
105		{rank: 9, name: "logs@local", kind: recentRouteLocal, selected: true},
106	}, ranked)
107}
108
109func TestFitRecentRouteEntriesKeepsWholeWideAndAttentionEntries(t *testing.T) {
110	tests := []struct {
111		name    string
112		entries []recentRouteDisplay
113		width   int
114		want    []string
115	}{
116		{
117			name: "wide rune consumes its full cell width",
118			entries: []recentRouteDisplay{
119				{name: "界"},
120				{name: "b"},
121			},
122			width: 12,
123			want:  []string{"界"},
124		},
125		{
126			name: "attention bell is part of the entry width",
127			entries: []recentRouteDisplay{
128				{name: "a", attention: true},
129				{name: "b"},
130			},
131			width: 12,
132			want:  []string{"a"},
133		},
134	}
135
136	for _, tt := range tests {
137		t.Run(tt.name, func(t *testing.T) {
138			got := fitMRU(tt.entries, tt.width, 0, "copy")
139			names := make([]string, len(got))
140			for i, entry := range got {
141				names[i] = entry.name
142			}
143			require.Equal(t, tt.want, names)
144		})
145	}
146}
147
148func TestRankedRecentKeepsCapturedRanksWhenFitting(t *testing.T) {
149	entries := []rankedRecent{
150		{rank: 3, name: "wide界"},
151		{rank: 7, name: "next"},
152	}
153
154	got := fitRankedRecent(entries, 17, 0, "")
155
156	require.Equal(t, []rankedRecent{{rank: 3, name: "wide界"}}, got)
157}

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