Files
zyt/app/tests/test_dialog_detail_layout.py
T
2026-09-07 12:30:42 +08:00

146 lines
6.6 KiB
Python

from __future__ import annotations
import os
from types import SimpleNamespace
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
import pytest
from PySide6.QtCore import QPoint
from PySide6.QtGui import QPalette
from PySide6.QtWidgets import QApplication, QDialog, QLabel, QVBoxLayout, QWidget
from doctor_workstation.core import PermissionSet
from doctor_workstation.ui import apply_theme
from doctor_workstation.ui.appointment_drawer import APPOINTMENT_DRAWER_QSS, _SlotCard
from doctor_workstation.ui.diagnosis_drawer import DIAGNOSIS_QSS, DiagnosisTabWidget
from doctor_workstation.ui.dialogs import prescription_ai as ai_module
from doctor_workstation.ui.dialogs.prescription import PrescriptionEditorDialog
from doctor_workstation.ui.dialogs.prescription_ai import PrescriptionAiReportDialog
@pytest.fixture(scope="module")
def application() -> QApplication:
app = QApplication.instance() or QApplication([])
apply_theme(app)
return app
def settle(application: QApplication) -> None:
for _ in range(10):
application.processEvents()
@pytest.mark.parametrize("width", [600, 760, 860])
def test_prescription_form_keeps_date_gender_and_controls_readable(
application: QApplication, width: int
) -> None:
host = QWidget()
host.resize(width, 640)
host.show()
editor = PrescriptionEditorDialog(
SimpleNamespace(list_medicines=lambda **kwargs: {"lists": [], "count": 0}),
{"diagnosis_id": 501, "patient_name": "林晓岚", "prescription_type": "浓缩水丸"},
parent=host,
)
editor.show()
settle(application)
try:
display = editor.date_edit._display
assert display.width() >= display.fontMetrics().horizontalAdvance(display.text()) + 15
for button in (editor.gender_male, editor.gender_female):
assert button.width() >= button.sizeHint().width()
controls = [editor.patient_name, editor.age, editor.date_edit, editor.prescription_type, editor.times_per_day]
assert len({control.height() for control in controls}) == 1
assert editor.body_content.width() == editor.body_scroll.viewport().width()
assert editor.body_scroll.horizontalScrollBar().maximum() == 0
footer_position = editor.footer.mapToGlobal(QPoint())
editor.body_scroll.verticalScrollBar().setValue(editor.body_scroll.verticalScrollBar().maximum())
settle(application)
assert editor.footer.mapToGlobal(QPoint()) == footer_position
assert editor.save_button.visibleRegion().boundingRect() == editor.save_button.rect()
# Changing prescription type keeps its conditional fields reachable after reflow.
editor.prescription_type.setCurrentText("饮片")
settle(application)
assert not editor._main_decoction_field.isHidden()
assert editor._main_bag_field.isHidden()
if width == 860:
for resized_width in (600, 860):
editor.resize(resized_width, 640)
settle(application)
assert display.width() >= display.fontMetrics().horizontalAdvance(display.text()) + 15
assert editor._main_decoction_field.width() >= editor.need_decoction.minimumSizeHint().width() + 104
finally:
editor.close()
host.close()
def test_diagnosis_overflow_rail_leaves_selected_tab_underline_visible(application: QApplication) -> None:
tabs = DiagnosisTabWidget()
tabs.setObjectName("DiagnosisDrawerTabs")
tabs.setStyleSheet(DIAGNOSIS_QSS)
for title in ("病历", "医生备注", "日常记录", "处方", "业务订单", "视频录制回放", "聊天", "指派医助记录", "挂号记录"):
tabs.addTab(QWidget(), title)
tabs.resize(614, 480)
tabs.show()
settle(application)
try:
selected = tabs.tabBar().tabRect(tabs.currentIndex())
selected.moveTopLeft(tabs.tabBar().pos() + selected.topLeft())
assert tabs.tab_scrollbar.isVisible()
assert not selected.intersects(tabs.tab_scrollbar.geometry())
tabs.tab_scrollbar.setValue(tabs.count() - 1)
assert tabs.currentIndex() == tabs.count() - 1
finally:
tabs.close()
def test_appointment_slot_labels_follow_checked_and_disabled_states(application: QApplication) -> None:
dialog = QDialog()
dialog.setObjectName("AppointmentDrawerOverlay")
dialog.setStyleSheet(APPOINTMENT_DRAWER_QSS)
layout = QVBoxLayout(dialog)
card = _SlotCard("09:00-09:30", "可约")
card.setProperty("appointmentSlot", True)
card.setProperty("availability", "available")
card.setCheckable(True)
layout.addWidget(card)
dialog.show()
settle(application)
try:
for checked, expected in ((True, "#ffffff"), (False, "#1a1c1f")):
card.setChecked(checked)
settle(application)
assert card.time_label.palette().color(QPalette.ColorRole.WindowText).name() == expected
card.setChecked(True)
card.setEnabled(False)
settle(application)
assert card.time_label.palette().color(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText).name() == "#8e8f90"
finally:
dialog.close()
def test_ai_report_columns_keep_headings_at_the_same_text_baseline(
application: QApplication, monkeypatch: pytest.MonkeyPatch
) -> None:
def immediate(function, **kwargs):
kwargs["on_success"](function())
if kwargs.get("on_finished"):
kwargs["on_finished"]()
monkeypatch.setattr(ai_module, "run_async", immediate)
report = {"summary": "复核参考", "possible_symptoms": ["乏力", "纳差", "睡眠不安"], "main_indications": "脾气不足", "efficacy": ["健脾", "益气", "养阴"], "suitable_people": ["需由医师辨证确认"]}
repository = SimpleNamespace(list_prescription_template_ai_reports=lambda template_id: {"prescription_id": template_id, "reports": [{"report_id": 1, "model_key": "qwen", "report": report}]})
dialog = PrescriptionAiReportDialog(repository, PermissionSet(["*"]))
dialog.open_for({"id": 1, "prescription_name": "测试方", "herbs": []})
dialog.show()
settle(application)
try:
labels = {label.text(): label for label in dialog.findChildren(QLabel) if label.objectName() == "PrescriptionAiSectionTitle"}
for left, right in (("可能症状与证候", "主治方向"), ("主要功效", "可能适用人群")):
# QLabel vertically centers text: compare the centers, not just widget origins.
centers = [labels[text].mapTo(dialog, labels[text].rect().center()).y() for text in (left, right)]
assert abs(centers[0] - centers[1]) <= 1
finally:
dialog.close()