1package cef
2
3import (
4 "context"
5 "sync"
6 "testing"
7
8 purecef "github.com/bnema/purego-cef/cef"
9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"
11
12 "github.com/bnema/dumber/internal/application/port"
13)
14
15type accessibilityStateHost struct {
16 purecef.BrowserHost
17
18 mu sync.Mutex
19 states []purecef.State
20}
21
22func (h *accessibilityStateHost) SetAccessibilityState(state purecef.State) {
23 h.mu.Lock()
24 defer h.mu.Unlock()
25 h.states = append(h.states, state)
26}
27
28func (h *accessibilityStateHost) recordedStates() []purecef.State {
29 h.mu.Lock()
30 defer h.mu.Unlock()
31 out := make([]purecef.State, len(h.states))
32 copy(out, h.states)
33 return out
34}
35
36func TestWebView_EnableAccessibility_NoOpWhenDestroyedOrHostNil(t *testing.T) {
37 t.Parallel()
38
39 destroyed := &WebView{}
40 destroyed.destroyed.Store(true)
41 host := &accessibilityStateHost{}
42 destroyed.host = host
43 destroyed.EnableAccessibility()
44 assert.Empty(t, host.recordedStates())
45 assert.False(t, destroyed.a11yRequested.Load())
46
47 noHost := &WebView{}
48 noHost.EnableAccessibility()
49 assert.False(t, noHost.a11yEnabled.Load())
50 assert.True(t, noHost.a11yRequested.Load(), "early request must persist while host is nil")
51}
52
53func TestWebView_EnableAccessibility_IdempotentOncePerWebView(t *testing.T) {
54 t.Parallel()
55
56 host := &accessibilityStateHost{}
57 wv := &WebView{host: host}
58
59 wv.EnableAccessibility()
60 wv.EnableAccessibility()
61 wv.EnableAccessibility()
62
63 states := host.recordedStates()
64 require.Len(t, states, 1)
65 assert.Equal(t, purecef.StateStateEnabled, states[0])
66 assert.True(t, wv.a11yEnabled.Load())
67}
68
69func TestWebView_EnableAccessibility_IndependentPerWebView(t *testing.T) {
70 t.Parallel()
71
72 hostA := &accessibilityStateHost{}
73 hostB := &accessibilityStateHost{}
74 wvA := &WebView{host: hostA}
75 wvB := &WebView{host: hostB}
76
77 wvA.EnableAccessibility()
78 wvA.EnableAccessibility()
79 wvB.EnableAccessibility()
80
81 require.Len(t, hostA.recordedStates(), 1)
82 require.Len(t, hostB.recordedStates(), 1)
83 assert.Equal(t, purecef.StateStateEnabled, hostA.recordedStates()[0])
84 assert.Equal(t, purecef.StateStateEnabled, hostB.recordedStates()[0])
85}
86
87func TestWebView_EnableAccessibility_PersistsUntilHostAttachment(t *testing.T) {
88 t.Parallel()
89
90 wv := &WebView{}
91 wv.EnableAccessibility()
92 assert.True(t, wv.a11yRequested.Load())
93 assert.False(t, wv.a11yEnabled.Load())
94
95 host := &accessibilityStateHost{}
96 wv.host = host
97 wv.applyAccessibilityIfReady()
98
99 require.Len(t, host.recordedStates(), 1)
100 assert.Equal(t, purecef.StateStateEnabled, host.recordedStates()[0])
101 assert.True(t, wv.a11yEnabled.Load())
102}
103
104func TestWebView_EnableAccessibility_ConcurrentCallsEnableOnce(t *testing.T) {
105 t.Parallel()
106
107 host := &accessibilityStateHost{}
108 wv := &WebView{host: host}
109
110 var wg sync.WaitGroup
111 for range 32 {
112 wg.Add(1)
113 go func() {
114 defer wg.Done()
115 wv.EnableAccessibility()
116 }()
117 }
118 wg.Wait()
119
120 require.Len(t, host.recordedStates(), 1)
121 assert.Equal(t, purecef.StateStateEnabled, host.recordedStates()[0])
122}
123
124func TestWebView_EnableAccessibility_RequestThenDestroyBeforeAttachmentNeverEnables(t *testing.T) {
125 t.Parallel()
126
127 wv := &WebView{}
128 wv.EnableAccessibility()
129 assert.True(t, wv.a11yRequested.Load())
130
131 wv.Destroy()
132
133 host := &accessibilityStateHost{}
134 wv.host = host
135 wv.applyAccessibilityIfReady()
136 wv.EnableAccessibility()
137
138 assert.Empty(t, host.recordedStates())
139 assert.False(t, wv.a11yEnabled.Load())
140}
141
142func TestFinishAfterCreated_AppliesPendingAccessibilityOnce(t *testing.T) {
143 t.Parallel()
144
145 wv := &WebView{ctx: context.Background()}
146 wv.EnableAccessibility()
147 assert.False(t, wv.a11yEnabled.Load())
148
149 host := &accessibilityStateHost{}
150 wv.host = host
151 h := &handlerSet{wv: wv}
152 h.finishAfterCreated(nil, host, afterCreatedState{})
153 h.finishAfterCreated(nil, host, afterCreatedState{})
154
155 require.Len(t, host.recordedStates(), 1)
156 assert.Equal(t, purecef.StateStateEnabled, host.recordedStates()[0])
157}
158
159func TestFinishAfterCreated_SkipsWhenNoPendingRequest(t *testing.T) {
160 t.Parallel()
161
162 host := &accessibilityStateHost{}
163 wv := &WebView{ctx: context.Background(), host: host}
164 h := &handlerSet{wv: wv}
165 h.finishAfterCreated(nil, host, afterCreatedState{})
166
167 assert.Empty(t, host.recordedStates())
168 assert.False(t, wv.a11yEnabled.Load())
169}
170
171func TestWebView_ImplementsAccessibilityEnabler(t *testing.T) {
172 t.Parallel()
173 var _ port.AccessibilityEnabler = (*WebView)(nil)
174}