Files
AareDAQ/tests/unit/gui/test_tell_sample_panel.py
duan_jandClaude Fable 5 4a728baf79 feat: clicking the '#' header selects all rows
The status column holds no sortable data, so its header click slot
was free: it now does Ctrl+A, pairing with the Unmeasured chip to
grab everything still to be done in one motion. The sort indicator
is put back afterwards - the click drags it onto '#' even though
the model ignores sorting there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 15:34:58 +02:00

145 lines
4.7 KiB
Python

"""The combined sample dock: chip row filters the table by status, chips
double as drop targets for queue/flag relabeling, and a pop-out panel shares
the docked panel's model so both stay in sync without wiring."""
import pytest
from aarecommon.models.models import DewarAddress, SampleShortInfo, SampleShortInfoList
from aare.gui.panels.tell_sample_panel import TellSamplePanel
@pytest.fixture
def samples():
return SampleShortInfoList(
s=[
SampleShortInfo(
db_id=i,
puck_name=f"P{i}",
dewar_name="D1",
sample_name=f"S{i}",
run_number=i,
user="U1",
pin=i,
location=DewarAddress(segment="A", pos=i),
)
for i in (1, 2, 3)
]
)
@pytest.fixture
def panel(qtbot, samples):
panel = TellSamplePanel(samples=samples)
qtbot.addWidget(panel)
panel.table_model.set_show_all_pgroups(True)
return panel
def _chip(panel, key):
return next(c for c in panel.status_chips.buttons() if c.property("status_key") == key)
def test_chip_click_drives_the_status_filter(panel):
panel.table_model.set_queued_ids({2})
_chip(panel, "queued").click()
assert panel.table_model.status_filter == "queued"
assert panel.table_model.rowCount() == 1
_chip(panel, None).click()
assert panel.table_model.status_filter is None
assert panel.table_model.rowCount() == 3
def test_set_status_chip_syncs_without_filtering(panel):
panel.set_status_chip("flagged")
assert _chip(panel, "flagged").isChecked()
# Sync only checks the chip; it must not fire the filter.
assert panel.table_model.status_filter is None
def test_queued_chip_drop_relays_to_the_queue(panel, qtbot, samples):
chip = _chip(panel, "queued")
with qtbot.waitSignal(panel.add_to_queue) as blocker:
chip.samples_dropped.emit(samples)
assert [s.db_id for s in blocker.args[0].s] == [1, 2, 3]
def test_flagged_chip_drop_flags_in_the_model(panel, samples):
panel._flag_dropped_samples(SampleShortInfoList(s=samples.s[:2]))
assert panel.table_model.flagged_ids == {1, 2}
def test_popout_panel_shares_the_model(panel, qtbot):
popout = TellSamplePanel(model=panel.table_model)
qtbot.addWidget(popout)
assert popout.table_model is panel.table_model
panel.table_model.set_flagged(3, True)
assert 3 in popout.table_model.flagged_ids
def test_new_sample_list_updates_rows(panel, samples):
extra = samples.s + [
SampleShortInfo(
db_id=9,
puck_name="P9",
dewar_name="D2",
sample_name="S9",
run_number=9,
user="U2",
pin=9,
location=DewarAddress(segment="B", pos=1),
)
]
panel.new_sample_list(SampleShortInfoList(s=extra))
assert panel.table_model.rowCount() == 4
def test_queue_drop_chip_accepts_sample_payloads(panel, qtbot, samples):
from PySide6.QtCore import QMimeData, QPointF, Qt
from PySide6.QtGui import QDropEvent
from aare.gui.panels.tell_sample_panel import QueueDropChip
chip = next(c for c in panel.status_chips.buttons() if isinstance(c, QueueDropChip))
# The event only borrows the QMimeData (C++ pointer), so the mime must
# outlive the dropEvent call — hence created in the test's scope.
def drop(mime):
return QDropEvent(
QPointF(1, 1),
Qt.DropAction.CopyAction,
mime,
Qt.MouseButton.NoButton,
Qt.KeyboardModifier.NoModifier,
)
good = QMimeData()
good.setText(samples.model_dump_json())
with qtbot.waitSignal(chip.samples_dropped):
chip.dropEvent(drop(good))
# A non-sample payload is ignored, not crashed on.
bad = QMimeData()
bad.setText("not json")
with qtbot.assertNotEmitted(chip.samples_dropped):
chip.dropEvent(drop(bad))
def test_status_header_click_selects_all(panel):
header = panel.table_view.frozen.horizontalHeader()
header.sectionClicked.emit(0)
assert len(panel.table_view.selectionModel().selectedRows()) == 3
# Other sections keep their normal sort-click behavior.
panel.table_view.clearSelection()
header.sectionClicked.emit(1)
assert len(panel.table_view.selectionModel().selectedRows()) == 0
def test_selected_samples_follow_the_click(panel):
view = panel.table_view
view.selectRow(0)
row_ids = [panel.table_model.get_id(r).db_id for r in range(3)]
# Click inside the selection: the selection is acted on.
assert [s.db_id for s in panel._selected_samples(0)] == [row_ids[0]]
# Click outside the selection: only the clicked row is acted on.
assert [s.db_id for s in panel._selected_samples(2)] == [row_ids[2]]