From 608008894a1d9352519ef2362c4306e28d414feb Mon Sep 17 00:00:00 2001 From: Christopher Toth Date: Thu, 29 Oct 2026 12:13:25 -0600 Subject: [PATCH] Explain issue report submission failures clearly --- agent-notes/improve-report-feedback.md | 26 ++++++++++ app_elements/interface.py | 6 +-- tests/test_report_feedback.py | 67 ++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 agent-notes/improve-report-feedback.md create mode 100644 tests/test_report_feedback.py diff --git a/agent-notes/improve-report-feedback.md b/agent-notes/improve-report-feedback.md new file mode 100644 index 0000000..966a596 --- /dev/null +++ b/agent-notes/improve-report-feedback.md @@ -0,0 +1,26 @@ +# Goal + +Give users a clear translated issue-submission failure and next action. + +# State + +Isolated branch based on improve/integration-contract (PR #1). Changes only the +failure wording and logger message in the existing asynchronous reporting action. +Tests exercise failure, success metadata and cancellation through the generator. + +# Decisions and why + +Keep report collection/send machinery and existing dialog lifetime. Say the report +was not submitted and offer retry later rather than guessing a network/auth cause. +Use the existing app_elements translation registration; no fabricated translations +or binary catalog edits. New source messages fall back to English until translated. + +# Surprises + +The original failure text did not explain an action. This slice changes no protocol, +transport, async scheduling, cancellation policy or issue_reporter implementation. + +# Next step + +Publish and read CI. Controlled generator/service tests do not establish real wx +scheduling, server delivery or live screen-reader output. No merge or release. diff --git a/app_elements/interface.py b/app_elements/interface.py index dd4dab6..ff38622 100644 --- a/app_elements/interface.py +++ b/app_elements/interface.py @@ -73,10 +73,10 @@ def report_issue(*args, **kwargs): try: yield Task(application.issue_reporter.send_report, report) except Exception as e: - logger.exception("Error submitting issue report. Not a good day.") + logger.exception("Error submitting issue report") popups.warning_message( - title=_("Error"), - message=_("There was an issue submitting your issue report. Badness."), + title=_("Issue report not submitted"), + message=_("Your issue report could not be submitted. Please try again later."), ) return popups.message_box(title=_("Issue submitted"), message=_("Thanks for your report!")) diff --git a/tests/test_report_feedback.py b/tests/test_report_feedback.py new file mode 100644 index 0000000..b2130ce --- /dev/null +++ b/tests/test_report_feedback.py @@ -0,0 +1,67 @@ +import types +import unittest +from unittest.mock import Mock, patch + +from support import load_component + + +class ReportFeedbackTests(unittest.TestCase): + def dialog(self, modules, result=1): + report = types.SimpleNamespace(log_paths=[]) + dialog = Mock() + dialog.display_modal.return_value = result + dialog.get_report.return_value = report + gui = types.ModuleType("issue_reporter.gui") + gui.IssueReporterDialog = Mock(return_value=dialog) + package = types.ModuleType("issue_reporter") + package.gui = gui + modules.update({"issue_reporter": package, "issue_reporter.gui": gui}) + return dialog, report + + def application_values(self): + return dict(main_window=object(), version="1.2", error_log_path="example.log", + issue_reporter=Mock()) + + def test_send_failure_warns_without_a_success_message(self): + with load_component("app_elements.interface", **self.application_values()) as (component, app, modules): + dialog, report = self.dialog(modules) + with patch.dict("sys.modules", modules): + operation = component.report_issue() + next(operation) + dialog.destroy.assert_called_once() + modules["app_framework.background"].Task.assert_called_once_with(app.issue_reporter.send_report, report) + with self.assertLogs("app_elements.interface", level="ERROR"): + with self.assertRaises(StopIteration): + operation.throw(ConnectionError("offline")) + warning = modules["wx_utils.popups"].warning_message + warning.assert_called_once_with( + title="Issue report not submitted", + message="Your issue report could not be submitted. Please try again later.", + ) + modules["wx_utils.popups"].message_box.assert_not_called() + + def test_success_keeps_report_metadata_and_confirmation(self): + with load_component("app_elements.interface", **self.application_values()) as (component, app, modules): + dialog, report = self.dialog(modules) + with patch.dict("sys.modules", modules): + operation = component.report_issue() + next(operation) + with self.assertRaises(StopIteration): + operation.send(None) + self.assertEqual(report.application_name, app.name) + self.assertEqual(report.application_version, app.version) + self.assertEqual(report.log_paths, [app.error_log_path]) + dialog.destroy.assert_called_once() + modules["wx_utils.popups"].message_box.assert_called_once() + modules["wx_utils.popups"].warning_message.assert_not_called() + + def test_cancel_destroys_dialog_without_sending_or_announcing_success(self): + with load_component("app_elements.interface", **self.application_values()) as (component, _, modules): + dialog, _ = self.dialog(modules, result=0) + with patch.dict("sys.modules", modules): + with self.assertRaises(StopIteration): + next(component.report_issue()) + dialog.destroy.assert_called_once() + modules["app_framework.background"].Task.assert_not_called() + modules["wx_utils.popups"].message_box.assert_not_called() + modules["wx_utils.popups"].warning_message.assert_not_called()