e397da2733
- add the tui command using Bubble Tea and Lip Gloss - preselect eligible repositories and disable selection for blocked repositories - add keyboard controls for navigation, selection, refresh, sync, and cancellation - require confirmation before sync and show the branch-deletion warning - show dirty paths, block reasons, and hub output for the focused repository - add TUI model tests and set the embedded version to 0.2.0
74 lines
2.3 KiB
Go
74 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
func testTUIRepos() []repo {
|
|
return []repo{
|
|
{Path: "/workspace/clean", RelativePath: "clean", Branch: "main", Eligible: true},
|
|
{Path: "/workspace/dirty", RelativePath: "dirty", Branch: "develop", BlockReason: "dirty worktree (1 changes)", Dirty: []string{" M file.txt"}},
|
|
}
|
|
}
|
|
|
|
func TestTUIOnlySelectsEligibleRepositories(t *testing.T) {
|
|
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
|
if got := len(m.selectedRepos()); got != 1 {
|
|
t.Fatalf("selected repositories = %d, want 1", got)
|
|
}
|
|
|
|
m.cursor = 1
|
|
updated, _ := m.Update(tea.KeyMsg{Type: tea.KeySpace})
|
|
m = updated.(tuiModel)
|
|
if got := len(m.selectedRepos()); got != 1 {
|
|
t.Fatalf("blocked repository became selectable; selected = %d", got)
|
|
}
|
|
}
|
|
|
|
func TestTUIRequiresExplicitConfirmation(t *testing.T) {
|
|
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
|
updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
|
|
m = updated.(tuiModel)
|
|
if m.mode != modeConfirm || cmd != nil {
|
|
t.Fatalf("sync should enter confirmation without starting command: mode=%v cmd=%v", m.mode, cmd)
|
|
}
|
|
|
|
updated, cmd = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'n'}})
|
|
m = updated.(tuiModel)
|
|
if m.mode != modeReady || cmd != nil || !strings.Contains(m.status, "cancelled") {
|
|
t.Fatalf("cancel should return to ready state: %+v", m)
|
|
}
|
|
}
|
|
|
|
func TestTUIViewExplainsBlockedRepository(t *testing.T) {
|
|
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
|
m.width, m.height, m.cursor = 100, 30, 1
|
|
view := m.View()
|
|
for _, want := range []string{"STACK SYNC", "dirty worktree", "file.txt", "1 blocked"} {
|
|
if !strings.Contains(view, want) {
|
|
t.Fatalf("view missing %q:\n%s", want, view)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTUIViewShowsLastHubOutputForFocusedRepository(t *testing.T) {
|
|
m := newTUIModel("/workspace", testTUIRepos(), options{})
|
|
m.width, m.height = 100, 30
|
|
m.lastResult = []syncResult{{Path: "clean", Success: true, Message: "Updated branch main", Duration: "120ms"}}
|
|
view := m.View()
|
|
for _, want := range []string{"LAST SYNC", "120ms", "Updated branch main"} {
|
|
if !strings.Contains(view, want) {
|
|
t.Fatalf("view missing %q:\n%s", want, view)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTruncateMiddle(t *testing.T) {
|
|
if got := truncateMiddle("abcdefghij", 7); got != "abc…hij" {
|
|
t.Fatalf("truncateMiddle() = %q", got)
|
|
}
|
|
}
|