v0.33.2 57f21d6

browse_args_test.go RawHistoryFolder

1package cmd
2
3import (
4	"io"
5	"testing"
6
7	"github.com/spf13/cobra"
8	"github.com/stretchr/testify/require"
9)
10
11func TestParseBrowseArgs(t *testing.T) {
12	for _, tt := range []struct {
13		name, url, instance string
14		args                []string
15		ok                  bool
16	}{
17		{"normal", "", "", nil, true},
18		{"named", "", "work", []string{"--instance", "work"}, true},
19		{"url after", "https://example.com", "work", []string{"--instance", "work", "https://example.com"}, true},
20		{"url before", "https://example.com", "work", []string{"https://example.com", "--instance", "work"}, true},
21		{"missing name", "", "", []string{"--instance"}, false},
22		{"empty equals", "", "", []string{"--instance="}, false},
23		{"empty separate", "", "", []string{"--instance", ""}, false},
24		{"flag-like name", "", "", []string{"--instance", "--ephemeral"}, false},
25		{"flag-like profile", "", "", []string{"--profile", "--ephemeral"}, false},
26		{"help", "", "", []string{"--help"}, false},
27		{"unknown", "", "", []string{"--unknown"}, false},
28		{"extra", "", "", []string{"--instance", "work", "a", "b"}, false},
29	} {
30		t.Run(tt.name, func(t *testing.T) {
31			url, instance, ok := ParseBrowseArgs(tt.args)
32			require.Equal(t, tt.url, url)
33			require.Equal(t, tt.instance, instance)
34			require.Equal(t, tt.ok, ok)
35		})
36	}
37	require.NotNil(t, browseCmd.Flags().Lookup("instance"))
38	require.NotNil(t, browseCmd.Flags().Lookup("profile"))
39	require.NotNil(t, browseCmd.Flags().Lookup("ephemeral"))
40}
41
42func TestParseBrowseLaunchArgsProfiles(t *testing.T) {
43	parsed, ok := ParseBrowseLaunchArgs([]string{"--instance", "scratch", "https://example.com"})
44	require.True(t, ok)
45	require.Equal(t, BrowseArgs{URL: "https://example.com", Instance: "scratch"}, parsed)
46
47	parsed, ok = ParseBrowseLaunchArgs([]string{"--instance", "work", "--profile", "work"})
48	require.True(t, ok)
49	require.Equal(t, BrowseArgs{Instance: "work", Profile: "work"}, parsed)
50
51	parsed, ok = ParseBrowseLaunchArgs([]string{"--instance", "private", "--ephemeral"})
52	require.True(t, ok)
53	require.Equal(t, BrowseArgs{Instance: "private", Ephemeral: true}, parsed)
54
55	_, ok = ParseBrowseLaunchArgs([]string{"--profile", "work", "--ephemeral"})
56	require.False(t, ok)
57
58	_, ok = ParseBrowseLaunchArgs([]string{"--profile", "--ephemeral"})
59	require.False(t, ok, "a flag-like profile must not be treated as a profile name")
60
61	_, ok = ParseBrowseLaunchArgs([]string{"--instance", "--ephemeral"})
62	require.False(t, ok, "a flag-like instance must not be treated as an instance name")
63}
64
65// browsePreRunEError exercises the real browse PreRunE through Cobra flag
66// parsing, which is the fallback path main takes when early launch parsing
67// rejects the arguments.
68func browsePreRunEError(t *testing.T, args []string) error {
69	t.Helper()
70	cmd := &cobra.Command{
71		Use:           "browse",
72		Args:          cobra.MaximumNArgs(1),
73		SilenceErrors: true,
74		SilenceUsage:  true,
75		PreRunE:       browseCmd.PreRunE,
76		Run:           func(*cobra.Command, []string) {},
77	}
78	cmd.Flags().AddFlagSet(browseFlags())
79	cmd.SetOut(io.Discard)
80	cmd.SetErr(io.Discard)
81	cmd.SetArgs(args)
82	return cmd.Execute()
83}
84
85func TestBrowseCommandPreRunERejectsFlagLikeValues(t *testing.T) {
86	require.Error(t, browsePreRunEError(t, []string{"--instance", "--ephemeral"}))
87	require.Error(t, browsePreRunEError(t, []string{"--profile", "--ephemeral"}))
88	require.Error(t, browsePreRunEError(t, []string{"--instance", ""}))
89	require.Error(t, browsePreRunEError(t, []string{"--profile", ""}))
90
91	require.NoError(t, browsePreRunEError(t, []string{"--instance", "work"}))
92	require.NoError(t, browsePreRunEError(t, []string{"--instance", "private", "--ephemeral"}))
93	require.NoError(t, browsePreRunEError(t, []string{"--profile", "work"}))
94}

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 8

Tags 42

↑↓ moveenter openesc close