v0.3.2 a514042

view_test.go RawHistoryFolder

1package visual
2
3import (
4	"testing"
5	"time"
6	"unicode/utf8"
7
8	"github.com/bnema/neferclient"
9	"github.com/stretchr/testify/require"
10)
11
12func TestFadeAlphaInterpolatesAndArrives(t *testing.T) {
13	t0 := time.Unix(100, 0)
14	a, done := fadeAlpha(0, 1, t0, 2*time.Second, t0.Add(time.Second))
15	require.InDelta(t, 0.5, a, 1e-9)
16	require.False(t, done)
17	a, done = fadeAlpha(0, 1, t0, 2*time.Second, t0.Add(3*time.Second))
18	require.Equal(t, 1.0, a)
19	require.True(t, done)
20	_, done = fadeAlpha(1, 0, t0, 0, t0)
21	require.True(t, done)
22}
23
24// Surfaces share the model but draw at different instants: a surface that
25// drew just after the end must not stop redraws for one that drew just before.
26func TestFadeKeepsTickingUntilEverySurfaceDrewTheEnd(t *testing.T) {
27	m := newFadeModel()
28	m.send(fadeCmd{black: true, dur: time.Second})
29	t0 := m.base.Add(time.Minute)
30	a, done := m.step(t0)
31	require.Equal(t, 0.0, a)
32	require.False(t, done)
33
34	before := t0.Add(time.Second - time.Millisecond)
35	after := t0.Add(time.Second + time.Millisecond)
36	_, done = m.step(before) // surface A
37	require.False(t, done)
38	a, done = m.step(after) // surface B reaches the end
39	require.Equal(t, 1.0, a)
40	require.True(t, done)
41	require.True(t, m.animatingAt(after), "surface A still needs a redraw")
42	require.False(t, m.animatingAt(t0.Add(time.Hour)))
43}
44
45func TestFadeLayerIsClickThroughOverlayOnEveryOutput(t *testing.T) {
46	l := fadeLayer("DP-1")
47	require.Equal(t, "DP-1", l.Output)
48	require.Equal(t, neferclient.LayerOverlay, l.Level)
49	require.Equal(t, neferclient.KeyboardNone, l.Keyboard)
50	require.Equal(t, int32(-1), l.ExclusiveZone)
51	require.NotNil(t, l.InputRects)
52	require.Empty(t, l.InputRects)
53	all := neferclient.AnchorTop | neferclient.AnchorBottom | neferclient.AnchorLeft | neferclient.AnchorRight
54	require.Equal(t, all, l.Anchors)
55}
56
57func TestHintsShowStatusThenPromptNotesAndFitTheBox(t *testing.T) {
58	for _, tc := range []struct {
59		s    lockStatus
60		p    promptKind
61		want string
62	}{
63		{lockFailed, promptFallback, "Denied"},
64		{lockFailed, promptMore, "Denied"},
65		{lockBusy, promptPIN, "..."},
66		{lockIdle, promptFallback, "PIN off, use password"},
67		{lockIdle, promptUnavailable, "Authentication unavailable"},
68		{lockIdle, promptMore, "Enter again"},
69		{lockIdle, promptPassword, " "},
70		{lockIdle, promptPIN, " "},
71	} {
72		got := hintFor(tc.s, tc.p)
73		require.Equal(t, tc.want, got)
74		require.LessOrEqual(t, utf8.RuneCountInString(got), maxHintRunes, got)
75	}
76}
77
78func TestMaskIsCappedToTheField(t *testing.T) {
79	mask, _ := maskFor(lockState{Mask: 3}, promptPIN)
80	require.Equal(t, "●●●", mask)
81	mask, _ = maskFor(lockState{Mask: 3}, promptFallback)
82	require.Equal(t, "•••_", mask)
83	mask, _ = maskFor(lockState{Mask: 512}, promptPIN)
84	require.Equal(t, maxPINMask, utf8.RuneCountInString(mask))
85	mask, _ = maskFor(lockState{Mask: 512}, promptPassword)
86	require.Equal(t, maxPasswordMask+1, utf8.RuneCountInString(mask))
87}
88
89func TestFieldsAreCenteredInTheBox(t *testing.T) {
90	for _, w := range []int{passwordW, pinW} {
91		outer := w + 2*fieldPadX + fieldEdge
92		require.Equal(t, boxContentW-outer-fieldMargin(w), fieldMargin(w), w)
93	}
94}
95
96func TestClockWakeStopsWhenDone(t *testing.T) {
97	done := make(chan struct{})
98	stopped := make(chan struct{})
99	go func() { defer close(stopped); clockWake(done, make(chan struct{})) }()
100	close(done)
101	<-stopped
102}

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 2

Tags 9

↑↓ moveenter openesc close