199 lines
8.4 KiB
Python
199 lines
8.4 KiB
Python
"""Native Qt coverage for presentation-only filter folding on both queues."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from datetime import date
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
import pytest
|
|
from PySide6.QtCore import QPoint, QRect, Qt, Signal
|
|
from PySide6.QtTest import QTest
|
|
from PySide6.QtWidgets import QApplication, QPushButton, QWidget
|
|
|
|
from doctor_workstation.ui import shell as shell_module
|
|
from doctor_workstation.ui.pages import appointments, consultations
|
|
from doctor_workstation.ui.shell import NavigationItem, ShellWindow
|
|
from doctor_workstation.ui.theme import apply_theme
|
|
|
|
|
|
class _Dialog(QWidget):
|
|
saved = Signal()
|
|
|
|
def __init__(self, _repository: Any, parent: QWidget | None = None) -> None:
|
|
super().__init__(parent)
|
|
|
|
|
|
class _Repository:
|
|
def __init__(self) -> None:
|
|
self.calls: list[dict[str, Any]] = []
|
|
|
|
def _list(self, **query: Any) -> dict[str, Any]:
|
|
self.calls.append(query)
|
|
rows = [{
|
|
"id": index, "diagnosis_id": index, "patient_id": index + 2000,
|
|
"source_patient_id": index + 2000, "patient_name": f"演示患者{index:02d}",
|
|
"patient_phone": "13800001234", "gender": 2, "age": 38,
|
|
"status": 1, "status_desc": "待接诊", "doctor_name": "演示医生",
|
|
"doctor_id": 30, "assistant_name": "演示医助", "has_appointment": True,
|
|
"appointment_id": index + 1000, "appointment_status": 1,
|
|
"appointment_date": date.today().isoformat(), "appointment_time": "09:00-09:30",
|
|
"diagnosis_confirmed": True, "has_prescription": False,
|
|
"appointments": [{"id": index + 1000, "status": 1,
|
|
"doctor_name": "演示医生", "time_text": "09:00-09:30"}],
|
|
} for index in range(1, 16)]
|
|
return {"lists": rows, "count": 15, "extend": {"status_count": {"1": 15}}}
|
|
|
|
list_appointments = _list
|
|
list_consultations = _list
|
|
|
|
|
|
def _inline(function: Any, *, on_success=None, on_error=None, on_finished=None) -> None:
|
|
try:
|
|
result = function()
|
|
except Exception as error:
|
|
if on_error is not None:
|
|
on_error(error)
|
|
else:
|
|
if on_success is not None:
|
|
on_success(result)
|
|
finally:
|
|
if on_finished is not None:
|
|
on_finished()
|
|
|
|
|
|
def _settle(application: QApplication) -> None:
|
|
for _ in range(4):
|
|
application.processEvents()
|
|
QTest.qWait(10)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def application() -> QApplication:
|
|
app = QApplication.instance() or QApplication([])
|
|
apply_theme(app)
|
|
return app
|
|
|
|
|
|
@pytest.fixture
|
|
def list_window(application: QApplication, monkeypatch: pytest.MonkeyPatch):
|
|
monkeypatch.setattr(appointments, "run_async", _inline)
|
|
monkeypatch.setattr(consultations, "run_async", _inline)
|
|
monkeypatch.setattr(consultations, "DiagnosisDialog", _Dialog)
|
|
monkeypatch.setattr(consultations.ConsultationsPage, "_load_filter_options", lambda self: None)
|
|
monkeypatch.setattr(consultations.ConsultationsPage, "_refresh_counts", lambda self: None)
|
|
monkeypatch.setattr(appointments.AppointmentsPage, "_load_departments", lambda self: None)
|
|
monkeypatch.setattr(shell_module.motion, "reduced_motion", lambda: True)
|
|
navigation = [
|
|
NavigationItem("appointments", "挂号列表", "号", appointments.AppointmentsPage,
|
|
("doctor.appointment/lists",)),
|
|
NavigationItem("consultations", "问诊列表", "询", consultations.ConsultationsPage,
|
|
("tcm.diagnosis/lists",)),
|
|
]
|
|
monkeypatch.setattr(shell_module, "_resolve_navigation",
|
|
lambda *_args, **_kwargs: [(item, item.title) for item in navigation])
|
|
repository = _Repository()
|
|
window = ShellWindow(repository, {"user": {"name": "演示医生", "role_id": 1},
|
|
"demo_mode": True}, permissions={"*"})
|
|
yield window, repository
|
|
window.close()
|
|
window.deleteLater()
|
|
_settle(application)
|
|
|
|
|
|
@pytest.mark.parametrize("kind", ["appointments", "consultations"])
|
|
@pytest.mark.parametrize(("width", "height"), [(1366, 768), (1536, 960)])
|
|
def test_folding_reclaims_rows_and_retains_query_refresh_and_navigation_state(
|
|
application: QApplication, list_window: Any, kind: str, width: int, height: int,
|
|
) -> None:
|
|
window, repository = list_window
|
|
window.resize(width, height)
|
|
window.show()
|
|
assert window.navigate(kind)
|
|
_settle(application)
|
|
page = window.pages[kind]
|
|
page.poll_timer.stop()
|
|
panel = page.filter_panel if kind == "appointments" else page.filters_card
|
|
header = page.header if kind == "appointments" else page.page_header
|
|
search = page.patient_input if kind == "appointments" else page.patient_name_edit
|
|
nested = page.more_filters_button if kind == "appointments" else page.more_filter_button
|
|
action = page.toolbar_edit_button if kind == "appointments" else page.add_button
|
|
disclosure = page.filter_disclosure
|
|
filters = page._query_filters if kind == "appointments" else page._filters
|
|
assert not disclosure.expanded
|
|
assert panel.isHidden() and not search.isVisible()
|
|
assert header.height() <= 44
|
|
assert not header.subtitle_label.isVisible()
|
|
assert disclosure.button.isVisible() and action.isVisible()
|
|
assert 30 <= disclosure.button.height() <= 34
|
|
assert window.refresh_button.isVisible()
|
|
assert any(button.isVisible() and button.text() == "刷新"
|
|
for button in page.findChildren(QPushButton))
|
|
collapsed_height = page.table.viewport().height()
|
|
capture_dir = os.environ.get("COLLAPSIBLE_LIST_SCREENSHOTS")
|
|
if capture_dir:
|
|
Path(capture_dir).mkdir(parents=True, exist_ok=True)
|
|
assert window.grab().save(str(Path(capture_dir) / f"{kind}-{width}-collapsed.png"))
|
|
|
|
calls_before = len(repository.calls)
|
|
QTest.mouseClick(disclosure.button, Qt.MouseButton.LeftButton)
|
|
_settle(application)
|
|
assert len(repository.calls) == calls_before
|
|
assert panel.isVisible() and search.isVisible()
|
|
assert collapsed_height >= page.table.viewport().height() + 100
|
|
assert panel.rect().contains(QRect(search.mapTo(panel, QPoint()), search.size()))
|
|
assert search.parentWidget().rect().contains(search.geometry())
|
|
assert not page.advanced_filters.isVisible()
|
|
if capture_dir:
|
|
assert window.grab().save(str(Path(capture_dir) / f"{kind}-{width}-expanded.png"))
|
|
(Path(capture_dir) / f"{kind}-{width}-metrics.json").write_text(
|
|
json.dumps({"window": [width, height], "header_height": header.height(),
|
|
"toggle_height": disclosure.button.height(),
|
|
"collapsed_viewport_height": collapsed_height,
|
|
"expanded_viewport_height": page.table.viewport().height(),
|
|
"viewport_gain": collapsed_height - page.table.viewport().height()},
|
|
indent=2), encoding="utf-8",
|
|
)
|
|
|
|
search.setText("演示患者")
|
|
query_button = (page.findChild(QPushButton, "AppointmentSearchButton")
|
|
if kind == "appointments" else page.search_button)
|
|
QTest.mouseClick(query_button, Qt.MouseButton.LeftButton)
|
|
_settle(application)
|
|
assert len(repository.calls) > calls_before
|
|
assert repository.calls[-1]["patient_name"] == "演示患者"
|
|
QTest.mouseClick(nested, Qt.MouseButton.LeftButton)
|
|
_settle(application)
|
|
assert page.advanced_filters.isVisible()
|
|
query = dict(filters())
|
|
calls_before = len(repository.calls)
|
|
QTest.mouseClick(disclosure.button, Qt.MouseButton.LeftButton)
|
|
_settle(application)
|
|
assert len(repository.calls) == calls_before
|
|
assert filters() == query
|
|
assert search.text() == "演示患者"
|
|
assert panel.isHidden()
|
|
assert page.table.viewport().height() == collapsed_height
|
|
|
|
window.resize(width - 50, height - 20)
|
|
window.refresh_button.click()
|
|
_settle(application)
|
|
assert not disclosure.expanded and panel.isHidden()
|
|
assert filters() == query
|
|
other = "consultations" if kind == "appointments" else "appointments"
|
|
assert window.navigate(other)
|
|
assert window.navigate(kind)
|
|
_settle(application)
|
|
assert not disclosure.expanded and panel.isHidden()
|
|
QTest.mouseClick(disclosure.button, Qt.MouseButton.LeftButton)
|
|
_settle(application)
|
|
assert page.advanced_filters.isVisible() and nested.isChecked()
|
|
window.refresh_button.click()
|
|
_settle(application)
|
|
assert disclosure.expanded and panel.isVisible()
|
|
assert filters() == query
|