230 lines
6.3 KiB
Go
230 lines
6.3 KiB
Go
package tui
|
|
|
|
import "testing"
|
|
|
|
func TestPushHistory_Basic(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.pushHistory("world")
|
|
|
|
if len(m.promptHistory) != 2 {
|
|
t.Fatalf("expected 2 history entries, got %d", len(m.promptHistory))
|
|
}
|
|
if m.promptHistory[0] != "hello" || m.promptHistory[1] != "world" {
|
|
t.Errorf("unexpected history: %v", m.promptHistory)
|
|
}
|
|
}
|
|
|
|
func TestPushHistory_Empty(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("")
|
|
if len(m.promptHistory) != 0 {
|
|
t.Error("empty string should not be added to history")
|
|
}
|
|
}
|
|
|
|
func TestPushHistory_DedupConsecutive(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.pushHistory("hello")
|
|
m.pushHistory("hello")
|
|
|
|
if len(m.promptHistory) != 1 {
|
|
t.Errorf("expected 1 entry after dedup, got %d", len(m.promptHistory))
|
|
}
|
|
}
|
|
|
|
func TestPushHistory_DedupNonConsecutive(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.pushHistory("world")
|
|
m.pushHistory("hello")
|
|
|
|
if len(m.promptHistory) != 3 {
|
|
t.Errorf("non-consecutive duplicates should be kept, got %d", len(m.promptHistory))
|
|
}
|
|
}
|
|
|
|
func TestPushHistory_CapAt100(t *testing.T) {
|
|
m := newTestModel(t)
|
|
for i := 0; i < 110; i++ {
|
|
m.pushHistory(string(rune('a' + i%26)) + string(rune('0'+i/26)))
|
|
}
|
|
|
|
if len(m.promptHistory) > 100 {
|
|
t.Errorf("history should be capped at 100, got %d", len(m.promptHistory))
|
|
}
|
|
}
|
|
|
|
func TestNavigateHistory_EmptyHistory(t *testing.T) {
|
|
m := newTestModel(t)
|
|
if m.navigateHistory(-1) {
|
|
t.Error("up on empty history should return false")
|
|
}
|
|
if m.navigateHistory(1) {
|
|
t.Error("down on empty history should return false")
|
|
}
|
|
}
|
|
|
|
func TestNavigateHistory_UpDown(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("first")
|
|
m.pushHistory("second")
|
|
m.pushHistory("third")
|
|
|
|
// Set current input
|
|
m.input.SetValue("current")
|
|
|
|
// Press up: should go to "third" (most recent)
|
|
if !m.navigateHistory(-1) {
|
|
t.Fatal("up should succeed")
|
|
}
|
|
if m.input.Value() != "third" {
|
|
t.Errorf("expected 'third', got %q", m.input.Value())
|
|
}
|
|
if m.historySaved != "current" {
|
|
t.Errorf("current input should be saved, got %q", m.historySaved)
|
|
}
|
|
|
|
// Press up again: "second"
|
|
if !m.navigateHistory(-1) {
|
|
t.Fatal("up should succeed")
|
|
}
|
|
if m.input.Value() != "second" {
|
|
t.Errorf("expected 'second', got %q", m.input.Value())
|
|
}
|
|
|
|
// Press up again: "first"
|
|
if !m.navigateHistory(-1) {
|
|
t.Fatal("up should succeed")
|
|
}
|
|
if m.input.Value() != "first" {
|
|
t.Errorf("expected 'first', got %q", m.input.Value())
|
|
}
|
|
|
|
// Press up again: at oldest, should fail
|
|
if m.navigateHistory(-1) {
|
|
t.Error("up at oldest should return false")
|
|
}
|
|
|
|
// Press down: "second"
|
|
if !m.navigateHistory(1) {
|
|
t.Fatal("down should succeed")
|
|
}
|
|
if m.input.Value() != "second" {
|
|
t.Errorf("expected 'second', got %q", m.input.Value())
|
|
}
|
|
|
|
// Press down: "third"
|
|
if !m.navigateHistory(1) {
|
|
t.Fatal("down should succeed")
|
|
}
|
|
if m.input.Value() != "third" {
|
|
t.Errorf("expected 'third', got %q", m.input.Value())
|
|
}
|
|
|
|
// Press down past newest: restore saved input
|
|
if !m.navigateHistory(1) {
|
|
t.Fatal("down past newest should succeed")
|
|
}
|
|
if m.input.Value() != "current" {
|
|
t.Errorf("expected restored 'current', got %q", m.input.Value())
|
|
}
|
|
if m.historyIndex != -1 {
|
|
t.Errorf("historyIndex should be -1 after exiting history, got %d", m.historyIndex)
|
|
}
|
|
}
|
|
|
|
func TestNavigateHistory_DownNotBrowsing(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
|
|
// Down without first pressing up should return false
|
|
if m.navigateHistory(1) {
|
|
t.Error("down when not browsing should return false")
|
|
}
|
|
}
|
|
|
|
func TestHistoryKey_OnlyWhenIdleAndEmpty(t *testing.T) {
|
|
t.Run("idle_empty_with_history", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.state = StateIdle
|
|
m.overlay = OverlayNone
|
|
|
|
updated, _ := m.Update(upKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.input.Value() != "hello" {
|
|
t.Errorf("up key should navigate history, got %q", m.input.Value())
|
|
}
|
|
})
|
|
|
|
t.Run("idle_nonempty_no_history", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.state = StateIdle
|
|
m.overlay = OverlayNone
|
|
m.input.SetValue("typing something")
|
|
|
|
updated, _ := m.Update(upKey())
|
|
m = updated.(*Model)
|
|
|
|
// Should NOT navigate history when input has content and not already browsing
|
|
if m.historyIndex != -1 {
|
|
t.Error("up key should not navigate history when input is non-empty")
|
|
}
|
|
})
|
|
|
|
t.Run("waiting_no_history", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.state = StateWaiting
|
|
|
|
updated, _ := m.Update(upKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.historyIndex != -1 {
|
|
t.Error("up key should not navigate history when not idle")
|
|
}
|
|
})
|
|
|
|
t.Run("overlay_no_history", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("hello")
|
|
m.state = StateIdle
|
|
m.overlay = OverlayHelp
|
|
|
|
updated, _ := m.Update(upKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.historyIndex != -1 {
|
|
t.Error("up key should not navigate history when overlay is open")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestHistoryKey_AlreadyBrowsing(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.pushHistory("first")
|
|
m.pushHistory("second")
|
|
m.state = StateIdle
|
|
m.overlay = OverlayNone
|
|
// Input must be empty to start browsing
|
|
m.input.SetValue("")
|
|
|
|
// Press up — enters history (input is empty, so allowed)
|
|
updated, _ := m.Update(upKey())
|
|
m = updated.(*Model)
|
|
if m.input.Value() != "second" {
|
|
t.Fatalf("expected 'second', got %q", m.input.Value())
|
|
}
|
|
|
|
// Now input is non-empty (from history), up should still work because historyIndex != -1
|
|
updated, _ = m.Update(upKey())
|
|
m = updated.(*Model)
|
|
if m.input.Value() != "first" {
|
|
t.Errorf("expected 'first', got %q", m.input.Value())
|
|
}
|
|
}
|