bouquin/tests/test_tabs.py
Miguel Jacq 6bc5b66d3f
All checks were successful
CI / test (push) Successful in 3m49s
Lint / test (push) Successful in 29s
Trivy / test (push) Successful in 21s
Add the ability to choose the database path at startup. Add more tests. Add bandit
2025-11-17 15:15:00 +11:00

195 lines
6 KiB
Python

import types
from PySide6.QtWidgets import QFileDialog
from PySide6.QtGui import QTextCursor
from bouquin.theme import ThemeManager, ThemeConfig, Theme
from bouquin.settings import get_settings
from bouquin.main_window import MainWindow
from bouquin.history_dialog import HistoryDialog
def test_tabs_open_and_deduplicate(qtbot, app, tmp_db_cfg, fresh_db):
# point to the temp encrypted DB
s = get_settings()
s.setValue("db/default_db", str(tmp_db_cfg.path))
s.setValue("db/key", tmp_db_cfg.key)
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
w = MainWindow(themes=themes)
qtbot.addWidget(w)
w.show()
# first tab is today's date
date1 = w.calendar.selectedDate()
initial_count = w.tab_widget.count()
# opening the same date should NOT create a new tab
w._open_date_in_tab(date1)
assert w.tab_widget.count() == initial_count
assert w.tab_widget.currentWidget().current_date == date1
# opening a different date should create exactly one new tab
date2 = date1.addDays(1)
w._open_date_in_tab(date2)
assert w.tab_widget.count() == initial_count + 1
assert w.tab_widget.currentWidget().current_date == date2
# jumping back to date1 just focuses the existing tab
w._open_date_in_tab(date1)
assert w.tab_widget.count() == initial_count + 1
assert w.tab_widget.currentWidget().current_date == date1
def test_toolbar_signals_dispatch_once_per_click(
qtbot, app, tmp_db_cfg, fresh_db, monkeypatch
):
s = get_settings()
s.setValue("db/default_db", str(tmp_db_cfg.path))
s.setValue("db/key", tmp_db_cfg.key)
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
w = MainWindow(themes=themes)
qtbot.addWidget(w)
w.show()
tb = w.toolBar
# Spy on the first tab's editor
calls1 = {
"bold": 0,
"italic": 0,
"strike": 0,
"code": 0,
"heading": 0,
"bullets": 0,
"numbers": 0,
"checkboxes": 0,
}
def mk(key):
def _spy(self, *a, **k):
calls1[key] += 1
return _spy
w.editor.apply_weight = types.MethodType(mk("bold"), w.editor)
w.editor.apply_italic = types.MethodType(mk("italic"), w.editor)
w.editor.apply_strikethrough = types.MethodType(mk("strike"), w.editor)
w.editor.apply_code = types.MethodType(mk("code"), w.editor)
w.editor.apply_heading = types.MethodType(mk("heading"), w.editor)
w.editor.toggle_bullets = types.MethodType(mk("bullets"), w.editor)
w.editor.toggle_numbers = types.MethodType(mk("numbers"), w.editor)
w.editor.toggle_checkboxes = types.MethodType(mk("checkboxes"), w.editor)
# Click all the things once
tb.boldRequested.emit()
tb.italicRequested.emit()
tb.strikeRequested.emit()
tb.codeRequested.emit()
tb.headingRequested.emit(24)
tb.bulletsRequested.emit()
tb.numbersRequested.emit()
tb.checkboxesRequested.emit()
assert all(v == 1 for v in calls1.values()) # fired once each
# Switch to a new tab and make sure clicks go ONLY to the active editor
date2 = w.calendar.selectedDate().addDays(1)
w._open_date_in_tab(date2)
calls2 = {"bold": 0}
w.editor.apply_weight = types.MethodType(
lambda self: calls2.__setitem__("bold", calls2["bold"] + 1), w.editor
)
tb.boldRequested.emit()
assert calls1["bold"] == 1
assert calls2["bold"] == 1
w._open_date_in_tab(date2.addDays(-1)) # back to first tab
tb.boldRequested.emit()
assert calls1["bold"] == 2
assert calls2["bold"] == 1
def test_history_and_insert_image_not_duplicated(
qtbot, app, tmp_db_cfg, fresh_db, monkeypatch, tmp_path
):
s = get_settings()
s.setValue("db/default_db", str(tmp_db_cfg.path))
s.setValue("db/key", tmp_db_cfg.key)
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
w = MainWindow(themes=themes)
qtbot.addWidget(w)
w.show()
# History dialog opens exactly once
opened = {"count": 0}
def fake_exec(self):
opened["count"] += 1
return 0 # Rejected
monkeypatch.setattr(HistoryDialog, "exec", fake_exec, raising=False)
w.toolBar.historyRequested.emit()
assert opened["count"] == 1
# Insert image: simulate user selecting one file, and ensure it's inserted once
dummy = tmp_path / "x.png"
dummy.write_bytes(b"\x89PNG\r\n\x1a\n")
inserted = {"count": 0}
def fake_insert(self, p):
inserted["count"] += 1
w.editor.insert_image_from_path = types.MethodType(fake_insert, w.editor)
monkeypatch.setattr(
QFileDialog,
"getOpenFileNames",
lambda *a, **k: ([str(dummy)], "Images (*.png)"),
raising=False,
)
w.toolBar.insertImageRequested.emit()
assert inserted["count"] == 1
def test_highlighter_attached_after_text_load(qtbot, app, tmp_db_cfg, fresh_db):
s = get_settings()
s.setValue("db/default_db", str(tmp_db_cfg.path))
s.setValue("db/key", tmp_db_cfg.key)
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
w = MainWindow(themes=themes)
qtbot.addWidget(w)
w.show()
w.editor.from_markdown("**bold**\n- [ ] task\n~~strike~~")
assert w.editor.highlighter is not None
assert w.editor.highlighter.document() is w.editor.document()
def test_findbar_works_for_current_tab(qtbot, app, tmp_db_cfg, fresh_db):
s = get_settings()
s.setValue("db/default_db", str(tmp_db_cfg.path))
s.setValue("db/key", tmp_db_cfg.key)
themes = ThemeManager(app, ThemeConfig(theme=Theme.LIGHT))
w = MainWindow(themes=themes)
qtbot.addWidget(w)
w.show()
# Tab 1 content
w.editor.from_markdown("alpha bravo charlie")
w.findBar.show_bar()
w.findBar.edit.setText("bravo")
w.findBar.find_next()
assert w.editor.textCursor().selectedText() == "bravo"
# Tab 2 content (contains the query too)
date2 = w.calendar.selectedDate().addDays(1)
w._open_date_in_tab(date2)
w.editor.from_markdown("x bravo y bravo z")
w.editor.moveCursor(QTextCursor.Start)
w.findBar.find_next()
assert w.editor.textCursor().selectedText() == "bravo"