mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-12-27 09:31:18 +01:00
379 lines
15 KiB
Python
379 lines
15 KiB
Python
"""
|
|
Unit tests for the Developer View widget.
|
|
|
|
This module tests the DeveloperView widget functionality including:
|
|
- Widget initialization and setup
|
|
- Monaco editor integration
|
|
- IDE Explorer integration
|
|
- File operations (open, save, format)
|
|
- Context menu actions
|
|
- Toolbar functionality
|
|
"""
|
|
|
|
import os
|
|
import tempfile
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from qtpy.QtWidgets import QDialog
|
|
|
|
from bec_widgets.applications.views.developer_view.developer_widget import DeveloperWidget
|
|
from bec_widgets.widgets.editors.monaco.monaco_dock import MonacoDock
|
|
from bec_widgets.widgets.editors.monaco.monaco_widget import MonacoWidget
|
|
from bec_widgets.widgets.utility.ide_explorer.ide_explorer import IDEExplorer
|
|
|
|
from .client_mocks import mocked_client
|
|
|
|
|
|
@pytest.fixture
|
|
def developer_view(qtbot, mocked_client):
|
|
"""Create a DeveloperWidget for testing."""
|
|
widget = DeveloperWidget(client=mocked_client)
|
|
qtbot.addWidget(widget)
|
|
qtbot.waitExposed(widget)
|
|
yield widget
|
|
|
|
|
|
@pytest.fixture
|
|
def temp_python_file():
|
|
"""Create a temporary Python file for testing."""
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
|
|
f.write(
|
|
"""# Test Python file
|
|
import os
|
|
import sys
|
|
|
|
def test_function():
|
|
return "Hello, World!"
|
|
|
|
if __name__ == "__main__":
|
|
print(test_function())
|
|
"""
|
|
)
|
|
temp_file_path = f.name
|
|
|
|
yield temp_file_path
|
|
|
|
# Cleanup
|
|
if os.path.exists(temp_file_path):
|
|
os.unlink(temp_file_path)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_scan_control_dialog():
|
|
"""Mock the ScanControlDialog for testing."""
|
|
with mock.patch(
|
|
"bec_widgets.widgets.editors.monaco.scan_control_dialog.ScanControlDialog"
|
|
) as mock_dialog:
|
|
# Configure the mock dialog
|
|
mock_dialog_instance = mock.MagicMock()
|
|
mock_dialog_instance.exec_.return_value = QDialog.DialogCode.Accepted
|
|
mock_dialog_instance.get_scan_code.return_value = (
|
|
"scans.ascan(dev.samx, 0, 1, 10, exp_time=0.1)"
|
|
)
|
|
mock_dialog.return_value = mock_dialog_instance
|
|
yield mock_dialog_instance
|
|
|
|
|
|
class TestDeveloperViewInitialization:
|
|
"""Test developer view initialization and basic functionality."""
|
|
|
|
def test_developer_view_initialization(self, developer_view):
|
|
"""Test that the developer view initializes correctly."""
|
|
# Check that main components are created
|
|
assert hasattr(developer_view, "monaco")
|
|
assert hasattr(developer_view, "explorer")
|
|
assert hasattr(developer_view, "console")
|
|
assert hasattr(developer_view, "terminal")
|
|
assert hasattr(developer_view, "toolbar")
|
|
assert hasattr(developer_view, "dock_manager")
|
|
assert hasattr(developer_view, "plotting_ads")
|
|
assert hasattr(developer_view, "signature_help")
|
|
|
|
def test_monaco_editor_integration(self, developer_view):
|
|
"""Test that Monaco editor is properly integrated."""
|
|
assert isinstance(developer_view.monaco, MonacoDock)
|
|
assert developer_view.monaco.parent() is not None
|
|
|
|
def test_ide_explorer_integration(self, developer_view):
|
|
"""Test that IDE Explorer is properly integrated."""
|
|
assert isinstance(developer_view.explorer, IDEExplorer)
|
|
assert developer_view.explorer.parent() is not None
|
|
|
|
def test_toolbar_components(self, developer_view):
|
|
"""Test that toolbar components are properly set up."""
|
|
assert developer_view.toolbar is not None
|
|
|
|
# Check for expected toolbar actions
|
|
toolbar_components = developer_view.toolbar.components
|
|
expected_actions = ["save", "save_as", "run", "stop", "vim"]
|
|
|
|
for action_name in expected_actions:
|
|
assert toolbar_components.exists(action_name)
|
|
|
|
def test_dock_manager_setup(self, developer_view):
|
|
"""Test that dock manager is properly configured."""
|
|
assert developer_view.dock_manager is not None
|
|
|
|
# Check that docks are added
|
|
dock_widgets = developer_view.dock_manager.dockWidgets()
|
|
assert len(dock_widgets) >= 4 # Explorer, Monaco, Console, Terminal
|
|
|
|
|
|
class TestFileOperations:
|
|
"""Test file operation functionality."""
|
|
|
|
def test_open_new_file(self, developer_view, temp_python_file, qtbot):
|
|
"""Test opening a new file in the Monaco editor."""
|
|
# Simulate opening a file through the IDE explorer signal
|
|
developer_view._open_new_file(temp_python_file, "scripts/local")
|
|
|
|
# Wait for the file to be loaded
|
|
qtbot.waitUntil(
|
|
lambda: temp_python_file in developer_view.monaco._get_open_files(), timeout=2000
|
|
)
|
|
|
|
# Check that the file was opened
|
|
assert temp_python_file in developer_view.monaco._get_open_files()
|
|
|
|
# Check that content was loaded (simplified check)
|
|
# Get the editor dock for the file and check its content
|
|
dock = developer_view.monaco._get_editor_dock(temp_python_file)
|
|
if dock:
|
|
editor_widget = dock.widget()
|
|
assert "test_function" in editor_widget.get_text()
|
|
|
|
def test_open_shared_file_readonly(self, developer_view, temp_python_file, qtbot):
|
|
"""Test that shared files are opened in read-only mode."""
|
|
# Open file with shared scope
|
|
developer_view._open_new_file(temp_python_file, "scripts/shared")
|
|
|
|
qtbot.waitUntil(
|
|
lambda: temp_python_file in developer_view.monaco._get_open_files(), timeout=2000
|
|
)
|
|
|
|
# Check that the file is set to read-only
|
|
dock = developer_view.monaco._get_editor_dock(temp_python_file)
|
|
if dock:
|
|
monaco_widget = dock.widget()
|
|
# Check that the widget is in read-only mode
|
|
# This depends on MonacoWidget having a readonly property or method
|
|
assert monaco_widget is not None
|
|
|
|
def test_file_icon_assignment(self, developer_view, temp_python_file, qtbot):
|
|
"""Test that file icons are assigned based on scope."""
|
|
# Test script file icon
|
|
developer_view._open_new_file(temp_python_file, "scripts/local")
|
|
|
|
qtbot.waitUntil(
|
|
lambda: temp_python_file in developer_view.monaco._get_open_files(), timeout=2000
|
|
)
|
|
|
|
# Check that an icon was set (simplified check)
|
|
dock = developer_view.monaco._get_editor_dock(temp_python_file)
|
|
if dock:
|
|
assert not dock.icon().isNull()
|
|
|
|
def test_save_functionality(self, developer_view, qtbot):
|
|
"""Test the save functionality."""
|
|
# Get the currently focused editor widget (if any)
|
|
if developer_view.monaco.last_focused_editor:
|
|
editor_widget = developer_view.monaco.last_focused_editor.widget()
|
|
test_text = "print('Hello from save test')"
|
|
editor_widget.set_text(test_text)
|
|
|
|
qtbot.waitUntil(lambda: editor_widget.get_text() == test_text, timeout=1000)
|
|
|
|
# Test the save action
|
|
with mock.patch.object(developer_view.monaco, "save_file") as mock_save:
|
|
developer_view.on_save()
|
|
mock_save.assert_called_once()
|
|
|
|
def test_save_as_functionality(self, developer_view, qtbot):
|
|
"""Test the save as functionality."""
|
|
# Get the currently focused editor widget (if any)
|
|
if developer_view.monaco.last_focused_editor:
|
|
editor_widget = developer_view.monaco.last_focused_editor.widget()
|
|
test_text = "print('Hello from save as test')"
|
|
editor_widget.set_text(test_text)
|
|
|
|
qtbot.waitUntil(lambda: editor_widget.get_text() == test_text, timeout=1000)
|
|
|
|
# Test the save as action
|
|
with mock.patch.object(developer_view.monaco, "save_file") as mock_save:
|
|
developer_view.on_save_as()
|
|
mock_save.assert_called_once_with(force_save_as=True)
|
|
|
|
|
|
class TestMonacoEditorIntegration:
|
|
"""Test Monaco editor specific functionality."""
|
|
|
|
def test_vim_mode_toggle(self, developer_view, qtbot):
|
|
"""Test vim mode toggle functionality."""
|
|
# Test enabling vim mode
|
|
with mock.patch.object(developer_view.monaco, "set_vim_mode") as mock_vim:
|
|
developer_view.on_vim_triggered()
|
|
# The actual call depends on the checkbox state
|
|
mock_vim.assert_called_once()
|
|
|
|
def test_context_menu_insert_scan(self, developer_view, mock_scan_control_dialog, qtbot):
|
|
"""Test the Insert Scan context menu action."""
|
|
# This functionality is handled by individual MonacoWidget instances
|
|
# Test that the dock has editor widgets
|
|
dock_widgets = developer_view.monaco.dock_manager.dockWidgets()
|
|
assert len(dock_widgets) >= 1
|
|
|
|
# Test on the first available editor
|
|
first_dock = dock_widgets[0]
|
|
monaco_widget = first_dock.widget()
|
|
assert isinstance(monaco_widget, MonacoWidget)
|
|
|
|
def test_context_menu_format_code(self, developer_view, qtbot):
|
|
"""Test the Format Code context menu action."""
|
|
# Get an editor widget from the dock manager
|
|
dock_widgets = developer_view.monaco.dock_manager.dockWidgets()
|
|
if dock_widgets:
|
|
first_dock = dock_widgets[0]
|
|
monaco_widget = first_dock.widget()
|
|
|
|
# Set some unformatted Python code
|
|
unformatted_code = "import os,sys\ndef test():\n x=1+2\n return x"
|
|
monaco_widget.set_text(unformatted_code)
|
|
|
|
qtbot.waitUntil(lambda: monaco_widget.get_text() == unformatted_code, timeout=1000)
|
|
|
|
# Test format action on the individual widget
|
|
with mock.patch.object(monaco_widget, "format") as mock_format:
|
|
monaco_widget.format()
|
|
mock_format.assert_called_once()
|
|
|
|
def test_save_enabled_signal_handling(self, developer_view, qtbot):
|
|
"""Test that save enabled signals are handled correctly."""
|
|
# Mock the toolbar update method
|
|
with mock.patch.object(developer_view, "_on_save_enabled_update") as mock_update:
|
|
# Simulate save enabled signal
|
|
developer_view.monaco.save_enabled.emit(True)
|
|
mock_update.assert_called_with(True)
|
|
|
|
developer_view.monaco.save_enabled.emit(False)
|
|
mock_update.assert_called_with(False)
|
|
|
|
|
|
class TestIDEExplorerIntegration:
|
|
"""Test IDE Explorer integration."""
|
|
|
|
def test_file_open_signal_connection(self, developer_view):
|
|
"""Test that file open signals are properly connected."""
|
|
# Test that the signal connection works by mocking the connected method
|
|
with mock.patch.object(developer_view, "_open_new_file") as mock_open:
|
|
# Emit the signal to test the connection
|
|
developer_view.explorer.file_open_requested.emit("test_file.py", "scripts/local")
|
|
mock_open.assert_called_once_with("test_file.py", "scripts/local")
|
|
|
|
def test_file_preview_signal_connection(self, developer_view):
|
|
"""Test that file preview signals are properly connected."""
|
|
# Test that the signal exists and can be emitted (basic connection test)
|
|
try:
|
|
developer_view.explorer.file_preview_requested.emit("test_file.py", "scripts/local")
|
|
# If no exception is raised, the signal exists and is connectable
|
|
assert True
|
|
except AttributeError:
|
|
assert False, "file_preview_requested signal not found"
|
|
|
|
def test_sections_configuration(self, developer_view):
|
|
"""Test that IDE Explorer sections are properly configured."""
|
|
assert "scripts" in developer_view.explorer.sections
|
|
assert "macros" in developer_view.explorer.sections
|
|
|
|
|
|
class TestToolbarIntegration:
|
|
"""Test toolbar functionality and integration."""
|
|
|
|
def test_toolbar_save_button_state(self, developer_view):
|
|
"""Test toolbar save button state management."""
|
|
# Test that save buttons exist and can be controlled
|
|
save_action = developer_view.toolbar.components.get_action("save")
|
|
save_as_action = developer_view.toolbar.components.get_action("save_as")
|
|
|
|
# Test that the actions exist and are accessible
|
|
assert save_action.action is not None
|
|
assert save_as_action.action is not None
|
|
|
|
# Test that they can be enabled/disabled via the update method
|
|
developer_view._on_save_enabled_update(False)
|
|
assert not save_action.action.isEnabled()
|
|
assert not save_as_action.action.isEnabled()
|
|
|
|
developer_view._on_save_enabled_update(True)
|
|
assert save_action.action.isEnabled()
|
|
assert save_as_action.action.isEnabled()
|
|
|
|
def test_vim_mode_button_toggle(self, developer_view, qtbot):
|
|
"""Test vim mode button toggle functionality."""
|
|
vim_action = developer_view.toolbar.components.get_action("vim")
|
|
|
|
if vim_action:
|
|
# Test toggling vim mode
|
|
initial_state = vim_action.action.isChecked()
|
|
|
|
# Simulate button click
|
|
vim_action.action.trigger()
|
|
|
|
# Check that state changed
|
|
assert vim_action.action.isChecked() != initial_state
|
|
|
|
|
|
class TestErrorHandling:
|
|
"""Test error handling in various scenarios."""
|
|
|
|
def test_invalid_scope_handling(self, developer_view, temp_python_file):
|
|
"""Test handling of invalid scope parameters."""
|
|
# Test with invalid scope
|
|
try:
|
|
developer_view._open_new_file(temp_python_file, "invalid/scope")
|
|
except Exception as e:
|
|
assert False, f"Invalid scope should be handled gracefully: {e}"
|
|
|
|
def test_monaco_editor_error_handling(self, developer_view):
|
|
"""Test error handling in Monaco editor operations."""
|
|
# Test with editor widgets from dock manager
|
|
dock_widgets = developer_view.monaco.dock_manager.dockWidgets()
|
|
if dock_widgets:
|
|
first_dock = dock_widgets[0]
|
|
monaco_widget = first_dock.widget()
|
|
|
|
# Test setting invalid text
|
|
try:
|
|
monaco_widget.set_text(None) # This might cause an error
|
|
except Exception:
|
|
# Errors should be handled gracefully
|
|
pass
|
|
|
|
|
|
class TestSignalIntegration:
|
|
"""Test signal connections and data flow."""
|
|
|
|
def test_file_open_signal_flow(self, developer_view, temp_python_file, qtbot):
|
|
"""Test the complete file open signal flow."""
|
|
# Mock the _open_new_file method to verify it gets called
|
|
with mock.patch.object(developer_view, "_open_new_file") as mock_open:
|
|
# Emit the file open signal from explorer
|
|
developer_view.explorer.file_open_requested.emit(temp_python_file, "scripts/local")
|
|
|
|
# Verify the signal was handled
|
|
mock_open.assert_called_once_with(temp_python_file, "scripts/local")
|
|
|
|
def test_save_enabled_signal_flow(self, developer_view, qtbot):
|
|
"""Test the save enabled signal flow."""
|
|
# Mock the update method (the actual method is _on_save_enabled_update)
|
|
with mock.patch.object(developer_view, "_on_save_enabled_update") as mock_update:
|
|
# Simulate monaco dock emitting save enabled signal
|
|
developer_view.monaco.save_enabled.emit(True)
|
|
|
|
# Verify the signal was handled
|
|
mock_update.assert_called_once_with(True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__])
|