更新
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
import pytest
|
||||
from PySide6.QtCore import QSettings
|
||||
from PySide6.QtWidgets import QApplication, QWidget
|
||||
|
||||
from doctor_workstation.ui.login import LoginWindow
|
||||
from doctor_workstation.ui.widgets import BusyOverlay
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def application() -> QApplication:
|
||||
return QApplication.instance() or QApplication([])
|
||||
|
||||
|
||||
def _visible_windows(application: QApplication) -> list[QWidget]:
|
||||
return [
|
||||
widget
|
||||
for widget in application.topLevelWidgets()
|
||||
if widget.isWindow() and widget.isVisible()
|
||||
]
|
||||
|
||||
|
||||
def test_busy_overlay_children_are_not_windows(application: QApplication) -> None:
|
||||
host = QWidget()
|
||||
host.resize(360, 240)
|
||||
host.show()
|
||||
application.processEvents()
|
||||
before = {id(widget) for widget in _visible_windows(application)}
|
||||
|
||||
overlay = BusyOverlay(host, "正在验证账号…")
|
||||
overlay.setVisible(True)
|
||||
application.processEvents()
|
||||
|
||||
assert overlay.parentWidget() is host
|
||||
assert not overlay.isWindow()
|
||||
assert overlay.progress.objectName() == "BusyOverlayProgress"
|
||||
assert overlay.progress.parentWidget() is overlay
|
||||
assert overlay.label.parentWidget() is overlay
|
||||
assert not overlay.progress.isWindow()
|
||||
assert not overlay.label.isWindow()
|
||||
extra = [
|
||||
widget
|
||||
for widget in _visible_windows(application)
|
||||
if id(widget) not in before and widget is not host
|
||||
]
|
||||
assert extra == []
|
||||
|
||||
overlay.setVisible(False)
|
||||
host.close()
|
||||
application.processEvents()
|
||||
|
||||
|
||||
def test_login_loading_does_not_spawn_extra_windows(
|
||||
application: QApplication, tmp_path: Any
|
||||
) -> None:
|
||||
settings = QSettings(str(tmp_path / "login.ini"), QSettings.Format.IniFormat)
|
||||
window = LoginWindow(
|
||||
object(),
|
||||
config=SimpleNamespace(
|
||||
api_base_url="https://127.0.0.1:9",
|
||||
request_timeout=30,
|
||||
demo_mode=False,
|
||||
remembered_account="admin",
|
||||
),
|
||||
settings=settings,
|
||||
)
|
||||
window.show()
|
||||
application.processEvents()
|
||||
before = {id(widget) for widget in _visible_windows(application)}
|
||||
|
||||
window._set_loading(True)
|
||||
application.processEvents()
|
||||
|
||||
extra = [widget for widget in _visible_windows(application) if id(widget) not in before]
|
||||
assert extra == []
|
||||
assert window.busy_overlay.isVisible()
|
||||
assert not window.busy_overlay.isWindow()
|
||||
assert not window.busy_overlay.progress.isWindow()
|
||||
assert window.busy_overlay.label.text() == "正在验证账号…"
|
||||
|
||||
window.close()
|
||||
application.processEvents()
|
||||
@@ -7,9 +7,12 @@ from typing import Any
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
import pytest
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtCore import QSize, Qt
|
||||
from PySide6.QtGui import QColor, QImage, QPainter
|
||||
from PySide6.QtPdf import QPdfDocument
|
||||
from PySide6.QtWidgets import QApplication
|
||||
|
||||
from doctor_workstation import app as app_module
|
||||
from doctor_workstation.core import PermissionSet
|
||||
from doctor_workstation.services import DemoDoctorRepository
|
||||
from doctor_workstation.ui.dialogs import prescription as dialog_module
|
||||
@@ -530,8 +533,9 @@ def test_order_payload_and_a4_print_document(
|
||||
assert "林晓岚" in rendered
|
||||
assert "黄芪" in rendered
|
||||
assert "酸枣仁" in rendered
|
||||
assert "服药前请核对姓名、电话、医生等信息" in rendered
|
||||
assert '<table align="center" class="paper" width="794" height="1123"' in rendered
|
||||
assert "服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点" in rendered
|
||||
assert '<table align="center" class="paper" width="100%"' in rendered
|
||||
assert 'height="1123"' not in rendered
|
||||
assert '<col width="44"/><col/><col width="64"/><col width="24"/>' in rendered
|
||||
assert "Rp." in rendered
|
||||
assert "药房联" in rendered
|
||||
@@ -539,18 +543,93 @@ def test_order_payload_and_a4_print_document(
|
||||
assert "辅方" in rendered
|
||||
assert "105克" in rendered
|
||||
assert "84克" in rendered
|
||||
assert "单剂量:</span> 27克" in rendered
|
||||
assert "剂量:</span> 27克" in rendered
|
||||
assert "每天2次, 一次2袋, 每袋5g, 温水送服, 饭后" in rendered
|
||||
assert "主服法:" in rendered
|
||||
assert "辅服法:" in rendered
|
||||
assert "处方编号:" in rendered
|
||||
assert "流转编号(挂号):" in rendered
|
||||
assert "成都双流甄养堂互联网医院有限公司 联系方式:4001667339" in rendered
|
||||
assert "四川省成都市双流区黄甲街道黄龙大道二段280号" in rendered
|
||||
user_rendered = render_prescription_html(prescription, variant="user")
|
||||
assert "成都双流甄养堂互联网医院 处方笺" in user_rendered
|
||||
assert "用量" in user_rendered
|
||||
assert "总量" not in user_rendered
|
||||
assert "主服法" not in user_rendered
|
||||
assert "类型:" not in user_rendered
|
||||
assert "黄芪 (15克)" not in user_rendered
|
||||
assert "黄芪" in user_rendered
|
||||
viewer = PrescriptionDetailDialog(prescription)
|
||||
document_html = viewer.document.toHtml()
|
||||
assert "用药 (单剂)" in document_html
|
||||
assert viewer.document.idealWidth() == pytest.approx(794.0)
|
||||
assert viewer.preview.__class__.__name__ == "_PrescriptionPaperPreview"
|
||||
assert [viewer.tabs.tabText(index) for index in range(2)] == ["药房联", "处方联"]
|
||||
viewer.close()
|
||||
order.close()
|
||||
application.processEvents()
|
||||
|
||||
|
||||
def test_prescription_pdf_export_writes_visible_content(
|
||||
application: QApplication,
|
||||
tmp_path: Any,
|
||||
) -> None:
|
||||
prescription = {
|
||||
"id": 12,
|
||||
"sn": "CF-12",
|
||||
"patient_name": "林晓岚",
|
||||
"phone": "13800000000",
|
||||
"gender": 0,
|
||||
"age": 33,
|
||||
"clinical_diagnosis": "脾气虚",
|
||||
"doctor_name": "周医生",
|
||||
"audit_status": 1,
|
||||
"dose_count": 7,
|
||||
"herbs": [{"name": "黄芪", "dosage": 15, "formula_type": "主方"}],
|
||||
}
|
||||
image = dialog_module.render_prescription_slip_image(prescription)
|
||||
ink = dialog_module.count_slip_ink_pixels(image)
|
||||
page = QImage(1240, 1754, QImage.Format.Format_RGB32)
|
||||
page.fill(QColor("#ffffff"))
|
||||
painter = QPainter(page)
|
||||
dialog_module._draw_slip_image_on_page(painter, image)
|
||||
painter.end()
|
||||
page_ink = dialog_module.count_slip_ink_pixels(page)
|
||||
png_path = tmp_path / "prescription-slip.png"
|
||||
assert image.save(str(png_path), "PNG")
|
||||
viewer = PrescriptionDetailDialog(prescription)
|
||||
output = tmp_path / "prescription-slip.pdf"
|
||||
app_module._install_chinese_translations(application)
|
||||
viewer.export_pdf(output)
|
||||
payload = output.read_bytes()
|
||||
pdf = QPdfDocument()
|
||||
assert pdf.load(str(output)) == QPdfDocument.Error.None_
|
||||
assert pdf.pageCount() == 1
|
||||
page_size = pdf.pagePointSize(0)
|
||||
rendered_page = pdf.render(0, QSize(1240, 1754))
|
||||
visible_ink = sum(
|
||||
1
|
||||
for y in range(0, rendered_page.height(), 3)
|
||||
for x in range(0, rendered_page.width(), 3)
|
||||
if (color := rendered_page.pixelColor(x, y)).alpha() > 0 and color.lightness() < 245
|
||||
)
|
||||
viewer.close()
|
||||
application.processEvents()
|
||||
|
||||
assert dialog_module._ensure_slip_fonts()
|
||||
assert image.format() == QImage.Format.Format_RGB32
|
||||
assert image.width() >= 700
|
||||
assert image.height() >= 400
|
||||
assert ink > 200
|
||||
assert page_ink > 200
|
||||
assert png_path.stat().st_size > 40_000
|
||||
assert payload.startswith(b"%PDF")
|
||||
assert b"/Image" in payload or b"/XObject" in payload
|
||||
assert len(payload) > 50_000
|
||||
assert page_size.width() == pytest.approx(595.0, abs=2.0)
|
||||
assert page_size.height() == pytest.approx(842.0, abs=2.0)
|
||||
assert visible_ink > 1_000
|
||||
|
||||
|
||||
def test_prescription_detail_can_open_immutable_case_record_tab(
|
||||
application: QApplication,
|
||||
) -> None:
|
||||
@@ -569,7 +648,9 @@ def test_prescription_detail_can_open_immutable_case_record_tab(
|
||||
}
|
||||
viewer = PrescriptionDetailDialog(prescription, initial_tab="case")
|
||||
|
||||
assert viewer.tabs.count() == 2
|
||||
assert viewer.tabs.count() == 3
|
||||
assert viewer.tabs.tabText(0) == "药房联"
|
||||
assert viewer.tabs.tabText(1) == "处方联"
|
||||
assert viewer.tabs.tabText(viewer.tabs.currentIndex()) == "详细病历"
|
||||
assert viewer.case_document is not None
|
||||
case_html = viewer.case_document.toHtml()
|
||||
|
||||
@@ -19,8 +19,9 @@ class _ShellPageDouble(QWidget):
|
||||
*,
|
||||
permissions: Any,
|
||||
current_user: Any,
|
||||
parent: QWidget | None = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
super().__init__(parent)
|
||||
self.permissions = permissions
|
||||
self.current_user = current_user
|
||||
self.refresh_count = 0
|
||||
@@ -93,6 +94,12 @@ def test_shell_matches_admin_geometry_at_both_acceptance_sizes(
|
||||
assert image.pixelColor(220, 110).name().lower() == "#f5f7fb"
|
||||
|
||||
|
||||
def test_registered_pages_are_not_top_level_windows(shell_window: ShellWindow) -> None:
|
||||
for page in shell_window.pages.values():
|
||||
assert page.parentWidget() is shell_window.stack
|
||||
assert not page.isWindow()
|
||||
|
||||
|
||||
def test_every_visible_page_navigates_and_visited_tabs_track_active_page(
|
||||
shell_window: ShellWindow,
|
||||
) -> None:
|
||||
|
||||
@@ -3,7 +3,8 @@ from __future__ import annotations
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QSettings
|
||||
from PySide6.QtCore import QCoreApplication, QSettings
|
||||
from PySide6.QtGui import QPageSize
|
||||
from PySide6.QtWidgets import QApplication, QDialogButtonBox, QMessageBox
|
||||
|
||||
from doctor_workstation import app as app_module
|
||||
@@ -319,7 +320,9 @@ def test_server_settings_panel_keeps_controls_separated_at_minimum_window(
|
||||
assert window.server_url_label.geometry().bottom() < window.server_url_edit.geometry().top()
|
||||
assert window.server_url_edit.geometry().bottom() < window.timeout_label.geometry().top()
|
||||
assert window.timeout_spin.geometry().right() < window.save_server_button.geometry().left()
|
||||
assert window.timeout_label.geometry().bottom() < window.allow_self_signed_check.geometry().top()
|
||||
assert (
|
||||
window.timeout_label.geometry().bottom() < window.allow_self_signed_check.geometry().top()
|
||||
)
|
||||
assert window.allow_self_signed_check.geometry().bottom() < window.server_hint.geometry().top()
|
||||
|
||||
window.allow_self_signed_check.setChecked(True)
|
||||
@@ -425,9 +428,7 @@ def test_qt_standard_dialog_buttons_are_localized_to_chinese() -> None:
|
||||
app_module._install_chinese_translations(application)
|
||||
|
||||
question = QMessageBox()
|
||||
question.setStandardButtons(
|
||||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel
|
||||
)
|
||||
question.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel)
|
||||
assert question.button(QMessageBox.StandardButton.Yes).text() == "是"
|
||||
assert question.button(QMessageBox.StandardButton.Cancel).text() == "取消"
|
||||
|
||||
@@ -439,12 +440,12 @@ def test_qt_standard_dialog_buttons_are_localized_to_chinese() -> None:
|
||||
assert buttons.button(QDialogButtonBox.StandardButton.Ok).text() == "确定"
|
||||
assert buttons.button(QDialogButtonBox.StandardButton.Save).text() == "保存"
|
||||
assert buttons.button(QDialogButtonBox.StandardButton.Close).text() == "关闭"
|
||||
assert QCoreApplication.translate("QPageSize", "A4") == "A4"
|
||||
assert QPageSize(QPageSize.PageSizeId.A4).isValid()
|
||||
|
||||
|
||||
def test_friendly_error_hides_english_technical_messages() -> None:
|
||||
message = friendly_error(
|
||||
TypeError("invoke() takes 2 positional arguments but 3 were given")
|
||||
)
|
||||
message = friendly_error(TypeError("invoke() takes 2 positional arguments but 3 were given"))
|
||||
assert message == "程序执行失败,请重试;若问题持续出现,请联系管理员。"
|
||||
assert friendly_error(RuntimeError("API response envelope must be an object")) == (
|
||||
"服务器返回的数据格式不正确,请联系管理员检查接口。"
|
||||
@@ -463,9 +464,7 @@ def test_certificate_error_opens_server_settings(tmp_path: Any) -> None:
|
||||
)
|
||||
window = LoginWindow(object(), config=config, settings=settings)
|
||||
|
||||
window._on_login_error(
|
||||
RuntimeError("[SSL: CERTIFICATE_VERIFY_FAILED] self-signed certificate")
|
||||
)
|
||||
window._on_login_error(RuntimeError("[SSL: CERTIFICATE_VERIFY_FAILED] self-signed certificate"))
|
||||
|
||||
assert window.server_toggle.isChecked()
|
||||
assert not window.server_panel.isHidden()
|
||||
|
||||
Reference in New Issue
Block a user