bouquin/tests/test_code_block_editor_dialog.py
Miguel Jacq 0b76f0b490
All checks were successful
Lint / test (push) Successful in 34s
Trivy / test (push) Successful in 22s
CI / test (push) Successful in 6m4s
Consolidate some code related to opening documents using the Documents feature. More code coverage
2025-12-02 11:01:27 +11:00

327 lines
9.9 KiB
Python

from PySide6.QtWidgets import QPushButton
from bouquin import strings
from PySide6.QtCore import QRect, QSize
from PySide6.QtGui import QPaintEvent, QFont
from bouquin.code_block_editor_dialog import (
CodeBlockEditorDialog,
CodeEditorWithLineNumbers,
)
def _find_button_by_text(widget, text):
for btn in widget.findChildren(QPushButton):
if text.lower() in btn.text().lower():
return btn
return None
def test_code_block_dialog_delete_flow(qtbot):
dlg = CodeBlockEditorDialog("print(1)", "python", allow_delete=True)
qtbot.addWidget(dlg)
delete_txt = strings._("delete_code_block")
delete_btn = _find_button_by_text(dlg, delete_txt)
assert delete_btn is not None
assert not dlg.was_deleted()
with qtbot.waitSignal(dlg.finished, timeout=2000):
delete_btn.click()
assert dlg.was_deleted()
def test_code_block_dialog_language_and_code(qtbot):
dlg = CodeBlockEditorDialog("x = 1", "not-a-lang", allow_delete=False)
qtbot.addWidget(dlg)
delete_txt = strings._("delete_code_block")
assert _find_button_by_text(dlg, delete_txt) is None
assert dlg.code() == "x = 1"
assert dlg.language() is None
def test_line_number_area_size_hint(qtbot, app):
"""Test _LineNumberArea.sizeHint() method."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
line_area = editor._line_number_area
size_hint = line_area.sizeHint()
# Should return a QSize with width from editor
assert isinstance(size_hint, QSize)
assert size_hint.width() > 0
assert size_hint.height() == 0
def test_line_number_area_paint_event(qtbot, app):
"""Test _LineNumberArea.paintEvent() method."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.setPlainText("Line 1\nLine 2\nLine 3")
editor.show()
# Trigger a paint event on the line number area
line_area = editor._line_number_area
paint_event = QPaintEvent(QRect(0, 0, line_area.width(), line_area.height()))
line_area.paintEvent(paint_event)
# Should not crash
def test_line_number_font_pixel_size_fallback(qtbot, app):
"""Test _line_number_font() with pixel-sized font."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
# Set a pixel-sized font (pointSize will be -1)
font = QFont()
font.setPixelSize(14)
editor.setFont(font)
# Get line number font - should use the fallback
line_font = editor._line_number_font()
# Should have calculated a size
assert line_font.pointSizeF() > 0 or line_font.pixelSize() > 0
def test_code_editor_resize_event(qtbot, app):
"""Test CodeEditorWithLineNumbers.resizeEvent() method."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.show()
# Resize the editor
editor.resize(400, 300)
# Line number area should be repositioned
line_area = editor._line_number_area
assert line_area.geometry().width() > 0
assert line_area.geometry().height() == editor.contentsRect().height()
def test_code_editor_update_with_scroll(qtbot, app):
"""Test _update_line_number_area with dy (scroll) parameter."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
# Add enough text to enable scrolling
text = "\n".join([f"Line {i}" for i in range(100)])
editor.setPlainText(text)
editor.show()
# Trigger update with scroll offset
rect = QRect(0, 0, 100, 100)
editor._update_line_number_area(rect, dy=10)
# Should not crash
def test_code_editor_update_without_scroll(qtbot, app):
"""Test _update_line_number_area without scroll (dy=0)."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.setPlainText("Line 1\nLine 2")
editor.show()
# Trigger update without scroll
rect = QRect(0, 0, 100, 100)
editor._update_line_number_area(rect, dy=0)
# Should not crash
def test_code_editor_update_contains_viewport(qtbot, app):
"""Test _update_line_number_area when rect contains viewport."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.setPlainText("Test")
editor.show()
# Trigger update with rect that contains viewport
viewport_rect = editor.viewport().rect()
editor._update_line_number_area(viewport_rect, dy=0)
# Should trigger width update (covers line 82)
def test_line_number_area_paint_with_multiple_blocks(qtbot, app):
"""Test line_number_area_paint_event with multiple text blocks."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
# Add multiple lines
text = "\n".join([f"Line {i}" for i in range(20)])
editor.setPlainText(text)
editor.show()
# Force a paint event
line_area = editor._line_number_area
rect = QRect(0, 0, line_area.width(), line_area.height())
paint_event = QPaintEvent(rect)
# This should exercise the painting loop (lines 87-130)
editor.line_number_area_paint_event(paint_event)
# Should not crash
def test_line_number_area_paint_with_long_file(qtbot, app):
"""Test line_number_area_paint_event with many lines."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
# Add 1000+ lines to test digit calculation and painting
text = "\n".join([f"Line {i}" for i in range(1000)])
editor.setPlainText(text)
editor.show()
# Trigger paint event
line_area = editor._line_number_area
paint_event = QPaintEvent(line_area.rect())
editor.line_number_area_paint_event(paint_event)
# Line number width should accommodate 4 digits
width = editor.line_number_area_width()
assert width > 30 # Should be wider for 4-digit numbers
def test_code_block_editor_dialog_with_delete(qtbot, app):
"""Test CodeBlockEditorDialog with allow_delete=True."""
dialog = CodeBlockEditorDialog("print('hello')", "python", allow_delete=True)
qtbot.addWidget(dialog)
# Should have delete button functionality
assert hasattr(dialog, "_delete_requested")
assert dialog._delete_requested is False
# Simulate delete click
dialog._on_delete_clicked()
assert dialog._delete_requested is True
assert dialog.was_deleted() is True
def test_code_block_editor_dialog_without_delete(qtbot, app):
"""Test CodeBlockEditorDialog with allow_delete=False."""
dialog = CodeBlockEditorDialog("print('hello')", "python", allow_delete=False)
qtbot.addWidget(dialog)
# Should not have been deleted
assert dialog.was_deleted() is False
def test_code_block_editor_dialog_language_selection(qtbot, app):
"""Test language selection in dialog."""
dialog = CodeBlockEditorDialog("test", "javascript")
qtbot.addWidget(dialog)
# Should have selected javascript
assert dialog.language() == "javascript"
# Change language
dialog._lang_combo.setCurrentText("python")
assert dialog.language() == "python"
# Empty language
dialog._lang_combo.setCurrentText("")
assert dialog.language() is None
def test_code_block_editor_dialog_code_retrieval(qtbot, app):
"""Test getting code from dialog."""
original_code = "def foo():\n pass"
dialog = CodeBlockEditorDialog(original_code, None)
qtbot.addWidget(dialog)
# Should return the code
assert dialog.code() == original_code
# Modify code
new_code = "def bar():\n return 42"
dialog._code_edit.setPlainText(new_code)
assert dialog.code() == new_code
def test_code_editor_with_empty_text(qtbot, app):
"""Test editor with no text."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.show()
# Should still paint line numbers
line_area = editor._line_number_area
paint_event = QPaintEvent(line_area.rect())
editor.line_number_area_paint_event(paint_event)
# Should not crash
def test_code_editor_block_count_changed(qtbot, app):
"""Test that block count changes trigger width updates."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
initial_width = editor.line_number_area_width()
# Add lots of lines (should require more digits)
text = "\n".join([f"Line {i}" for i in range(1000)])
editor.setPlainText(text)
new_width = editor.line_number_area_width()
# Width should increase for more digits
assert new_width > initial_width
def test_code_editor_cursor_position_changed(qtbot, app):
"""Test that cursor position changes update line number area."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.setPlainText("Line 1\nLine 2\nLine 3")
editor.show()
# Move cursor
cursor = editor.textCursor()
cursor.movePosition(cursor.MoveOperation.End)
editor.setTextCursor(cursor)
# Should trigger line number area update (via signal connection)
# Just verify it doesn't crash
def test_line_number_area_width_calculation(qtbot, app):
"""Test line number area width calculation with various block counts."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
# Test with 1 line (should use minimum 2 digits)
editor.setPlainText("One line")
width_1 = editor.line_number_area_width()
assert width_1 > 0
# Test with 10 lines (2 digits)
editor.setPlainText("\n".join(["Line"] * 10))
width_10 = editor.line_number_area_width()
assert width_10 >= width_1
# Test with 100 lines (3 digits)
editor.setPlainText("\n".join(["Line"] * 100))
width_100 = editor.line_number_area_width()
assert width_100 > width_10
def test_code_editor_viewport_margins(qtbot, app):
"""Test that viewport margins are set correctly."""
editor = CodeEditorWithLineNumbers()
qtbot.addWidget(editor)
editor.setPlainText("Test")
editor.show()
# Left margin should equal line number area width
margins = editor.viewportMargins()
line_width = editor.line_number_area_width()
assert margins.left() == line_width
assert margins.top() == 0
assert margins.right() == 0
assert margins.bottom() == 0