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}