mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-13 19:21:50 +02:00
test: add tests for config dialog
This commit is contained in:
@ -430,7 +430,7 @@ class ListMetadataField(DynamicFormItem):
|
|||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
def setValue(self, value: list):
|
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}")
|
raise ValueError(f"This widget only accepts items of type {self._types.item}")
|
||||||
self._repop(value)
|
self._repop(value)
|
||||||
|
|
||||||
|
@ -193,8 +193,8 @@ class DictBackedTable(QWidget):
|
|||||||
def clear(self):
|
def clear(self):
|
||||||
self._table_model.replaceData({})
|
self._table_model.replaceData({})
|
||||||
|
|
||||||
def replace_data(self, data: dict):
|
def replace_data(self, data: dict | None):
|
||||||
self._table_model.replaceData(data)
|
self._table_model.replaceData(data or {})
|
||||||
|
|
||||||
def delete_selected_rows(self):
|
def delete_selected_rows(self):
|
||||||
"""Delete rows which are part of the selection model"""
|
"""Delete rows which are part of the selection model"""
|
||||||
|
@ -28,9 +28,13 @@ class DeviceConfigDialog(BECWidget, QDialog):
|
|||||||
RPC = False
|
RPC = False
|
||||||
|
|
||||||
def __init__(
|
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._config_helper = config_helper or ConfigHelper(
|
||||||
self.client.connector, self.client._service_name
|
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
|
import pytest
|
||||||
from pydantic import BaseModel, Field
|
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 (
|
from bec_widgets.utils.forms_from_types.items import (
|
||||||
FloatDecimalMetadataField,
|
FloatDecimalMetadataField,
|
||||||
IntMetadataField,
|
IntMetadataField,
|
@ -1,5 +1,5 @@
|
|||||||
import sys
|
import sys
|
||||||
from typing import Any, Literal
|
from typing import Any, Literal, get_args
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pydantic import ValidationError
|
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},
|
{"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 = (
|
itype, vals, extra = (
|
||||||
request.param.get("type"),
|
request.param.get("type"),
|
||||||
request.param.get("value"),
|
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))
|
spec = FormItemSpec(item_type=itype, name="test_list", info=FieldInfo(annotation=itype))
|
||||||
(widget := ListMetadataField(parent=None, spec=spec)).setValue(vals)
|
(widget := ListMetadataField(parent=None, spec=spec)).setValue(vals)
|
||||||
qtbot.addWidget(widget)
|
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]):
|
def test_list_metadata_field(list_field_and_values: tuple[ListMetadataField, list, Any, type]):
|
||||||
list_metadata_field, vals, extra = list_metadata_field_and_values
|
list_field, vals, extra, _ = list_field_and_values
|
||||||
assert list_metadata_field.getValue() == vals
|
assert list_field.getValue() == vals
|
||||||
assert list_metadata_field._main_widget.count() == 3
|
assert list_field._main_widget.count() == 3
|
||||||
|
|
||||||
list_metadata_field._add_button.click()
|
list_field._add_button.click()
|
||||||
assert len(list_metadata_field.getValue()) == 4
|
assert len(list_field.getValue()) == 4
|
||||||
assert list_metadata_field._main_widget.count() == 4
|
assert list_field._main_widget.count() == 4
|
||||||
|
|
||||||
list_metadata_field._main_widget.setCurrentRow(-1)
|
list_field._main_widget.setCurrentRow(-1)
|
||||||
list_metadata_field._remove_button.click()
|
list_field._remove_button.click()
|
||||||
assert len(list_metadata_field.getValue()) == 4
|
assert len(list_field.getValue()) == 4
|
||||||
assert list_metadata_field._main_widget.count() == 4
|
assert list_field._main_widget.count() == 4
|
||||||
|
|
||||||
list_metadata_field._main_widget.setCurrentRow(2)
|
list_field._main_widget.setCurrentRow(2)
|
||||||
list_metadata_field._remove_button.click()
|
list_field._remove_button.click()
|
||||||
assert list_metadata_field.getValue() == vals[:2] + [list_metadata_field._types.default]
|
assert list_field.getValue() == vals[:2] + [list_field._types.default]
|
||||||
assert list_metadata_field._main_widget.count() == 3
|
assert list_field._main_widget.count() == 3
|
||||||
|
|
||||||
list_metadata_field._main_widget.setCurrentRow(1)
|
list_field._main_widget.setCurrentRow(1)
|
||||||
WidgetIO.set_value(
|
WidgetIO.set_value(list_field._main_widget.itemWidget(list_field._main_widget.item(1)), extra)
|
||||||
list_metadata_field._main_widget.itemWidget(list_metadata_field._main_widget.item(1)), extra
|
assert list_field._main_widget.count() == 3
|
||||||
)
|
assert list_field.getValue() == [vals[0], extra, list_field._types.default]
|
||||||
assert list_metadata_field._main_widget.count() == 3
|
|
||||||
assert list_metadata_field.getValue() == [vals[0], extra, list_metadata_field._types.default]
|
|
||||||
|
|
||||||
list_metadata_field._add_item(extra)
|
list_field._add_item(extra)
|
||||||
assert list_metadata_field._main_widget.count() == 4
|
assert list_field._main_widget.count() == 4
|
||||||
assert list_metadata_field.getValue() == [
|
assert list_field.getValue() == [vals[0], extra, list_field._types.default, extra]
|
||||||
vals[0],
|
|
||||||
extra,
|
|
||||||
list_metadata_field._types.default,
|
def test_list_field_value_acceptance(
|
||||||
extra,
|
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