v0.31.0 bf0756b

semantic_navigation_test.go RawHistoryFolder

1package cef
2
3import (
4	"context"
5	"testing"
6
7	purecef "github.com/bnema/purego-cef/cef"
8	cefmocks "github.com/bnema/purego-cef/cef/mocks"
9	"github.com/stretchr/testify/assert"
10	"github.com/stretchr/testify/mock"
11	"github.com/stretchr/testify/require"
12
13	"github.com/bnema/dumber/internal/application/dto"
14)
15
16func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testing.T) {
17	browser := cefmocks.NewMockBrowser(t)
18	frame := cefmocks.NewMockFrame(t)
19	browser.EXPECT().GetMainFrame().Return(frame).Once()
20	frame.EXPECT().ExecuteJavaScript(mock.MatchedBy(mockStringContaining(t,
21		"document.querySelectorAll(\"h1,h2,h3,h4,h5,h6\")",
22		"heading.closest(\"[hidden],[inert],[aria-hidden=\\\"true\\\"]\")",
23		"window.getComputedStyle(heading)",
24		"firstAtOrAfterViewportTop",
25		"if (next < 0 || next >= headings.length) return;",
26		"previous && previous !== target",
27		"scrollIntoView",
28		"window[stateKey] = target",
29		"const highlightColor = \"#22c55e\"",
30		"const targetAttribute = \"data-dumber-vim-heading-",
31		"let style = document.querySelector(\"style[\" + styleAttribute + \"]\")",
32		"style.textContent = \"[\" + targetAttribute + \"]\"",
33		"1 * 3",
34	)), "", int32(0)).Once()
35
36	wv := &WebView{browser: browser}
37	err := wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
38		Target:         dto.SemanticNavigationTargetHeading,
39		Direction:      dto.SemanticNavigationForward,
40		Count:          3,
41		HighlightColor: "#22c55e",
42	})
43	require.NoError(t, err)
44}
45
46func TestWebViewClearSemanticNavigationHighlightRemovesTargetArtifacts(t *testing.T) {
47	browser := cefmocks.NewMockBrowser(t)
48	frame := cefmocks.NewMockFrame(t)
49	browser.EXPECT().GetMainFrame().Return(frame).Once()
50	frame.EXPECT().ExecuteJavaScript(mock.MatchedBy(mockStringContaining(t,
51		"const targetAttribute = \"data-dumber-vim-heading-",
52		"document.querySelectorAll(\"[\" + targetAttribute + \"]\")",
53		"target.removeAttribute(targetAttribute)",
54		"style[\" + styleAttribute + \"]",
55		"delete window[stateKey]",
56	)), "", int32(0)).Once()
57
58	wv := &WebView{browser: browser}
59	require.NoError(t, wv.ClearSemanticNavigationHighlight(context.Background()))
60}
61
62func TestWebViewClearSemanticNavigationHighlightReportsSchedulingFailure(t *testing.T) {
63	oldNewTask, oldPostTask := cefNewTask, cefPostTask
64	defer func() { cefNewTask, cefPostTask = oldNewTask, oldPostTask }()
65	cefNewTask = func(task purecef.Task) purecef.Task { return task }
66	cefPostTask = func(purecef.ThreadID, purecef.Task) int32 { return 0 }
67
68	wv := &WebView{engine: &Engine{}, browser: cefmocks.NewMockBrowser(t)}
69	require.Error(t, wv.ClearSemanticNavigationHighlight(context.Background()))
70}
71
72func TestWebViewNavigateSemantic_NormalizesCountAndRejectsUnsupportedRequests(t *testing.T) {
73	forward := headingNavigationScript(1, 1, "", "test")
74	backward := headingNavigationScript(-1, 1, "#4ade80", "test")
75	assert.Contains(t, forward, "index = firstAtOrAfterViewportTop - 1")
76	assert.Contains(t, forward, "if (index < -1) index = -1;")
77	assert.Contains(t, backward, "index = firstAtOrAfterViewportTop >= 0 ? firstAtOrAfterViewportTop : headings.length")
78	assert.Contains(t, backward, "-1 * 1")
79	assert.NotContains(t, backward, "window.innerHeight")
80	assert.Contains(t, forward, "const highlightColor = \"#fbbf24\"")
81	assert.Contains(t, backward, "const highlightColor = \"#4ade80\"")
82
83	wv := &WebView{}
84	err := wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
85		Target:    dto.SemanticNavigationTargetHeading,
86		Direction: dto.SemanticNavigationForward,
87		Count:     0,
88	})
89	require.NoError(t, err)
90
91	err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
92		Target:    dto.SemanticNavigationTargetHeading + 1,
93		Direction: dto.SemanticNavigationForward,
94	})
95	require.Error(t, err)
96
97	err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
98		Target:    dto.SemanticNavigationTargetHeading,
99		Direction: 0,
100	})
101	require.Error(t, err)
102}
103
104func mockStringContaining(t *testing.T, fragments ...string) func(string) bool {
105	t.Helper()
106	return func(script string) bool {
107		for _, fragment := range fragments {
108			if !assert.Contains(t, script, fragment) {
109				return false
110			}
111		}
112		return true
113	}
114}

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