243 lines
9.1 KiB
Python
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()
|