324 lines
8.5 KiB
Python
324 lines
8.5 KiB
Python
import bouquin.bug_report_dialog as bugmod
|
|
from bouquin.bug_report_dialog import BugReportDialog
|
|
from bouquin import strings
|
|
from PySide6.QtWidgets import QMessageBox
|
|
from PySide6.QtGui import QTextCursor
|
|
|
|
|
|
def test_bug_report_truncates_text_to_max_chars(qtbot):
|
|
dlg = BugReportDialog()
|
|
qtbot.addWidget(dlg)
|
|
dlg.show()
|
|
|
|
max_chars = getattr(dlg, "MAX_CHARS", 5000)
|
|
|
|
# Make a string longer than the allowed maximum
|
|
long_text = "x" * (max_chars + 50)
|
|
|
|
# Setting the text should trigger textChanged -> _enforce_max_length
|
|
dlg.text_edit.setPlainText(long_text)
|
|
|
|
# Let Qt process the signal/slot if needed
|
|
qtbot.wait(10)
|
|
|
|
current = dlg.text_edit.toPlainText()
|
|
assert len(current) == max_chars
|
|
assert current == long_text[:max_chars]
|
|
|
|
|
|
def test_bug_report_allows_up_to_max_chars_unchanged(qtbot):
|
|
dlg = BugReportDialog()
|
|
qtbot.addWidget(dlg)
|
|
dlg.show()
|
|
|
|
max_chars = getattr(dlg, "MAX_CHARS", 5000)
|
|
exact_text = "y" * max_chars
|
|
|
|
dlg.text_edit.setPlainText(exact_text)
|
|
qtbot.wait(10)
|
|
|
|
current = dlg.text_edit.toPlainText()
|
|
# Should not be trimmed if it's exactly the limit
|
|
assert len(current) == max_chars
|
|
assert current == exact_text
|
|
|
|
|
|
def test_bug_report_send_success_201_shows_info_and_accepts(qtbot, monkeypatch):
|
|
dlg = BugReportDialog()
|
|
qtbot.addWidget(dlg)
|
|
dlg.show()
|
|
|
|
# Non-empty message so we don't hit the "empty" warning branch
|
|
dlg.text_edit.setPlainText("Hello, something broke.")
|
|
qtbot.wait(10)
|
|
|
|
# Make version() deterministic
|
|
def fake_version(pkg_name):
|
|
assert pkg_name == "bouquin"
|
|
return "1.2.3"
|
|
|
|
monkeypatch.setattr(
|
|
bugmod.importlib.metadata, "version", fake_version, raising=True
|
|
)
|
|
|
|
# Capture the POST call and fake a 201 Created response
|
|
calls = {}
|
|
|
|
class DummyResp:
|
|
status_code = 201
|
|
|
|
def fake_post(url, json=None, timeout=None):
|
|
calls["url"] = url
|
|
calls["json"] = json
|
|
calls["timeout"] = timeout
|
|
return DummyResp()
|
|
|
|
monkeypatch.setattr(bugmod.requests, "post", fake_post, raising=True)
|
|
|
|
# Capture information / critical message boxes
|
|
info_called = {}
|
|
crit_called = {}
|
|
|
|
def fake_info(parent, title, text, *a, **k):
|
|
info_called["title"] = title
|
|
info_called["text"] = str(text)
|
|
return 0
|
|
|
|
def fake_critical(parent, title, text, *a, **k):
|
|
crit_called["title"] = title
|
|
crit_called["text"] = str(text)
|
|
return 0
|
|
|
|
monkeypatch.setattr(
|
|
bugmod.QMessageBox, "information", staticmethod(fake_info), raising=True
|
|
)
|
|
monkeypatch.setattr(
|
|
bugmod.QMessageBox, "critical", staticmethod(fake_critical), raising=True
|
|
)
|
|
|
|
# Don't actually close the dialog in the test; just record that accept() was called
|
|
accepted = {}
|
|
|
|
def fake_accept():
|
|
accepted["called"] = True
|
|
|
|
dlg.accept = fake_accept
|
|
|
|
# Call the send logic directly
|
|
dlg._send()
|
|
|
|
# --- Assertions ---------------------------------------------------------
|
|
|
|
# POST was called with the expected URL and JSON payload
|
|
assert calls["url"] == f"{bugmod.BUG_REPORT_HOST}/{bugmod.ROUTE}"
|
|
assert calls["json"]["message"] == "Hello, something broke."
|
|
assert calls["json"]["version"] == "1.2.3"
|
|
# No attachment fields expected any more
|
|
|
|
# Success path: information dialog shown, critical not shown
|
|
assert "title" in info_called
|
|
assert "text" in info_called
|
|
assert crit_called == {}
|
|
|
|
# Dialog accepted
|
|
assert accepted.get("called") is True
|
|
|
|
|
|
def test_bug_report_send_failure_non_201_shows_critical_and_not_accepted(
|
|
qtbot, monkeypatch
|
|
):
|
|
dlg = BugReportDialog()
|
|
qtbot.addWidget(dlg)
|
|
dlg.show()
|
|
|
|
dlg.text_edit.setPlainText("Broken again.")
|
|
qtbot.wait(10)
|
|
|
|
# Stub version() again
|
|
monkeypatch.setattr(
|
|
bugmod.importlib.metadata,
|
|
"version",
|
|
lambda name: "9.9.9",
|
|
raising=True,
|
|
)
|
|
|
|
# Fake a non-201 response (e.g. 500)
|
|
calls = {}
|
|
|
|
class DummyResp:
|
|
status_code = 500
|
|
|
|
def fake_post(url, json=None, timeout=None):
|
|
calls["url"] = url
|
|
calls["json"] = json
|
|
calls["timeout"] = timeout
|
|
return DummyResp()
|
|
|
|
monkeypatch.setattr(bugmod.requests, "post", fake_post, raising=True)
|
|
|
|
info_called = {}
|
|
crit_called = {}
|
|
|
|
def fake_info(parent, title, text, *a, **k):
|
|
info_called["title"] = title
|
|
info_called["text"] = str(text)
|
|
return 0
|
|
|
|
def fake_critical(parent, title, text, *a, **k):
|
|
crit_called["title"] = title
|
|
crit_called["text"] = str(text)
|
|
return 0
|
|
|
|
monkeypatch.setattr(
|
|
bugmod.QMessageBox, "information", staticmethod(fake_info), raising=True
|
|
)
|
|
monkeypatch.setattr(
|
|
bugmod.QMessageBox, "critical", staticmethod(fake_critical), raising=True
|
|
)
|
|
|
|
accepted = {}
|
|
|
|
def fake_accept():
|
|
accepted["called"] = True
|
|
|
|
dlg.accept = fake_accept
|
|
|
|
dlg._send()
|
|
|
|
# POST still called with JSON payload
|
|
assert calls["url"] == f"{bugmod.BUG_REPORT_HOST}/{bugmod.ROUTE}"
|
|
assert calls["json"]["message"] == "Broken again."
|
|
assert calls["json"]["version"] == "9.9.9"
|
|
|
|
# Failure path: critical dialog shown, information not shown
|
|
assert crit_called # non-empty
|
|
assert info_called == {}
|
|
|
|
# Dialog should NOT be accepted on failure
|
|
assert accepted.get("called") is not True
|
|
|
|
|
|
def test_bug_report_dialog_text_limit_clamps_cursor(qtbot):
|
|
"""Test that cursor position is clamped when text exceeds limit."""
|
|
strings.load_strings("en")
|
|
dialog = BugReportDialog()
|
|
qtbot.addWidget(dialog)
|
|
dialog.show()
|
|
|
|
# Set text that exceeds MAX_CHARS
|
|
max_chars = dialog.MAX_CHARS
|
|
long_text = "A" * (max_chars + 100)
|
|
|
|
# Set text and move cursor to end
|
|
dialog.text_edit.setPlainText(long_text)
|
|
dialog.text_edit.moveCursor(QTextCursor.MoveOperation.End)
|
|
|
|
# Text should be truncated
|
|
assert len(dialog.text_edit.toPlainText()) == max_chars
|
|
|
|
# Cursor should be clamped to max position
|
|
final_cursor = dialog.text_edit.textCursor()
|
|
assert final_cursor.position() <= max_chars
|
|
|
|
|
|
def test_bug_report_dialog_empty_text_shows_warning(qtbot, monkeypatch):
|
|
"""Test that sending empty report shows warning."""
|
|
strings.load_strings("en")
|
|
dialog = BugReportDialog()
|
|
qtbot.addWidget(dialog)
|
|
dialog.show()
|
|
|
|
# Clear any text
|
|
dialog.text_edit.clear()
|
|
|
|
warning_shown = {"shown": False}
|
|
|
|
def mock_warning(*args):
|
|
warning_shown["shown"] = True
|
|
|
|
monkeypatch.setattr(QMessageBox, "warning", mock_warning)
|
|
|
|
# Try to send empty report
|
|
dialog._send()
|
|
|
|
assert warning_shown["shown"]
|
|
|
|
|
|
def test_bug_report_dialog_whitespace_only_shows_warning(qtbot, monkeypatch):
|
|
"""Test that sending whitespace-only report shows warning."""
|
|
strings.load_strings("en")
|
|
dialog = BugReportDialog()
|
|
qtbot.addWidget(dialog)
|
|
dialog.show()
|
|
|
|
# Set whitespace only
|
|
dialog.text_edit.setPlainText(" \n\n \t\t ")
|
|
|
|
warning_shown = {"shown": False}
|
|
|
|
def mock_warning(*args):
|
|
warning_shown["shown"] = True
|
|
|
|
monkeypatch.setattr(QMessageBox, "warning", mock_warning)
|
|
|
|
dialog._send()
|
|
|
|
assert warning_shown["shown"]
|
|
|
|
|
|
def test_bug_report_dialog_network_error(qtbot, monkeypatch):
|
|
"""Test handling network error during send."""
|
|
strings.load_strings("en")
|
|
dialog = BugReportDialog()
|
|
qtbot.addWidget(dialog)
|
|
dialog.show()
|
|
|
|
dialog.text_edit.setPlainText("Test bug report")
|
|
|
|
# Mock requests.post to raise exception
|
|
import requests
|
|
|
|
def mock_post(*args, **kwargs):
|
|
raise requests.exceptions.ConnectionError("Network error")
|
|
|
|
monkeypatch.setattr(requests, "post", mock_post)
|
|
|
|
critical_shown = {"shown": False}
|
|
|
|
def mock_critical(*args):
|
|
critical_shown["shown"] = True
|
|
|
|
monkeypatch.setattr(QMessageBox, "critical", mock_critical)
|
|
|
|
dialog._send()
|
|
|
|
assert critical_shown["shown"]
|
|
|
|
|
|
def test_bug_report_dialog_timeout_error(qtbot, monkeypatch):
|
|
"""Test handling timeout error during send."""
|
|
strings.load_strings("en")
|
|
dialog = BugReportDialog()
|
|
qtbot.addWidget(dialog)
|
|
dialog.show()
|
|
|
|
dialog.text_edit.setPlainText("Test bug report")
|
|
|
|
# Mock requests.post to raise timeout
|
|
import requests
|
|
|
|
def mock_post(*args, **kwargs):
|
|
raise requests.exceptions.Timeout("Request timed out")
|
|
|
|
monkeypatch.setattr(requests, "post", mock_post)
|
|
|
|
critical_shown = {"shown": False}
|
|
|
|
def mock_critical(*args):
|
|
critical_shown["shown"] = True
|
|
|
|
monkeypatch.setattr(QMessageBox, "critical", mock_critical)
|
|
|
|
dialog._send()
|
|
|
|
assert critical_shown["shown"]
|