Fix focusing on editor after leaving the app and returning. More code coverage and removing obsolete bits of code

This commit is contained in:
Miguel Jacq 2025-11-07 13:53:27 +11:00
parent 74177f2cd3
commit aad1ba5d7d
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
16 changed files with 264 additions and 100 deletions

View file

@ -1,6 +1,5 @@
from __future__ import annotations
import os
from pathlib import Path
import pytest
@ -74,25 +73,14 @@ def test_revert_to_version_by_number_and_id_and_errors(cfg: DBConfig):
ver2_id, ver2_no = mgr.save_new_version("2025-01-04", "<p>v2</p>", note="edit")
assert ver1_no == 1 and ver2_no == 2
# Revert using version_no (exercises branch where version_id is None)
mgr.revert_to_version(date_iso="2025-01-04", version_no=1, version_id=None)
cur = mgr.conn.cursor()
head = cur.execute(
"SELECT current_version_id FROM pages WHERE date=?", ("2025-01-04",)
).fetchone()[0]
assert head == ver1_id
# Revert using version_id directly should also work
# Revert using version_id
mgr.revert_to_version(date_iso="2025-01-04", version_id=ver2_id)
cur = mgr.conn.cursor()
head2 = cur.execute(
"SELECT current_version_id FROM pages WHERE date=?", ("2025-01-04",)
).fetchone()[0]
assert head2 == ver2_id
# Error: version not found for date (non-existent version_no)
with pytest.raises(ValueError):
mgr.revert_to_version(date_iso="2025-01-04", version_no=99)
# Error: version_id belongs to a different date
other_id, _ = mgr.save_new_version("2025-01-05", "<p>other</p>")
with pytest.raises(ValueError):

View file

@ -114,7 +114,7 @@ def test_export_by_extension_and_unknown(tmp_path):
import types
mgr.get_all_entries = types.MethodType(lambda self: entries, mgr)
for ext in [".json", ".csv", ".txt", ".html"]:
for ext in [".json", ".csv", ".txt", ".html", ".md"]:
path = tmp_path / f"route{ext}"
mgr.export_by_extension(str(path))
assert path.exists()

View file

@ -145,10 +145,6 @@ def test_linkify_trims_trailing_punctuation(qtbot):
def test_code_block_enter_exits_on_empty_line(qtbot):
from PySide6.QtCore import Qt
from PySide6.QtGui import QTextCursor
from PySide6.QtTest import QTest
from bouquin.editor import Editor
e = _mk_editor()
qtbot.addWidget(e)

View file

@ -1,9 +1,8 @@
import base64
from io import BytesIO
import pytest
from PySide6.QtCore import Qt, QMimeData, QByteArray
from PySide6.QtGui import QImage, QPixmap, QKeyEvent, QTextCursor
from PySide6.QtGui import QImage, QTextCursor
from PySide6.QtWidgets import QApplication
from PySide6.QtTest import QTest

View file

@ -1,6 +1,4 @@
import base64
from pathlib import Path
from PySide6.QtCore import QUrl, QByteArray
from PySide6.QtCore import QUrl
from PySide6.QtGui import QImage, QTextCursor, QTextImageFormat, QColor
from bouquin.theme import ThemeManager
from bouquin.editor import Editor

136
tests/test_editor_more.py Normal file
View file

