Add arrow-key menus and report loading status

wizard: when stdin and stdout are both TTYs, menus render inline and
are driven by keys: up/down/j/k/Home/End move the bold '>' highlight,
Enter accepts, Esc/Ctrl-C/q back out of cancellable menus while Ctrl-C
on fixed menus still aborts. Rows redraw with erase+CR across the
option block only and clear before returning so prompts stay aligned.
Non-TTY stdio keeps the original numbered prompt verbatim.

report: each configured source shows a transient "# Generating
report: pulling <name> data..." line that is erased on completion,
only when stdout is a TTY; piped output remains exactly the report.

tests: cover the numbered fallback, row rendering, status suppression
and show+clear parity; reformat stray test files to satisfy ruff.
This commit is contained in:
2026-08-27 07:46:48 +00:00
parent 5f4487f3ad
commit 85ddfb4a71
6 changed files with 340 additions and 28 deletions
+9 -6
View File
@@ -74,8 +74,9 @@ class BuildSpecsTest(unittest.TestCase):
)
specs = build_specs(config)
# All launchpad accounts first, then github, then bts; listing order kept.
self.assertEqual([s.username for s in specs], ["lp-a", "lp-b", "gh-a", "gh-b",
"one@x.org", "two@x.org"])
self.assertEqual(
[s.username for s in specs], ["lp-a", "lp-b", "gh-a", "gh-b", "one@x.org", "two@x.org"]
)
self.assertEqual(
[s.name for s in specs],
[
@@ -99,8 +100,9 @@ class BuildSpecsTest(unittest.TestCase):
def test_launchpad_spec_expands_tilde_credentials_file(self) -> None:
config = ActivityConfig(launchpad=[LaunchpadSettings(username="anon")])
config.launchpad.append(LaunchpadSettings(mode="credentials", username="u",
credentials_file="~/creds.json"))
config.launchpad.append(
LaunchpadSettings(mode="credentials", username="u", credentials_file="~/creds.json")
)
specs = build_specs(config)
with mock.patch("weekly_activity.aggregate.LaunchpadSource") as fake_source:
for spec in specs:
@@ -152,8 +154,9 @@ class CollectAndFormatTest(unittest.TestCase):
def test_heading_uses_account_label_not_bare_source_name(self) -> None:
since, until = window()
specs = [SourceSpec(name="github/work", username="acme-jane",
make=lambda: StubSource("github"))]
specs = [
SourceSpec(name="github/work", username="acme-jane", make=lambda: StubSource("github"))
]
text = format_combined(collect_specs(specs, since, until), since, until)
self.assertIn("Activity report — github/work / ~acme-jane", text)