From 7adb6f813b1628e5b4ce7b5e0487d3b4f49c231f Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Fri, 23 Oct 2026 18:47:03 -0400 Subject: [PATCH] Drive the notification title test through Core's declared progress states The test still called the removed show_result/show_error API and built MessageNotification from the stale positional shape with `detail` and `busy` fields that no longer exist, so it failed on main. It now hosts the widget inside a real conversation (the project-path owner its details need), feeds it CheckedNotifications and UnavailableNotifications through show(), and takes the states from Core's family: ReadingProgress is what a busy selected response shows (notification_progress.py ShownToModelProgress.selected_notification), CompletedProgress what a completed assignment shows. Titles are compared with MessageNotification.summary instead of restated strings. Co-Authored-By: Claude Opus 5.5 --- .../test_message_notification_title.py | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/tests/textual/test_message_notification_title.py b/tests/textual/test_message_notification_title.py index cee332f8f..9f6a9f1a0 100644 --- a/tests/textual/test_message_notification_title.py +++ b/tests/textual/test_message_notification_title.py @@ -3,44 +3,48 @@ from dataclasses import replace from agent_comms.audience_manifest import FrozenRecipient +from agent_comms.notification_progress import CompletedProgress, ReadingProgress, ShownToModelProgress from agent_comms.presentation import MessageNotification -from textual.app import App +from agent_comms.thread_identity import RecordedThreadIncarnation -from toad.textual_colours import ThemedApp +from toad.notification_status import CheckedNotifications, UnavailableNotifications from toad.widgets.message_notifications import MessageNotifications -class FeedbackApp(ThemedApp, App): - def compose(self): - yield MessageNotifications() - - -async def test_changed_details_and_errors_keep_the_title_layout_but_changed_summaries_publish(): - app = FeedbackApp() - async with app.run_test(size=(90, 20)) as pilot: - feedback = app.query_one(MessageNotifications) - first = MessageNotification(FrozenRecipient("peer", "peer"), "Responding", "First outcome", 1, True) - feedback.show_result((first,)) - await pilot.pause() +async def test_changed_details_and_errors_keep_the_title_layout_but_changed_summaries_publish(workspace): + workspace.thread("source") + feedback = MessageNotifications() + async with workspace.app(size=(90, 30), resume="source") as ui: + app, pilot = ui.textual + # Sent-message feedback lives inside a conversation, which owns its project path. + await ui.conversation.mount(feedback) + # The state Core shows while the recipient is producing its response. + responding = MessageNotification(FrozenRecipient("peer", "peer"), "owner", ReadingProgress(1)) + feedback.show(CheckedNotifications((responding,))) + await ui.step() title = feedback.query_one("CollapsibleTitle") - await pilot.click(title) - await pilot.pause() + assert await pilot.click(title) + await ui.step() assert not feedback.collapsed before = title._layout_updates - feedback.show_result((replace(first, detail="Updated outcome"),)) - await pilot.pause() + read_by = replace(responding, displayed_to=(RecordedThreadIncarnation("reader", 1.0),)) + feedback.show(CheckedNotifications((read_by,))) + await ui.step() assert title._layout_updates == before - assert "Updated outcome" in str(feedback.details.render()) - assert "Responding (1)" in str(feedback.title) and feedback.has_class("-working") - feedback.show_result((replace(first, state="Responded", busy=False),)) - await pilot.pause() - assert "Responded (1)" in str(feedback.title) and not feedback.has_class("-working") - feedback.show_error(OSError("First read error")) - await pilot.pause() + await ui.until(lambda: "Read by reader" in " ".join(ui.painted(feedback).split()), why="the changed details") + assert str(feedback.title) == MessageNotification.summary((read_by,)) + assert feedback.has_class("-working") + completed = replace(read_by, state=CompletedProgress(ShownToModelProgress(1))) + feedback.show(CheckedNotifications((completed,))) + await ui.step() + assert str(feedback.title) == MessageNotification.summary((completed,)) + assert str(feedback.title) != MessageNotification.summary((read_by,)) + assert not feedback.has_class("-working") + feedback.show(UnavailableNotifications("First read error")) + await ui.step() before = title._layout_updates - feedback.show_error(OSError("Changed read error")) - await pilot.pause() + feedback.show(UnavailableNotifications("Changed read error")) + await ui.step() assert title._layout_updates == before - assert "Changed read error" in str(feedback.details.render()) + await ui.until(lambda: "Changed read error" in " ".join(ui.painted(feedback).split()), why="the changed error") assert feedback.has_class("-unavailable") - assert app._exception is None