webview_runjavascript_test.go RawHistoryFolder

1package webkit
2
3import (
4	"context"
5	"errors"
6	"fmt"
7	"testing"
8	"time"
9
10	"github.com/bnema/dumber/internal/application/port"
11	"github.com/stretchr/testify/assert"
12	"github.com/stretchr/testify/require"
13)
14
15func TestClassifyRunJSEvaluateError(t *testing.T) {
16	t.Run("non-fatal canceled", func(t *testing.T) {
17		nonFatal, signature := classifyRunJSEvaluateError(errors.New("Operation was canceled"))
18		assert.True(t, nonFatal)
19		assert.Equal(t, "evaluate_error:operation was canceled", signature)
20	})
21
22	t.Run("non-fatal context destroyed", func(t *testing.T) {
23		nonFatal, signature := classifyRunJSEvaluateError(errors.New("JavaScript execution context was destroyed"))
24		assert.True(t, nonFatal)
25		assert.Equal(t, "evaluate_error:javascript execution context was destroyed", signature)
26	})
27
28	t.Run("fatal unknown", func(t *testing.T) {
29		nonFatal, signature := classifyRunJSEvaluateError(errors.New("segmentation fault in JS runtime"))
30		assert.False(t, nonFatal)
31		assert.Equal(t, "evaluate_error:segmentation fault in js runtime", signature)
32	})
33}
34
35func TestNormalizeRunJSErrorSignature(t *testing.T) {
36	assert.Equal(t, "empty", normalizeRunJSErrorSignature(" \n\t "))
37	assert.Equal(t, "foo bar baz", normalizeRunJSErrorSignature(" Foo   BAR\tbaz "))
38}
39
40func TestWebViewRunJSDomain(t *testing.T) {
41	wv := &WebView{uri: "https://Sub.Example.com/path?q=1"}
42	assert.Equal(t, "sub.example.com", wv.runJSDomain())
43
44	wv = &WebView{uri: "not a uri"}
45	assert.Equal(t, "unknown", wv.runJSDomain())
46}
47
48func TestShouldLogRunJSError_NonFatalAggregation(t *testing.T) {
49	wv := &WebView{}
50	now := time.Now()
51
52	shouldLog, count := wv.shouldLogRunJSError("example.com", "sig", true, now)
53	assert.True(t, shouldLog)
54	assert.Equal(t, uint64(1), count)
55
56	shouldLog, count = wv.shouldLogRunJSError("example.com", "sig", true, now.Add(10*time.Second))
57	assert.False(t, shouldLog)
58	assert.Equal(t, uint64(2), count)
59
60	shouldLog, count = wv.shouldLogRunJSError("example.com", "sig", true, now.Add(31*time.Second))
61	assert.True(t, shouldLog)
62	assert.Equal(t, uint64(3), count)
63}
64
65func TestShouldLogRunJSError_NonFatalLogsEveryN(t *testing.T) {
66	wv := &WebView{}
67	base := time.Now()
68	for i := 1; i < runJSAggregateLogEvery; i++ {
69		shouldLog, _ := wv.shouldLogRunJSError("example.com", "sig", true, base)
70		if i == 1 {
71			assert.True(t, shouldLog)
72			continue
73		}
74		assert.False(t, shouldLog)
75	}
76
77	shouldLog, count := wv.shouldLogRunJSError("example.com", "sig", true, base)
78	assert.True(t, shouldLog)
79	assert.Equal(t, uint64(runJSAggregateLogEvery), count)
80}
81
82func TestShouldLogRunJSError_FatalAlwaysLogs(t *testing.T) {
83	wv := &WebView{}
84	base := time.Now()
85
86	shouldLog, count := wv.shouldLogRunJSError("example.com", "fatal-sig", false, base)
87	assert.True(t, shouldLog)
88	assert.Equal(t, uint64(1), count)
89
90	shouldLog, count = wv.shouldLogRunJSError("example.com", "fatal-sig", false, base)
91	assert.True(t, shouldLog)
92	assert.Equal(t, uint64(2), count)
93}
94
95func TestScrollPage_Destroyed_ReturnsError(t *testing.T) {
96	wv := &WebView{}
97	wv.destroyed.Store(true)
98
99	err := wv.ScrollPage(context.Background(), port.PageScrollRequest{
100		Command:    port.PageScrollCommandDown,
101		FallbackDX: 0,
102		FallbackDY: 80,
103	})
104
105	require.Error(t, err)
106	assert.Contains(t, err.Error(), "destroyed")
107}
108
109func TestScrollPage_UsesFallbackDeltasAndRunsJavaScript(t *testing.T) {
110	oldBuilder := buildPageScrollFallbackJS
111	oldRunner := runPageScrollFallbackJS
112	defer func() {
113		buildPageScrollFallbackJS = oldBuilder
114		runPageScrollFallbackJS = oldRunner
115	}()
116
117	var gotDX, gotDY int
118	var gotScript string
119	var runCount int
120	buildPageScrollFallbackJS = func(dx, dy int) string {
121		gotDX, gotDY = dx, dy
122		return fmt.Sprintf("scroll(%d,%d)", dx, dy)
123	}
124	runPageScrollFallbackJS = func(_ context.Context, _ *WebView, script string) {
125		runCount++
126		gotScript = script
127	}
128
129	wv := &WebView{
130		uri:             "https://example.com",
131		runJSErrorStats: make(map[string]runJSErrorStat),
132	}
133
134	err := wv.ScrollPage(context.Background(), port.PageScrollRequest{
135		Command:    port.PageScrollCommandLeft,
136		FallbackDX: -12,
137		FallbackDY: 80,
138	})
139
140	require.NoError(t, err)
141	assert.Equal(t, -12, gotDX)
142	assert.Equal(t, 80, gotDY)
143	assert.Equal(t, "scroll(-12,80)", gotScript)
144	assert.Equal(t, 1, runCount)
145}
146
147func TestScrollPage_VariousRequestsForwardFallbackDeltas(t *testing.T) {
148	oldBuilder := buildPageScrollFallbackJS
149	oldRunner := runPageScrollFallbackJS
150	defer func() {
151		buildPageScrollFallbackJS = oldBuilder
152		runPageScrollFallbackJS = oldRunner
153	}()
154
155	tests := []struct {
156		name string
157		req  port.PageScrollRequest
158	}{
159		{"zero request", port.PageScrollRequest{}},
160		{"down", port.PageScrollRequest{Command: port.PageScrollCommandDown, FallbackDY: 80}},
161		{"up", port.PageScrollRequest{Command: port.PageScrollCommandUp, FallbackDY: -80}},
162		{"left", port.PageScrollRequest{Command: port.PageScrollCommandLeft, FallbackDX: -80}},
163		{"right", port.PageScrollRequest{Command: port.PageScrollCommandRight, FallbackDX: 80}},
164		{"up fast", port.PageScrollRequest{Command: port.PageScrollCommandUpFast, FallbackDY: -320}},
165		{"down fast", port.PageScrollRequest{Command: port.PageScrollCommandDownFast, FallbackDY: 320}},
166	}
167
168	for _, tt := range tests {
169		t.Run(tt.name, func(t *testing.T) {
170			var gotDX, gotDY int
171			buildCalls, runCalls := 0, 0
172			buildPageScrollFallbackJS = func(dx, dy int) string {
173				buildCalls++
174				gotDX, gotDY = dx, dy
175				return "ok"
176			}
177			runPageScrollFallbackJS = func(_ context.Context, _ *WebView, script string) {
178				runCalls++
179				assert.Equal(t, "ok", script)
180			}
181
182			wv := &WebView{
183				uri:             "https://example.com",
184				runJSErrorStats: make(map[string]runJSErrorStat),
185			}
186			err := wv.ScrollPage(context.Background(), tt.req)
187			require.NoError(t, err)
188			assert.Equal(t, 1, buildCalls)
189			assert.Equal(t, 1, runCalls)
190			assert.Equal(t, tt.req.FallbackDX, gotDX)
191			assert.Equal(t, tt.req.FallbackDY, gotDY)
192		})
193	}
194}

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