"""Parity contracts for the admin appointment list port.""" from __future__ import annotations import os from copy import deepcopy from types import SimpleNamespace from typing import Any os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") import pytest from PySide6.QtWidgets import ( QAbstractItemView, QApplication, QDialog, QDialogButtonBox, QLabel, QPushButton, ) from doctor_workstation.core.errors import ApiProtocolError from doctor_workstation.core.models import Appointment, PageResult from doctor_workstation.core.permissions import PermissionSet from doctor_workstation.services.mock_repository import DemoDoctorRepository from doctor_workstation.services.repository import RemoteDoctorRepository from doctor_workstation.ui.pages import appointments as appointments_module from doctor_workstation.ui.pages.appointments import ( AppointmentsPage, _diagnosis_id, _video_patient_id, prescription_action_label, ) from doctor_workstation.ui.shell import _match_navigation class RecordingClient: def __init__(self) -> None: self.get_calls: list[tuple[str, dict[str, Any]]] = [] self.post_calls: list[tuple[str, dict[str, Any]]] = [] def get(self, endpoint: str, params: dict[str, Any] | None = None) -> Any: self.get_calls.append((endpoint, dict(params or {}))) if endpoint == "doctor.appointment/detail": return {"id": int((params or {}).get("id", 0)), "patient_name": "测试"} if endpoint == "dept.dept/all": return [{"id": 10, "name": "中医门诊", "children": []}] return {"lists": [], "count": 0, "extend": {"status_count": {"1": 2}}} def post(self, endpoint: str, payload: dict[str, Any] | None = None) -> Any: self.post_calls.append((endpoint, dict(payload or {}))) if endpoint == "tcm.diagnosis/generateMiniProgramQrcode": return {"qrcode_url": "https://example.test/uploads/video-qr.png"} return {"ok": True} @pytest.fixture(scope="module") def application() -> QApplication: return QApplication.instance() or QApplication([]) def test_remote_appointment_list_and_detail_hit_admin_endpoints() -> None: client = RecordingClient() repo = RemoteDoctorRepository(client) # type: ignore[arg-type] repo.list_appointments( page_no=1, page_size=15, status=1, start_date="2026-08-11", end_date="2026-08-11", include_status_counts=1, diagnosis_confirmed="1", assistant_dept_id=10, patient_name="林", ) detail = repo.get_appointment_detail(101) departments = repo.list_departments() assert client.get_calls[0][0] == "doctor.appointment/lists" assert client.get_calls[0][1]["include_status_counts"] == 1 assert client.get_calls[0][1]["assistant_dept_id"] == 10 assert "diag_scope_relax" not in client.get_calls[0][1] assert client.get_calls[1] == ("doctor.appointment/detail", {"id": 101}) assert client.get_calls[2] == ("dept.dept/all", {}) assert detail["id"] == 101 assert departments[0]["id"] == 10 def test_unlimited_date_relaxes_today_scope_for_pending_status() -> None: client = RecordingClient() repo = RemoteDoctorRepository(client) # type: ignore[arg-type] repo.list_appointments(status=1, diag_scope_relax=1, page_no=1, page_size=15) params = client.get_calls[0][1] assert "start_date" not in params assert "end_date" not in params assert "diag_scope_relax" not in params def test_video_qr_uses_doctor_id_without_fake_diagnosis_id() -> None: client = RecordingClient() repo = RemoteDoctorRepository(client) # type: ignore[arg-type] result = repo.generate_video_qrcode( diagnosis_id=501, doctor_id=88, patient_id=301, share_user_id=9, ) endpoint, payload = client.post_calls[-1] assert endpoint == "tcm.diagnosis/generateMiniProgramQrcode" assert payload == { "diagnosis_id": 501, "doctor_id": 88, "patient_id": 301, "share_user_id": 9, "mini_program_path": "pages/login/login", } assert result["qrcode_url"].endswith("video-qr.png") def test_legacy_empty_string_prescription_response_is_scoped_to_not_found() -> None: class LegacyClient(RecordingClient): def get(self, endpoint: str, params: dict[str, Any] | None = None) -> Any: if endpoint == "tcm.prescription/getByAppointment": raise ApiProtocolError("envelope must be an object", data="") return super().get(endpoint, params) repo = RemoteDoctorRepository(LegacyClient()) # type: ignore[arg-type] assert repo.get_prescription_by_appointment(101) is None class BrokenClient(LegacyClient): def get(self, endpoint: str, params: dict[str, Any] | None = None) -> Any: raise ApiProtocolError("invalid JSON") broken = RemoteDoctorRepository(BrokenClient()) # type: ignore[arg-type] with pytest.raises(ApiProtocolError, match="invalid JSON"): broken.get_prescription_by_appointment(101) def test_shell_matches_appointment_list_by_route_not_shared_permission() -> None: row = { "name": "挂号列表", "paths": "/appointments", "component": "tcm/appointment/list", "perms": "doctor.appointment/lists", } item = _match_navigation(row) assert item is not None assert item.key == "appointments" reception = { "name": "接诊台", "paths": "/reception", "component": "patient/reception/index", "perms": "doctor.appointment/lists", } assert _match_navigation(reception).key == "reception" # type: ignore[union-attr] def test_diagnosis_and_patient_ids_stay_distinct_for_video() -> None: row = Appointment.from_dict( { "id": 101, "patient_id": 301, "diagnosis_id": 501, "source_patient_id": 301, "status": 1, "has_prescription": 0, } ) assert _diagnosis_id(row) == 501 assert _video_patient_id(row) == 301 assert _video_patient_id({"diagnosis_id": 501, "patient_id": 301}) == 0 assert _video_patient_id({"diagnosis_id": 501, "patient_id": 501}) == 0 assert prescription_action_label(row) == "开方" approved = Appointment.from_dict( { "id": 102, "prescription_audit_status": 1, "prescription_void_status": 0, "has_prescription": 1, } ) assert prescription_action_label(approved) == "查看" pending = Appointment.from_dict( { "id": 103, "prescription_audit_status": 0, "prescription_void_status": 0, "has_prescription": 1, } ) assert prescription_action_label(pending) == "编辑处方" historical_only = Appointment.from_dict( { "id": 104, "prescription_audit_status": 1, "prescription_void_status": 0, "has_prescription": 1, "current_has_prescription": 0, "current_prescription_id": 0, } ) assert prescription_action_label(historical_only) == "开方" def test_appointment_view_intent_never_degrades_to_edit( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: row = { "id": 101, "appointment_id": 101, "diagnosis_id": 501, "current_has_prescription": 1, "current_prescription_id": 81, "prescription_audit_status": 1, "prescription_void_status": 0, } page = AppointmentsPage(SimpleNamespace(), permissions=PermissionSet(["*"])) monkeypatch.setattr(page, "_current_row", lambda: row) requested: list[tuple[Any, str]] = [] monkeypatch.setattr( page, "_begin_prescription_load", lambda source, *, mode="open": requested.append((source, mode)), ) page._open_prescription() assert requested == [(row, "view")] monkeypatch.setattr( page, "_open_existing_prescription_editor", lambda _existing: pytest.fail("view intent must never open the editor"), ) page._prescription_loaded( { "id": 81, "appointment_id": 101, "audit_status": 2, "void_status": 0, }, row, page._prescription_generation, "view", ) assert "状态已变化" in page.banner.label.text() page.close() application.processEvents() def test_appointment_im_action_opens_chat_without_an_existing_live_call( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet(["doctor.appointment/prescription"]), ) row = { "id": 101, "appointment_id": 101, "diagnosis_id": 501, "patient_id": 501, "source_patient_id": 301, "patient_name": "测试患者", "status": 1, } emitted: list[dict[str, Any]] = [] monkeypatch.setattr(page, "_current_row", lambda: row) page.video_requested.connect(emitted.append) page._request_video() assert len(emitted) == 1 assert emitted[0]["mode"] == "im" assert emitted[0]["appointment_id"] == 101 assert emitted[0]["diagnosis_id"] == 501 assert emitted[0]["patient_id"] == 301 warnings: list[str] = [] monkeypatch.setattr( appointments_module, "show_toast", lambda _parent, message, _kind: warnings.append(message), ) row["status"] = 2 page._request_video() assert len(emitted) == 1 assert warnings == ["已取消的挂号不可进入 IM 问诊。"] page.close() application.processEvents() def test_appointment_pending_prescription_uses_full_edit_contract( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: updated: list[tuple[int, dict[str, Any]]] = [] existing = { "id": 81, "diagnosis_id": 501, "audit_status": 0, "void_status": 0, "patient_name": "林晓岚", } class Repository: def update_prescription( self, prescription: int, changes: dict[str, Any] ) -> dict[str, Any]: updated.append((prescription, dict(changes))) return {"id": prescription} class AcceptedEditor: def __init__(self, _repository: Any, source: Any, **kwargs: Any) -> None: assert source is existing assert kwargs["mode"] == "edit" def exec(self) -> QDialog.DialogCode: return QDialog.DialogCode.Accepted def payload(self) -> dict[str, Any]: return {"id": 81, "clinical_diagnosis": "脾气虚"} def run_immediately(function: Any, **callbacks: Any) -> object: try: result = function() except Exception as error: callbacks["on_error"](error) else: callbacks["on_success"](result) finally: callbacks["on_finished"]() return object() monkeypatch.setattr(appointments_module, "PrescriptionEditorDialog", AcceptedEditor) monkeypatch.setattr(appointments_module, "run_async", run_immediately) page = AppointmentsPage(Repository(), permissions=PermissionSet(["*"])) monkeypatch.setattr(page, "refresh", lambda **_kwargs: None) page._prescription_loaded(existing, {"id": 101}, page._prescription_generation) assert updated == [(81, {"id": 81, "clinical_diagnosis": "脾气虚"})] page.close() application.processEvents() def test_appointment_prescription_seed_keeps_admin_observation_fields( application: QApplication, ) -> None: page = AppointmentsPage(SimpleNamespace(), permissions=PermissionSet(["*"])) seed = page._prescription_seed( {"id": 101, "appointment_id": 101, "diagnosis_id": 501}, { "id": 501, "patient_name": "林晓岚", "tongue": "面象哨兵", "tongue_image": "舌象哨兵", "pulse": "脉象哨兵", "pulse_condition": "脉象详情哨兵", }, ) assert seed["tongue"] == "面象哨兵" assert seed["tongue_image"] == "舌象哨兵" assert seed["pulse"] == "脉象哨兵" assert seed["pulse_condition"] == "脉象详情哨兵" page.close() application.processEvents() def test_appointments_page_default_query_is_today_pending( application: QApplication, ) -> None: repo = DemoDoctorRepository() page = AppointmentsPage( repo, permissions=PermissionSet( [ "doctor.appointment/lists", "doctor.appointment/complete", "doctor.appointment/cancel", "doctor.appointment/prescription", "doctor.appointment/addDoctorNote", "tcm.diagnosis/edit", "tcm.diagnosis/kaifang", "tcm.diagnosis/videoQr", ] ), current_user={"id": 1001, "name": "陈医生", "role_id": 1}, ) page.show() application.processEvents() page.refresh() application.processEvents() filters = page._query_filters() assert filters["status"] == 1 assert filters["include_status_counts"] == 1 assert filters["start_date"] == filters["end_date"] assert "diag_scope_relax" not in filters assert page.table.rowCount() >= 1 def test_demo_appointment_status_counts_respect_date_scope() -> None: repo = DemoDoctorRepository() today = repo._today.isoformat() result = repo.list_appointments( page_no=1, page_size=20, start_date=today, end_date=today, include_status_counts=1, ) assert isinstance(result, PageResult) counts = result.extend.get("status_count", {}) assert int(counts.get("1", counts.get(1, 0))) >= 1 assert int(counts.get("3", counts.get(3, 0))) >= 1 def test_appointment_multiline_cells_receive_enough_row_height( application: QApplication, ) -> None: page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet(["doctor.appointment/lists"]), current_user={"id": 1001, "role_id": 1}, ) page._loaded( { "lists": [ { "id": 15534, "patient_name": "张玉英", "patient_phone": "13800138000", "gender": 0, "age": 42, "height": 162, "weight": 55, "doctor_name": "徐国军", "appointment_date": "2026-08-11", "appointment_time": "14:30", "assistant_name": "蒋露露", "diagnosis_confirmed": 0, "has_prescription": 0, "status": 1, "status_desc": "已预约", "remark": "—", } ], "count": 1, }, page._generation, False, ) patient_text = page.table.item(0, 2).text() appointment_text = page.table.item(0, 4).text() assert patient_text == "张玉英" assert appointment_text.count("\n") == 2 assert "2026-08-11 14:30" in appointment_text required = 3 * max(16, page.table.fontMetrics().lineSpacing()) + 10 assert 60 <= page.table.rowHeight(0) <= 66 assert page.table.rowHeight(0) >= min(required, 66) assert page.table.item(0, 4).toolTip() == appointment_text page.close() def test_video_qr_dialog_renders_downloaded_image_inside_app( application: QApplication, ) -> None: repo = DemoDoctorRepository() page = AppointmentsPage( repo, permissions=PermissionSet(["tcm.diagnosis/videoQr"]), current_user={"id": 1001, "role_id": 1}, ) url = "https://demo.invalid/qrcode/video/88.png" dialog = page._build_qr_dialog( {"patient_name": "鹿立核"}, url, {"qrcode_url": url, "_image_bytes": repo.download_public_image(url)}, ) image = dialog.findChild(QLabel, "VideoQrImage") assert image is not None assert image.pixmap() is not None and not image.pixmap().isNull() assert dialog.findChild(QLabel, "VideoQrImage").text() == "" assert dialog.objectName() == "VideoQrDialog" assert dialog.property("businessDialog") is True buttons = dialog.findChild(QDialogButtonBox) assert next(button for button in buttons.buttons() if button.text() == "浏览器打开").property( "variant" ) == "primary" dialog.close() page.close() application.processEvents() def test_page_owned_appointment_dialogs_have_stable_visual_contract( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet( ["doctor.appointment/complete", "doctor.appointment/addDoctorNote"] ), current_user={"id": 1001, "role_id": 1}, ) captured: list[QDialog] = [] def reject_dialog(dialog: QDialog) -> QDialog.DialogCode: captured.append(dialog) return QDialog.DialogCode.Rejected monkeypatch.setattr(QDialog, "exec", reject_dialog) page._open_custom_date() page._show_detail({"id": 101, "patient_name": "鹿立核"}, page._action_generation) page.table.set_rows( [{"id": 101, "diagnosis_id": 501, "patient_id": 301, "status": 1}] ) page.table.selectRow(0) page._complete_selected() assert [dialog.objectName() for dialog in captured] == [ "AppointmentCustomDateDialog", "AppointmentDetailDialog", "AppointmentCompleteDialog", ] assert all(dialog.property("businessDialog") is True for dialog in captured) assert all( any(label.property("dialogRole") == "title" for label in dialog.findChildren(QLabel)) for dialog in captured ) custom_buttons = captured[0].findChild(QDialogButtonBox) complete_buttons = captured[2].findChild(QDialogButtonBox) assert custom_buttons.button(QDialogButtonBox.StandardButton.Ok).property( "variant" ) == "primary" assert complete_buttons.button(QDialogButtonBox.StandardButton.Ok).property( "variant" ) == "primary" for dialog in captured: dialog.close() page.close() application.processEvents() def test_prescription_case_snapshot_uses_keyword_diagnosis_id( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: requested: list[int] = [] opened: list[tuple[Any, Any]] = [] class Repository: def get_diagnosis_detail(self, *, diagnosis_id: int) -> dict[str, Any]: requested.append(diagnosis_id) return {"id": diagnosis_id, "patient_name": "测试患者"} def run_immediately(function: Any, **callbacks: Any) -> object: try: callbacks["on_success"](function()) except Exception as error: # pragma: no cover - assertion output is clearer callbacks["on_error"](error) finally: callbacks["on_finished"]() return object() monkeypatch.setattr(appointments_module, "run_async", run_immediately) page = AppointmentsPage( Repository(), permissions=PermissionSet(["tcm.diagnosis/kaifang"]), current_user={"id": 9, "role_id": 1}, ) monkeypatch.setattr( page, "_open_prescription_editor", lambda record, detail: opened.append((record, detail)), ) page._begin_case_record_load( {"id": 101, "appointment_id": 101, "diagnosis_id": 501, "patient_id": 501} ) assert requested == [501] assert opened and opened[0][1]["id"] == 501 page.close() application.processEvents() def test_appointment_ai_report_button_visible_with_reception_permission( application: QApplication, ) -> None: hidden = AppointmentsPage( SimpleNamespace(), permissions=PermissionSet(["tcm.diagnosis/kaifang"]), ) assert hidden.ai_button.isHidden() hidden.close() page = AppointmentsPage( SimpleNamespace(), permissions=PermissionSet(["doctor.appointment/reception"]), ) assert not page.ai_button.isHidden() assert not page.ai_button.isEnabled() page.close() application.processEvents() def test_appointments_use_full_width_table_with_per_row_im_consult( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setattr(appointments_module, "run_async", lambda *_args, **_kwargs: object()) page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet( ["doctor.appointment/lists", "doctor.appointment/prescription"] ), current_user={"id": 1001, "role_id": 1}, ) page.resize(1460, 820) page.show() page._apply_responsive_layout() page._loaded( { "lists": [ { "id": 101, "diagnosis_id": 501, "source_patient_id": 301, "patient_name": "赵俊霞", "gender": 0, "age": 53, "assistant_name": "自媒体4", "appointment_date": "2026-08-13", "appointment_time": "09:50", "status": 1, "status_desc": "已挂号", "video_call_hint": {"state": "live", "label": "视频通话进行中"}, } ], "count": 1, }, page._generation, False, ) application.processEvents() assert page.content_layout.count() == 1 assert not hasattr(page, "video_panel") assert not hasattr(page, "video_list") assert page.table_card.width() == page.content_host.width() assert page.table.objectName() == "AppointmentTable" assert ( page.table.verticalScrollMode() == QAbstractItemView.ScrollMode.ScrollPerPixel ) assert page.table.horizontalHeaderItem(10).text() == "IM 问诊" im_host = page.table.cellWidget(0, 10) assert im_host is not None im_button = im_host.findChild(QPushButton, "AppointmentImConsultButton") assert im_button is not None assert im_button.text() == "IM 问诊" assert im_button.isEnabled() assert im_button.accessibleName() == "与赵俊霞进行 IM 问诊" emitted: list[dict[str, Any]] = [] page.video_requested.connect(emitted.append) im_button.click() assert emitted and emitted[0]["mode"] == "im" assert emitted[0]["appointment_id"] == 101 assert page.date_buttons["today"].isChecked() page.resize(1024, 640) page._apply_responsive_layout() application.processEvents() assert page.table_card.width() == page.content_host.width() assert not page.date_overflow_button.isHidden() assert page.date_buttons["yesterday"].isHidden() assert not page.date_buttons["today"].isHidden() page.close() application.processEvents() def test_im_entry_does_not_require_a_live_video_hint( application: QApplication, ) -> None: page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet(["doctor.appointment/prescription"]), ) rows = [ { "id": 101, "diagnosis_id": 501, "source_patient_id": 301, "patient_name": "已接通患者", "status": 1, "video_call_hint": {"state": "ended", "label": "视频通话已结束"}, }, { "id": 102, "diagnosis_id": 502, "source_patient_id": 302, "patient_name": "等待患者", "status": 1, }, ] page._loaded({"lists": rows, "count": 2}, page._generation, False) application.processEvents() buttons = [ page.table.cellWidget(index, 10).findChild( QPushButton, "AppointmentImConsultButton", ) for index in range(page.table.rowCount()) ] buttons_by_name = { button.accessibleName(): button for button in buttons if button is not None } assert set(buttons_by_name) == { "与已接通患者进行 IM 问诊", "与等待患者进行 IM 问诊", } assert buttons_by_name["与已接通患者进行 IM 问诊"].isEnabled() waiting = buttons_by_name["与等待患者进行 IM 问诊"] assert waiting.isEnabled() assert waiting.toolTip() == "打开患者 IM,可发送消息并从会话中发起视频" page.close() application.processEvents() def test_im_entry_allows_fulfillable_statuses_and_rejects_terminal_or_unknown_statuses( application: QApplication, ) -> None: page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet(["doctor.appointment/prescription"]), ) rows = [ { "id": index + 100, "diagnosis_id": index + 500, "source_patient_id": index + 300, "patient_name": name, "status": status, } for index, (name, status) in enumerate( ( ("已预约患者", 1), ("已过号患者", 4), ("已取消患者", 2), ("已完成患者", 3), ("未知状态患者", 0), ) ) ] page._loaded({"lists": rows, "count": len(rows)}, page._generation, False) application.processEvents() buttons = { button.accessibleName(): button for row_index in range(page.table.rowCount()) if ( button := page.table.cellWidget(row_index, 10).findChild( QPushButton, "AppointmentImConsultButton", ) ) is not None } assert buttons["与已预约患者进行 IM 问诊"].isEnabled() assert buttons["与已过号患者进行 IM 问诊"].isEnabled() assert not buttons["与已取消患者进行 IM 问诊"].isEnabled() assert buttons["与已取消患者进行 IM 问诊"].toolTip() == ( "已取消的挂号不可进入 IM 问诊" ) assert not buttons["与已完成患者进行 IM 问诊"].isEnabled() assert buttons["与已完成患者进行 IM 问诊"].toolTip() == ( "已完成的挂号不可再进入 IM 问诊" ) assert not buttons["与未知状态患者进行 IM 问诊"].isEnabled() assert buttons["与未知状态患者进行 IM 问诊"].toolTip() == ( "当前挂号状态不可进入 IM 问诊" ) page.close() application.processEvents() def test_identical_appointment_poll_keeps_existing_cell_widgets( application: QApplication, ) -> None: page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet(["doctor.appointment/lists"]), current_user={"id": 1001, "role_id": 1}, ) result = { "lists": [ { "id": 101, "diagnosis_id": 501, "patient_id": 301, "patient_name": "赵俊霞", "gender": 2, "age": 53, "assistant_name": "周医助", "appointment_date": "2026-08-17", "appointment_time": "09:50", "status": 1, "status_desc": "已挂号", } ], "count": 1, "extend": {"status_count": {"1": 1}}, } page._loaded(result, page._generation, True) selector = page.table.cellWidget(0, 0) appointment_info = page.table.cellWidget(0, 4) im_action = page.table.cellWidget(0, 10) page._loaded(deepcopy(result), page._generation, True) assert page.table.cellWidget(0, 0) is selector assert page.table.cellWidget(0, 4) is appointment_info assert page.table.cellWidget(0, 10) is im_action changed = deepcopy(result) changed["lists"][0]["assistant_name"] = "新医助" page._loaded(changed, page._generation, True) assert page.table.cellWidget(0, 0) is not selector assert page.table.cellWidget(0, 4) is not appointment_info assert page.table.cellWidget(0, 10) is not im_action page.close() application.processEvents() def test_appointments_density_fits_four_rows_in_1366_shell_viewport( application: QApplication, monkeypatch: pytest.MonkeyPatch, ) -> None: monkeypatch.setattr(appointments_module, "run_async", lambda *_args, **_kwargs: object()) page = AppointmentsPage( DemoDoctorRepository(), permissions=PermissionSet(["*"]), current_user={"id": 1001, "role_id": 1}, ) # 1366x768 shell minus its 179 px appointment rail, 26 px outer gutter, # and 62 px top bar leaves a 1161x680 page viewport. page.resize(1161, 680) page.show() application.processEvents() rows = [ { "id": 100 + index, "diagnosis_id": 500 + index, "patient_id": 300 + index, "patient_name": f"患者{index}", "gender": 2, "age": 40 + index, "doctor_name": "陈医生", "assistant_name": "周医助", "appointment_date": "2026-08-17", "appointment_time": f"{8 + index:02d}:00", "status": 1, "status_desc": "已挂号", "diagnosis_confirmed": 0, "has_prescription": 0, } for index in range(8) ] page._loaded( {"lists": rows, "count": len(rows), "extend": {"status_count": {"1": 8}}}, page._generation, False, ) application.processEvents() heights = [page.table.rowHeight(index) for index in range(page.table.rowCount())] # 与其余列表页一致的“面包屑 + 标题 + 副标题”页头。 assert page.header.height() == 62 assert page.filter_panel.height() <= 84 assert all(60 <= height <= 66 for height in heights) assert page.table.viewport().height() // max(heights) >= 4 assert page.pager.isVisibleTo(page) assert page.content_layout.count() == 1 assert page.table_card.width() == page.content_host.width() page.resize(1024, 640) application.processEvents() assert page.table_card.width() == page.content_host.width() page.close() application.processEvents()