1package cef
2
3import (
4 "context"
5 "testing"
6
7 purecef "github.com/bnema/purego-cef/cef"
8 "github.com/stretchr/testify/require"
9
10 "github.com/bnema/dumber/internal/application/port"
11)
12
13type stubContextMenuParams struct {
14 x, y int32
15 pageURL string
16 linkURL string
17 sourceURL string
18 selection string
19 editable bool
20 imageContent bool
21}
22
23func (p stubContextMenuParams) GetXcoord() int32 { return p.x }
24func (p stubContextMenuParams) GetYcoord() int32 { return p.y }
25func (p stubContextMenuParams) GetTypeFlags() purecef.ContextMenuTypeFlags { return 0 }
26func (p stubContextMenuParams) GetLinkURL() string { return p.linkURL }
27func (p stubContextMenuParams) GetUnfilteredLinkURL() string { return p.linkURL }
28func (p stubContextMenuParams) GetSourceURL() string { return p.sourceURL }
29func (p stubContextMenuParams) HasImageContents() bool { return p.imageContent }
30func (p stubContextMenuParams) GetTitleText() string { return "" }
31func (p stubContextMenuParams) GetPageURL() string { return p.pageURL }
32func (p stubContextMenuParams) GetFrameURL() string { return "" }
33func (p stubContextMenuParams) GetFrameCharset() string { return "" }
34func (p stubContextMenuParams) GetMediaType() purecef.ContextMenuMediaType { return 0 }
35func (p stubContextMenuParams) GetMediaStateFlags() purecef.ContextMenuMediaStateFlags {
36 return 0
37}
38func (p stubContextMenuParams) GetSelectionText() string { return p.selection }
39func (p stubContextMenuParams) GetMisspelledWord() string { return "" }
40func (p stubContextMenuParams) GetDictionarySuggestions(purecef.StringList) int32 { return 0 }
41func (p stubContextMenuParams) IsEditable() bool { return p.editable }
42func (p stubContextMenuParams) IsSpellCheckEnabled() bool { return false }
43func (p stubContextMenuParams) GetEditStateFlags() purecef.ContextMenuEditStateFlags {
44 return 0
45}
46func (p stubContextMenuParams) IsCustomMenu() bool { return false }
47
48func TestBuildMenuContextFromCEFParams(t *testing.T) {
49 wv := &WebView{}
50 wv.updateURI("https://example.com/page")
51 wv.updateLoadState(false, true, false)
52
53 ctx := buildMenuContext(wv, stubContextMenuParams{
54 x: 17,
55 y: 42,
56 pageURL: "https://example.com/page",
57 linkURL: "https://example.com/link",
58 sourceURL: "https://example.com/image.png",
59 selection: "selected text",
60 editable: true,
61 imageContent: true,
62 })
63
64 require.Equal(t, "https://example.com/page", ctx.PageURI)
65 require.Equal(t, "https://example.com/link", ctx.LinkURI)
66 require.Equal(t, "https://example.com/image.png", ctx.ImageURI)
67 require.Equal(t, "selected text", ctx.SelectionText)
68 require.True(t, ctx.HasSelection)
69 require.True(t, ctx.IsEditable)
70 require.True(t, ctx.CanGoBack)
71 require.False(t, ctx.CanGoForward)
72 require.Equal(t, 17, ctx.X)
73 require.Equal(t, 42, ctx.Y)
74}
75
76type stubRunContextMenuCallback struct {
77 contCalls int
78 cancelCalls int
79 commandID int32
80}
81
82type stubContextMenuExecutor struct {
83 executeCalls int
84 action port.MenuAction
85 menuContext port.MenuContext
86 err error
87}
88
89func (s *stubContextMenuExecutor) ExecuteMenuAction(_ context.Context, action port.MenuAction, menuContext port.MenuContext) error {
90 s.executeCalls++
91 s.action = action
92 s.menuContext = menuContext
93 return s.err
94}
95
96func (c *stubRunContextMenuCallback) Cont(commandID int32, _ purecef.EventFlags) {
97 c.contCalls++
98 c.commandID = commandID
99}
100
101func (c *stubRunContextMenuCallback) Cancel() {
102 c.cancelCalls++
103}
104
105func TestContextMenuSelectionCancelsWhenCEFCommandMissing(t *testing.T) {
106 callback := &stubRunContextMenuCallback{}
107
108 dispatchContextMenuSelection(context.Background(), nil, callback, nil, map[port.MenuAction]int32{
109 port.MenuActionReload: 102,
110 }, port.MenuItem{Action: port.MenuActionInspectElement, Label: "Inspect Element"}, port.MenuContext{})
111
112 require.Zero(t, callback.contCalls)
113 require.Equal(t, 1, callback.cancelCalls)
114}
115
116func TestContextMenuSelectionContinuesWhenCEFCommandPresent(t *testing.T) {
117 callback := &stubRunContextMenuCallback{}
118
119 dispatchContextMenuSelection(context.Background(), nil, callback, nil, map[port.MenuAction]int32{
120 port.MenuActionInspectElement: 204,
121 }, port.MenuItem{Action: port.MenuActionInspectElement, Label: "Inspect Element"}, port.MenuContext{})
122
123 require.Equal(t, 1, callback.contCalls)
124 require.Zero(t, callback.cancelCalls)
125 require.Equal(t, int32(204), callback.commandID)
126}
127
128func TestContextMenuSelectionExecutesDirectActionWhenExecutorAvailable(t *testing.T) {
129 callback := &stubRunContextMenuCallback{}
130 executor := &stubContextMenuExecutor{}
131 menuContext := port.MenuContext{PageURI: "https://example.com"}
132
133 dispatchContextMenuSelection(
134 context.Background(),
135 executor,
136 callback,
137 nil,
138 map[port.MenuAction]int32{port.MenuActionReload: 102},
139 port.MenuItem{Action: port.MenuActionReload, Label: "Reload"},
140 menuContext,
141 )
142
143 require.Equal(t, 1, executor.executeCalls)
144 require.Equal(t, port.MenuActionReload, executor.action)
145 require.Equal(t, menuContext, executor.menuContext)
146 require.Zero(t, callback.contCalls)
147 require.Zero(t, callback.commandID)
148 require.Equal(t, 1, callback.cancelCalls)
149}
150
151func TestContextMenuSelectionExecutesCopyImageDirectlyWhenExecutorAvailable(t *testing.T) {
152 callback := &stubRunContextMenuCallback{}
153 executor := &stubContextMenuExecutor{}
154 menuContext := port.MenuContext{ImageURI: "https://example.com/image.png"}
155
156 dispatchContextMenuSelection(
157 context.Background(),
158 executor,
159 callback,
160 nil,
161 map[port.MenuAction]int32{},
162 port.MenuItem{Action: port.MenuActionCopyImage, Label: "Copy Image"},
163 menuContext,
164 )
165
166 require.Equal(t, 1, executor.executeCalls)
167 require.Equal(t, port.MenuActionCopyImage, executor.action)
168 require.Equal(t, menuContext, executor.menuContext)
169 require.Zero(t, callback.contCalls)
170 require.Zero(t, callback.commandID)
171 require.Equal(t, 1, callback.cancelCalls)
172}
173
174func TestContextMenuSelectionExecutesCopySelectionDirectlyWhenSelectionTextAvailable(t *testing.T) {
175 callback := &stubRunContextMenuCallback{}
176 executor := &stubContextMenuExecutor{}
177 var copiedLens []int
178 menuContext := port.MenuContext{SelectionText: "selected text", HasSelection: true}
179
180 dispatchContextMenuSelection(
181 context.Background(),
182 executor,
183 callback,
184 func(text string) { copiedLens = append(copiedLens, len(text)) },
185 map[port.MenuAction]int32{port.MenuActionCopySelection: 333},
186 port.MenuItem{Action: port.MenuActionCopySelection, Label: "Copy"},
187 menuContext,
188 )
189
190 require.Equal(t, 1, executor.executeCalls)
191 require.Equal(t, port.MenuActionCopySelection, executor.action)
192 require.Equal(t, menuContext, executor.menuContext)
193 require.Zero(t, callback.contCalls)
194 require.Zero(t, callback.commandID)
195 require.Equal(t, 1, callback.cancelCalls)
196 require.Equal(t, []int{len("selected text")}, copiedLens)
197}
198
199func TestContextMenuSelectionFallsBackToNativeCopySelectionWhenTextUnavailable(t *testing.T) {
200 callback := &stubRunContextMenuCallback{}
201 executor := &stubContextMenuExecutor{}
202
203 dispatchContextMenuSelection(
204 context.Background(),
205 executor,
206 callback,
207 nil,
208 map[port.MenuAction]int32{port.MenuActionCopySelection: 333},
209 port.MenuItem{Action: port.MenuActionCopySelection, Label: "Copy"},
210 port.MenuContext{HasSelection: true},
211 )
212
213 require.Equal(t, 1, callback.contCalls)
214 require.Zero(t, callback.cancelCalls)
215 require.Equal(t, int32(333), callback.commandID)
216 require.Zero(t, executor.executeCalls)
217}
218
219func TestContextMenuAnchorPositionScalesCEFCoordinatesForDeviceBacking(t *testing.T) {
220 x, y := contextMenuAnchorPosition(stubContextMenuParams{x: 320, y: 180}, 1.25, 1)
221
222 require.Equal(t, int32(320), x)
223 require.Equal(t, int32(180), y)
224
225 x, y = contextMenuAnchorPosition(stubContextMenuParams{x: 320, y: 180}, 1.25, 1.25)
226 require.Equal(t, int32(256), x)
227 require.Equal(t, int32(144), y)
228}
229
230func TestContextMenuRawPositionDefaultsToZeroWhenParamsNil(t *testing.T) {
231 x, y := contextMenuRawPosition(nil)
232
233 require.Zero(t, x)
234 require.Zero(t, y)
235}