updated tests and installer
Python CI / test (push) Successful in 1m11s
Lint / lint (push) Successful in 1m9s
Tests / test (3.10) (push) Successful in 55s
Tests / test (3.11) (push) Successful in 56s
Tests / test (3.9) (push) Successful in 53s

This commit is contained in:
2025-11-11 13:21:35 +01:00
parent ca21f3ae4c
commit a7558e3c39
10 changed files with 2467 additions and 101 deletions
+87
View File
@@ -228,6 +228,44 @@ class TestGetElementAt:
result = widget._get_element_at(351, 301)
assert result is None
def test_get_element_at_rotated_element(self, qtbot, mock_page_renderer):
"""Test _get_element_at handles rotated elements correctly"""
widget = TestSelectionWidget()
qtbot.addWidget(widget)
page = Page(layout=PageLayout(width=210, height=297), page_number=1)
# Create element rotated 45 degrees
elem = ImageData(image_path="test.jpg", x=100, y=100, width=200, height=150)
elem.rotation = 45
page.layout.add_element(elem)
widget._page_renderers = [(mock_page_renderer, page)]
# Click at center of rotated element (should still be inside)
# Center is at (100 + 200/2, 100 + 150/2) = (200, 175) in page coords
# Screen coords: (50 + 200, 50 + 175) = (250, 225)
result = widget._get_element_at(250, 225)
assert result == elem
assert hasattr(result, '_page_renderer')
assert hasattr(result, '_parent_page')
def test_get_element_at_rotated_element_outside(self, qtbot, mock_page_renderer):
"""Test _get_element_at correctly rejects clicks outside rotated element"""
widget = TestSelectionWidget()
qtbot.addWidget(widget)
page = Page(layout=PageLayout(width=210, height=297), page_number=1)
# Create element rotated 90 degrees
elem = ImageData(image_path="test.jpg", x=100, y=100, width=200, height=150)
elem.rotation = 90
page.layout.add_element(elem)
widget._page_renderers = [(mock_page_renderer, page)]
# Click far outside element
result = widget._get_element_at(500, 500)
assert result is None
class TestGetResizeHandleAt:
"""Test _get_resize_handle_at method"""
@@ -350,6 +388,55 @@ class TestGetResizeHandleAt:
result = widget._get_resize_handle_at(250, 225)
assert result is None
def test_get_resize_handle_rotated_element(self, qtbot, mock_page_renderer):
"""Test _get_resize_handle_at handles rotated elements"""
widget = TestSelectionWidget()
qtbot.addWidget(widget)
elem = ImageData(image_path="test.jpg", x=100, y=100, width=200, height=150)
elem.rotation = 45
elem._page_renderer = mock_page_renderer
widget.selected_element = elem
# Mock window
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
mock_window = Mock()
mock_window.project = Project(name="Test")
page = Page(layout=PageLayout(width=210, height=297), page_number=1)
mock_window.project.pages = [page]
widget.window = Mock(return_value=mock_window)
# The rotation code should still detect handles - test NW handle
# For rotated element, the handle positions are transformed
result = widget._get_resize_handle_at(150, 150)
# Should detect a handle (exact handle depends on rotation transform)
assert result is None or result in ['nw', 'ne', 'sw', 'se']
def test_get_resize_handle_rotated_90_degrees(self, qtbot, mock_page_renderer):
"""Test _get_resize_handle_at handles 90-degree rotated elements"""
widget = TestSelectionWidget()
qtbot.addWidget(widget)
elem = ImageData(image_path="test.jpg", x=100, y=100, width=200, height=150)
elem.rotation = 90
elem._page_renderer = mock_page_renderer
widget.selected_element = elem
# Mock window
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
mock_window = Mock()
mock_window.project = Project(name="Test")
page = Page(layout=PageLayout(width=210, height=297), page_number=1)
mock_window.project.pages = [page]
widget.window = Mock(return_value=mock_window)
# Test clicking at various positions - rotation code should handle them
# Just verify the method runs without crashing
result = widget._get_resize_handle_at(200, 200)
assert result is None or result in ['nw', 'ne', 'sw', 'se']
class TestMultiSelect:
"""Test multi-selection functionality"""
+473
View File
@@ -0,0 +1,473 @@
"""
Tests for PageLayout and GridLayout classes
"""
import pytest
from pyPhotoAlbum.page_layout import PageLayout, GridLayout
from pyPhotoAlbum.models import ImageData, PlaceholderData, TextBoxData
from pyPhotoAlbum.snapping import SnappingSystem
class TestPageLayoutInitialization:
"""Test PageLayout initialization"""
def test_initialization_default(self):
"""Test PageLayout with default values"""
layout = PageLayout()
assert layout.size == (210, 297)
assert layout.base_width == 210
assert layout.is_facing_page is False
assert layout.elements == []
assert layout.grid_layout is None
assert layout.background_color == (1.0, 1.0, 1.0)
assert isinstance(layout.snapping_system, SnappingSystem)
assert layout.show_snap_lines is True
def test_initialization_custom_size(self):
"""Test PageLayout with custom dimensions"""
layout = PageLayout(width=150, height=200)
assert layout.size == (150, 200)
assert layout.base_width == 150
assert layout.is_facing_page is False
def test_initialization_facing_page(self):
"""Test PageLayout as facing page (double width)"""
layout = PageLayout(width=210, height=297, is_facing_page=True)
assert layout.size == (420, 297) # Width doubled
assert layout.base_width == 210
assert layout.is_facing_page is True
class TestElementManagement:
"""Test element add/remove operations"""
def test_add_element(self):
"""Test adding element to layout"""
layout = PageLayout()
elem = ImageData(image_path="test.jpg")
layout.add_element(elem)
assert len(layout.elements) == 1
assert layout.elements[0] == elem
def test_add_multiple_elements(self):
"""Test adding multiple elements"""
layout = PageLayout()
elem1 = ImageData(image_path="test1.jpg")
elem2 = PlaceholderData()
elem3 = TextBoxData(text_content="Hello")
layout.add_element(elem1)
layout.add_element(elem2)
layout.add_element(elem3)
assert len(layout.elements) == 3
assert layout.elements[0] == elem1
assert layout.elements[1] == elem2
assert layout.elements[2] == elem3
def test_remove_element(self):
"""Test removing element from layout"""
layout = PageLayout()
elem1 = ImageData(image_path="test1.jpg")
elem2 = PlaceholderData()
layout.add_element(elem1)
layout.add_element(elem2)
layout.remove_element(elem1)
assert len(layout.elements) == 1
assert layout.elements[0] == elem2
def test_remove_element_not_in_list_raises_error(self):
"""Test removing non-existent element raises error"""
layout = PageLayout()
elem = ImageData(image_path="test.jpg")
with pytest.raises(ValueError):
layout.remove_element(elem)
class TestGridLayout:
"""Test setting grid layout"""
def test_set_grid_layout(self):
"""Test setting a grid layout"""
layout = PageLayout()
grid = GridLayout(rows=2, columns=3)
layout.set_grid_layout(grid)
assert layout.grid_layout == grid
assert layout.grid_layout.rows == 2
assert layout.grid_layout.columns == 3
class TestPageLayoutSerialization:
"""Test PageLayout serialization"""
def test_serialize_empty_layout(self):
"""Test serializing empty layout"""
layout = PageLayout(width=210, height=297)
data = layout.serialize()
assert data["size"] == (210, 297)
assert data["base_width"] == 210
assert data["is_facing_page"] is False
assert data["background_color"] == (1.0, 1.0, 1.0)
assert data["elements"] == []
assert data["grid_layout"] is None
assert "snapping_system" in data
assert data["show_snap_lines"] is True
def test_serialize_with_elements(self):
"""Test serializing layout with elements"""
layout = PageLayout()
elem1 = ImageData(image_path="test1.jpg", x=10, y=20)
elem2 = PlaceholderData(x=30, y=40)
layout.add_element(elem1)
layout.add_element(elem2)
data = layout.serialize()
assert len(data["elements"]) == 2
assert data["elements"][0]["type"] == "image"
assert data["elements"][1]["type"] == "placeholder"
def test_serialize_with_grid_layout(self):
"""Test serializing layout with grid"""
layout = PageLayout()
grid = GridLayout(rows=3, columns=2, spacing=15.0)
layout.set_grid_layout(grid)
data = layout.serialize()
assert data["grid_layout"] is not None
assert data["grid_layout"]["rows"] == 3
assert data["grid_layout"]["columns"] == 2
assert data["grid_layout"]["spacing"] == 15.0
def test_serialize_facing_page(self):
"""Test serializing facing page layout"""
layout = PageLayout(width=210, height=297, is_facing_page=True)
data = layout.serialize()
assert data["size"] == (420, 297)
assert data["base_width"] == 210
assert data["is_facing_page"] is True
class TestPageLayoutDeserialization:
"""Test PageLayout deserialization"""
def test_deserialize_basic_layout(self):
"""Test deserializing basic layout"""
layout = PageLayout()
data = {
"size": (150, 200),
"base_width": 150,
"is_facing_page": False,
"background_color": (0.9, 0.9, 0.9),
"elements": [],
"grid_layout": None,
"snapping_system": {},
"show_snap_lines": False
}
layout.deserialize(data)
assert layout.size == (150, 200)
assert layout.base_width == 150
assert layout.is_facing_page is False
assert layout.background_color == (0.9, 0.9, 0.9)
assert layout.show_snap_lines is False
def test_deserialize_with_elements(self):
"""Test deserializing layout with elements"""
layout = PageLayout()
data = {
"size": (210, 297),
"elements": [
{
"type": "image",
"image_path": "test1.jpg",
"position": (10, 20),
"size": (100, 100),
"rotation": 0,
"z_index": 0,
"crop_info": (0, 0, 1, 1)
},
{
"type": "placeholder",
"placeholder_type": "image",
"default_content": "",
"position": (30, 40),
"size": (80, 80),
"rotation": 0,
"z_index": 1
},
{
"type": "textbox",
"text_content": "Hello",
"font_settings": {"family": "Arial", "size": 12, "color": (0, 0, 0)},
"alignment": "left",
"position": (50, 60),
"size": (120, 40),
"rotation": 0,
"z_index": 2
}
]
}
layout.deserialize(data)
assert len(layout.elements) == 3
assert isinstance(layout.elements[0], ImageData)
assert isinstance(layout.elements[1], PlaceholderData)
assert isinstance(layout.elements[2], TextBoxData)
def test_deserialize_elements_sorted_by_z_index(self):
"""Test elements are sorted by z_index during deserialization"""
layout = PageLayout()
data = {
"size": (210, 297),
"elements": [
{
"type": "image",
"image_path": "test1.jpg",
"position": (10, 20),
"size": (100, 100),
"rotation": 0,
"z_index": 5, # Higher z_index
"crop_info": (0, 0, 1, 1)
},
{
"type": "placeholder",
"placeholder_type": "image",
"default_content": "",
"position": (30, 40),
"size": (80, 80),
"rotation": 0,
"z_index": 1 # Lower z_index - should be first
}
]
}
layout.deserialize(data)
assert len(layout.elements) == 2
# Lower z_index should come first in list
assert layout.elements[0].z_index == 1
assert layout.elements[1].z_index == 5
def test_deserialize_with_grid_layout(self):
"""Test deserializing layout with grid"""
layout = PageLayout()
data = {
"size": (210, 297),
"elements": [],
"grid_layout": {
"rows": 2,
"columns": 3,
"spacing": 12.5,
"merged_cells": [(0, 0), (1, 1)]
}
}
layout.deserialize(data)
assert layout.grid_layout is not None
assert layout.grid_layout.rows == 2
assert layout.grid_layout.columns == 3
assert layout.grid_layout.spacing == 12.5
assert layout.grid_layout.merged_cells == [(0, 0), (1, 1)]
def test_deserialize_unknown_element_type_skipped(self):
"""Test that unknown element types are skipped"""
layout = PageLayout()
data = {
"size": (210, 297),
"elements": [
{
"type": "unknown_type",
"position": (10, 20)
},
{
"type": "image",
"image_path": "test.jpg",
"position": (30, 40),
"size": (100, 100),
"rotation": 0,
"z_index": 0,
"crop_info": (0, 0, 1, 1)
}
]
}
layout.deserialize(data)
# Only valid element should be deserialized
assert len(layout.elements) == 1
assert isinstance(layout.elements[0], ImageData)
def test_deserialize_with_defaults(self):
"""Test deserialization with missing fields uses defaults"""
layout = PageLayout()
data = {}
layout.deserialize(data)
assert layout.size == (210, 297)
assert layout.background_color == (1.0, 1.0, 1.0)
assert layout.show_snap_lines is True
class TestSerializationRoundtrip:
"""Test serialization/deserialization roundtrips"""
def test_serialize_deserialize_roundtrip(self):
"""Test that serialize/deserialize are inverse operations"""
original = PageLayout(width=200, height=280, is_facing_page=False)
original.background_color = (0.95, 0.95, 0.95)
original.show_snap_lines = False
elem1 = ImageData(image_path="test.jpg", x=10, y=20, width=100, height=80)
elem2 = PlaceholderData(x=30, y=40, width=50, height=50)
original.add_element(elem1)
original.add_element(elem2)
grid = GridLayout(rows=2, columns=3, spacing=10.0)
original.set_grid_layout(grid)
# Serialize and deserialize
data = original.serialize()
restored = PageLayout()
restored.deserialize(data)
# Verify restoration
assert restored.size == original.size
assert restored.base_width == original.base_width
assert restored.is_facing_page == original.is_facing_page
assert restored.background_color == original.background_color
assert restored.show_snap_lines == original.show_snap_lines
assert len(restored.elements) == len(original.elements)
assert restored.grid_layout is not None
assert restored.grid_layout.rows == original.grid_layout.rows
class TestGridLayoutClass:
"""Test GridLayout class"""
def test_initialization_default(self):
"""Test GridLayout initialization with defaults"""
grid = GridLayout()
assert grid.rows == 1
assert grid.columns == 1
assert grid.spacing == 10.0
assert grid.merged_cells == []
def test_initialization_custom(self):
"""Test GridLayout initialization with custom values"""
grid = GridLayout(rows=3, columns=4, spacing=15.5)
assert grid.rows == 3
assert grid.columns == 4
assert grid.spacing == 15.5
def test_merge_cells(self):
"""Test merging cells in grid"""
grid = GridLayout(rows=3, columns=3)
grid.merge_cells(0, 0)
grid.merge_cells(1, 1)
assert len(grid.merged_cells) == 2
assert (0, 0) in grid.merged_cells
assert (1, 1) in grid.merged_cells
def test_get_cell_position(self):
"""Test calculating cell position"""
grid = GridLayout(rows=2, columns=2, spacing=10.0)
# Cell (0, 0) - top left
x, y = grid.get_cell_position(0, 0, page_width=800, page_height=600)
assert x == 10.0 # spacing
assert y == 10.0 # spacing
# Cell (0, 1) - top right
x, y = grid.get_cell_position(0, 1, page_width=800, page_height=600)
# spacing + col * (cell_width + spacing)
# cell_width = (800 - 30) / 2 = 385
# x = 10 + 1 * (385 + 10) = 405
assert x == 405.0
def test_get_cell_size(self):
"""Test calculating cell size"""
grid = GridLayout(rows=2, columns=3, spacing=10.0)
width, height = grid.get_cell_size(page_width=900, page_height=600)
# width = (900 - 10 * 4) / 3 = 860 / 3 ≈ 286.67
# height = (600 - 10 * 3) / 2 = 570 / 2 = 285
assert width == pytest.approx(286.666, rel=0.01)
assert height == 285.0
def test_grid_serialization(self):
"""Test GridLayout serialization"""
grid = GridLayout(rows=3, columns=2, spacing=12.0)
grid.merge_cells(0, 1)
grid.merge_cells(2, 0)
data = grid.serialize()
assert data["rows"] == 3
assert data["columns"] == 2
assert data["spacing"] == 12.0
assert data["merged_cells"] == [(0, 1), (2, 0)]
def test_grid_deserialization(self):
"""Test GridLayout deserialization"""
grid = GridLayout()
data = {
"rows": 4,
"columns": 5,
"spacing": 8.5,
"merged_cells": [(1, 2), (3, 3)]
}
grid.deserialize(data)
assert grid.rows == 4
assert grid.columns == 5
assert grid.spacing == 8.5
assert grid.merged_cells == [(1, 2), (3, 3)]
def test_grid_deserialization_with_defaults(self):
"""Test GridLayout deserialization with missing fields"""
grid = GridLayout()
data = {}
grid.deserialize(data)
assert grid.rows == 1
assert grid.columns == 1
assert grid.spacing == 10.0
assert grid.merged_cells == []
def test_grid_serialize_deserialize_roundtrip(self):
"""Test GridLayout serialize/deserialize roundtrip"""
original = GridLayout(rows=3, columns=4, spacing=11.5)
original.merge_cells(0, 0)
original.merge_cells(1, 2)
original.merge_cells(2, 3)
data = original.serialize()
restored = GridLayout()
restored.deserialize(data)
assert restored.rows == original.rows
assert restored.columns == original.columns
assert restored.spacing == original.spacing
assert restored.merged_cells == original.merged_cells
+474
View File
@@ -0,0 +1,474 @@
"""
Tests for ViewOperationsMixin
"""
import pytest
from unittest.mock import Mock, patch
from PyQt6.QtWidgets import QMainWindow, QDialog
from pyPhotoAlbum.mixins.operations.view_ops import ViewOperationsMixin
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
class TestViewWindow(ViewOperationsMixin, QMainWindow):
"""Test window with view operations mixin"""
def __init__(self):
super().__init__()
self.gl_widget = Mock()
self.gl_widget.zoom_level = 1.0
self.gl_widget.current_page_index = 0
self.gl_widget.width = Mock(return_value=800)
self.gl_widget.height = Mock(return_value=600)
self.project = Mock(spec=Project)
self.project.working_dpi = 300
self._update_view_called = False
self._status_message = None
def get_current_page(self):
if hasattr(self, '_current_page'):
return self._current_page
return None
def update_view(self):
self._update_view_called = True
def show_status(self, message, timeout=0):
self._status_message = message
class TestZoomOperations:
"""Test zoom operations"""
def test_zoom_in_success(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 1.0
window.zoom_in()
assert window.gl_widget.zoom_level == 1.2
assert window._update_view_called
assert "120%" in window._status_message
def test_zoom_in_max_limit(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 4.5
window.zoom_in()
# 4.5 * 1.2 = 5.4, but clamped to 5.0
assert window.gl_widget.zoom_level == 5.0
assert window._update_view_called
def test_zoom_out_success(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 1.2
window.zoom_out()
assert window.gl_widget.zoom_level == 1.0
assert window._update_view_called
assert "100%" in window._status_message
def test_zoom_out_min_limit(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 0.11
window.zoom_out()
# 0.11 / 1.2 ≈ 0.092, but clamped to 0.1
assert window.gl_widget.zoom_level == 0.1
assert window._update_view_called
def test_zoom_fit_success(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
# Setup page
layout = PageLayout(width=210, height=297) # A4 in mm
page = Mock()
page.layout = layout
window.project.pages = [page]
window.zoom_fit()
# Should calculate zoom to fit page in widget
assert window._update_view_called
assert "zoom" in window._status_message.lower()
# Zoom level should be set based on widget size
assert 0.1 <= window.gl_widget.zoom_level <= 5.0
def test_zoom_fit_no_pages(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.project.pages = []
window.zoom_fit()
# Should return early without error
assert not window._update_view_called
class TestSnappingToggles:
"""Test snapping toggle operations"""
def test_toggle_grid_snap_enable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_grid = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_grid_snap()
assert layout.snapping_system.snap_to_grid is True
assert "enabled" in window._status_message.lower()
assert window._update_view_called
def test_toggle_grid_snap_disable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_grid = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_grid_snap()
assert layout.snapping_system.snap_to_grid is False
assert "disabled" in window._status_message.lower()
assert window._update_view_called
def test_toggle_grid_snap_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.toggle_grid_snap()
assert not window._update_view_called
def test_toggle_edge_snap_enable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_edges = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_edge_snap()
assert layout.snapping_system.snap_to_edges is True
assert "enabled" in window._status_message.lower()
assert window._update_view_called
def test_toggle_edge_snap_disable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_edges = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_edge_snap()
assert layout.snapping_system.snap_to_edges is False
assert "disabled" in window._status_message.lower()
def test_toggle_guide_snap_enable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_guides = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_guide_snap()
assert layout.snapping_system.snap_to_guides is True
assert "enabled" in window._status_message.lower()
def test_toggle_guide_snap_disable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_guides = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_guide_snap()
assert layout.snapping_system.snap_to_guides is False
assert "disabled" in window._status_message.lower()
def test_toggle_snap_lines_show(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.show_snap_lines = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_snap_lines()
assert layout.show_snap_lines is True
assert "visible" in window._status_message.lower()
def test_toggle_snap_lines_hide(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.show_snap_lines = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_snap_lines()
assert layout.show_snap_lines is False
assert "hidden" in window._status_message.lower()
class TestGuideOperations:
"""Test guide add/clear operations"""
def test_add_horizontal_guide(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout(width=210, height=297)
page = Mock()
page.layout = layout
window._current_page = page
window.add_horizontal_guide()
# Guide should be added at vertical center (297 / 2 = 148.5mm)
assert "148.5" in window._status_message
assert window._update_view_called
def test_add_horizontal_guide_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.add_horizontal_guide()
assert not window._update_view_called
def test_add_vertical_guide(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout(width=210, height=297)
page = Mock()
page.layout = layout
window._current_page = page
window.add_vertical_guide()
# Guide should be added at horizontal center (210 / 2 = 105.0mm)
assert "105.0" in window._status_message
assert window._update_view_called
def test_add_vertical_guide_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.add_vertical_guide()
assert not window._update_view_called
def test_clear_guides_with_guides(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
# Add some guides
layout.snapping_system.add_guide(100, 'vertical')
layout.snapping_system.add_guide(150, 'horizontal')
page = Mock()
page.layout = layout
window._current_page = page
window.clear_guides()
assert len(layout.snapping_system.guides) == 0
assert "2" in window._status_message # Cleared 2 guides
assert window._update_view_called
def test_clear_guides_no_guides(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
page = Mock()
page.layout = layout
window._current_page = page
window.clear_guides()
assert "0" in window._status_message
assert window._update_view_called
def test_clear_guides_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.clear_guides()
assert not window._update_view_called
class TestGridSettingsDialog:
"""Test grid settings dialog"""
def test_set_grid_size_no_page(self, qtbot):
"""Test set_grid_size with no current page"""
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.set_grid_size()
# Should return early without error
assert not window._update_view_called
def test_set_grid_size_with_page(self, qtbot):
"""Test set_grid_size creates dialog with current page"""
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.grid_size_mm = 10.0
layout.snapping_system.snap_threshold_mm = 5.0
page = Mock()
page.layout = layout
window._current_page = page
# Mock the full dialog workflow
mock_dialog = Mock(spec=QDialog)
mock_dialog.exec.return_value = QDialog.DialogCode.Rejected
with patch('PyQt6.QtWidgets.QDialog', return_value=mock_dialog):
window.set_grid_size()
# Dialog should have been created and exec called
mock_dialog.exec.assert_called_once()
class TestLayoutTabDelegation:
"""Test Layout tab methods delegate to main methods"""
def test_layout_toggle_grid_snap_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_grid = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_grid_snap()
# Should delegate to toggle_grid_snap
assert layout.snapping_system.snap_to_grid is True
assert window._update_view_called
def test_layout_toggle_edge_snap_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_edges = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_edge_snap()
assert layout.snapping_system.snap_to_edges is True
assert window._update_view_called
def test_layout_toggle_guide_snap_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_guides = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_guide_snap()
assert layout.snapping_system.snap_to_guides is True
assert window._update_view_called
def test_layout_toggle_snap_lines_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.show_snap_lines = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_snap_lines()
assert layout.show_snap_lines is True
assert window._update_view_called
def test_layout_set_grid_size_delegates(self, qtbot):
"""Test layout tab grid size delegates to main method"""
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
page = Mock()
page.layout = layout
window._current_page = page
# Mock dialog to verify delegation
mock_dialog = Mock(spec=QDialog)
mock_dialog.exec.return_value = QDialog.DialogCode.Rejected
with patch('PyQt6.QtWidgets.QDialog', return_value=mock_dialog):
window.layout_set_grid_size()
# Verify method was called (dialog creation attempted)
mock_dialog.exec.assert_called_once()