Skip to content

Commit 2ce93a0

Browse files
committed
fix: correct topic-complete header and welcome copy (CodeRabbit on #31)
Addresses the real findings from CodeRabbit's review of #31: - Major: topic-only completion called OutputPanel.show_final(), which hardcodes the 'All exercises complete' header — misleading when only the current topic is done. Added show_topic_complete() (shared body factored into _show_complete) with a 'Topic complete' header, and used it in both the on-mount already-complete path and the post-pass else branch. The whole-curriculum celebration still uses show_final(). - Minor: welcome copy said 'Save -- ... as you type', which is contradictory and wrong for v0.4.0 (the built-in editor reruns checks as you type, no manual save). Copy and its test updated. - Minor: added 'from __future__ import annotations' to test_welcome_pilot.py per repo convention. Skipped the RUF012 nit on WelcomeScreen.BINDINGS: a plain list matches every other screen in the codebase (Textual idiom) and the rule is not enforced. Tests: 2 new output-panel header tests; full suite 147 passed on 3.9 and 3.13.
1 parent 29e29bb commit 2ce93a0

6 files changed

Lines changed: 44 additions & 6 deletions

File tree

pythonlings/screens/track.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def on_mount(self) -> None:
6868
self.current = self._initial_exercise()
6969
self._render_state()
7070
if self.current is None:
71-
self.query_one(OutputPanel).show_final(
71+
self.query_one(OutputPanel).show_topic_complete(
7272
f"Topic '{self.topic}' complete."
7373
)
7474
return
@@ -198,7 +198,7 @@ def _apply_result(self, exercise: Exercise, result: RunResult) -> None:
198198
celebration_message(len(all_exercises))
199199
)
200200
else:
201-
self.query_one(OutputPanel).show_final(
201+
self.query_one(OutputPanel).show_topic_complete(
202202
f"Topic '{self.topic}' complete — press F4 for topics."
203203
)
204204
return

pythonlings/screens/welcome.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def welcome_text() -> str:
1515
return (
1616
"You learn Python here by fixing small broken programs. The loop is:\n\n"
1717
" 1. Edit the current exercise in the built-in editor.\n"
18-
" 2. Save -- the check reruns automatically as you type.\n"
18+
" 2. Checks rerun automatically as you type.\n"
1919
" 3. Remove the `# I AM NOT DONE` marker to advance to the next one.\n\n"
2020
"Handy keys: F1 hint - F3 exercise list - F4 topics - "
2121
"F5 local docs - Ctrl+Q quit.\n\n"

pythonlings/widgets/output_panel.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,19 @@ def render_result(
113113
)
114114

115115
def show_final(self, message: str) -> None:
116-
"""Render the curriculum-complete screen."""
116+
"""Render the whole-curriculum-complete screen."""
117+
self._show_complete("All exercises complete", message)
118+
119+
def show_topic_complete(self, message: str) -> None:
120+
"""Render a single-topic-complete screen (not the whole curriculum)."""
121+
self._show_complete("Topic complete", message)
122+
123+
def _show_complete(self, header: str, message: str) -> None:
117124
self.remove_class("failed", "pending")
118125
self.add_class("passed")
119126
self.query_one("#hint", Static).remove_class("visible")
120127
self.query_one("#output-header", Static).update(
121-
"[bold green]All exercises complete[/bold green]"
128+
f"[bold green]{header}[/bold green]"
122129
)
123130
self.query_one("#goal", Static).update("")
124131
self.query_one("#docs", Static).update("")

tests/tui/test_output_panel.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,29 @@ async def test_full_hint_toggles_on_f1(tmp_path: Path) -> None:
126126
panel.toggle_hint("Full hint text.")
127127
await pilot.pause()
128128
assert "Full hint text." in str(panel.query_one("#hint", Static).content)
129+
130+
131+
@pytest.mark.asyncio
132+
async def test_show_topic_complete_uses_topic_header_not_global(tmp_path: Path) -> None:
133+
app = _Harness()
134+
async with app.run_test() as pilot:
135+
await pilot.pause()
136+
panel = app.query_one(OutputPanel)
137+
panel.show_topic_complete("Topic 'variables' complete — press F4 for topics.")
138+
await pilot.pause()
139+
header = str(panel.query_one("#output-header", Static).content)
140+
assert "Topic complete" in header
141+
assert "All exercises complete" not in header
142+
assert "Topic 'variables' complete" in panel.renderable_text()
143+
144+
145+
@pytest.mark.asyncio
146+
async def test_show_final_keeps_global_header(tmp_path: Path) -> None:
147+
app = _Harness()
148+
async with app.run_test() as pilot:
149+
await pilot.pause()
150+
panel = app.query_one(OutputPanel)
151+
panel.show_final("🎉 done")
152+
await pilot.pause()
153+
header = str(panel.query_one("#output-header", Static).content)
154+
assert "All exercises complete" in header

tests/tui/test_welcome_pilot.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import shutil
24
from pathlib import Path
35

tests/unit/test_welcome.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ def test_welcome_text_explains_the_loop() -> None:
77
text = welcome_text()
88

99
assert "I AM NOT DONE" in text
10-
assert "save" in text.lower()
10+
# v0.4.0 reruns checks as you type in the built-in editor — no manual save,
11+
# so the copy must not tell users to "save".
12+
assert "as you type" in text.lower()
13+
assert "save" not in text.lower()
1114
assert "F1" in text

0 commit comments

Comments
 (0)