mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-12 18:51:50 +02:00
test: add tests for config dialog
This commit is contained in:
@ -430,7 +430,7 @@ class ListMetadataField(DynamicFormItem):
|
||||
return self._data
|
||||
|
||||
def setValue(self, value: list):
|
||||
if set(map(type, value)) != {self._types.item}:
|
||||
if set(map(type, value)) | {self._types.item} != {self._types.item}:
|
||||
raise ValueError(f"This widget only accepts items of type {self._types.item}")
|
||||
self._repop(value)
|
||||
|
||||
|
@ -193,8 +193,8 @@ class DictBackedTable(QWidget):
|
||||
def clear(self):
|
||||
self._table_model.replaceData({})
|
||||
|
||||
def replace_data(self, data: dict):
|
||||
self._table_model.replaceData(data)
|
||||
def replace_data(self, data: dict | None):
|
||||
self._table_model.replaceData(data or {})
|
||||
|
||||
def delete_selected_rows(self):
|
||||
"""Delete rows which are part of the selection model"""
|
||||
|
@ -28,9 +28,13 @@ class DeviceConfigDialog(BECWidget, QDialog):
|
||||
RPC = False
|
||||
|
||||
def __init__(
|
||||
self, parent=None, device: str | None = None, config_helper: ConfigHelper | None = None
|
||||
self,
|
||||
parent=None,
|
||||
device: str | None = None,
|
||||
config_helper: ConfigHelper | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(parent=parent)
|
||||
super().__init__(parent=parent, **kwargs)
|
||||
self._config_helper = config_helper or ConfigHelper(
|
||||
self.client.connector, self.client._service_name
|
||||
)
|
||||
|
97
tests/unit_tests/test_device_config_form_dialog.py
Normal file
97
tests/unit_tests/test_device_config_form_dialog.py
Normal file
@ -0,0 +1,97 @@
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from bec_lib.atlas_models import Device as DeviceConfigModel
|
||||
|
||||
from bec_widgets.widgets.services.device_browser.device_item.device_config_dialog import (
|
||||
DeviceConfigDialog,
|
||||
)
|
||||
|
||||
_BASIC_CONFIG = {
|
||||
"name": "test_device",
|
||||
"enabled": True,
|
||||
"deviceClass": "TestDevice",
|
||||
"readoutPriority": "monitored",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dialog(qtbot):
|
||||
"""Fixture to create a DeviceConfigDialog instance."""
|
||||
mock_device = MagicMock(_config=DeviceConfigModel.model_validate(_BASIC_CONFIG).model_dump())
|
||||
mock_client = MagicMock()
|
||||
mock_client.device_manager.devices = {"test_device": mock_device}
|
||||
dialog = DeviceConfigDialog(device="test_device", config_helper=MagicMock(), client=mock_client)
|
||||
qtbot.addWidget(dialog)
|
||||
return dialog
|
||||
|
||||
|
||||
def test_initialization(dialog):
|
||||
assert dialog._device == "test_device"
|
||||
assert dialog._container.count() == 2
|
||||
|
||||
|
||||
def test_fill_form(dialog):
|
||||
with patch.object(dialog._form, "set_data") as mock_set_data:
|
||||
dialog._fill_form()
|
||||
mock_set_data.assert_called_once_with(DeviceConfigModel.model_validate(_BASIC_CONFIG))
|
||||
|
||||
|
||||
def test_updated_config(dialog):
|
||||
"""Test that updated_config returns the correct changes."""
|
||||
dialog._initial_config = {"key1": "value1", "key2": "value2"}
|
||||
with patch.object(
|
||||
dialog._form, "get_form_data", return_value={"key1": "value1", "key2": "new_value"}
|
||||
):
|
||||
updated = dialog.updated_config()
|
||||
assert updated == {"key2": "new_value"}
|
||||
|
||||
|
||||
def test_apply(dialog):
|
||||
with patch.object(dialog, "_process_update_action") as mock_process_update:
|
||||
dialog.apply()
|
||||
mock_process_update.assert_called_once()
|
||||
|
||||
|
||||
def test_accept(dialog):
|
||||
with (
|
||||
patch.object(dialog, "_process_update_action") as mock_process_update,
|
||||
patch("qtpy.QtWidgets.QDialog.accept") as mock_parent_accept,
|
||||
):
|
||||
dialog.accept()
|
||||
mock_process_update.assert_called_once()
|
||||
mock_parent_accept.assert_called_once()
|
||||
|
||||
|
||||
def test_waiting_display(dialog, qtbot):
|
||||
with (
|
||||
patch.object(dialog._spinner, "start") as mock_spinner_start,
|
||||
patch.object(dialog._spinner, "stop") as mock_spinner_stop,
|
||||
):
|
||||
dialog.show()
|
||||
dialog._start_waiting_display()
|
||||
qtbot.waitUntil(dialog._overlay_widget.isVisible, timeout=100)
|
||||
mock_spinner_start.assert_called_once()
|
||||
mock_spinner_stop.assert_not_called()
|
||||
dialog._stop_waiting_display()
|
||||
qtbot.waitUntil(lambda: not dialog._overlay_widget.isVisible(), timeout=100)
|
||||
mock_spinner_stop.assert_called_once()
|
||||
|
||||
|
||||
def test_update_cycle(dialog, qtbot):
|
||||
update = {"enabled": False, "readoutPriority": "baseline", "deviceTags": ["tag"]}
|
||||
|
||||
def _mock_send(a, c, w):
|
||||
dialog.client.device_manager.devices["test_device"]._config = c["test_device"]
|
||||
|
||||
dialog._config_helper.send_config_request = MagicMock(side_effect=_mock_send)
|
||||
for item in dialog._form.enumerate_form_widgets():
|
||||
if (val := update.get(item.label.property("_model_field_name"))) is not None:
|
||||
item.widget.setValue(val)
|
||||
|
||||
assert dialog.updated_config() == update
|
||||
dialog.apply()
|
||||
|
||||
dialog._config_helper.send_config_request.assert_called_with(
|
||||
action="update", config={"test_device": update}, wait_for_response=False
|
||||
)
|
@ -3,7 +3,7 @@ from decimal import Decimal
|
||||
import pytest
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from bec_widgets.utils.forms_from_types.forms import PydanticModelForm
|
||||
from bec_widgets.utils.forms_from_types.forms import PydanticModelForm, TypedForm
|
||||
from bec_widgets.utils.forms_from_types.items import (
|
||||
FloatDecimalMetadataField,
|
||||
IntMetadataField,
|
@ -1,5 +1,5 @@
|
||||
import sys
|
||||
from typing import Any, Literal
|
||||
from typing import Any, Literal, get_args
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
@ -68,7 +68,7 @@ def test_form_item_spec(input, validity):
|
||||
{"type": list[float], "value": [0.1, 0.2, 0.3], "extra": 79.0},
|
||||
]
|
||||
)
|
||||
def list_metadata_field_and_values(request, qtbot):
|
||||
def list_field_and_values(request, qtbot):
|
||||
itype, vals, extra = (
|
||||
request.param.get("type"),
|
||||
request.param.get("value"),
|
||||
@ -77,40 +77,49 @@ def list_metadata_field_and_values(request, qtbot):
|
||||
spec = FormItemSpec(item_type=itype, name="test_list", info=FieldInfo(annotation=itype))
|
||||
(widget := ListMetadataField(parent=None, spec=spec)).setValue(vals)
|
||||
qtbot.addWidget(widget)
|
||||
yield widget, vals, extra
|
||||
yield widget, vals, extra, get_args(itype)[0]
|
||||
|
||||
|
||||
def test_list_metadata_field(list_metadata_field_and_values: tuple[ListMetadataField, list, Any]):
|
||||
list_metadata_field, vals, extra = list_metadata_field_and_values
|
||||
assert list_metadata_field.getValue() == vals
|
||||
assert list_metadata_field._main_widget.count() == 3
|
||||
def test_list_metadata_field(list_field_and_values: tuple[ListMetadataField, list, Any, type]):
|
||||
list_field, vals, extra, _ = list_field_and_values
|
||||
assert list_field.getValue() == vals
|
||||
assert list_field._main_widget.count() == 3
|
||||
|
||||
list_metadata_field._add_button.click()
|
||||
assert len(list_metadata_field.getValue()) == 4
|
||||
assert list_metadata_field._main_widget.count() == 4
|
||||
list_field._add_button.click()
|
||||
assert len(list_field.getValue()) == 4
|
||||
assert list_field._main_widget.count() == 4
|
||||
|
||||
list_metadata_field._main_widget.setCurrentRow(-1)
|
||||
list_metadata_field._remove_button.click()
|
||||
assert len(list_metadata_field.getValue()) == 4
|
||||
assert list_metadata_field._main_widget.count() == 4
|
||||
list_field._main_widget.setCurrentRow(-1)
|
||||
list_field._remove_button.click()
|
||||
assert len(list_field.getValue()) == 4
|
||||
assert list_field._main_widget.count() == 4
|
||||
|
||||
list_metadata_field._main_widget.setCurrentRow(2)
|
||||
list_metadata_field._remove_button.click()
|
||||
assert list_metadata_field.getValue() == vals[:2] + [list_metadata_field._types.default]
|
||||
assert list_metadata_field._main_widget.count() == 3
|
||||
list_field._main_widget.setCurrentRow(2)
|
||||
list_field._remove_button.click()
|
||||
assert list_field.getValue() == vals[:2] + [list_field._types.default]
|
||||
assert list_field._main_widget.count() == 3
|
||||
|
||||
list_metadata_field._main_widget.setCurrentRow(1)
|
||||
WidgetIO.set_value(
|
||||
list_metadata_field._main_widget.itemWidget(list_metadata_field._main_widget.item(1)), extra
|
||||
)
|
||||
assert list_metadata_field._main_widget.count() == 3
|
||||
assert list_metadata_field.getValue() == [vals[0], extra, list_metadata_field._types.default]
|
||||
list_field._main_widget.setCurrentRow(1)
|
||||
WidgetIO.set_value(list_field._main_widget.itemWidget(list_field._main_widget.item(1)), extra)
|
||||
assert list_field._main_widget.count() == 3
|
||||
assert list_field.getValue() == [vals[0], extra, list_field._types.default]
|
||||
|
||||
list_metadata_field._add_item(extra)
|
||||
assert list_metadata_field._main_widget.count() == 4
|
||||
assert list_metadata_field.getValue() == [
|
||||
vals[0],
|
||||
extra,
|
||||
list_metadata_field._types.default,
|
||||
extra,
|
||||
]
|
||||
list_field._add_item(extra)
|
||||
assert list_field._main_widget.count() == 4
|
||||
assert list_field.getValue() == [vals[0], extra, list_field._types.default, extra]
|
||||
|
||||
|
||||
def test_list_field_value_acceptance(
|
||||
list_field_and_values: tuple[ListMetadataField, list, Any, type],
|
||||
):
|
||||
class _WrongType(object): ...
|
||||
|
||||
list_field, _, _, t = list_field_and_values
|
||||
list_field.setValue([])
|
||||
assert list_field._main_widget.count() == 0
|
||||
list_field.setValue([t(), t(), t()])
|
||||
assert list_field._main_widget.count() == 3
|
||||
with pytest.raises(ValueError) as e:
|
||||
list_field.setValue([_WrongType()])
|
||||
assert list_field._main_widget.count() == 3
|
||||
assert e.match(f"This widget only accepts items of type {t}")
|
||||
|
Reference in New Issue
Block a user