v0.29.0 21b3150

context_menu_adapter_test.go RawHistoryFolder

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}

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