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