Fix focusing on editor after leaving the app and returning. More code coverage and removing obsolete bits of code
This commit is contained in:
parent
74177f2cd3
commit
aad1ba5d7d
16 changed files with 264 additions and 100 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
136
tests/test_editor_more.py
Normal 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 (574–584 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 (605–614)
|
||||
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 (680–684)
|
||||
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 (945–946)
|
||||
e.toggle_bullets()
|
||||
e.toggle_bullets()
|
||||
# numbers twice -> second call removes (955–956)
|
||||
e.toggle_numbers()
|
||||
e.toggle_numbers()
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import runpy
|
||||
import types
|
||||
import sys
|
||||
import builtins
|
||||
|
||||
|
||||
def test_dunder_main_executes_without_launching_qt(monkeypatch):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
15
tests/test_search_edgecase.py
Normal file
15
tests/test_search_edgecase.py
Normal 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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import pytest
|
||||
from PySide6.QtWidgets import QWidget
|
||||
from bouquin.search import Search
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue