Render repos in a Rich table with [F]ork/[A]rchive/[M]irror indicators, color-coded issue counts, relative release dates, and a milestones section. Handles empty repo lists gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
217 lines
6.2 KiB
Python
217 lines
6.2 KiB
Python
"""Tests for Rich dashboard display."""
|
|
|
|
from io import StringIO
|
|
|
|
from rich.console import Console
|
|
|
|
from gitea_dashboard.collector import RepoData
|
|
from gitea_dashboard.display import render_dashboard
|
|
|
|
|
|
def _make_console():
|
|
"""Create a console that captures output for testing.
|
|
|
|
highlight=False desactive le highlighting automatique de Rich
|
|
qui fragmente les nombres et noms avec des codes ANSI.
|
|
"""
|
|
buf = StringIO()
|
|
return Console(file=buf, force_terminal=True, width=120, highlight=False), buf
|
|
|
|
|
|
def _make_repo(
|
|
name="my-repo",
|
|
full_name="admin/my-repo",
|
|
description="A repo",
|
|
open_issues=3,
|
|
is_fork=False,
|
|
is_archived=False,
|
|
is_mirror=False,
|
|
latest_release=None,
|
|
milestones=None,
|
|
):
|
|
"""Build a RepoData for testing."""
|
|
return RepoData(
|
|
name=name,
|
|
full_name=full_name,
|
|
description=description,
|
|
open_issues=open_issues,
|
|
is_fork=is_fork,
|
|
is_archived=is_archived,
|
|
is_mirror=is_mirror,
|
|
latest_release=latest_release,
|
|
milestones=milestones if milestones is not None else [],
|
|
)
|
|
|
|
|
|
class TestRenderDashboardTable:
|
|
"""Test the main repos table rendering."""
|
|
|
|
def test_basic_repo_displayed(self):
|
|
"""Repo name and issues count appear in output."""
|
|
console, buf = _make_console()
|
|
repos = [
|
|
_make_repo(
|
|
name="mon-projet",
|
|
open_issues=3,
|
|
latest_release={"tag_name": "v1.2.0", "published_at": "2026-03-08T10:00:00Z"},
|
|
),
|
|
]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "mon-projet" in output
|
|
assert "v1.2.0" in output
|
|
|
|
def test_repo_without_release_shows_dash(self):
|
|
"""Repo with no release shows a dash character."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="no-release", latest_release=None)]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "no-release" in output
|
|
# The dash character for "no release"
|
|
assert "\u2014" in output or "—" in output
|
|
|
|
def test_fork_indicator(self):
|
|
"""Fork repos show [F] indicator."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="forked-repo", is_fork=True)]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "forked-repo" in output
|
|
assert "[F]" in output
|
|
|
|
def test_archive_indicator(self):
|
|
"""Archived repos show [A] indicator."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="old-repo", is_archived=True)]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "[A]" in output
|
|
|
|
def test_mirror_indicator(self):
|
|
"""Mirror repos show [M] indicator."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="mirror-repo", is_mirror=True)]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "[M]" in output
|
|
|
|
def test_multiple_indicators(self):
|
|
"""Repo with multiple flags shows all indicators."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="special", is_fork=True, is_archived=True)]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "[F]" in output
|
|
assert "[A]" in output
|
|
|
|
def test_issues_zero(self):
|
|
"""Repos with 0 issues display 0."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="clean-repo", open_issues=0)]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "0" in output
|
|
|
|
def test_multiple_repos(self):
|
|
"""Multiple repos all appear in the output."""
|
|
console, buf = _make_console()
|
|
repos = [
|
|
_make_repo(name="repo-alpha", open_issues=1),
|
|
_make_repo(name="repo-beta", open_issues=5),
|
|
]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "repo-alpha" in output
|
|
assert "repo-beta" in output
|
|
|
|
|
|
class TestRenderDashboardMilestones:
|
|
"""Test the milestones section rendering."""
|
|
|
|
def test_milestones_displayed(self):
|
|
"""Repos with milestones show milestone info."""
|
|
console, buf = _make_console()
|
|
repos = [
|
|
_make_repo(
|
|
name="mon-projet",
|
|
milestones=[
|
|
{
|
|
"title": "v2.0",
|
|
"open_issues": 2,
|
|
"closed_issues": 3,
|
|
"due_on": "2026-04-01T00:00:00Z",
|
|
},
|
|
],
|
|
),
|
|
]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "v2.0" in output
|
|
assert "3/5" in output # closed/total
|
|
assert "60%" in output
|
|
|
|
def test_milestone_without_due_date(self):
|
|
"""Milestone without due_on omits the deadline."""
|
|
console, buf = _make_console()
|
|
repos = [
|
|
_make_repo(
|
|
name="projet",
|
|
milestones=[
|
|
{
|
|
"title": "backlog",
|
|
"open_issues": 5,
|
|
"closed_issues": 1,
|
|
"due_on": None,
|
|
},
|
|
],
|
|
),
|
|
]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "backlog" in output
|
|
assert "1/6" in output
|
|
|
|
def test_no_milestones_section_when_none(self):
|
|
"""When no repos have milestones, milestone section is absent."""
|
|
console, buf = _make_console()
|
|
repos = [_make_repo(name="simple", milestones=[])]
|
|
|
|
render_dashboard(repos, console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "Milestones" not in output
|
|
|
|
|
|
class TestRenderDashboardEmpty:
|
|
"""Test empty repo list handling."""
|
|
|
|
def test_empty_list_shows_message(self):
|
|
"""Empty repo list shows informative message."""
|
|
console, buf = _make_console()
|
|
|
|
render_dashboard([], console=console)
|
|
output = buf.getvalue()
|
|
|
|
assert "Aucun repo" in output
|