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}