diff --git a/tests/unit/gui/test_beamline_state_panel.py b/tests/unit/gui/test_beamline_state_panel.py new file mode 100644 index 00000000..1c42d411 --- /dev/null +++ b/tests/unit/gui/test_beamline_state_panel.py @@ -0,0 +1,122 @@ +from aarecommon.models.models import BeamlineStateEnum +from PySide6.QtCore import Qt + +from aare.gui.panels.beamline_state_panel import BeamlineStatePanel + + +def _panel(qtbot): + panel = BeamlineStatePanel() + qtbot.addWidget(panel) + return panel + + +def test_availability_from_maintenance(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + assert panel._available_targets() == frozenset({BeamlineStateEnum.SampleExchange}) + + +def test_availability_is_union_of_routes_and_menu_shortcuts(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.SampleAlignment) + targets = panel._available_targets() + assert BeamlineStateEnum.FluxMeasurement in targets # one-hop route + assert BeamlineStateEnum.SampleExchange in targets # status-bar shortcut + assert BeamlineStateEnum.XtalSnapshot not in targets # two hops away + + +def test_no_targets_while_moving_or_unknown(qtbot): + panel = _panel(qtbot) + panel.set_current_state(None) + assert panel._available_targets() == frozenset() + panel.set_current_state(BeamlineStateEnum.Moving) + assert panel._available_targets() == frozenset() + + +def test_emit_gated_by_availability(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + with qtbot.waitSignal(panel.sample_exchange, timeout=1000): + panel._emit_for_state(BeamlineStateEnum.SampleExchange) + with qtbot.assertNotEmitted(panel.flux_measurement): + panel._emit_for_state(BeamlineStateEnum.FluxMeasurement) + + +def test_active_maintenance_is_red_and_bold(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + maintenance = panel._buttons[BeamlineStateEnum.Maintenance] + assert maintenance.font().bold() + assert "rgb(200, 30, 30)" in maintenance.styleSheet() + + +def test_availability_palette_and_cursors(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + available = panel._buttons[BeamlineStateEnum.SampleExchange] + grey = panel._buttons[BeamlineStateEnum.FluxMeasurement] + assert "rgb(237, 137, 54)" in available.styleSheet() + assert available.cursor().shape() == Qt.CursorShape.PointingHandCursor + assert not available.font().bold() + assert "rgb(140, 150, 165)" in grey.styleSheet() + assert grey.cursor().shape() == Qt.CursorShape.ForbiddenCursor + assert grey.toolTip() == "" + assert available.toolTip() != "" + + +def test_active_non_maintenance_is_blue(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.SampleAlignment) + active = panel._buttons[BeamlineStateEnum.SampleAlignment] + assert active.font().bold() + assert "rgb(0, 92, 170)" in active.styleSheet() + + +def test_labels_wrap_when_narrow_and_unwrap_when_wide(qtbot): + panel = _panel(qtbot) + sample_exchange = panel._buttons[BeamlineStateEnum.SampleExchange] + + panel.resize(600, 60) + panel._update_label_mode() + assert not panel._single_line + assert sample_exchange.text() == "Manual sample\nexchange" + + panel.resize(4000, 60) + panel._update_label_mode() + assert panel._single_line + assert sample_exchange.text() == "Manual sample exchange" + + +def test_left_click_never_transitions_but_hints(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + with qtbot.assertNotEmitted(panel.sample_exchange): + panel._on_left_click(BeamlineStateEnum.SampleExchange) # reminder tip + panel._on_left_click(BeamlineStateEnum.FluxMeasurement) # reachability hint + panel._on_left_click(BeamlineStateEnum.Maintenance) # current: no-op + + +def test_hover_hint_timer_lifecycle(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + panel._set_hovered_state(BeamlineStateEnum.FluxMeasurement) + assert panel._hover_hint_timer.isActive() + panel._show_hover_hint() # runs the unavailable-hint path + panel._clear_hovered_state() + assert not panel._hover_hint_timer.isActive() + assert panel._hovered_state is None + + +def test_update_daq_status_sets_state(qtbot, daq_status_factory): + panel = _panel(qtbot) + panel.update_daq_status(daq_status_factory(state=BeamlineStateEnum.SampleAlignment)) + assert panel._current_state == BeamlineStateEnum.SampleAlignment + + +def test_pending_target_cleared_on_arrival(qtbot): + panel = _panel(qtbot) + panel.set_current_state(BeamlineStateEnum.Maintenance) + panel._emit_for_state(BeamlineStateEnum.SampleExchange) + assert panel._pending_target_state == BeamlineStateEnum.SampleExchange + panel.set_current_state(BeamlineStateEnum.SampleExchange) + assert panel._pending_target_state is None