@ -0,0 +1,136 @@
from PySide6.QtCore import Qt, QEvent, QUrl, QObject, Slot
from PySide6.QtGui import QImage, QMouseEvent, QTextCursor
from PySide6.QtTest import QTest
from PySide6.QtWidgets import QApplication
from bouquin.editor import Editor
from bouquin.theme import ThemeManager, ThemeConfig
def _mk_editor() -> Editor:
app = QApplication.instance()
tm = ThemeManager(app, ThemeConfig())
e = Editor(tm)
e.resize(700, 400)
e.show()
return e
def _point_for_char(e: Editor, pos: int):
c = e.textCursor()
c.setPosition(pos)
r = e.cursorRect(c)
return r.center()
def test_trim_url_and_linkify_and_ctrl_mouse(qtbot):
e = _mk_editor()
qtbot.addWidget(e)
assert e._trim_url_end("https://ex.com)") == "https://ex.com"
assert e._trim_url_end("www.mysite.org]") == "www.mysite.org"
url = "https://example.org/path"
QTest.keyClicks(e, url)
qtbot.waitUntil(lambda: url in e.toPlainText())
p = _point_for_char(e, 0)
move = QMouseEvent(
QEvent.MouseMove, p, Qt.NoButton, Qt.NoButton, Qt.ControlModifier
)
e.mouseMoveEvent(move)
assert e.viewport().cursor().shape() == Qt.PointingHandCursor
opened = {}
class Catcher(QObject):
@Slot(QUrl)
def handle(self, u: QUrl):
opened["u"] = u.toString()
from PySide6.QtGui import QDesktopServices
catcher = Catcher()
QDesktopServices.setUrlHandler("https", catcher, "handle")
try:
rel = QMouseEvent(
QEvent.MouseButtonRelease,
p,
Qt.LeftButton,
Qt.LeftButton,
Qt.ControlModifier,
)
e.mouseReleaseEvent(rel)
got_signal = []
e.linkActivated.connect(lambda href: got_signal.append(href))
e.mouseReleaseEvent(rel)
assert opened or got_signal
finally:
QDesktopServices.unsetUrlHandler("https")
def test_insert_images_and_image_helpers(qtbot, tmp_path):
e = _mk_editor()
qtbot.addWidget(e)
# No image under cursor yet (412 guard)
tc, fmt, orig = e._image_info_at_cursor()
assert tc is None and fmt is None and orig is None
# Insert a real image file (574584 path)
img_path = tmp_path / "tiny.png"
img = QImage(4, 4, QImage.Format_ARGB32)
img.fill(0xFF336699)
assert img.save(str(img_path), "PNG")
e.insert_images([str(img_path)], autoscale=False)
assert "<img" in e.toHtml()
# Guards when not on an image (453, 464)
e._scale_image_at_cursor(1.1)
e._fit_image_to_editor_width()
def test_checkbox_click_and_enter_continuation(qtbot):
e = _mk_editor()
qtbot.addWidget(e)
e.setPlainText("☐ task one")
# Need it visible for mouse coords
e.resize(600, 300)
e.show()
qtbot.waitExposed(e)
# Click on the checkbox glyph to toggle (605614)
start_point = _point_for_char(e, 0)
press = QMouseEvent(
QEvent.MouseButtonPress,
start_point,
Qt.LeftButton,
Qt.LeftButton,
Qt.NoModifier,
)
e.mousePressEvent(press)
assert e.toPlainText().startswith("")
# Press Enter at end -> new line with fresh checkbox (680684)
c = e.textCursor()
c.movePosition(QTextCursor.End)
e.setTextCursor(c)
QTest.keyClick(e, Qt.Key_Return)
lines = e.toPlainText().splitlines()
assert len(lines) >= 2 and lines[1].startswith("")
def test_heading_and_lists_toggle_remove(qtbot):
e = _mk_editor()
qtbot.addWidget(e)
e.setPlainText("para")
# "Normal" path is size=0 (904…)
e.apply_heading(0)
# bullets twice -> second call removes (945946)
e.toggle_bullets()
e.toggle_bullets()
# numbers twice -> second call removes (955956)
e.toggle_numbers()
e.toggle_numbers()

View file

@ -1,7 +1,6 @@
import runpy
import types
import sys
import builtins
def test_dunder_main_executes_without_launching_qt(monkeypatch):

View file

@ -1,7 +1,4 @@
import os
from datetime import date, timedelta
from pathlib import Path
from PySide6.QtCore import QDate, QByteArray
from PySide6.QtCore import QDate
from bouquin.theme import ThemeManager
from bouquin.main_window import MainWindow
from bouquin.settings import save_db_config

View file

@ -0,0 +1,15 @@
from bouquin.search import Search as SearchWidget
class DummyDB:
def search_entries(self, q):
return []
def test_make_html_snippet_no_match_triggers_start_window(qtbot):
w = SearchWidget(db=DummyDB())
qtbot.addWidget(w)
html = "<p>" + ("x" * 300) + "</p>" # long text, no token present
frag, left, right = w._make_html_snippet(html, "notfound", radius=10, maxlen=80)
assert frag != ""
assert left is False and right is True

View file

@ -1,5 +1,3 @@
import os
import tempfile
from PySide6.QtWidgets import QApplication
import pytest
@ -47,7 +45,6 @@ def test_search_exception_path_closed_db_triggers_quiet_handling(app, fresh_db,
# Typing should not raise; exception path returns empty results
w._search("anything")
assert w.results.isHidden() # remains hidden because there are no rows
# Also, the "resultDatesChanged" signal should emit an empty list (coverage on that branch)
def test_make_html_snippet_ellipses_both_sides(app, fresh_db):
@ -59,3 +56,15 @@ def test_make_html_snippet_ellipses_both_sides(app, fresh_db):
assert snippet # non-empty
assert left_ell is True
assert right_ell is True
def test_search_results_middle(app, fresh_db, qtbot):
w = Search(fresh_db)
w.show()
qtbot.addWidget(w)
# Choose a query so that the first match sits well inside a long string,
# forcing both left and right ellipses.
assert fresh_db.connect()
w._search("middle")
assert w.results.isVisible()

View file

@ -1,5 +1,4 @@
import pytest
from PySide6.QtWidgets import QWidget
from bouquin.search import Search

View file

@ -1,6 +1,4 @@
import types
import pytest
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QDialog, QWidget
from bouquin.db import DBConfig, DBManager