mirror of
https://github.com/bec-project/bec_widgets.git
synced 2026-04-08 01:37:53 +02:00
feat(developer_view): add developer view
This commit is contained in:
378
tests/unit_tests/test_developer_view.py
Normal file
378
tests/unit_tests/test_developer_view.py
Normal file
@@ -0,0 +1,378 @@
|
||||
"""
|
||||
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__])
|
||||
Reference in New Issue
Block a user