v0.12.1 7328df5

ci_actions_test.go RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package usecase
4
5import (
6	"strings"
7	"testing"
8
9	"github.com/stretchr/testify/assert"
10
11	"bnema.dev/ermite/internal/ports"
12)
13
14func TestParseActionRef(t *testing.T) {
15	t.Parallel()
16	tests := []struct {
17		uses                         string
18		owner, repo, path, ref, fail string
19	}{
20		{uses: "actions/checkout@v4", owner: "actions", repo: "checkout", ref: "v4"},
21		{uses: "actions/cache/save@v4", owner: "actions", repo: "cache", path: "save", ref: "v4"},
22		{uses: "o/r/a/b/c@feature/x", owner: "o", repo: "r", path: "a/b/c", ref: "feature/x"},
23		{uses: "o\\r\\p@v1", owner: "o", repo: "r", path: "p", ref: "v1"}, // GitHub splits on both separators
24		{uses: "o//r@v1", owner: "o", repo: "r", ref: "v1"},               // and ignores empty segments
25		{uses: "noslash@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'noslash@v1'"},
26		{uses: "o/r", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'o/r'"},
27		{uses: "o/r@", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'o/r@'"},
28		{uses: "@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual '@v1'"},
29		{uses: "o/r@a@b", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'o/r@a@b'"},
30		{uses: "o/r/../x@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'o/r/../x@v1'"},
31		{uses: "o/r/./x@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'o/r/./x@v1'"},
32		{uses: "a/...@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'a/...@v1'"},
33		{uses: "../..@" + strings.Repeat("a", 40), fail: "Expected format {org}/{repo}[/path]@ref. Actual '../..@" + strings.Repeat("a", 40) + "'"},
34		{uses: "a%2f/b@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'a%2f/b@v1'"},
35		{uses: "a/b?x@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'a/b?x@v1'"},
36		{uses: "a/b#x@v1", fail: "Expected format {org}/{repo}[/path]@ref. Actual 'a/b#x@v1'"},
37		{uses: "-a/_b.c@v1", owner: "-a", repo: "_b.c", ref: "v1"},
38		{uses: "", fail: "Expected format {org}/{repo}[/path]@ref. Actual ''"},
39	}
40	for _, tt := range tests {
41		t.Run(tt.uses, func(t *testing.T) {
42			t.Parallel()
43			a := parseActionRef(tt.uses)
44			assert.Equal(t, tt.fail, a.err)
45			if tt.fail == "" {
46				assert.Equal(t, []string{tt.owner, tt.repo, tt.path, tt.ref}, []string{a.owner, a.repo, a.path, a.ref})
47			}
48		})
49	}
50	assert.Equal(t, parseActionRef("Actions/Checkout@v4").key(), parseActionRef("actions/checkout@v4").key(), "names are case-insensitive")
51	assert.NotEqual(t, parseActionRef("a/b@V4").key(), parseActionRef("a/b@v4").key(), "refs are not")
52}
53
54func TestPickRemoteRef(t *testing.T) {
55	t.Parallel()
56	refs := []ports.RemoteRef{
57		{Name: "refs/heads/v1", ID: "b1"},
58		{Name: "refs/tags/v1", ID: "t1"},
59		{Name: "refs/tags/v2", ID: "tobj", Peeled: "c2"},
60		{Name: "refs/heads/main", ID: "m"},
61	}
62	assert.Equal(t, "t1", pickRemoteRef(refs, "v1"), "a tag before a branch")
63	assert.Equal(t, "c2", pickRemoteRef(refs, "v2"), "the peeled commit of an annotated tag")
64	assert.Equal(t, "m", pickRemoteRef(refs, "main"))
65	assert.Empty(t, pickRemoteRef(refs, "refs/heads/main"), "short names only")
66	assert.Empty(t, pickRemoteRef(refs, "nope"))
67	assert.Empty(t, pickRemoteRef(nil, "v1"))
68}

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 45

↑↓ moveenter openesc close