"""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