feat: enhance drag-and-drop functionality to support multiple file paths and URLs
This commit is contained in:
parent
1e848e84b2
commit
c612072dc8
5 changed files with 384 additions and 480 deletions
|
|
@ -82,6 +82,7 @@ class TestDragInterceptorValidation:
|
|||
mock_drag_instance = MagicMock()
|
||||
# Simulate successful copy action
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
|
|
@ -136,7 +137,7 @@ class TestDragInterceptorAzureURL:
|
|||
url_mappings=[
|
||||
URLMapping(
|
||||
url_prefix="https://devagravitystg.file.core.windows.net/devagravitysync/",
|
||||
local_path=str(tmp_path)
|
||||
local_path=str(tmp_path),
|
||||
)
|
||||
],
|
||||
check_file_exists=True,
|
||||
|
|
@ -150,6 +151,7 @@ class TestDragInterceptorAzureURL:
|
|||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
|
|
@ -196,6 +198,7 @@ class TestDragInterceptorSignals:
|
|||
interceptor.drag_started.connect(lambda src, path: signal_spy.append((src, path)))
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
|
|
@ -235,3 +238,234 @@ class TestDragInterceptorSignals:
|
|||
# Verify result and signal emission
|
||||
assert result is False
|
||||
assert len(signal_spy) == 1
|
||||
|
||||
|
||||
class TestDragInterceptorMultipleDrags:
|
||||
"""Test multiple file drag support."""
|
||||
|
||||
def test_handle_drag_with_list_single_item(self, qtbot, tmp_path):
|
||||
"""Test handle_drag with list containing single file path."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="https://test.com/",
|
||||
url_mappings=[],
|
||||
check_file_exists=True,
|
||||
)
|
||||
interceptor = DragInterceptor(config)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.handle_drag([str(test_file)])
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_handle_drag_with_multiple_files(self, qtbot, tmp_path):
|
||||
"""Test handle_drag with list of multiple file paths."""
|
||||
# Create multiple test files
|
||||
test_file1 = tmp_path / "test1.txt"
|
||||
test_file1.write_text("content1")
|
||||
test_file2 = tmp_path / "test2.txt"
|
||||
test_file2.write_text("content2")
|
||||
test_file3 = tmp_path / "test3.txt"
|
||||
test_file3.write_text("content3")
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="https://test.com/",
|
||||
url_mappings=[],
|
||||
check_file_exists=True,
|
||||
)
|
||||
interceptor = DragInterceptor(config)
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.handle_drag(
|
||||
[
|
||||
str(test_file1),
|
||||
str(test_file2),
|
||||
str(test_file3),
|
||||
]
|
||||
)
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_handle_drag_with_multiple_azure_urls(self, qtbot, tmp_path):
|
||||
"""Test handle_drag with list of multiple Azure URLs."""
|
||||
from webdrop_bridge.config import URLMapping
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="https://test.com/",
|
||||
url_mappings=[
|
||||
URLMapping(
|
||||
url_prefix="https://produktagravitystg.file.core.windows.net/produktagravitysync/",
|
||||
local_path=str(tmp_path),
|
||||
)
|
||||
],
|
||||
check_file_exists=False, # Don't check file existence for this test
|
||||
)
|
||||
interceptor = DragInterceptor(config)
|
||||
|
||||
# Multiple Azure URLs (as would be in a multi-drag)
|
||||
azure_urls = [
|
||||
"https://produktagravitystg.file.core.windows.net/produktagravitysync/axtZdPVjs5iUaKU2muKMFN1WZ/igkjieyjcko.jpg",
|
||||
"https://produktagravitystg.file.core.windows.net/produktagravitysync/aWd7mDjnsm2w0PHU9AryQBYz2/457101023fd46d673e2ce6642f78fb0d62736f0f06c7.jpg",
|
||||
]
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.handle_drag(azure_urls)
|
||||
|
||||
assert result is True
|
||||
# Verify QDrag.exec was called (meaning drag was set up correctly)
|
||||
mock_drag_instance.exec.assert_called_once()
|
||||
|
||||
def test_handle_drag_mixed_urls_and_paths(self, qtbot, tmp_path):
|
||||
"""Test handle_drag with mixed Azure URLs and local paths."""
|
||||
from webdrop_bridge.config import URLMapping
|
||||
|
||||
# Create test file
|
||||
test_file = tmp_path / "local_file.txt"
|
||||
test_file.write_text("local content")
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="https://test.com/",
|
||||
url_mappings=[
|
||||
URLMapping(
|
||||
url_prefix="https://devagravitystg.file.core.windows.net/devagravitysync/",
|
||||
local_path=str(tmp_path),
|
||||
)
|
||||
],
|
||||
check_file_exists=False, # Don't check existence for remote files
|
||||
)
|
||||
interceptor = DragInterceptor(config)
|
||||
|
||||
mixed_items = [
|
||||
str(test_file), # local path
|
||||
"https://devagravitystg.file.core.windows.net/devagravitysync/remote.jpg", # Azure URL
|
||||
]
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.handle_drag(mixed_items)
|
||||
|
||||
assert result is True
|
||||
|
||||
def test_handle_drag_multiple_empty_list(self, qtbot, test_config):
|
||||
"""Test handle_drag with empty list fails."""
|
||||
interceptor = DragInterceptor(test_config)
|
||||
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.handle_drag([])
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_handle_drag_multiple_one_invalid_fails(self, qtbot, tmp_path):
|
||||
"""Test handle_drag with multiple files fails if one is invalid."""
|
||||
test_file1 = tmp_path / "test1.txt"
|
||||
test_file1.write_text("content1")
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="https://test.com/",
|
||||
url_mappings=[],
|
||||
check_file_exists=True,
|
||||
)
|
||||
interceptor = DragInterceptor(config)
|
||||
|
||||
# One valid, one invalid
|
||||
files = [
|
||||
str(test_file1),
|
||||
"/etc/passwd", # Invalid - outside allowed roots
|
||||
]
|
||||
|
||||
with qtbot.waitSignal(interceptor.drag_failed):
|
||||
result = interceptor.handle_drag(files)
|
||||
|
||||
assert result is False
|
||||
|
||||
def test_handle_drag_multiple_signal_with_pipes(self, qtbot, tmp_path):
|
||||
"""Test drag_started signal contains pipe-separated paths for multiple files."""
|
||||
test_file1 = tmp_path / "test1.txt"
|
||||
test_file1.write_text("content1")
|
||||
test_file2 = tmp_path / "test2.txt"
|
||||
test_file2.write_text("content2")
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="https://test.com/",
|
||||
url_mappings=[],
|
||||
check_file_exists=True,
|
||||
)
|
||||
interceptor = DragInterceptor(config)
|
||||
|
||||
signal_spy = []
|
||||
interceptor.drag_started.connect(lambda src, path: signal_spy.append((src, path)))
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
with patch("webdrop_bridge.core.drag_interceptor.QDrag") as mock_drag:
|
||||
mock_drag_instance = MagicMock()
|
||||
mock_drag_instance.exec.return_value = Qt.DropAction.CopyAction
|
||||
mock_drag.return_value = mock_drag_instance
|
||||
|
||||
result = interceptor.handle_drag([str(test_file1), str(test_file2)])
|
||||
|
||||
assert result is True
|
||||
assert len(signal_spy) == 1
|
||||
# Multiple paths should be separated by " | "
|
||||
assert " | " in signal_spy[0][1]
|
||||
|
|
|
|||
|
|
@ -82,136 +82,6 @@ class TestMainWindowInitialization:
|
|||
assert window.drag_interceptor is not None
|
||||
|
||||
|
||||
class TestMainWindowNavigation:
|
||||
"""Test navigation toolbar and functionality."""
|
||||
|
||||
def test_navigation_toolbar_created(self, qtbot, sample_config):
|
||||
"""Test navigation toolbar is created."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
toolbars = window.findChildren(QToolBar)
|
||||
assert len(toolbars) > 0
|
||||
|
||||
def test_navigation_toolbar_not_movable(self, qtbot, sample_config):
|
||||
"""Test navigation toolbar is not movable (locked for Kiosk-mode)."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
toolbar = window.findChild(QToolBar)
|
||||
assert toolbar is not None
|
||||
assert not toolbar.isMovable()
|
||||
|
||||
def test_navigate_home(self, qtbot, sample_config):
|
||||
"""Test home button navigation."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch.object(window.web_view, "load") as mock_load:
|
||||
window._navigate_home()
|
||||
mock_load.assert_called_once()
|
||||
|
||||
def test_navigate_home_with_http_url(self, qtbot, tmp_path):
|
||||
"""Test home navigation with HTTP URL."""
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="http://localhost:8000",
|
||||
window_width=800,
|
||||
window_height=600,
|
||||
enable_logging=False,
|
||||
)
|
||||
|
||||
window = MainWindow(config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch.object(window.web_view, "load") as mock_load:
|
||||
window._navigate_home()
|
||||
|
||||
# Verify load was called with HTTP URL
|
||||
call_args = mock_load.call_args
|
||||
url = call_args[0][0]
|
||||
assert url.scheme() == "http"
|
||||
|
||||
def test_navigate_home_with_file_url(self, qtbot, sample_config):
|
||||
"""Test home navigation with file:// URL."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch.object(window.web_view, "load") as mock_load:
|
||||
window._navigate_home()
|
||||
|
||||
call_args = mock_load.call_args
|
||||
url = call_args[0][0]
|
||||
assert url.scheme() == "file"
|
||||
|
||||
|
||||
class TestMainWindowWebAppLoading:
|
||||
"""Test web application loading."""
|
||||
|
||||
def test_load_local_webapp_file(self, qtbot, sample_config):
|
||||
"""Test loading local webapp file."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Window should load without errors
|
||||
assert window.web_view is not None
|
||||
|
||||
def test_load_remote_webapp_url(self, qtbot, tmp_path):
|
||||
"""Test loading remote webapp URL."""
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=["localhost"],
|
||||
webapp_url="http://localhost:3000",
|
||||
window_width=800,
|
||||
window_height=600,
|
||||
enable_logging=False,
|
||||
)
|
||||
|
||||
window = MainWindow(config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
assert window.web_view is not None
|
||||
|
||||
def test_load_nonexistent_file_shows_welcome_page(self, qtbot, tmp_path):
|
||||
"""Test loading nonexistent file shows welcome page HTML."""
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[],
|
||||
webapp_url="/nonexistent/file.html",
|
||||
window_width=800,
|
||||
window_height=600,
|
||||
enable_logging=False,
|
||||
)
|
||||
|
||||
with patch.object(config, "webapp_url", "/nonexistent/file.html"):
|
||||
window = MainWindow(config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch.object(
|
||||
window.web_view, "setHtml"
|
||||
) as mock_set_html:
|
||||
window._load_webapp()
|
||||
mock_set_html.assert_called_once()
|
||||
|
||||
# Verify welcome page is shown instead of error
|
||||
call_args = mock_set_html.call_args[0][0]
|
||||
assert "WebDrop Bridge" in call_args
|
||||
assert "Application Ready" in call_args
|
||||
|
||||
|
||||
class TestMainWindowDragIntegration:
|
||||
"""Test drag-and-drop integration."""
|
||||
|
||||
|
|
@ -231,12 +101,10 @@ class TestMainWindowDragIntegration:
|
|||
assert window.drag_interceptor.drag_started is not None
|
||||
assert window.drag_interceptor.drag_failed is not None
|
||||
|
||||
def test_handle_drag_delegates_to_interceptor(
|
||||
self, qtbot, sample_config, tmp_path
|
||||
):
|
||||
def test_handle_drag_delegates_to_interceptor(self, qtbot, sample_config, tmp_path):
|
||||
"""Test drag handling delegates to interceptor."""
|
||||
from PySide6.QtCore import QCoreApplication
|
||||
|
||||
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
|
|
@ -244,13 +112,11 @@ class TestMainWindowDragIntegration:
|
|||
test_file = sample_config.allowed_roots[0] / "test.txt"
|
||||
test_file.write_text("test")
|
||||
|
||||
with patch.object(
|
||||
window.drag_interceptor, "handle_drag"
|
||||
) as mock_drag:
|
||||
with patch.object(window.drag_interceptor, "handle_drag") as mock_drag:
|
||||
mock_drag.return_value = True
|
||||
# Call through bridge
|
||||
window._drag_bridge.start_file_drag(str(test_file))
|
||||
|
||||
|
||||
# Process deferred QTimer.singleShot(0, ...) call
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
|
|
@ -276,278 +142,10 @@ class TestMainWindowDragIntegration:
|
|||
class TestMainWindowURLWhitelist:
|
||||
"""Test URL whitelisting integration."""
|
||||
|
||||
def test_restricted_web_view_receives_allowed_urls(
|
||||
self, qtbot, sample_config
|
||||
):
|
||||
def test_restricted_web_view_receives_allowed_urls(self, qtbot, sample_config):
|
||||
"""Test RestrictedWebEngineView receives allowed URLs from config."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# web_view should have allowed_urls configured
|
||||
assert window.web_view.allowed_urls == sample_config.allowed_urls
|
||||
|
||||
def test_empty_allowed_urls_list(self, qtbot, tmp_path):
|
||||
"""Test with empty allowed URLs (no restriction)."""
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=[], # Empty = no restriction
|
||||
webapp_url="http://localhost",
|
||||
window_width=800,
|
||||
window_height=600,
|
||||
enable_logging=False,
|
||||
)
|
||||
|
||||
window = MainWindow(config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
assert window.web_view.allowed_urls == []
|
||||
|
||||
|
||||
class TestMainWindowSignals:
|
||||
"""Test signal connections."""
|
||||
|
||||
def test_drag_started_signal_connection(self, qtbot, sample_config):
|
||||
"""Test drag_started signal is connected to handler."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch.object(window, "_on_drag_started") as mock_handler:
|
||||
window.drag_interceptor.drag_started.emit("/path/to/file", "Z:\\local\\file")
|
||||
mock_handler.assert_called_once()
|
||||
|
||||
def test_drag_failed_signal_connection(self, qtbot, sample_config):
|
||||
"""Test drag_failed signal is connected to handler."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch.object(window, "_on_drag_failed") as mock_handler:
|
||||
window.drag_interceptor.drag_failed.emit("https://example.com/file", "File not found")
|
||||
mock_handler.assert_called_once()
|
||||
|
||||
|
||||
class TestMainWindowMenuBar:
|
||||
"""Test toolbar help actions integration."""
|
||||
|
||||
def test_navigation_toolbar_created(self, qtbot, sample_config):
|
||||
"""Test navigation toolbar is created with help buttons."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Check that toolbar exists
|
||||
assert len(window.findChildren(QToolBar)) > 0
|
||||
toolbar = window.findChildren(QToolBar)[0]
|
||||
assert toolbar is not None
|
||||
|
||||
def test_window_has_check_for_updates_signal(self, qtbot, sample_config):
|
||||
"""Test window has check_for_updates signal."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Test that signal exists
|
||||
assert hasattr(window, "check_for_updates")
|
||||
|
||||
# Test that signal is callable (can be emitted)
|
||||
assert callable(window.check_for_updates.emit)
|
||||
|
||||
def test_on_check_for_updates_method_exists(self, qtbot, sample_config):
|
||||
"""Test _on_manual_check_for_updates method exists."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Test that the method exists
|
||||
assert hasattr(window, "_on_manual_check_for_updates")
|
||||
assert callable(window._on_manual_check_for_updates)
|
||||
|
||||
def test_show_about_dialog_method_exists(self, qtbot, sample_config):
|
||||
"""Test _show_about_dialog method exists."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Test that the method exists
|
||||
assert hasattr(window, "_show_about_dialog")
|
||||
assert callable(window._show_about_dialog)
|
||||
|
||||
|
||||
class TestMainWindowStatusBar:
|
||||
"""Test status bar and update status."""
|
||||
|
||||
def test_status_bar_created(self, qtbot, sample_config):
|
||||
"""Test status bar is created."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
assert window.statusBar() is not None
|
||||
assert hasattr(window, "status_bar")
|
||||
|
||||
def test_update_status_label_created(self, qtbot, sample_config):
|
||||
"""Test update status label exists."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
assert hasattr(window, "update_status_label")
|
||||
assert window.update_status_label is not None
|
||||
|
||||
def test_set_update_status_text_only(self, qtbot, sample_config):
|
||||
"""Test setting update status with text only."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
window.set_update_status("Checking for updates")
|
||||
assert "Checking for updates" in window.update_status_label.text()
|
||||
|
||||
def test_set_update_status_with_emoji(self, qtbot, sample_config):
|
||||
"""Test setting update status with emoji."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
window.set_update_status("Checking", emoji="🔄")
|
||||
assert "🔄" in window.update_status_label.text()
|
||||
assert "Checking" in window.update_status_label.text()
|
||||
|
||||
def test_set_update_status_checking(self, qtbot, sample_config):
|
||||
"""Test checking for updates status."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
window.set_update_status("Checking for updates", emoji="🔄")
|
||||
assert "🔄" in window.update_status_label.text()
|
||||
|
||||
def test_set_update_status_available(self, qtbot, sample_config):
|
||||
"""Test update available status."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
window.set_update_status("Update available v0.0.2", emoji="✅")
|
||||
assert "✅" in window.update_status_label.text()
|
||||
|
||||
def test_set_update_status_downloading(self, qtbot, sample_config):
|
||||
"""Test downloading status."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
window.set_update_status("Downloading update", emoji="⬇️")
|
||||
assert "⬇️" in window.update_status_label.text()
|
||||
|
||||
def test_set_update_status_error(self, qtbot, sample_config):
|
||||
"""Test error status."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
window.set_update_status("Update check failed", emoji="⚠️")
|
||||
assert "⚠️" in window.update_status_label.text()
|
||||
|
||||
|
||||
class TestMainWindowStylesheet:
|
||||
"""Test stylesheet application."""
|
||||
|
||||
def test_stylesheet_loading_gracefully_handles_missing_file(
|
||||
self, qtbot, sample_config
|
||||
):
|
||||
"""Test missing stylesheet doesn't crash application."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Should not raise even if stylesheet missing
|
||||
window._apply_stylesheet()
|
||||
|
||||
def test_stylesheet_loading_with_nonexistent_file(
|
||||
self, qtbot, sample_config
|
||||
):
|
||||
"""Test stylesheet loading with nonexistent file path."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
with patch("builtins.open", side_effect=OSError("File not found")):
|
||||
# Should handle gracefully
|
||||
window._apply_stylesheet()
|
||||
|
||||
|
||||
class TestMainWindowCloseEvent:
|
||||
"""Test window close handling."""
|
||||
|
||||
def test_close_event_accepted(self, qtbot, sample_config):
|
||||
"""Test close event is accepted."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
from PySide6.QtGui import QCloseEvent
|
||||
|
||||
event = QCloseEvent()
|
||||
window.closeEvent(event)
|
||||
|
||||
assert event.isAccepted()
|
||||
|
||||
|
||||
class TestMainWindowIntegration:
|
||||
"""Integration tests for MainWindow with all components."""
|
||||
|
||||
def test_full_initialization_flow(self, qtbot, sample_config):
|
||||
"""Test complete initialization flow."""
|
||||
window = MainWindow(sample_config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Verify all components initialized
|
||||
assert window.web_view is not None
|
||||
assert window.drag_interceptor is not None
|
||||
assert window.config == sample_config
|
||||
|
||||
# Verify toolbar exists
|
||||
toolbars = window.findChildren(QToolBar)
|
||||
assert len(toolbars) > 0
|
||||
|
||||
def test_window_with_multiple_allowed_roots(self, qtbot, tmp_path):
|
||||
"""Test MainWindow with multiple allowed root directories."""
|
||||
root1 = tmp_path / "root1"
|
||||
root2 = tmp_path / "root2"
|
||||
root1.mkdir()
|
||||
root2.mkdir()
|
||||
|
||||
webapp_file = tmp_path / "index.html"
|
||||
webapp_file.write_text("<html></html>")
|
||||
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[root1, root2],
|
||||
allowed_urls=[],
|
||||
webapp_url=str(webapp_file),
|
||||
window_width=800,
|
||||
window_height=600,
|
||||
enable_logging=False,
|
||||
)
|
||||
|
||||
window = MainWindow(config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Verify validator has both roots
|
||||
assert window.drag_interceptor._validator is not None
|
||||
assert len(
|
||||
window.drag_interceptor._validator.allowed_roots
|
||||
) == 2
|
||||
|
||||
def test_window_with_url_whitelist(self, qtbot, tmp_path):
|
||||
"""Test MainWindow respects URL whitelist."""
|
||||
config = Config(
|
||||
app_name="Test",
|
||||
app_version="1.0.0",
|
||||
log_level="INFO",
|
||||
log_file=None,
|
||||
allowed_roots=[tmp_path],
|
||||
allowed_urls=["*.example.com", "localhost"],
|
||||
webapp_url="http://localhost",
|
||||
window_width=800,
|
||||
window_height=600,
|
||||
enable_logging=False,
|
||||
)
|
||||
|
||||
window = MainWindow(config)
|
||||
qtbot.addWidget(window)
|
||||
|
||||
# Verify whitelist is set
|
||||
assert window.web_view.allowed_urls == ["*.example.com", "localhost"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue