preset_test.go

  1package screens
  2
  3import (
  4	"image/color"
  5	"testing"
  6
  7	tea "charm.land/bubbletea/v2"
  8
  9	"git.secluded.site/keld/internal/theme"
 10	"git.secluded.site/keld/internal/ui"
 11)
 12
 13func testPresets() []string {
 14	return []string{"home@cloud", "work@local", "media"}
 15}
 16
 17func TestPresetTitle(t *testing.T) {
 18	t.Parallel()
 19
 20	p := NewPreset(testPresets(), testStyles())
 21	if got := p.Title(); got != "Select a preset" {
 22		t.Errorf("Title() = %q, want %q", got, "Select a preset")
 23	}
 24}
 25
 26func TestPresetSelectionEmpty(t *testing.T) {
 27	t.Parallel()
 28
 29	p := NewPreset(testPresets(), testStyles())
 30	if got := p.Selection(); got != "" {
 31		t.Errorf("Selection() before interaction = %q, want empty", got)
 32	}
 33}
 34
 35func TestPresetKeyBindings(t *testing.T) {
 36	t.Parallel()
 37
 38	p := NewPreset(testPresets(), testStyles())
 39	bindings := p.KeyBindings()
 40
 41	if len(bindings) == 0 {
 42		t.Fatal("KeyBindings() returned no bindings")
 43	}
 44}
 45
 46func TestPresetEscWhenNotFilteringReturnsBack(t *testing.T) {
 47	t.Parallel()
 48
 49	p := NewPreset(testPresets(), testStyles())
 50	// Initialise the form so huh is ready to receive input.
 51	p.Init()
 52	// Send a WindowSizeMsg so huh can lay out its fields.
 53	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
 54
 55	// Esc when not filtering should signal back navigation.
 56	_, cmd := p.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
 57
 58	if cmd == nil {
 59		t.Fatal("expected BackCmd on Esc when not filtering")
 60	}
 61	msg := cmd()
 62	if _, ok := msg.(ui.BackMsg); !ok {
 63		t.Errorf("cmd produced %T, want ui.BackMsg", msg)
 64	}
 65}
 66
 67func TestPresetEscWhileFilteringDoesNotBack(t *testing.T) {
 68	t.Parallel()
 69
 70	p := NewPreset(testPresets(), testStyles())
 71	p.Init()
 72	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
 73
 74	// Activate filter mode by pressing '/'.
 75	p.Update(tea.KeyPressMsg{Code: '/', Text: "/"})
 76
 77	// Esc while filtering should close the filter, not back out.
 78	updated, cmd := p.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
 79	preset := updated.(*Preset)
 80
 81	// Should still be on this screen (no BackCmd).
 82	if cmd != nil {
 83		msg := cmd()
 84		if _, ok := msg.(ui.BackMsg); ok {
 85			t.Error("Esc while filtering should not produce BackMsg")
 86		}
 87	}
 88
 89	// Screen should still be functional (not backed out).
 90	if preset.Selection() != "" {
 91		t.Error("preset should not have a selection after Esc from filter")
 92	}
 93}
 94
 95func TestPresetCompleteReturnsDone(t *testing.T) {
 96	t.Parallel()
 97
 98	p := NewPreset(testPresets(), testStyles())
 99	p.Init()
100	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
101
102	// Press enter to select the focused option (first one: global defaults).
103	// huh uses an internal message chain (nextFieldMsg → nextGroupMsg →
104	// StateCompleted), so we drain until DoneMsg appears.
105	var screen ui.Screen
106	var cmd tea.Cmd
107	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
108	_, cmd = drain(screen.(*Preset), cmd)
109
110	if cmd == nil {
111		t.Fatal("expected DoneCmd after form completion")
112	}
113	msg := cmd()
114	if _, ok := msg.(ui.DoneMsg); !ok {
115		t.Errorf("cmd produced %T, want ui.DoneMsg", msg)
116	}
117}
118
119func TestPresetSelectionValue(t *testing.T) {
120	t.Parallel()
121
122	p := NewPreset(testPresets(), testStyles())
123	p.Init()
124	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
125
126	// Move down to the first real preset ("home@cloud") and select it.
127	p.Update(tea.KeyPressMsg{Code: tea.KeyDown})
128	var screen ui.Screen
129	var cmd tea.Cmd
130	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
131	p, _ = drain(screen.(*Preset), cmd)
132
133	if got := p.Selection(); got != "home@cloud" {
134		t.Errorf("Selection() = %q, want %q", got, "home@cloud")
135	}
136}
137
138func TestPresetGlobalDefaultDisplayLabel(t *testing.T) {
139	t.Parallel()
140
141	// When the user selects "(global defaults only)", the resolved
142	// value is "" but the breadcrumb display should be meaningful.
143	p := NewPreset(testPresets(), testStyles())
144	p.Init()
145	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
146
147	// First option is the global default. Select it.
148	var screen ui.Screen
149	var cmd tea.Cmd
150	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
151	p, _ = drain(screen.(*Preset), cmd)
152
153	// Selection() should return a display-friendly label, not "".
154	sel := p.Selection()
155	if sel == "" {
156		t.Error("Selection() for global defaults should return a display label, not empty string")
157	}
158}
159
160func TestPresetReinitPreservesSelection(t *testing.T) {
161	t.Parallel()
162
163	p := NewPreset(testPresets(), testStyles())
164	p.Init()
165	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
166
167	// Move down to "home@cloud" and select it.
168	p.Update(tea.KeyPressMsg{Code: tea.KeyDown})
169	var screen ui.Screen
170	var cmd tea.Cmd
171	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
172	p, _ = drain(screen.(*Preset), cmd)
173
174	if p.Selection() != "home@cloud" {
175		t.Fatalf("precondition: Selection() = %q, want %q", p.Selection(), "home@cloud")
176	}
177
178	// Re-initialise (simulates back navigation). The form state
179	// should carry the previous selection through.
180	p.Init()
181
182	// The internal selected value should still be "home@cloud".
183	if p.selected != "home@cloud" {
184		t.Errorf("after re-init, selected = %q, want %q (should preserve previous choice)", p.selected, "home@cloud")
185	}
186}
187
188func TestPresetReinitAfterBack(t *testing.T) {
189	t.Parallel()
190
191	p := NewPreset(testPresets(), testStyles())
192	p.Init()
193	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
194
195	// Complete the form first so the state is no longer StateNormal.
196	var screen ui.Screen
197	var cmd tea.Cmd
198	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
199	p, _ = drain(screen.(*Preset), cmd)
200
201	// Re-initialise (simulates the session navigating back to this screen).
202	p.Init()
203	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
204
205	// Should be functional again — selecting should produce DoneCmd.
206	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
207	_, cmd = drain(screen.(*Preset), cmd)
208
209	if cmd == nil {
210		t.Fatal("expected DoneCmd after re-init, form may not have been reset")
211	}
212	msg := cmd()
213	if _, ok := msg.(ui.DoneMsg); !ok {
214		t.Errorf("after re-init, cmd produced %T, want ui.DoneMsg", msg)
215	}
216}
217
218func TestPresetRebuildsFormOnBackgroundChange(t *testing.T) {
219	t.Parallel()
220
221	styles := testStyles()
222	p := NewPreset(testPresets(), styles)
223	p.Init()
224	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
225
226	originalForm := p.form
227
228	// Simulate background detection switching to light. The session
229	// would have already mutated *styles before forwarding this msg.
230	*styles = theme.New(false)
231	updated, _ := p.Update(tea.BackgroundColorMsg{Color: color.White})
232	p = updated.(*Preset)
233
234	// The form should have been rebuilt with the new theme.
235	if p.form == originalForm {
236		t.Error("form was not rebuilt after BackgroundColorMsg; cached theme is stale")
237	}
238}
239
240func TestPresetValueReturnsActualPresetName(t *testing.T) {
241	t.Parallel()
242
243	p := NewPreset(testPresets(), testStyles())
244	p.Init()
245	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
246
247	// Move down to "home@cloud" and select it.
248	p.Update(tea.KeyPressMsg{Code: tea.KeyDown})
249	var screen ui.Screen
250	var cmd tea.Cmd
251	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
252	p, _ = drain(screen.(*Preset), cmd)
253
254	if got := p.Value(); got != "home@cloud" {
255		t.Errorf("Value() = %q, want %q", got, "home@cloud")
256	}
257}
258
259func TestPresetValueEmptyForGlobalDefaults(t *testing.T) {
260	t.Parallel()
261
262	p := NewPreset(testPresets(), testStyles())
263	p.Init()
264	p.Update(tea.WindowSizeMsg{Width: 80, Height: 20})
265
266	// First option is global defaults. Select it.
267	var screen ui.Screen
268	var cmd tea.Cmd
269	screen, cmd = p.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
270	p, _ = drain(screen.(*Preset), cmd)
271
272	if got := p.Value(); got != "" {
273		t.Errorf("Value() = %q, want empty for global defaults", got)
274	}
275}