更新
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user