303 lines
9.9 KiB
Python
303 lines
9.9 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
import pytest
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtWidgets import QApplication, QWidget
|
|
|
|
from doctor_workstation.ui import shell as shell_module
|
|
from doctor_workstation.ui.shell import NavigationItem, ShellWindow
|
|
|
|
|
|
class _ShellPageDouble(QWidget):
|
|
def __init__(
|
|
self,
|
|
_repository: Any,
|
|
*,
|
|
permissions: Any,
|
|
current_user: Any,
|
|
parent: QWidget | None = None,
|
|
) -> None:
|
|
super().__init__(parent)
|
|
self.permissions = permissions
|
|
self.current_user = current_user
|
|
self.refresh_count = 0
|
|
self.show_count = 0
|
|
|
|
def refresh(self) -> None:
|
|
self.refresh_count += 1
|
|
|
|
def showEvent(self, event: Any) -> None: # noqa: N802 - Qt virtual
|
|
super().showEvent(event)
|
|
self.show_count += 1
|
|
self.refresh()
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def application() -> QApplication:
|
|
return QApplication.instance() or QApplication([])
|
|
|
|
|
|
def test_patients_navigation_keeps_the_product_menu_title() -> None:
|
|
resolved = shell_module._resolve_navigation(
|
|
[
|
|
{
|
|
"name": "我的患者",
|
|
"component": "first_visit/my_patients",
|
|
"perms": "firstvisit.myPatient/lists",
|
|
}
|
|
],
|
|
{"firstvisit.myPatient/lists"},
|
|
demo_mode=False,
|
|
)
|
|
|
|
assert [(item.key, title) for item, title in resolved] == [("patients", "我的患者")]
|
|
|
|
|
|
def test_appointments_navigation_is_named_reception_and_always_first() -> None:
|
|
resolved = shell_module._resolve_navigation(
|
|
[
|
|
{
|
|
"name": "我的患者",
|
|
"component": "first_visit/my_patients",
|
|
"perms": "firstvisit.myPatient/lists",
|
|
"sort": 99,
|
|
},
|
|
{
|
|
"name": "挂号列表",
|
|
"component": "tcm/appointment/list",
|
|
"perms": "doctor.appointment/lists",
|
|
"sort": 1,
|
|
},
|
|
],
|
|
{"doctor.appointment/lists", "firstvisit.myPatient/lists"},
|
|
demo_mode=False,
|
|
)
|
|
|
|
assert [(item.key, title) for item, title in resolved] == [
|
|
("appointments", "问诊列表"),
|
|
("patients", "我的患者"),
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def shell_window(
|
|
application: QApplication,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> ShellWindow:
|
|
navigation = [
|
|
NavigationItem(key, title, glyph, _ShellPageDouble, (permission,))
|
|
for key, title, glyph, permission in (
|
|
("appointments", "问诊列表", "号", "doctor.appointment/lists"),
|
|
("reception", "接诊台", "◎", "doctor.appointment/lists"),
|
|
(
|
|
"prescription_library",
|
|
"我的处方库",
|
|
"方",
|
|
"tcm.prescriptionLibrary/lists",
|
|
),
|
|
("prescriptions", "已开处方", "笺", "tcm.prescription/lists"),
|
|
("patients", "我的患者", "患", "firstvisit.myPatient/lists"),
|
|
("consultations", "问诊列表", "询", "tcm.diagnosis/lists"),
|
|
)
|
|
]
|
|
monkeypatch.setattr(
|
|
shell_module,
|
|
"_resolve_navigation",
|
|
lambda _menu, _permissions, *, demo_mode: [
|
|
(item, item.title) for item in navigation
|
|
],
|
|
)
|
|
window = ShellWindow(
|
|
object(),
|
|
{
|
|
"user": {"name": "陈医生", "department_name": "中医门诊", "role_ids": [1]},
|
|
"demo_mode": True,
|
|
},
|
|
permissions={item.permissions[0] for item in navigation},
|
|
)
|
|
window.show()
|
|
application.processEvents()
|
|
yield window
|
|
window.close()
|
|
application.processEvents()
|
|
|
|
|
|
def test_shell_matches_reference_geometry_at_both_acceptance_sizes(
|
|
application: QApplication,
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
for width, height in ((1024, 640), (1440, 900)):
|
|
shell_window.resize(width, height)
|
|
application.processEvents()
|
|
|
|
assert shell_window.sidebar.width() == 179
|
|
assert shell_window.topbar.height() == 62
|
|
assert shell_window.tabs_host.height() == 0
|
|
assert shell_window.workspace.width() == width - 26 - 179
|
|
assert shell_window.stack.width() == width - 26 - 179
|
|
assert shell_window.stack.height() == height - 26 - 62
|
|
assert shell_window.stack.geometry().right() < shell_window.workspace.width()
|
|
assert shell_window.stack.geometry().bottom() < shell_window.workspace.height()
|
|
|
|
image = shell_window.grab().toImage()
|
|
assert image.pixelColor(20, 300).name().lower() in {
|
|
"#f2f5fd",
|
|
"#f3f6fd",
|
|
"#f2f6fe",
|
|
"#f3f6fe",
|
|
}
|
|
assert image.pixelColor(610, 20).name().lower() == "#ffffff"
|
|
assert image.pixelColor(220, 90).name().lower() == "#fcfdfe"
|
|
|
|
|
|
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_reference_shell_has_integrated_search_ai_card_and_window_controls(
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
assert shell_window.windowFlags() & Qt.WindowType.FramelessWindowHint
|
|
assert (
|
|
shell_window.global_search.placeholderText() == "搜索患者姓名、手机号、病历号"
|
|
)
|
|
assert shell_window.assistant_card.isVisible()
|
|
assert shell_window.assistant_button.text() == "开始对话"
|
|
assert "GPT-4o 医疗版" in shell_window.model_label.text()
|
|
assert shell_window.minimize_button.text() == ""
|
|
assert shell_window.close_button.text() == ""
|
|
|
|
shell_window.set_connection_state(False)
|
|
assert "离线" in shell_window.assistant_status.text()
|
|
shell_window.set_connection_state(True)
|
|
assert "在线" in shell_window.assistant_status.text()
|
|
|
|
|
|
def test_every_visible_page_navigates_and_visited_tabs_track_active_page(
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
changed: list[str] = []
|
|
shell_window.page_changed.connect(changed.append)
|
|
|
|
for key in (
|
|
"reception",
|
|
"appointments",
|
|
"prescription_library",
|
|
"prescriptions",
|
|
"patients",
|
|
"consultations",
|
|
):
|
|
assert shell_window.navigate(key)
|
|
assert shell_window.stack.currentWidget() is shell_window.pages[key]
|
|
assert shell_window.nav_buttons[key].isChecked()
|
|
assert shell_window.tab_bar.tabData(shell_window.tab_bar.currentIndex()) == key
|
|
|
|
assert shell_window.visited_tab_keys() == (
|
|
"appointments",
|
|
"reception",
|
|
"prescription_library",
|
|
"prescriptions",
|
|
"patients",
|
|
"consultations",
|
|
)
|
|
assert changed[-6:] == [
|
|
"reception",
|
|
"appointments",
|
|
"prescription_library",
|
|
"prescriptions",
|
|
"patients",
|
|
"consultations",
|
|
]
|
|
|
|
|
|
def test_real_navigation_refreshes_once_and_current_page_click_is_a_noop(
|
|
application: QApplication,
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
appointments = shell_window.pages["appointments"]
|
|
reception = shell_window.pages["reception"]
|
|
assert isinstance(appointments, _ShellPageDouble)
|
|
assert isinstance(reception, _ShellPageDouble)
|
|
assert appointments.refresh_count == 1
|
|
assert appointments.show_count == 1
|
|
assert reception.refresh_count == 0
|
|
|
|
shell_window.nav_buttons["reception"].click()
|
|
application.processEvents()
|
|
assert reception.refresh_count == 1
|
|
assert reception.show_count == 1
|
|
|
|
shell_window.nav_buttons["reception"].click()
|
|
application.processEvents()
|
|
assert reception.refresh_count == 1
|
|
assert reception.show_count == 1
|
|
|
|
shell_window.nav_buttons["appointments"].click()
|
|
application.processEvents()
|
|
assert appointments.refresh_count == 2
|
|
assert appointments.show_count == 2
|
|
|
|
|
|
def test_non_fixed_tabs_close_and_active_close_renavigates(
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
for key in ("patients", "consultations"):
|
|
assert shell_window.navigate(key)
|
|
|
|
assert not shell_window.close_tab("appointments")
|
|
assert shell_window.close_tab("patients")
|
|
assert "patients" not in shell_window.visited_tab_keys()
|
|
assert (
|
|
shell_window.tab_bar.tabData(shell_window.tab_bar.currentIndex())
|
|
== "consultations"
|
|
)
|
|
|
|
assert shell_window.close_current_tab()
|
|
assert (
|
|
shell_window.tab_bar.tabData(shell_window.tab_bar.currentIndex()) == "appointments"
|
|
)
|
|
assert shell_window.stack.currentWidget() is shell_window.pages["appointments"]
|
|
assert shell_window.nav_buttons["appointments"].isChecked()
|
|
|
|
shell_window.close_all_tabs()
|
|
assert shell_window.visited_tab_keys() == ("appointments",)
|
|
assert shell_window.stack.currentWidget() is shell_window.pages["appointments"]
|
|
|
|
|
|
def test_sidebar_collapse_preserves_active_navigation(
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
assert shell_window.navigate("consultations")
|
|
|
|
shell_window.toggle_sidebar()
|
|
assert shell_window.sidebar.width() == 68
|
|
assert shell_window.nav_buttons["consultations"].text() == ""
|
|
assert shell_window.nav_buttons["consultations"].isChecked()
|
|
|
|
shell_window.toggle_sidebar()
|
|
assert shell_window.sidebar.width() == 195
|
|
assert shell_window.nav_buttons["consultations"].text().endswith("问诊列表")
|
|
assert shell_window.nav_buttons["consultations"].isChecked()
|
|
|
|
|
|
def test_shell_directional_controls_have_no_unicode_arrow_text(
|
|
shell_window: ShellWindow,
|
|
) -> None:
|
|
assert shell_window.fold_button.text() == ""
|
|
assert shell_window.refresh_button.text() == ""
|
|
assert shell_window.fullscreen_button.text() == ""
|
|
assert shell_window.tabs_menu_button.text() == ""
|
|
assert all(
|
|
arrow not in button.text()
|
|
for button in shell_window.findChildren(QWidget)
|
|
if hasattr(button, "text") and callable(button.text)
|
|
for arrow in ("←", "→", "↑", "↓", "▲", "▼", "▴", "▾")
|
|
)
|