265 lines
7.5 KiB
Go
265 lines
7.5 KiB
Go
package tui
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestPlanForm_NewPrefilled(t *testing.T) {
|
|
pf := NewPlanFormState("refactor auth module")
|
|
|
|
if len(pf.Fields) != 3 {
|
|
t.Fatalf("expected 3 fields, got %d", len(pf.Fields))
|
|
}
|
|
|
|
// Task field should be pre-filled.
|
|
if pf.Fields[0].Input.Value() != "refactor auth module" {
|
|
t.Errorf("task field should be pre-filled, got %q", pf.Fields[0].Input.Value())
|
|
}
|
|
|
|
// Scope field should be select with 3 options.
|
|
if pf.Fields[1].Kind != "select" {
|
|
t.Errorf("scope field should be select, got %q", pf.Fields[1].Kind)
|
|
}
|
|
if len(pf.Fields[1].Options) != 3 {
|
|
t.Errorf("scope should have 3 options, got %d", len(pf.Fields[1].Options))
|
|
}
|
|
|
|
// Focus field should be text.
|
|
if pf.Fields[2].Kind != "text" {
|
|
t.Errorf("focus field should be text, got %q", pf.Fields[2].Kind)
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_AssemblePrompt(t *testing.T) {
|
|
pf := NewPlanFormState("build a REST API")
|
|
pf.Fields[1].OptionIndex = 1 // "module"
|
|
pf.Fields[2].Input.SetValue("keep backward compat")
|
|
|
|
prompt := pf.AssemblePrompt()
|
|
|
|
if !strings.Contains(prompt, "build a REST API") {
|
|
t.Error("prompt should contain task")
|
|
}
|
|
if !strings.Contains(prompt, "module") {
|
|
t.Error("prompt should contain scope")
|
|
}
|
|
if !strings.Contains(prompt, "keep backward compat") {
|
|
t.Error("prompt should contain focus")
|
|
}
|
|
if !strings.Contains(prompt, "step-by-step plan") {
|
|
t.Error("prompt should contain plan instruction")
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_AssemblePrompt_NoFocus(t *testing.T) {
|
|
pf := NewPlanFormState("fix the bug")
|
|
|
|
prompt := pf.AssemblePrompt()
|
|
|
|
if !strings.Contains(prompt, "fix the bug") {
|
|
t.Error("prompt should contain task")
|
|
}
|
|
if strings.Contains(prompt, "Focus:") {
|
|
t.Error("prompt should not contain Focus when empty")
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_OpenClose(t *testing.T) {
|
|
t.Run("open_sets_overlay", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPlanForm("test task")
|
|
|
|
if m.overlay != OverlayPlanForm {
|
|
t.Errorf("expected OverlayPlanForm, got %d", m.overlay)
|
|
}
|
|
if m.planFormState == nil {
|
|
t.Fatal("planFormState should not be nil")
|
|
}
|
|
if m.planFormState.Fields[0].Input.Value() != "test task" {
|
|
t.Error("task should be pre-filled")
|
|
}
|
|
})
|
|
|
|
t.Run("close_resets_state", func(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.planFormState = NewPlanFormState("test")
|
|
m.overlay = OverlayPlanForm
|
|
|
|
m.closePlanForm()
|
|
|
|
if m.planFormState != nil {
|
|
t.Error("planFormState should be nil after close")
|
|
}
|
|
if m.overlay != OverlayNone {
|
|
t.Errorf("overlay should be OverlayNone, got %d", m.overlay)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPlanForm_EscCancels(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPlanForm("some task")
|
|
|
|
updated, _ := m.Update(escKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState != nil {
|
|
t.Error("ESC should close plan form")
|
|
}
|
|
if m.overlay != OverlayNone {
|
|
t.Errorf("overlay should be OverlayNone, got %d", m.overlay)
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_FieldNavigation(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPlanForm("task")
|
|
|
|
// Initially on field 0.
|
|
if m.planFormState.ActiveField != 0 {
|
|
t.Fatalf("expected active field 0, got %d", m.planFormState.ActiveField)
|
|
}
|
|
|
|
// Tab advances to field 1.
|
|
updated, _ := m.Update(tabKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.ActiveField != 1 {
|
|
t.Errorf("expected active field 1, got %d", m.planFormState.ActiveField)
|
|
}
|
|
|
|
// Tab again advances to field 2.
|
|
updated, _ = m.Update(tabKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.ActiveField != 2 {
|
|
t.Errorf("expected active field 2, got %d", m.planFormState.ActiveField)
|
|
}
|
|
|
|
// Tab at last field stays on last field.
|
|
updated, _ = m.Update(tabKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.ActiveField != 2 {
|
|
t.Errorf("expected active field to stay at 2, got %d", m.planFormState.ActiveField)
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_SelectFieldLeftRight(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPlanForm("task")
|
|
|
|
// Tab to scope field.
|
|
updated, _ := m.Update(tabKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.ActiveField != 1 {
|
|
t.Fatalf("expected field 1, got %d", m.planFormState.ActiveField)
|
|
}
|
|
if m.planFormState.Fields[1].OptionIndex != 0 {
|
|
t.Fatalf("expected option 0, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
// Right should advance to option 1.
|
|
updated, _ = m.Update(rightKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 1 {
|
|
t.Errorf("expected option 1 after right, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
// Left should go back to option 0.
|
|
updated, _ = m.Update(leftKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 0 {
|
|
t.Errorf("expected option 0 after left, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_SelectFieldBounds(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPlanForm("task")
|
|
|
|
// Tab to scope field.
|
|
updated, _ := m.Update(tabKey())
|
|
m = updated.(*Model)
|
|
|
|
// Up at 0 stays at 0.
|
|
updated, _ = m.Update(upKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 0 {
|
|
t.Errorf("expected option 0 after up at boundary, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
// Left at 0 stays at 0.
|
|
updated, _ = m.Update(leftKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 0 {
|
|
t.Errorf("expected option 0 after left at boundary, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
// Navigate to last option.
|
|
updated, _ = m.Update(downKey())
|
|
m = updated.(*Model)
|
|
updated, _ = m.Update(downKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 2 {
|
|
t.Fatalf("expected option 2, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
// Down at last stays at last.
|
|
updated, _ = m.Update(downKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 2 {
|
|
t.Errorf("expected option 2 after down at boundary, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
// Right at last stays at last.
|
|
updated, _ = m.Update(rightKey())
|
|
m = updated.(*Model)
|
|
|
|
if m.planFormState.Fields[1].OptionIndex != 2 {
|
|
t.Errorf("expected option 2 after right at boundary, got %d", m.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
}
|
|
|
|
func TestPlanForm_SelectField(t *testing.T) {
|
|
m := newTestModel(t)
|
|
m.openPlanForm("task")
|
|
|
|
// Navigate to scope field (index 1).
|
|
m.Update(tabKey())
|
|
updated, _ := m.Update(tabKey())
|
|
m = updated.(*Model)
|
|
|
|
// Oops, we need to re-get m after first tab. Let me redo:
|
|
m2 := newTestModel(t)
|
|
m2.openPlanForm("task")
|
|
|
|
// Tab to scope field.
|
|
updated, _ = m2.Update(tabKey())
|
|
m2 = updated.(*Model)
|
|
|
|
if m2.planFormState.ActiveField != 1 {
|
|
t.Fatalf("expected field 1, got %d", m2.planFormState.ActiveField)
|
|
}
|
|
|
|
// Down should cycle scope option.
|
|
if m2.planFormState.Fields[1].OptionIndex != 0 {
|
|
t.Fatalf("expected option 0, got %d", m2.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
|
|
updated, _ = m2.Update(downKey())
|
|
m2 = updated.(*Model)
|
|
|
|
if m2.planFormState.Fields[1].OptionIndex != 1 {
|
|
t.Errorf("expected option 1 after down, got %d", m2.planFormState.Fields[1].OptionIndex)
|
|
}
|
|
}
|