Files
zyt/app/tests/test_collapsible_patients.py
T
2026-09-07 12:30:42 +08:00

243 lines
9.1 KiB
Python

"""Page disclosures reclaim list space without changing queries or tab state."""
from __future__ import annotations
import os
import socket
from copy import deepcopy
from typing import Any
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
import pytest
from PySide6.QtCore import QDate, QPoint, QRect, Qt, QTimer
from PySide6.QtTest import QTest
from PySide6.QtWidgets import QApplication
from doctor_workstation.core import PermissionSet
from doctor_workstation.ui.pages import patients as patients_module
from doctor_workstation.ui.pages.patients import PatientsPage
from doctor_workstation.ui.theme import apply_theme
class Repository:
def __init__(self) -> None:
self.calls: list[tuple[str, dict[str, Any]]] = []
def _result(self, kind: str, query: dict[str, Any]) -> dict[str, Any]:
self.calls.append((kind, deepcopy(query)))
return {
"lists": [{
"id": 101 + index, "diagnosis_id": 501 + index,
"patient_id": 301 + index, "patient_name": f"患者 {index + 1}",
"order_no": f"TEST-20260907-{index + 1}", "queue_no": index + 1,
"appointment_status": 1, "queue_status": "waiting",
"queue_status_text": "等待中", "doctor_name": "测试医生",
} for index in range(4)],
"count": 4,
"extend": {
"scope": {"label": "本人患者"}, "schedule_mode": "ownership",
"summary": {"waiting": 4, "today": 4, "orders": 4},
},
}
def list_patients(self, **query: Any) -> dict[str, Any]:
return self._result("patients", query)
def patient_orders(self, **query: Any) -> dict[str, Any]:
return self._result("orders", query)
def patient_progress(self, **query: Any) -> dict[str, Any]:
return self._result("progress", query)
def settle(application: QApplication) -> None:
for _ in range(4):
application.processEvents()
@pytest.fixture(scope="module")
def application() -> QApplication:
application = QApplication.instance() or QApplication([])
apply_theme(application)
return application
@pytest.fixture
def page_factory(application, monkeypatch):
def immediate(function, *, on_success=None, on_error=None, on_finished=None):
try:
result = function()
except Exception as error:
if on_error:
on_error(error)
raise
else:
if on_success:
on_success(result)
finally:
if on_finished:
on_finished()
def reject_network(*_args, **_kwargs):
pytest.fail("Disclosure tests must use local fixture data")
monkeypatch.setattr(patients_module, "run_async", immediate)
monkeypatch.setattr(socket.socket, "connect", reject_network)
monkeypatch.setattr(socket.socket, "connect_ex", reject_network)
monkeypatch.setattr(socket, "create_connection", reject_network)
opened = []
def create(width=1328, height=884):
repository = Repository()
page = PatientsPage(repository, PermissionSet(["*"]))
opened.append(page)
page.resize(width, height)
page.show()
settle(application)
return page, repository
yield create
for page in opened:
for timer in page.findChildren(QTimer):
timer.stop()
page.close()
page.deleteLater()
settle(application)
def regions(page, index):
return (
(page.patient_workspace.search_toolbar, page.patient_workspace.filter_card,
page.patient_workspace.summary_strip),
(page.order_workspace.filter_card, page.order_workspace.summary_strip),
(page.progress_workspace.overview_card, page.progress_workspace.schedule_card),
)[index]
@pytest.mark.parametrize("index", [0, 1, 2], ids=["patients", "orders", "progress"])
def test_tabs_default_collapsed_and_keyboard_expansion_gives_space_to_lists(
application, page_factory, index
):
page, repository = page_factory()
page.tabs.setCurrentIndex(index)
settle(application)
workspace = page.tabs.currentWidget()
table = workspace.queue_table if index == 2 else workspace.table
disclosure = page.filter_disclosure
assert not disclosure.expanded
assert all(widget.isHidden() for widget in regions(page, index))
assert sum(item.button.isVisible() for item in page.filter_disclosures) == 1
assert disclosure.button.text() == disclosure.button.accessibleName() == "展开筛选"
assert disclosure.button.height() == 32
assert page.header.height() <= 48
assert not page.header.subtitle_label.isVisible()
assert page.refresh_button.isVisible() and page.tabs.tabBar().isVisible()
collapsed_height = table.viewport().height()
before = deepcopy(repository.calls)
disclosure.button.setFocus()
QTest.keyClick(disclosure.button, Qt.Key.Key_Space)
settle(application)
assert disclosure.expanded and disclosure.button.text() == "收起筛选"
assert all(widget.isVisible() for widget in regions(page, index))
assert collapsed_height >= table.viewport().height() + 100
assert repository.calls == before
QTest.mouseClick(disclosure.button, Qt.MouseButton.LeftButton)
settle(application)
assert table.viewport().height() == collapsed_height
assert table.isVisible() and workspace.pager.isVisible()
assert repository.calls == before
@pytest.mark.parametrize("index", [0, 1], ids=["patient-filters", "order-filters"])
def test_search_values_survive_collapse_refresh_and_tab_switch(
application, page_factory, index
):
page, repository = page_factory()
page.tabs.setCurrentIndex(index)
page.filter_disclosure.set_expanded(True)
settle(application)
workspace = page.tabs.currentWidget()
workspace.keyword_edit.setText(" 林青 ")
if index == 0:
QTest.mouseClick(workspace.custom_date_button, Qt.MouseButton.LeftButton)
else:
workspace.use_dates.setChecked(True)
workspace.rx_audit.setCurrentIndex(2)
workspace.start_date.setDate(QDate(2026, 9, 1))
workspace.end_date.setDate(QDate(2026, 9, 7))
QTest.mouseClick(workspace.search_button, Qt.MouseButton.LeftButton)
settle(application)
query = deepcopy(repository.calls[-1])
before = deepcopy(repository.calls)
page.filter_disclosure.set_expanded(False)
settle(application)
assert repository.calls == before
assert workspace.keyword_edit.text() == " 林青 "
assert workspace.start_date.date() == QDate(2026, 9, 1)
assert workspace.end_date.date() == QDate(2026, 9, 7)
QTest.mouseClick(page.refresh_button, Qt.MouseButton.LeftButton)
settle(application)
assert repository.calls[-1] == query
assert not page.filter_disclosure.expanded
page.tabs.setCurrentIndex(2)
assert not page.filter_disclosure.expanded
page.filter_disclosure.set_expanded(True)
page.tabs.setCurrentIndex(index)
settle(application)
assert not page.filter_disclosure.expanded
assert repository.calls[-1] == query
page.filter_disclosure.set_expanded(True)
settle(application)
assert workspace.keyword_edit.isVisible() and workspace.start_date.isEnabled()
assert (workspace.custom_date_button.isChecked() if index == 0 else
workspace.rx_audit.currentIndex() == 2 and workspace.use_dates.isChecked())
page.tabs.setCurrentIndex(2)
assert page.filter_disclosure.expanded
@pytest.mark.parametrize(("width", "height"), [(1328, 884), (1158, 692), (816, 564)])
def test_resize_keeps_collapsed_regions_hidden_and_queue_reachable(
application, page_factory, width, height
):
page, _repository = page_factory()
page.resize(width, height)
for index in range(3):
page.tabs.setCurrentIndex(index)
settle(application)
workspace = page.tabs.currentWidget()
assert all(widget.isHidden() for widget in regions(page, index))
for widget in (page.filter_disclosure.button, page.refresh_button):
assert page.rect().contains(QRect(widget.mapTo(page, QPoint()), widget.size()))
assert workspace.scroll.verticalScrollBar().maximum() == 0
assert workspace.pager.isVisibleTo(page)
if index == 2:
assert workspace.splitter.minimumHeight() == 222
assert workspace.queue_card.isVisible()
assert workspace.queue_card.height() == workspace.splitter.height()
page.hide()
page.show()
settle(application)
assert not page.filter_disclosure.expanded
assert all(widget.isHidden() for widget in regions(page, 2))
def test_collapsed_refresh_coalesces_identical_in_flight_queries(
application, page_factory, monkeypatch
):
page, _repository = page_factory()
pending = []
monkeypatch.setattr(
patients_module, "run_async",
lambda function, **callbacks: pending.append((function, callbacks)),
)
page.refresh()
page.refresh()
assert len(pending) == 1
function, callbacks = pending[0]
callbacks["on_success"](function())
settle(application)
assert page.patient_workspace.table.rowCount() == 4
assert not page.filter_disclosure.expanded
assert not page.patient_workspace.search_toolbar.isVisible()