Add dashboard / description page

This commit is contained in:
2026-07-28 09:58:49 +02:00
parent 60d64dedb2
commit 43fa047765
9 changed files with 465 additions and 51 deletions

View File

@@ -166,6 +166,142 @@ func TestPaneFocusAndNavigation(t *testing.T) {
}
}
func TestOpeningPullRequestShowsDashboardBeforeThreads(t *testing.T) {
settings := defaultAppSettings()
settings.DashboardMode = "intermediate"
m := NewAppWithSettings(nil, "o", "r", false, 50, 10*time.Second, settings)
m.prs = []PullRequest{{
ID: "pr", Owner: "owner", Repository: "repo", RepoWithOwner: "owner/repo", Number: 9,
}}
updated, command := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
m = updated.(App)
if m.screen != dashboardScreen || m.details.Number != 9 || command == nil {
t.Fatalf("opening PR produced screen=%d number=%d command=%v", m.screen, m.details.Number, command)
}
updated, _ = m.Update(tea.KeyMsg{Type: tea.KeyEnter})
m = updated.(App)
if m.screen != threadScreen {
t.Fatal("enter did not open review threads from dashboard")
}
updated, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("b")})
m = updated.(App)
if m.screen != dashboardScreen {
t.Fatal("b did not return from threads to dashboard")
}
}
func TestDashboardHotkeyModeIsDefault(t *testing.T) {
if got := defaultAppSettings().DashboardMode; got != "hotkey" {
t.Fatalf("default dashboard mode = %q, want hotkey", got)
}
if got := defaultConfig().Display.DashboardMode; got != "hotkey" {
t.Fatalf("default config dashboard mode = %q, want hotkey", got)
}
}
func TestHotkeyDashboardModeOpensThreadsDirectly(t *testing.T) {
settings := defaultAppSettings()
settings.DashboardMode = "hotkey"
m := NewAppWithSettings(nil, "o", "r", false, 50, 10*time.Second, settings)
m.prs = []PullRequest{{
ID: "pr", Owner: "owner", Repository: "repo", RepoWithOwner: "owner/repo", Number: 9,
}}
updated, command := m.Update(tea.KeyMsg{Type: tea.KeyEnter})
m = updated.(App)
if m.screen != threadScreen || command == nil {
t.Fatalf("hotkey mode opened screen=%d command=%v", m.screen, command)
}
updated, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("d")})
m = updated.(App)
if m.screen != dashboardScreen || m.dashboardReturn != threadScreen {
t.Fatal("d did not open a dashboard that returns to threads")
}
updated, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("b")})
m = updated.(App)
if m.screen != threadScreen {
t.Fatal("dashboard did not return to the invoking thread screen")
}
}
func TestDashboardHotkeyWorksFromPicker(t *testing.T) {
settings := defaultAppSettings()
settings.DashboardMode = "hotkey"
m := NewAppWithSettings(nil, "o", "r", false, 50, 10*time.Second, settings)
m.prs = []PullRequest{{
ID: "pr", Owner: "owner", Repository: "repo", RepoWithOwner: "owner/repo", Number: 9,
}}
updated, command := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("d")})
m = updated.(App)
if m.screen != dashboardScreen || m.dashboardReturn != prScreen || command == nil {
t.Fatalf("picker dashboard hotkey produced screen=%d return=%d", m.screen, m.dashboardReturn)
}
}
func TestDashboardRendersDescriptionAndMetadata(t *testing.T) {
m := NewApp(nil, "o", "r", false, 50, 10*time.Second)
m.screen = dashboardScreen
m.width, m.height = 100, 40
m.details = PRDetails{
PullRequest: PullRequest{
RepoWithOwner: "owner/repo", Number: 9, Title: "Improve dashboard",
Author: "alice", URL: "https://github.com/owner/repo/pull/9",
UpdatedAt: time.Date(2026, 7, 1, 12, 0, 0, 0, time.UTC),
},
Body: "> Existing behavior\n\nUse `new_behavior` instead.",
CreatedAt: time.Date(2026, 6, 1, 12, 0, 0, 0, time.UTC),
BaseRef: "main",
HeadRef: "feature",
ReviewDecision: "REVIEW_REQUIRED",
CheckState: "SUCCESS",
MergeState: "CLEAN",
Assignees: []string{"bob"},
Reviewers: []Reviewer{{Login: "carol", State: "APPROVED"}},
Labels: []string{"ui", "review"},
Milestone: "v2",
Additions: 20,
Deletions: 4,
ChangedFiles: 3,
CommitCount: 2,
CommentCount: 5,
Threads: []ReviewThread{
{ID: "open"},
{ID: "old", IsOutdated: true},
{ID: "done", IsResolved: true, IsOutdated: true},
},
}
plain := ansi.Strip(m.View())
for _, wanted := range []string{
"Improve dashboard", "@alice", "feature → main", "@bob", "@carol",
"ui, review", "v2", "+20", "-4", "3 files", "2 commits",
"1 open", "1 outdated", "1 resolved", "Description",
"Existing behavior", "new_behavior",
} {
if !strings.Contains(plain, wanted) {
t.Fatalf("dashboard is missing %q:\n%s", wanted, plain)
}
}
}
func TestDashboardDescriptionScrolls(t *testing.T) {
m := NewApp(nil, "o", "r", false, 50, 10*time.Second)
m.screen = dashboardScreen
m.width, m.height = 60, 10
m.details = PRDetails{
PullRequest: PullRequest{RepoWithOwner: "owner/repo", Number: 1, Title: "Long", Author: "alice"},
BaseRef: "main",
HeadRef: "feature",
Body: strings.Repeat("A long description line.\n\n", 20),
}
updated, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("G")})
m = updated.(App)
if m.scroll == 0 || m.scroll != m.dashboardMaxScroll() {
t.Fatalf("dashboard scroll = %d, max = %d", m.scroll, m.dashboardMaxScroll())
}
}
func TestContextualHelpOpensAndClosesWithoutLeavingScreen(t *testing.T) {
m := NewApp(nil, "o", "r", false, 50, 10*time.Second)
m.screen = threadScreen
@@ -179,7 +315,7 @@ func TestContextualHelpOpensAndClosesWithoutLeavingScreen(t *testing.T) {
plain := ansi.Strip(m.View())
if !strings.Contains(plain, "Review thread keys") ||
!strings.Contains(plain, "Fuzzy-search file paths") ||
strings.Contains(plain, "Open pull request") {
strings.Contains(plain, "Next pull request") {
t.Fatalf("help was not contextual:\n%s", plain)
}