@@ -21,16 +21,34 @@ def _state(name: str, state_type: str, parameters: dict | None = None):
2121 )
2222
2323
24- def _limits_state (name : str = "limits" , ** overrides ):
25- parameters = {
24+ def _wire_state (
25+ state_class : type [bl_states .BeamlineState ], config : bl_states .BeamlineStateConfig
26+ ) -> messages .BeamlineStateConfig :
27+ return messages .BeamlineStateConfig (
28+ name = config .name ,
29+ state_type = state_class .__name__ ,
30+ parameters = config .model_dump (exclude = {"name" }),
31+ )
32+
33+
34+ def _limits_state (name : str = "limits" , ** overrides ) -> messages .BeamlineStateConfig :
35+ values = {
2636 "device" : "samx" ,
2737 "signal" : "samx" ,
2838 "low_limit" : 0.0 ,
2939 "high_limit" : 10.0 ,
3040 "tolerance" : 0.1 ,
3141 }
32- parameters .update (overrides )
33- return _state (name , "DeviceWithinLimitsState" , parameters )
42+ values .update (overrides )
43+ config = bl_states .DeviceWithinLimitsState .CONFIG_CLASS (name = name , ** values )
44+ return _wire_state (bl_states .DeviceWithinLimitsState , config )
45+
46+
47+ def _shutter_state (
48+ name : str = "shutter_open" , device : str = "samy"
49+ ) -> messages .BeamlineStateConfig :
50+ config = bl_states .ShutterState .CONFIG_CLASS (name = name , device = device )
51+ return _wire_state (bl_states .ShutterState , config )
3452
3553
3654def test_beamline_state_pill_updates_from_message (qtbot , mocked_client ):
@@ -132,6 +150,44 @@ def test_beamline_state_pill_reverts_changed_settings(qtbot, mocked_client):
132150 )
133151
134152
153+ def test_beamline_state_pill_releases_form_on_collapse (qtbot , mocked_client ):
154+ limits_pill = create_widget (qtbot , BeamlineStatePill , state_name = "limits" , client = mocked_client )
155+ limits_pill .set_state_config (_limits_state ())
156+
157+ limits_pill .set_expanded (True )
158+ device_widget = limits_pill ._config_form .input_widget ("device" )
159+
160+ limits_pill .set_expanded (False )
161+ QCoreApplication .sendPostedEvents (None , QEvent .Type .DeferredDelete )
162+
163+ assert limits_pill ._config_form is None
164+ assert not shiboken6 .isValid (device_widget )
165+
166+ limits_pill .set_expanded (True )
167+
168+ assert limits_pill ._config_form is not None
169+ assert limits_pill ._config_form .input_widget ("high_limit" ).value () == 10.0
170+ assert not limits_pill ._update_button .isEnabled ()
171+
172+
173+ def test_beamline_state_pill_collapse_discards_unsaved_edits (qtbot , mocked_client ):
174+ limits_pill = create_widget (qtbot , BeamlineStatePill , state_name = "limits" , client = mocked_client )
175+ limits_pill .set_state_config (_limits_state ())
176+
177+ limits_pill .set_expanded (True )
178+ limits_pill ._config_form .input_widget ("high_limit" ).setValue (20.0 )
179+ assert limits_pill ._update_button .isEnabled ()
180+
181+ limits_pill .set_expanded (False )
182+
183+ assert limits_pill ._config_form is None
184+
185+ limits_pill .set_expanded (True )
186+
187+ assert limits_pill ._config_form .input_widget ("high_limit" ).value () == 10.0
188+ assert not limits_pill ._update_button .isEnabled ()
189+
190+
135191def test_beamline_state_pill_does_not_override_themed_input_controls (qtbot , mocked_client ):
136192 limits_pill = create_widget (qtbot , BeamlineStatePill , state_name = "limits" , client = mocked_client )
137193 limits_pill .set_state_config (_limits_state ())
@@ -251,13 +307,7 @@ def test_beamline_state_manager_propagates_idle_card_background(qtbot, mocked_cl
251307def test_beamline_state_manager_filters_status (qtbot , mocked_client ):
252308 beamline_state_manager = create_widget (qtbot , BeamlineStateManager , client = mocked_client )
253309 beamline_state_manager .update_available_states (
254- {
255- "states" : [
256- _state ("shutter_open" , "ShutterState" , {"device" : "samy" }),
257- _state ("limits" , "DeviceWithinLimitsState" , {"device" : "samx" }),
258- ]
259- },
260- {},
310+ {"states" : [_shutter_state (), _limits_state ()]}, {}
261311 )
262312
263313 assert isinstance (beamline_state_manager ._toolbar , ModularToolBar )
@@ -324,8 +374,8 @@ def test_beamline_state_manager_filters_devices(qtbot, mocked_client, monkeypatc
324374 beamline_state_manager .update_available_states (
325375 {
326376 "states" : [
327- _state ( "samx_limits" , "DeviceWithinLimitsState" , { "device" : "samx" } ),
328- _state ( "samy_limits" , "DeviceWithinLimitsState" , { " device" : " samy"} ),
377+ _limits_state ( name = "samx_limits" ),
378+ _limits_state ( name = "samy_limits" , device = " samy" ),
329379 ]
330380 },
331381 {},
@@ -358,6 +408,23 @@ def exec(self):
358408 assert captured ["parent" ] is beamline_state_manager
359409
360410
411+ def test_beamline_state_manager_collapse_all (qtbot , mocked_client ):
412+ beamline_state_manager = create_widget (qtbot , BeamlineStateManager , client = mocked_client )
413+ beamline_state_manager .update_available_states (
414+ {"states" : [_limits_state (), _state ("shutter_open" , "ShutterState" , {"device" : "samy" })]},
415+ {},
416+ )
417+
418+ for pill in beamline_state_manager ._state_pills .values ():
419+ pill .set_expanded (True )
420+ assert all (pill .is_expanded () for pill in beamline_state_manager ._state_pills .values ())
421+
422+ collapse_action = beamline_state_manager ._toolbar .components .get_action ("collapse_all" )
423+ collapse_action .action .trigger ()
424+
425+ assert not any (pill .is_expanded () for pill in beamline_state_manager ._state_pills .values ())
426+
427+
361428def test_beamline_state_manager_backend_echo_repopulates_expanded_pill (qtbot , mocked_client ):
362429 beamline_state_manager = create_widget (qtbot , BeamlineStateManager , client = mocked_client )
363430 beamline_state_manager .update_available_states ({"states" : [_limits_state ()]}, {})
0 commit comments