v0.29.0 21b3150

manager_test.go RawHistoryFolder

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		GtkFont:       "Adwaita Sans",
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, appearance, 1.0, nil, mockResolver)
289
290	// Fonts should be stored (manager creation should succeed)
291	require.NotNil(t, manager)
292}
293
294func TestManager_ModeColors(t *testing.T) {
295	ctx := context.Background()
296	styling := &entity.WorkspaceStylingConfig{
297		PaneModeColor:    "#ff0000",
298		TabModeColor:     "#00ff00",
299		SessionModeColor: "#0000ff",
300		ResizeModeColor:  "#ffff00",
301	}
302
303	mockResolver := mocks.NewMockColorSchemeResolver(t)
304	mockResolver.EXPECT().Resolve().Return(port.ColorSchemePreference{
305		PrefersDark: true,
306		Source:      "test",
307	})
308
309	manager := NewManager(ctx, nil, 1.0, styling, mockResolver)
310	modeColors := manager.GetModeColors()
311
312	assert.Equal(t, "#ff0000", modeColors.PaneMode)
313	assert.Equal(t, "#00ff00", modeColors.TabMode)
314	assert.Equal(t, "#0000ff", modeColors.SessionMode)
315	assert.Equal(t, "#ffff00", modeColors.ResizeMode)
316}
317
318func TestDefaultGTKFont(t *testing.T) {
319	assert.Equal(t, "Adwaita Sans", DefaultGTKFont())
320}
321
322func TestFormatGTKFontName_UsesScaledConfiguredGTKFont(t *testing.T) {
323	assert.Equal(t, "Adwaita Sans 14", formatGTKFontName("Adwaita Sans", 1.3))
324}
325
326func TestFormatGTKFontName_DefaultsScaleAndRoundsToNearestPoint(t *testing.T) {
327	assert.Equal(t, "Inter 11", formatGTKFontName("Inter", 0))
328	assert.Equal(t, "Inter 12", formatGTKFontName("Inter", 1.05))
329	assert.Equal(t, "Adwaita Sans 11", formatGTKFontName("", 1.0))
330}
331
332func TestShouldApplyGTKFontName(t *testing.T) {
333	assert.True(t, shouldApplyGTKFontName("", "Fira Sans 14"))
334	assert.False(t, shouldApplyGTKFontName("Fira Sans 14", "Fira Sans 14"))
335	assert.False(t, shouldApplyGTKFontName("Fira Sans 14", ""))
336}

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