separator_test.go RawHistoryFolder

1package ui
2
3import (
4	"testing"
5
6	renderer "github.com/bnema/vev-vt"
7	"github.com/bnema/vev/internal/domain"
8)
9
10func TestDrawSeparator(t *testing.T) {
11	style := renderer.DefaultStyle()
12	style.Attrs = renderer.AttrDim
13	cases := []struct {
14		name        string
15		orientation SeparatorOrientation
16		rune        rune
17		rect        domain.Rect
18	}{
19		{"horizontal", SeparatorHorizontal, '─', domain.Rect{X: 1, Y: 2, Width: 3, Height: 1}},
20		{"vertical", SeparatorVertical, '│', domain.Rect{X: 2, Y: 1, Width: 1, Height: 3}},
21	}
22	for _, tc := range cases {
23		t.Run(tc.name, func(t *testing.T) {
24			f := renderer.NewFrame(6, 6)
25			DrawSeparator(f, tc.rect, tc.orientation, style)
26			for y := 0; y < 6; y++ {
27				for x := 0; x < 6; x++ {
28					want := renderer.BlankCell()
29					if (tc.orientation == SeparatorHorizontal && y == tc.rect.Y && x >= tc.rect.X && x < tc.rect.X+tc.rect.Width) ||
30						(tc.orientation == SeparatorVertical && x == tc.rect.X && y >= tc.rect.Y && y < tc.rect.Y+tc.rect.Height) {
31						want = renderer.Cell{Rune: tc.rune, Style: style}
32					}
33					if !f.At(x, y).Equal(want) {
34						t.Fatalf("cell %d,%d = %#v, want %#v", x, y, f.At(x, y), want)
35					}
36				}
37			}
38		})
39	}
40}
41
42func TestDrawSeparatorClipsAndRejectsInvalid(t *testing.T) {
43	style := renderer.DefaultStyle()
44	f := renderer.NewFrame(3, 3)
45	for y := range f.Height {
46		for x := range f.Width {
47			f.Set(x, y, renderer.Cell{Rune: 'x', Style: style})
48		}
49	}
50	DrawSeparator(f, domain.Rect{X: -2, Y: 1, Width: 8, Height: 1}, SeparatorHorizontal, style)
51	for x := 0; x < 3; x++ {
52		if f.At(x, 1).Rune != '─' {
53			t.Fatalf("clipping failed at %d", x)
54		}
55	}
56	before := f.Clone()
57	for _, rect := range []domain.Rect{{Width: 0, Height: 1}, {Width: 1, Height: 0}} {
58		DrawSeparator(f, rect, SeparatorHorizontal, style)
59	}
60	DrawSeparator(f, domain.Rect{Width: 1, Height: 1}, SeparatorOrientation(99), style)
61	if !framesEqual(f, before) {
62		t.Fatal("invalid rectangles/orientation changed frame")
63	}
64}
65
66func TestDrawSeparatorInvalidFrame(t *testing.T) {
67	f := renderer.Frame{Width: 2, Height: 2}
68	DrawSeparator(f, domain.Rect{Width: 2, Height: 1}, SeparatorHorizontal, renderer.DefaultStyle())
69}
70
71func framesEqual(a, b renderer.Frame) bool {
72	if a.Width != b.Width || a.Height != b.Height {
73		return false
74	}
75	for y := 0; y < a.Height; y++ {
76		for x := 0; x < a.Width; x++ {
77			if !a.At(x, y).Equal(b.At(x, y)) {
78				return false
79			}
80		}
81	}
82	return true
83}

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