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