v0.28.0 3cb3770

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	}
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}

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