146 lines
6.6 KiB
Python
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()
|