v0.33.1 9577fee

vim_page_interaction_test.go RawHistoryFolder

1package usecase
2
3import (
4	"context"
5	"errors"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9	"github.com/stretchr/testify/require"
10
11	"github.com/bnema/dumber/internal/application/dto"
12	portmocks "github.com/bnema/dumber/internal/application/port/mocks"
13)
14
15// interactiveWebView composes generated mocks so a WebView also exposes the
16// optional VimPageInteractor capability.
17type interactiveWebView struct {
18	*portmocks.MockWebView
19	*portmocks.MockVimPageInteractor
20}
21
22func newInteractiveWebView(t *testing.T) (*interactiveWebView, *portmocks.MockVimPageInteractor) {
23	t.Helper()
24	interactor := portmocks.NewMockVimPageInteractor(t)
25	return &interactiveWebView{MockWebView: portmocks.NewMockWebView(t), MockVimPageInteractor: interactor}, interactor
26}
27
28func TestVimNavigationUseCaseStartsPageInteractions(t *testing.T) {
29	tests := []struct {
30		action      string
31		want        dto.VimPageInteractionRequest
32		wantCapture bool
33	}{
34		{"hint-follow", dto.VimPageInteractionRequest{Kind: dto.VimPageHintFollow, HighlightColor: "#abc"}, true},
35		{"hint-follow-new", dto.VimPageInteractionRequest{Kind: dto.VimPageHintFollowNew, HighlightColor: "#abc"}, true},
36		{"hint-yank-url", dto.VimPageInteractionRequest{Kind: dto.VimPageHintYankURL, HighlightColor: "#abc"}, true},
37		{"visual", dto.VimPageInteractionRequest{Kind: dto.VimPageVisual, HighlightColor: "#abc"}, true},
38		{"yank-section", dto.VimPageInteractionRequest{
39			Kind: dto.VimPageYankObject, Object: dto.VimPageTextObjectSection, HighlightColor: "#abc",
40		}, false},
41		{"yank-paragraph", dto.VimPageInteractionRequest{
42			Kind: dto.VimPageYankObject, Object: dto.VimPageTextObjectParagraph, HighlightColor: "#abc",
43		}, false},
44		{"yank-code", dto.VimPageInteractionRequest{
45			Kind: dto.VimPageYankObject, Object: dto.VimPageTextObjectCode, HighlightColor: "#abc",
46		}, false},
47		{"yank-table", dto.VimPageInteractionRequest{
48			Kind: dto.VimPageYankObject, Object: dto.VimPageTextObjectTable, HighlightColor: "#abc",
49		}, false},
50		{"yank-list", dto.VimPageInteractionRequest{
51			Kind: dto.VimPageYankObject, Object: dto.VimPageTextObjectList, HighlightColor: "#abc",
52		}, false},
53	}
54	for _, tt := range tests {
55		t.Run(tt.action, func(t *testing.T) {
56			ctx := context.Background()
57			wv, interactor := newInteractiveWebView(t)
58			interactor.EXPECT().StartVimPageInteraction(ctx, tt.want).Return(nil).Once()
59
60			outcome, err := NewVimNavigationUseCase().Execute(ctx, wv, tt.action, 1, "#abc")
61
62			require.NoError(t, err)
63			assert.Equal(t, tt.wantCapture, outcome.CapturePageKeys)
64			if tt.wantCapture {
65				assert.Equal(t, dto.VimNavigationOutcome{
66					CapturePageKeys: true, PageKind: tt.want.Kind, PageMode: dto.VimPageModeHints,
67				}, outcome, "capturing interactions open on hints and name their kind for the label")
68			} else {
69				assert.Equal(t, dto.VimNavigationOutcome{}, outcome)
70			}
71		})
72	}
73}
74
75func TestVimNavigationUseCasePageInteractionErrorDoesNotCapture(t *testing.T) {
76	ctx := context.Background()
77	wv, interactor := newInteractiveWebView(t)
78	boom := errors.New("boom")
79	interactor.EXPECT().StartVimPageInteraction(ctx, dto.VimPageInteractionRequest{Kind: dto.VimPageVisual}).Return(boom).Once()
80
81	outcome, err := NewVimNavigationUseCase().Execute(ctx, wv, "visual", 1, "")
82
83	require.ErrorIs(t, err, boom)
84	require.ErrorContains(t, err, "vim navigation: start page interaction")
85	assert.False(t, outcome.CapturePageKeys)
86}
87
88func TestVimNavigationUseCasePageInteractionUnsupportedEngine(t *testing.T) {
89	uc := NewVimNavigationUseCase()
90	wv := portmocks.NewMockWebView(t)
91
92	outcome, err := uc.Execute(context.Background(), wv, "hint-follow", 1, "")
93	require.ErrorIs(t, err, errUnsupportedVimNavigationEngine)
94	assert.False(t, outcome.CapturePageKeys)
95
96	require.ErrorIs(t, uc.SendPageKey(context.Background(), wv, "a"), errUnsupportedVimNavigationEngine)
97	require.NoError(t, uc.CancelPageInteraction(context.Background(), wv))
98}
99
100func TestVimNavigationUseCaseForwardsPageKeysAndCancel(t *testing.T) {
101	ctx := context.Background()
102	wv, interactor := newInteractiveWebView(t)
103	interactor.EXPECT().SendVimPageKey(ctx, "J").Return(nil).Once()
104	interactor.EXPECT().CancelVimPageInteraction(ctx).Return(nil).Once()
105	uc := NewVimNavigationUseCase()
106
107	require.NoError(t, uc.SendPageKey(ctx, wv, "J"))
108	require.NoError(t, uc.CancelPageInteraction(ctx, wv))
109}
110
111func TestVimNavigationUseCaseWrapsPageKeyAndCancelErrors(t *testing.T) {
112	ctx := context.Background()
113	wv, interactor := newInteractiveWebView(t)
114	boom := errors.New("boom")
115	interactor.EXPECT().SendVimPageKey(ctx, "j").Return(boom).Once()
116	interactor.EXPECT().CancelVimPageInteraction(ctx).Return(boom).Once()
117	uc := NewVimNavigationUseCase()
118
119	err := uc.SendPageKey(ctx, wv, "j")
120	require.ErrorIs(t, err, boom)
121	require.ErrorContains(t, err, "vim navigation: send page key")
122	err = uc.CancelPageInteraction(ctx, wv)
123	require.ErrorIs(t, err, boom)
124	require.ErrorContains(t, err, "vim navigation: cancel page interaction")
125}

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