package tui import ( "strings" "testing" ) // ── TestMenuOptions ───────────────────────────────────────────────────── func TestTestMenuOptions_Count(t *testing.T) { opts := TestMenuOptions() if len(opts) != 4 { t.Fatalf("expected 4 test menu options, got %d", len(opts)) } } func TestTestMenuOptions_Labels(t *testing.T) { opts := TestMenuOptions() expected := []string{"Go Tests", "E2E Tests", "E2E Tests (headed)", "All Tests"} for i, want := range expected { if opts[i].Label != want { t.Errorf("option[%d]: expected label %q, got %q", i, want, opts[i].Label) } } } func TestMainMenuOptions_IncludesTests(t *testing.T) { opts := MainMenuOptions() found := false for _, opt := range opts { if opt.Label == "Tests" { found = true break } } if !found { t.Error("MainMenuOptions should include 'Tests'") } } func TestMainMenuOptions_TestsBeforeQuit(t *testing.T) { opts := MainMenuOptions() testsIdx, quitIdx := -1, -1 for i, opt := range opts { if opt.Label == "Tests" { testsIdx = i } if opt.Label == "Quit" { quitIdx = i } } if testsIdx < 0 || quitIdx < 0 { t.Fatal("expected both Tests and Quit in menu") } if testsIdx >= quitIdx { t.Errorf("Tests (index %d) should come before Quit (index %d)", testsIdx, quitIdx) } } func TestServerMenuOptions_NoRunTests(t *testing.T) { for _, running := range []bool{true, false} { opts := ServerMenuOptions(running) for _, opt := range opts { if opt.Label == "Run Tests" { t.Errorf("ServerMenuOptions(running=%v) should not have 'Run Tests', found it", running) } } } } func TestServerMenuOptions_HasTests(t *testing.T) { for _, running := range []bool{true, false} { opts := ServerMenuOptions(running) found := false for _, opt := range opts { if opt.Label == "Tests" { found = true } } if !found { t.Errorf("ServerMenuOptions(running=%v) should have 'Tests'", running) } } } // ── updateTestsScreen ─────────────────────────────────────────────────── func TestUpdateTestsScreen_Navigation(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 0, WindowHeight: 40} m, _ = Update(m, KeyMsg{Str: "down"}) if m.Cursor != 1 { t.Errorf("expected cursor 1 after down, got %d", m.Cursor) } m, _ = Update(m, KeyMsg{Str: "up"}) if m.Cursor != 0 { t.Errorf("expected cursor 0 after up, got %d", m.Cursor) } // Can't go below 0 m, _ = Update(m, KeyMsg{Str: "up"}) if m.Cursor != 0 { t.Errorf("expected cursor 0 clamped, got %d", m.Cursor) } } func TestUpdateTestsScreen_Back(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 2} m, _ = Update(m, KeyMsg{Str: "0"}) if m.Screen != ScreenMain { t.Errorf("expected ScreenMain, got %d", m.Screen) } if m.Cursor != 0 { t.Errorf("expected cursor reset to 0, got %d", m.Cursor) } } func TestUpdateTestsScreen_SelectGoTests(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 0} m, intents := Update(m, KeyMsg{Str: "enter"}) if len(intents) != 1 || intents[0].Kind != IntentRunGoTests { t.Errorf("expected IntentRunGoTests, got %v", intents) } if m.LastTestKind != TestKindGo { t.Errorf("expected LastTestKind=TestKindGo, got %d", m.LastTestKind) } } func TestUpdateTestsScreen_SelectE2ETests(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 1} _, intents := Update(m, KeyMsg{Str: "enter"}) if len(intents) != 1 || intents[0].Kind != IntentRunE2ETests { t.Errorf("expected IntentRunE2ETests, got %v", intents) } } func TestUpdateTestsScreen_SelectE2EHeaded(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 2} _, intents := Update(m, KeyMsg{Str: "enter"}) if len(intents) != 1 || intents[0].Kind != IntentRunE2EHeadTests { t.Errorf("expected IntentRunE2EHeadTests, got %v", intents) } } func TestUpdateTestsScreen_SelectAllTests(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 3} _, intents := Update(m, KeyMsg{Str: "enter"}) if len(intents) != 1 || intents[0].Kind != IntentRunAllTests { t.Errorf("expected IntentRunAllTests, got %v", intents) } } // ── MsgTestsDone ──────────────────────────────────────────────────────── func TestMsgTestsDone_SetsKindAndStatus(t *testing.T) { m := Model{Screen: ScreenTests} m, _ = Update(m, MsgTestsDone{Kind: TestKindE2E, Passed: true, Output: []string{"ok"}}) if m.Screen != ScreenTestOutput { t.Errorf("expected ScreenTestOutput, got %d", m.Screen) } if m.LastTestKind != TestKindE2E { t.Errorf("expected LastTestKind=TestKindE2E, got %d", m.LastTestKind) } if !strings.Contains(m.StatusMsg, "PASSED") { t.Errorf("expected PASSED in status, got %q", m.StatusMsg) } if !strings.Contains(m.StatusMsg, "E2E") { t.Errorf("expected E2E in status, got %q", m.StatusMsg) } } func TestMsgTestsDone_Failed(t *testing.T) { m := Model{} m, _ = Update(m, MsgTestsDone{Kind: TestKindGo, Passed: false, Output: []string{"FAIL"}}) if !strings.Contains(m.StatusMsg, "FAILED") { t.Errorf("expected FAILED in status, got %q", m.StatusMsg) } } // ── updateTestOutput ──────────────────────────────────────────────────── func TestUpdateTestOutput_BackGoesToTests(t *testing.T) { m := Model{Screen: ScreenTestOutput, LastTestKind: TestKindGo} m, _ = Update(m, KeyMsg{Str: "0"}) if m.Screen != ScreenTests { t.Errorf("expected ScreenTests, got %d", m.Screen) } } func TestUpdateTestOutput_RerunUsesLastKind(t *testing.T) { m := Model{Screen: ScreenTestOutput, LastTestKind: TestKindE2E, WindowHeight: 40} _, intents := Update(m, KeyMsg{Str: "r"}) if len(intents) != 1 || intents[0].Kind != IntentRunE2ETests { t.Errorf("expected IntentRunE2ETests, got %v", intents) } } func TestUpdateTestOutput_RerunDefaultsToGo(t *testing.T) { m := Model{Screen: ScreenTestOutput, LastTestKind: TestKindNone, WindowHeight: 40} _, intents := Update(m, KeyMsg{Str: "r"}) if len(intents) != 1 || intents[0].Kind != IntentRunGoTests { t.Errorf("expected IntentRunGoTests as default, got %v", intents) } } // ── viewTests ─────────────────────────────────────────────────────────── func TestViewTests_ShowsOptions(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 0, WindowWidth: 80, WindowHeight: 40} out := View(m) if !strings.Contains(out, "Go Tests") { t.Error("expected 'Go Tests' in view") } if !strings.Contains(out, "E2E Tests") { t.Error("expected 'E2E Tests' in view") } if !strings.Contains(out, "All Tests") { t.Error("expected 'All Tests' in view") } } func TestViewTests_ShowsCursor(t *testing.T) { m := Model{Screen: ScreenTests, Cursor: 1, WindowWidth: 80, WindowHeight: 40} out := View(m) // Cursor on E2E Tests (index 1) lines := strings.Split(out, "\n") foundCursor := false for _, line := range lines { if strings.Contains(line, "> ") && strings.Contains(line, "E2E Tests") && !strings.Contains(line, "(headed)") { foundCursor = true } } if !foundCursor { t.Error("expected cursor on E2E Tests") } } func TestViewTests_ShowsLastRun(t *testing.T) { m := Model{ Screen: ScreenTests, Cursor: 0, WindowWidth: 80, WindowHeight: 40, LastTestKind: TestKindGo, StatusMsg: "Go Tests PASSED", } out := View(m) if !strings.Contains(out, "Last run:") { t.Error("expected 'Last run:' in view") } if !strings.Contains(out, "PASSED") { t.Error("expected PASSED in last run") } } func TestViewTestOutput_ShowsKindInTitle(t *testing.T) { m := Model{ Screen: ScreenTestOutput, LastTestKind: TestKindE2E, StatusMsg: "E2E Tests PASSED", LogLines: []string{"ok"}, WindowWidth: 80, WindowHeight: 40, } out := View(m) if !strings.Contains(out, "Test Results — E2E Tests") { t.Errorf("expected 'Test Results — E2E Tests' in view, got:\n%s", out) } } // ── Main menu Tests navigation ────────────────────────────────────────── func TestMainMenu_TestsNavigation(t *testing.T) { m := Model{Screen: ScreenMain, Cursor: 2} // Tests is at index 2 m, intents := Update(m, KeyMsg{Str: "enter"}) if m.Screen != ScreenTests { t.Errorf("expected ScreenTests, got %d", m.Screen) } if len(intents) != 0 { t.Errorf("expected no intents for Tests nav, got %v", intents) } } // ── Server menu Tests navigation ──────────────────────────────────────── func TestServerMenu_TestsNavigation(t *testing.T) { // "Tests" is the last option in both running/stopped menus opts := ServerMenuOptions(false) testsIdx := -1 for i, o := range opts { if o.Label == "Tests" { testsIdx = i } } if testsIdx < 0 { t.Fatal("Tests not found in server menu") } m := Model{Screen: ScreenServer, Cursor: testsIdx} m, _ = Update(m, KeyMsg{Str: "enter"}) if m.Screen != ScreenTests { t.Errorf("expected ScreenTests, got %d", m.Screen) } } // ── testKindLabel ─────────────────────────────────────────────────────── func TestTestKindLabel(t *testing.T) { cases := []struct { kind TestKind want string }{ {TestKindGo, "Go Tests"}, {TestKindE2E, "E2E Tests"}, {TestKindE2EHead, "E2E Tests (headed)"}, {TestKindAll, "All Tests"}, {TestKindNone, "Tests"}, } for _, tc := range cases { got := testKindLabel(tc.kind) if got != tc.want { t.Errorf("testKindLabel(%d) = %q, want %q", tc.kind, got, tc.want) } } } // ── testKindIntent ────────────────────────────────────────────────────── func TestTestKindIntent(t *testing.T) { cases := []struct { kind TestKind want IntentKind }{ {TestKindGo, IntentRunGoTests}, {TestKindE2E, IntentRunE2ETests}, {TestKindE2EHead, IntentRunE2EHeadTests}, {TestKindAll, IntentRunAllTests}, {TestKindNone, ""}, } for _, tc := range cases { got := testKindIntent(tc.kind) if got != tc.want { t.Errorf("testKindIntent(%d) = %q, want %q", tc.kind, got, tc.want) } } }