1package theme
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/dumber/internal/application/port"
8 "github.com/bnema/dumber/internal/application/port/mocks"
9 "github.com/bnema/dumber/internal/domain/entity"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12)
13
14func TestNewManager_DarkMode(t *testing.T) {
15 ctx := context.Background()
16 appearance := &entity.AppearanceConfig{
17 ColorScheme: "default",
18 }
19
20 mockResolver := mocks.NewMockColorSchemeResolver(t)
21 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
22 PrefersDark: true,
23 Source: "test",
24 })
25
26 manager := NewManager(ctx, appearance, 1.0, nil, mockResolver)
27
28 assert.True(t, manager.PrefersDark())
29 assert.Equal(t, "default", manager.scheme)
30}
31
32func TestNewManager_LightMode(t *testing.T) {
33 ctx := context.Background()
34 appearance := &entity.AppearanceConfig{
35 ColorScheme: "default",
36 }
37
38 mockResolver := mocks.NewMockColorSchemeResolver(t)
39 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
40 PrefersDark: false,
41 Source: "test",
42 })
43
44 manager := NewManager(ctx, appearance, 1.0, nil, mockResolver)
45
46 assert.False(t, manager.PrefersDark())
47}
48
49func TestNewManager_WithNilConfig(t *testing.T) {
50 ctx := context.Background()
51
52 mockResolver := mocks.NewMockColorSchemeResolver(t)
53 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
54 PrefersDark: true,
55 Source: "fallback",
56 })
57
58 manager := NewManager(ctx, nil, 0, nil, mockResolver)
59
60 require.NotNil(t, manager)
61 assert.True(t, manager.PrefersDark())
62 assert.Equal(t, "system", manager.scheme)
63}
64
65func TestNewManager_UsesConfigScheme(t *testing.T) {
66 ctx := context.Background()
67 appearance := &entity.AppearanceConfig{
68 ColorScheme: "prefer-dark",
69 }
70
71 mockResolver := mocks.NewMockColorSchemeResolver(t)
72 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
73 PrefersDark: true,
74 Source: "config",
75 })
76
77 manager := NewManager(ctx, appearance, 1.0, nil, mockResolver)
78
79 assert.Equal(t, "prefer-dark", manager.scheme)
80}
81
82func TestManager_GetCurrentPalette_Dark(t *testing.T) {
83 ctx := context.Background()
84
85 mockResolver := mocks.NewMockColorSchemeResolver(t)
86 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
87 PrefersDark: true,
88 Source: "test",
89 })
90
91 manager := NewManager(ctx, nil, 0, nil, mockResolver)
92 palette := manager.GetCurrentPalette()
93
94 // Dark palette should be returned
95 assert.Equal(t, manager.GetDarkPalette(), palette)
96}
97
98func TestManager_GetCurrentPalette_Light(t *testing.T) {
99 ctx := context.Background()
100
101 mockResolver := mocks.NewMockColorSchemeResolver(t)
102 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
103 PrefersDark: false,
104 Source: "test",
105 })
106
107 manager := NewManager(ctx, nil, 0, nil, mockResolver)
108 palette := manager.GetCurrentPalette()
109
110 // Light palette should be returned
111 assert.Equal(t, manager.GetLightPalette(), palette)
112}
113
114func TestManager_SetColorScheme(t *testing.T) {
115 ctx := context.Background()
116
117 mockResolver := mocks.NewMockColorSchemeResolver(t)
118 // Initial resolve call
119 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
120 PrefersDark: true,
121 Source: "test",
122 })
123 // Refresh call when SetColorScheme is called
124 mockResolver.EXPECT().Refresh().Return(port.ColorSchemePreference{
125 PrefersDark: false,
126 Source: "config",
127 })
128
129 manager := NewManager(ctx, nil, 0, nil, mockResolver)
130 assert.True(t, manager.PrefersDark())
131
132 // Change scheme - this calls Refresh on resolver
133 manager.SetColorScheme(ctx, "prefer-light", nil)
134
135 assert.False(t, manager.PrefersDark())
136 assert.Equal(t, "prefer-light", manager.scheme)
137}
138
139func TestManager_UpdateFromConfig(t *testing.T) {
140 ctx := context.Background()
141 appearance := &entity.AppearanceConfig{
142 ColorScheme: "default",
143 }
144
145 mockResolver := mocks.NewMockColorSchemeResolver(t)
146 // Initial resolve
147 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
148 PrefersDark: true,
149 Source: "test",
150 })
151 // Refresh when UpdateFromConfig is called
152 mockResolver.EXPECT().Refresh().Return(port.ColorSchemePreference{
153 PrefersDark: false,
154 Source: "config",
155 })
156
157 manager := NewManager(ctx, appearance, 1.0, nil, mockResolver)
158 assert.True(t, manager.PrefersDark())
159
160 // Update with new config
161 newAppearance := &entity.AppearanceConfig{
162 ColorScheme: "prefer-light",
163 }
164 manager.UpdateFromConfig(ctx, newAppearance, 0, nil, nil)
165
166 assert.False(t, manager.PrefersDark())
167 assert.Equal(t, "prefer-light", manager.scheme)
168}
169
170func TestManager_UpdateFromConfig_NilConfig(t *testing.T) {
171 ctx := context.Background()
172
173 mockResolver := mocks.NewMockColorSchemeResolver(t)
174 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
175 PrefersDark: true,
176 Source: "test",
177 })
178
179 manager := NewManager(ctx, nil, 0, nil, mockResolver)
180 initialScheme := manager.scheme
181
182 // UpdateFromConfig with nil appearance should be a no-op
183 manager.UpdateFromConfig(ctx, nil, 0, nil, nil)
184
185 assert.Equal(t, initialScheme, manager.scheme)
186}
187
188func TestManager_GetWebUIThemeCSS(t *testing.T) {
189 ctx := context.Background()
190
191 mockResolver := mocks.NewMockColorSchemeResolver(t)
192 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
193 PrefersDark: true,
194 Source: "test",
195 })
196
197 manager := NewManager(ctx, nil, 0, nil, mockResolver)
198 css := manager.GetWebUIThemeCSS()
199
200 // Should contain both light and dark variables
201 assert.Contains(t, css, ":root{")
202 assert.Contains(t, css, ".dark{")
203}
204
205func TestManager_GetBackgroundRGBA(t *testing.T) {
206 ctx := context.Background()
207
208 mockResolver := mocks.NewMockColorSchemeResolver(t)
209 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
210 PrefersDark: true,
211 Source: "test",
212 })
213
214 manager := NewManager(ctx, nil, 0, nil, mockResolver)
215 r, g, b, a := manager.GetBackgroundRGBA()
216
217 // Should return valid RGBA values (0-1 range)
218 assert.GreaterOrEqual(t, r, float32(0))
219 assert.LessOrEqual(t, r, float32(1))
220 assert.GreaterOrEqual(t, g, float32(0))
221 assert.LessOrEqual(t, g, float32(1))
222 assert.GreaterOrEqual(t, b, float32(0))
223 assert.LessOrEqual(t, b, float32(1))
224 assert.GreaterOrEqual(t, a, float32(0))
225 assert.LessOrEqual(t, a, float32(1))
226}
227
228func TestManager_CustomPalettes(t *testing.T) {
229 ctx := context.Background()
230 appearance := &entity.AppearanceConfig{
231 LightPalette: entity.ColorPalette{
232 Background: "#ffffff",
233 Text: "#000000",
234 },
235 DarkPalette: entity.ColorPalette{
236 Background: "#000000",
237 Text: "#ffffff",
238 },
239 }
240
241 mockResolver := mocks.NewMockColorSchemeResolver(t)
242 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
243 PrefersDark: true,
244 Source: "test",
245 })
246
247 manager := NewManager(ctx, appearance, 1.0, nil, mockResolver)
248
249 lightPalette := manager.GetLightPalette()
250 darkPalette := manager.GetDarkPalette()
251
252 assert.Equal(t, "#ffffff", lightPalette.Background)
253 assert.Equal(t, "#000000", lightPalette.Text)
254 assert.Equal(t, "#000000", darkPalette.Background)
255 assert.Equal(t, "#ffffff", darkPalette.Text)
256}
257
258func TestManager_UIScale(t *testing.T) {
259 ctx := context.Background()
260
261 mockResolver := mocks.NewMockColorSchemeResolver(t)
262 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
263 PrefersDark: true,
264 Source: "test",
265 })
266
267 manager := NewManager(ctx, nil, 1.5, nil, mockResolver)
268
269 // UI scale should be stored (we can't directly access it, but we can verify
270 // the manager was created without error)
271 require.NotNil(t, manager)
272}
273
274func TestManager_Fonts(t *testing.T) {
275 ctx := context.Background()
276 appearance := &entity.AppearanceConfig{
277 SansFont: "Inter",
278 MonospaceFont: "JetBrains Mono",
279 }
280
281 mockResolver := mocks.NewMockColorSchemeResolver(t)
282 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
283 PrefersDark: true,
284 Source: "test",
285 })
286
287 manager := NewManager(ctx, appearance, 1.0, nil, mockResolver)
288
289 // Fonts should be stored (manager creation should succeed)
290 require.NotNil(t, manager)
291}
292
293func TestManager_ModeColors(t *testing.T) {
294 ctx := context.Background()
295 styling := &entity.WorkspaceStylingConfig{
296 PaneModeColor: "#ff0000",
297 TabModeColor: "#00ff00",
298 SessionModeColor: "#0000ff",
299 ResizeModeColor: "#ffff00",
300 }
301
302 mockResolver := mocks.NewMockColorSchemeResolver(t)
303 mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
304 PrefersDark: true,
305 Source: "test",
306 })
307
308 manager := NewManager(ctx, nil, 1.0, styling, mockResolver)
309 modeColors := manager.GetModeColors()
310
311 assert.Equal(t, "#ff0000", modeColors.PaneMode)
312 assert.Equal(t, "#00ff00", modeColors.TabMode)
313 assert.Equal(t, "#0000ff", modeColors.SessionMode)
314 assert.Equal(t, "#ffff00", modeColors.ResizeMode)
315}