execute_context_menu_action_test.go RawHistoryFolder

1package usecase
2
3import (
4	"context"
5	"errors"
6	"testing"
7
8	"github.com/bnema/dumber/internal/application/port"
9	portmocks "github.com/bnema/dumber/internal/application/port/mocks"
10	"github.com/bnema/dumber/internal/domain/entity"
11	"github.com/stretchr/testify/mock"
12	"github.com/stretchr/testify/require"
13)
14
15func TestExecuteContextMenuActionUseCase_CopyImageFailsWithoutResolvedData(t *testing.T) {
16	clipboard := portmocks.NewMockClipboard(t)
17	resolver := portmocks.NewMockImageDataResolver(t)
18	saver := portmocks.NewMockResolvedImageSaver(t)
19	delegator := portmocks.NewMockMenuActionDelegator(t)
20	resolver.EXPECT().
21		ResolveImageData(mock.Anything, "https://example.com/image.png").
22		Return(entity.ImageData{}, nil).
23		Once()
24	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
25
26	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
27		Action:  port.MenuActionCopyImage,
28		Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
29	})
30
31	require.Error(t, err)
32	require.Contains(t, err.Error(), "copy image:")
33	require.Contains(t, err.Error(), "image data not available")
34}
35
36func TestExecuteContextMenuActionUseCase_CopyImageWritesResolvedImage(t *testing.T) {
37	clipboard := portmocks.NewMockClipboard(t)
38	resolver := portmocks.NewMockImageDataResolver(t)
39	saver := portmocks.NewMockResolvedImageSaver(t)
40	delegator := portmocks.NewMockMenuActionDelegator(t)
41	image := entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"}
42	resolver.EXPECT().
43		ResolveImageData(mock.Anything, "https://example.com/image.png").
44		Return(image, nil).
45		Once()
46	clipboard.EXPECT().
47		WriteImage(mock.Anything, image).
48		Return(nil).
49		Once()
50	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
51
52	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
53		Action:  port.MenuActionCopyImage,
54		Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
55	})
56
57	require.NoError(t, err)
58}
59
60func TestExecuteContextMenuActionUseCase_CopyImageFailsFastWithoutClipboard(t *testing.T) {
61	resolver := portmocks.NewMockImageDataResolver(t)
62	saver := portmocks.NewMockResolvedImageSaver(t)
63	delegator := portmocks.NewMockMenuActionDelegator(t)
64	uc := NewExecuteContextMenuActionUseCase(nil, resolver, saver, delegator)
65
66	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
67		Action:  port.MenuActionCopyImage,
68		Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
69	})
70
71	require.Error(t, err)
72	require.Contains(t, err.Error(), "clipboard not available")
73}
74
75func TestExecuteContextMenuActionUseCase_SaveImageDelegatesResolvedImage(t *testing.T) {
76	clipboard := portmocks.NewMockClipboard(t)
77	resolver := portmocks.NewMockImageDataResolver(t)
78	saver := portmocks.NewMockResolvedImageSaver(t)
79	delegator := portmocks.NewMockMenuActionDelegator(t)
80	image := entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"}
81	menuContext := port.MenuContext{ImageURI: "https://example.com/image.png"}
82	resolver.EXPECT().
83		ResolveImageData(mock.Anything, menuContext.ImageURI).
84		Return(image, nil).
85		Once()
86	saver.EXPECT().
87		SaveResolvedImage(mock.Anything, image, menuContext).
88		Return(nil).
89		Once()
90	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
91
92	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
93		Action:  port.MenuActionSaveImage,
94		Context: menuContext,
95	})
96
97	require.NoError(t, err)
98}
99
100func TestExecuteContextMenuActionUseCase_SaveImageFailsFastWithoutSaver(t *testing.T) {
101	clipboard := portmocks.NewMockClipboard(t)
102	resolver := portmocks.NewMockImageDataResolver(t)
103	delegator := portmocks.NewMockMenuActionDelegator(t)
104	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, nil, delegator)
105
106	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
107		Action:  port.MenuActionSaveImage,
108		Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
109	})
110
111	require.Error(t, err)
112	require.Contains(t, err.Error(), "image saver not available")
113}
114
115func TestExecuteContextMenuActionUseCase_DelegatesInspect(t *testing.T) {
116	menuContext := port.MenuContext{
117		PageURI: "https://example.com",
118		X:       17,
119		Y:       42,
120	}
121	clipboard := portmocks.NewMockClipboard(t)
122	resolver := portmocks.NewMockImageDataResolver(t)
123	saver := portmocks.NewMockResolvedImageSaver(t)
124	delegator := portmocks.NewMockMenuActionDelegator(t)
125	delegator.EXPECT().
126		DelegateMenuAction(mock.Anything, port.MenuActionInspectElement, menuContext).
127		Return(nil).
128		Once()
129	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
130
131	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
132		Action:  port.MenuActionInspectElement,
133		Context: menuContext,
134	})
135
136	require.NoError(t, err)
137}
138
139func TestExecuteContextMenuActionUseCase_WrapsDelegateErrors(t *testing.T) {
140	clipboard := portmocks.NewMockClipboard(t)
141	resolver := portmocks.NewMockImageDataResolver(t)
142	saver := portmocks.NewMockResolvedImageSaver(t)
143	delegator := portmocks.NewMockMenuActionDelegator(t)
144	delegator.EXPECT().
145		DelegateMenuAction(mock.Anything, port.MenuActionCopySelection, port.MenuContext{HasSelection: true}).
146		Return(errors.New("boom")).
147		Once()
148	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
149
150	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
151		Action:  port.MenuActionCopySelection,
152		Context: port.MenuContext{HasSelection: true},
153	})
154
155	require.Error(t, err)
156	require.Contains(t, err.Error(), "delegate action copy_selection:")
157	require.Contains(t, err.Error(), "boom")
158}
159
160func TestExecuteContextMenuActionUseCase_CopySelectionWritesSelectedText(t *testing.T) {
161	clipboard := portmocks.NewMockClipboard(t)
162	resolver := portmocks.NewMockImageDataResolver(t)
163	saver := portmocks.NewMockResolvedImageSaver(t)
164	delegator := portmocks.NewMockMenuActionDelegator(t)
165	clipboard.EXPECT().
166		WriteText(mock.Anything, "selected text").
167		Return(nil).
168		Once()
169	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
170
171	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
172		Action: port.MenuActionCopySelection,
173		Context: port.MenuContext{
174			HasSelection:  true,
175			SelectionText: "selected text",
176		},
177	})
178
179	require.NoError(t, err)
180}
181
182func TestExecuteContextMenuActionUseCase_CopySelectionFallsBackToDelegatorWhenClipboardMissing(t *testing.T) {
183	resolver := portmocks.NewMockImageDataResolver(t)
184	saver := portmocks.NewMockResolvedImageSaver(t)
185	delegator := portmocks.NewMockMenuActionDelegator(t)
186	delegator.EXPECT().
187		DelegateMenuAction(mock.Anything, port.MenuActionCopySelection, port.MenuContext{
188			HasSelection:  true,
189			SelectionText: "selected text",
190		}).
191		Return(nil).
192		Once()
193	uc := NewExecuteContextMenuActionUseCase(nil, resolver, saver, delegator)
194
195	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
196		Action: port.MenuActionCopySelection,
197		Context: port.MenuContext{
198			HasSelection:  true,
199			SelectionText: "selected text",
200		},
201	})
202
203	require.NoError(t, err)
204}
205
206func TestExecuteContextMenuActionUseCase_CopyLinkWritesText(t *testing.T) {
207	clipboard := portmocks.NewMockClipboard(t)
208	resolver := portmocks.NewMockImageDataResolver(t)
209	saver := portmocks.NewMockResolvedImageSaver(t)
210	delegator := portmocks.NewMockMenuActionDelegator(t)
211	clipboard.EXPECT().
212		WriteText(mock.Anything, "https://example.com/link").
213		Return(nil).
214		Once()
215	uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
216
217	err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
218		Action:  port.MenuActionCopyLink,
219		Context: port.MenuContext{LinkURI: "https://example.com/link"},
220	})
221
222	require.NoError(t, err)
223}

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