fix: satisfy lint and diff-coverage gates for admin gating
basedpyright requires _admin_tip_anchor to exist after __init__, so the anchor is created eagerly instead of lazily. Cover the red-tip branch and the status-bar menu gate; the menu test swaps in a QMenu subclass with a no-op exec because PySide method lookup ignores class-attr monkeypatches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -353,6 +353,13 @@ class MainWindow(QMainWindow):
|
||||
# after the docks), not in the left column. Always visible.
|
||||
self.beamline_state_panel = BeamlineStatePanel(staff=self._decoded_token.staff, parent=self)
|
||||
|
||||
# Anchor for _show_admin_tip: QToolTip inherits QSS from the widget
|
||||
# it is shown for, so this hidden label carries the red warning wash
|
||||
# without reddening any other tooltip. Eager, not lazy — basedpyright
|
||||
# requires instance attributes to exist after __init__.
|
||||
self._admin_tip_anchor = QLabel(self)
|
||||
self._admin_tip_anchor.hide()
|
||||
|
||||
# Beamline / Experiment as tabs (like the Dewar samples dock) instead
|
||||
# of two stacked banner groups; the pages keep their banner children.
|
||||
# documentMode: no pane frame, so the fixed-width panels aren't inset.
|
||||
@@ -1477,12 +1484,8 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def _show_admin_tip(self, message: str) -> None:
|
||||
# "Not admin" denials are passive red tips, not QMessageBoxes:
|
||||
# nothing to click away. QToolTip inherits QSS from the widget it is
|
||||
# shown for, so a hidden anchor keeps the red wash off every other
|
||||
# tooltip; styled at show time so it always matches the theme.
|
||||
if not hasattr(self, "_admin_tip_anchor"):
|
||||
self._admin_tip_anchor = QLabel(self)
|
||||
self._admin_tip_anchor.hide()
|
||||
# nothing to click away. Styled at show time so it always matches
|
||||
# the theme.
|
||||
self._admin_tip_anchor.setStyleSheet(admin_tip_qss(self._theme_mode))
|
||||
QToolTip.showText(QCursor.pos(), message, self._admin_tip_anchor)
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ def test_non_staff_never_reach_admin_only_states(qtbot):
|
||||
assert "QToolTip" not in panel._buttons[BeamlineStateEnum.XtalSnapshot].styleSheet()
|
||||
with qtbot.assertNotEmitted(panel.beam_location):
|
||||
panel._emit_for_state(BeamlineStateEnum.BeamLocation)
|
||||
panel._on_left_click(BeamlineStateEnum.BeamLocation) # shows the admin tip
|
||||
|
||||
|
||||
def test_no_targets_while_moving_or_unknown(qtbot):
|
||||
|
||||
@@ -33,6 +33,35 @@ def test_operables_share_the_hover_affordance(qtbot):
|
||||
assert not bar.state_label.font().underline()
|
||||
|
||||
|
||||
def test_state_menu_gates_beam_location_for_non_staff(qtbot, daq_status_factory, monkeypatch):
|
||||
from aarecommon.models.models import BeamlineStateEnum
|
||||
from PySide6.QtWidgets import QMenu
|
||||
|
||||
import aare.gui.widgets.status_bar as status_bar_module
|
||||
|
||||
# exec() would block on a real popup, and PySide's method lookup ignores
|
||||
# a class-attribute monkeypatch — swap in a subclass instead. The menu
|
||||
# stays inspectable as a child of the bar afterwards.
|
||||
class _NoExecMenu(QMenu):
|
||||
def exec(self): # pyright: ignore[reportIncompatibleMethodOverride]
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(status_bar_module, "QMenu", _NoExecMenu)
|
||||
|
||||
def _state_menu_entries(bar):
|
||||
bar._status = daq_status_factory(state=BeamlineStateEnum.SampleAlignment)
|
||||
bar.show_state_menu()
|
||||
# Plain data, not QAction refs: the menu (and its actions) only
|
||||
# lives until the next GC pass once show_state_menu returns.
|
||||
return {a.text(): a.isEnabled() for a in bar.findChildren(QMenu)[-1].actions()}
|
||||
|
||||
assert _state_menu_entries(_bar(qtbot))["Beam location"]
|
||||
|
||||
non_staff = StatusBar(token=TokenData(sub="u", staff=False, pgroups=["p1"], session=1))
|
||||
qtbot.addWidget(non_staff)
|
||||
assert not _state_menu_entries(non_staff)["Beam location (admin mode only)"]
|
||||
|
||||
|
||||
def test_passives_left_operables_right(qtbot):
|
||||
bar = _bar(qtbot)
|
||||
# QStatusBar hides only the non-permanent (left) section behind a
|
||||
|
||||
Reference in New Issue
Block a user