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 "github.com/bnema/dumber/internal/domain/entity"
10 "github.com/stretchr/testify/require"
11)
12
13type fakeClipboard struct {
14 writeTextCalls int
15 writeImageCalls int
16 text string
17 image entity.ImageData
18}
19
20func (f *fakeClipboard) WriteText(_ context.Context, text string) error {
21 f.writeTextCalls++
22 f.text = text
23 return nil
24}
25
26func (f *fakeClipboard) WriteImage(_ context.Context, image entity.ImageData) error {
27 f.writeImageCalls++
28 f.image = image
29 return nil
30}
31
32func (*fakeClipboard) ReadText(context.Context) (string, error) { return "", nil }
33func (*fakeClipboard) Clear(context.Context) error { return nil }
34func (*fakeClipboard) HasText(context.Context) (bool, error) { return false, nil }
35
36type fakeImageResolver struct {
37 resolveCalls int
38 uri string
39 image entity.ImageData
40 err error
41}
42
43func (f *fakeImageResolver) ResolveImageData(_ context.Context, uri string) (entity.ImageData, error) {
44 f.resolveCalls++
45 f.uri = uri
46 return f.image, f.err
47}
48
49type fakeResolvedImageSaver struct {
50 saveCalls int
51 image entity.ImageData
52 menuContext port.MenuContext
53 err error
54}
55
56func (f *fakeResolvedImageSaver) SaveResolvedImage(_ context.Context, image entity.ImageData, menuContext port.MenuContext) error {
57 f.saveCalls++
58 f.image = image
59 f.menuContext = menuContext
60 return f.err
61}
62
63type fakeMenuActionDelegator struct {
64 delegateCalls int
65 action port.MenuAction
66 menuContext port.MenuContext
67 err error
68}
69
70func (f *fakeMenuActionDelegator) DelegateMenuAction(_ context.Context, action port.MenuAction, menuContext port.MenuContext) error {
71 f.delegateCalls++
72 f.action = action
73 f.menuContext = menuContext
74 return f.err
75}
76
77func TestExecuteContextMenuActionUseCase_CopyImageFailsWithoutResolvedData(t *testing.T) {
78 clipboard := &fakeClipboard{}
79 resolver := &fakeImageResolver{}
80 saver := &fakeResolvedImageSaver{}
81 delegator := &fakeMenuActionDelegator{}
82 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
83
84 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
85 Action: port.MenuActionCopyImage,
86 Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
87 })
88
89 require.Error(t, err)
90 require.Contains(t, err.Error(), "copy image:")
91 require.Contains(t, err.Error(), "image data not available")
92 require.Zero(t, clipboard.writeImageCalls)
93 require.Zero(t, saver.saveCalls)
94 require.Zero(t, delegator.delegateCalls)
95}
96
97func TestExecuteContextMenuActionUseCase_CopyImageWritesResolvedImage(t *testing.T) {
98 clipboard := &fakeClipboard{}
99 resolver := &fakeImageResolver{image: entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"}}
100 saver := &fakeResolvedImageSaver{}
101 delegator := &fakeMenuActionDelegator{}
102 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
103
104 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
105 Action: port.MenuActionCopyImage,
106 Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
107 })
108
109 require.NoError(t, err)
110 require.Equal(t, 1, resolver.resolveCalls)
111 require.Equal(t, "https://example.com/image.png", resolver.uri)
112 require.Equal(t, 1, clipboard.writeImageCalls)
113 require.Equal(t, entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"}, clipboard.image)
114 require.Zero(t, saver.saveCalls)
115 require.Zero(t, delegator.delegateCalls)
116}
117
118func TestExecuteContextMenuActionUseCase_CopyImageFailsFastWithoutClipboard(t *testing.T) {
119 resolver := &fakeImageResolver{}
120 saver := &fakeResolvedImageSaver{}
121 delegator := &fakeMenuActionDelegator{}
122 uc := NewExecuteContextMenuActionUseCase(nil, resolver, saver, delegator)
123
124 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
125 Action: port.MenuActionCopyImage,
126 Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
127 })
128
129 require.Error(t, err)
130 require.Contains(t, err.Error(), "clipboard not available")
131 require.Zero(t, resolver.resolveCalls)
132 require.Zero(t, saver.saveCalls)
133 require.Zero(t, delegator.delegateCalls)
134}
135
136func TestExecuteContextMenuActionUseCase_SaveImageDelegatesResolvedImage(t *testing.T) {
137 clipboard := &fakeClipboard{}
138 resolver := &fakeImageResolver{image: entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"}}
139 saver := &fakeResolvedImageSaver{}
140 delegator := &fakeMenuActionDelegator{}
141 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
142
143 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
144 Action: port.MenuActionSaveImage,
145 Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
146 })
147
148 require.NoError(t, err)
149 require.Equal(t, 1, resolver.resolveCalls)
150 require.Equal(t, "https://example.com/image.png", resolver.uri)
151 require.Equal(t, 1, saver.saveCalls)
152 require.Equal(t, entity.ImageData{Bytes: []byte{1, 2, 3}, MimeType: "image/png"}, saver.image)
153 require.Equal(t, port.MenuContext{ImageURI: "https://example.com/image.png"}, saver.menuContext)
154 require.Zero(t, clipboard.writeTextCalls)
155 require.Zero(t, clipboard.writeImageCalls)
156 require.Zero(t, delegator.delegateCalls)
157}
158
159func TestExecuteContextMenuActionUseCase_SaveImageFailsFastWithoutSaver(t *testing.T) {
160 clipboard := &fakeClipboard{}
161 resolver := &fakeImageResolver{}
162 delegator := &fakeMenuActionDelegator{}
163 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, nil, delegator)
164
165 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
166 Action: port.MenuActionSaveImage,
167 Context: port.MenuContext{ImageURI: "https://example.com/image.png"},
168 })
169
170 require.Error(t, err)
171 require.Contains(t, err.Error(), "image saver not available")
172 require.Zero(t, resolver.resolveCalls)
173 require.Zero(t, delegator.delegateCalls)
174}
175
176func TestExecuteContextMenuActionUseCase_DelegatesInspect(t *testing.T) {
177 clipboard := &fakeClipboard{}
178 resolver := &fakeImageResolver{}
179 saver := &fakeResolvedImageSaver{}
180 delegator := &fakeMenuActionDelegator{}
181 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
182
183 menuContext := port.MenuContext{
184 PageURI: "https://example.com",
185 X: 17,
186 Y: 42,
187 }
188 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
189 Action: port.MenuActionInspectElement,
190 Context: menuContext,
191 })
192
193 require.NoError(t, err)
194 require.Equal(t, 1, delegator.delegateCalls)
195 require.Equal(t, port.MenuActionInspectElement, delegator.action)
196 require.Equal(t, menuContext, delegator.menuContext)
197 require.Zero(t, clipboard.writeTextCalls)
198 require.Zero(t, clipboard.writeImageCalls)
199 require.Zero(t, resolver.resolveCalls)
200 require.Zero(t, saver.saveCalls)
201}
202
203func TestExecuteContextMenuActionUseCase_WrapsDelegateErrors(t *testing.T) {
204 clipboard := &fakeClipboard{}
205 resolver := &fakeImageResolver{}
206 saver := &fakeResolvedImageSaver{}
207 delegator := &fakeMenuActionDelegator{err: errors.New("boom")}
208 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
209
210 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
211 Action: port.MenuActionCopySelection,
212 Context: port.MenuContext{HasSelection: true},
213 })
214
215 require.Error(t, err)
216 require.Contains(t, err.Error(), "delegate action copy_selection:")
217 require.Contains(t, err.Error(), "boom")
218}
219
220func TestExecuteContextMenuActionUseCase_CopySelectionWritesSelectedText(t *testing.T) {
221 clipboard := &fakeClipboard{}
222 resolver := &fakeImageResolver{}
223 saver := &fakeResolvedImageSaver{}
224 delegator := &fakeMenuActionDelegator{}
225 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
226
227 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
228 Action: port.MenuActionCopySelection,
229 Context: port.MenuContext{
230 HasSelection: true,
231 SelectionText: "selected text",
232 },
233 })
234
235 require.NoError(t, err)
236 require.Equal(t, 1, clipboard.writeTextCalls)
237 require.Equal(t, "selected text", clipboard.text)
238 require.Zero(t, delegator.delegateCalls)
239}
240
241func TestExecuteContextMenuActionUseCase_CopySelectionFallsBackToDelegatorWhenClipboardMissing(t *testing.T) {
242 resolver := &fakeImageResolver{}
243 saver := &fakeResolvedImageSaver{}
244 delegator := &fakeMenuActionDelegator{}
245 uc := NewExecuteContextMenuActionUseCase(nil, resolver, saver, delegator)
246
247 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
248 Action: port.MenuActionCopySelection,
249 Context: port.MenuContext{
250 HasSelection: true,
251 SelectionText: "selected text",
252 },
253 })
254
255 require.NoError(t, err)
256 require.Equal(t, 1, delegator.delegateCalls)
257 require.Equal(t, port.MenuActionCopySelection, delegator.action)
258}
259
260func TestExecuteContextMenuActionUseCase_CopyLinkWritesText(t *testing.T) {
261 clipboard := &fakeClipboard{}
262 resolver := &fakeImageResolver{}
263 saver := &fakeResolvedImageSaver{}
264 delegator := &fakeMenuActionDelegator{}
265 uc := NewExecuteContextMenuActionUseCase(clipboard, resolver, saver, delegator)
266
267 err := uc.Execute(context.Background(), ExecuteContextMenuActionInput{
268 Action: port.MenuActionCopyLink,
269 Context: port.MenuContext{LinkURI: "https://example.com/link"},
270 })
271
272 require.NoError(t, err)
273 require.Equal(t, 1, clipboard.writeTextCalls)
274 require.Equal(t, "https://example.com/link", clipboard.text)
275 require.Zero(t, clipboard.writeImageCalls)
276 require.Zero(t, resolver.resolveCalls)
277 require.Zero(t, saver.saveCalls)
278 require.Zero(t, delegator.delegateCalls)
279}