484 lines
25 KiB
Python
484 lines
25 KiB
Python
"""Progress data/display contracts and native reachability after the blue redesign."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import socket
|
|
from copy import deepcopy
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
import pytest
|
|
from PySide6.QtCore import QDate, QPoint, QRect, Qt, QTimer
|
|
from PySide6.QtTest import QTest
|
|
from PySide6.QtWidgets import QAbstractItemView, QApplication, QLabel
|
|
|
|
from doctor_workstation.core import PermissionSet
|
|
from doctor_workstation.services import DemoDoctorRepository
|
|
from doctor_workstation.ui.pages import patients as patients_module
|
|
from doctor_workstation.ui.pages.patients import PatientProgressWorkspace, PatientsPage
|
|
from doctor_workstation.ui.shell import ShellWindow
|
|
from doctor_workstation.ui.theme import apply_theme
|
|
|
|
|
|
class _Repository:
|
|
def __init__(self) -> None:
|
|
self.queries: list[dict[str, Any]] = []
|
|
self.result = {
|
|
"lists": [{"id": 101, "diagnosis_id": 501, "patient_id": 301,
|
|
"patient_name": "林晓岚", "doctor_name": "陈医生(演示)",
|
|
"appointment_time": "09:00-09:30"}],
|
|
"count": 37,
|
|
"extend": {"scope": {"label": "本部门及下级"}, "schedule_mode": "self",
|
|
"summary": {"waiting": 37, "completed": 2, "missed": 3}},
|
|
}
|
|
|
|
def patient_progress(self, **query: Any) -> dict[str, Any]:
|
|
self.queries.append(deepcopy(query))
|
|
return deepcopy(self.result)
|
|
|
|
def list_patients(self, **_query: Any) -> dict[str, Any]:
|
|
return {"lists": [], "count": 0}
|
|
|
|
def patient_orders(self, **_query: Any) -> dict[str, Any]:
|
|
return {"lists": [], "count": 0}
|
|
|
|
|
|
def _settle(application: QApplication) -> None:
|
|
for _ in range(4):
|
|
application.processEvents()
|
|
|
|
|
|
def _texts(table) -> list[list[str]]:
|
|
return [[table.item(row, column).text() for column in range(table.columnCount())]
|
|
for row in range(table.rowCount())]
|
|
|
|
|
|
def _metrics(workspace) -> dict[str, tuple[str, str]]:
|
|
return {key: (value.text(), hint.text()) for key, (value, hint) in workspace.overview.items()}
|
|
|
|
|
|
def _rect_in(widget, parent) -> QRect:
|
|
return QRect(widget.mapTo(parent, QPoint()), widget.size())
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def application() -> QApplication:
|
|
app = QApplication.instance() or QApplication([])
|
|
apply_theme(app)
|
|
return app
|
|
|
|
|
|
@pytest.fixture
|
|
def workspace_factory(application: QApplication, monkeypatch: pytest.MonkeyPatch):
|
|
def immediate(function, *, on_success=None, on_error=None, on_finished=None):
|
|
try:
|
|
result = function()
|
|
except Exception as error:
|
|
if on_error:
|
|
on_error(error)
|
|
raise
|
|
else:
|
|
if on_success:
|
|
on_success(result)
|
|
finally:
|
|
if on_finished:
|
|
on_finished()
|
|
|
|
def reject_network(*_args, **_kwargs):
|
|
pytest.fail("Progress regression tests must use local fixtures or Demo only")
|
|
|
|
monkeypatch.setattr(socket.socket, "connect", reject_network)
|
|
monkeypatch.setattr(socket.socket, "connect_ex", reject_network)
|
|
monkeypatch.setattr(socket, "create_connection", reject_network)
|
|
monkeypatch.setattr(patients_module, "run_async", immediate)
|
|
monkeypatch.setenv("DOCTOR_SMOKE_TEST", "1")
|
|
opened = []
|
|
|
|
def create(*, page=False, shell=False, permissions=("*",), width=1274, height=696,
|
|
repository=None, refresh=True):
|
|
repository = repository if repository is not None else _Repository()
|
|
permission_set = PermissionSet(list(permissions))
|
|
if shell:
|
|
repository = DemoDoctorRepository()
|
|
session = repository.login(repository.DEMO_ACCOUNT, repository.DEMO_PASSWORD)
|
|
# A real shell with only the tested navigation route, not unrelated workspaces.
|
|
widget = ShellWindow(repository, {
|
|
"user": session.user, "permissions": permission_set,
|
|
"menu": [{"perms": "firstvisit.myPatient/lists"}],
|
|
}, permissions=permission_set)
|
|
elif page:
|
|
widget = PatientsPage(repository, permissions=permission_set)
|
|
else:
|
|
widget = PatientProgressWorkspace(repository)
|
|
opened.append(widget)
|
|
widget.resize(width, height)
|
|
widget.show()
|
|
if shell:
|
|
widget.navigate("patients")
|
|
widget.pages["patients"].tabs.setCurrentIndex(2)
|
|
elif page:
|
|
widget.tabs.setCurrentIndex(2)
|
|
elif refresh:
|
|
widget.refresh()
|
|
_settle(application)
|
|
return widget, repository
|
|
|
|
yield create
|
|
for widget in opened:
|
|
for timer in widget.findChildren(QTimer):
|
|
timer.stop()
|
|
widget.close()
|
|
widget.deleteLater()
|
|
_settle(application)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("extend", "total", "expected_mode", "expected"),
|
|
[
|
|
({"schedule_mode": "self", "summary": {"waiting": 5, "completed": 2, "missed": 1}},
|
|
99, "按本人归属", {"total": ("8", "0 位接诊医生"), "waiting": ("5", "本人归属患者"), "completed": ("2", "已过号 1 人")}),
|
|
({"schedule_mode": "ownership", "summary": {"completed": 3, "missed": 2}},
|
|
7, "按本人归属", {"total": ("12", "0 位接诊医生"), "waiting": ("7", "本人归属患者"), "completed": ("3", "已过号 2 人")}),
|
|
({"schedule_mode": "self", "summary": {"waiting": 8, "completed": 9, "missed": 7},
|
|
"today_overview": {"total_visits": 0, "booked": 0, "completed": 0, "missed": 0, "doctor_count": 4}},
|
|
99, "按本人归属", {"total": ("0", "4 位接诊医生"), "waiting": ("0", "本人归属患者"), "completed": ("0", "已过号 0 人")}),
|
|
({"schedule_mode": "roster", "summary": {"waiting": 30, "completed": 40, "missed": 5},
|
|
"today_overview": {"total_visits": 18, "booked": 12, "completed": 4, "empty_slots": 6, "doctor_count": 3, "missed": 2}},
|
|
99, "与排班合并", {"total": ("18", "3 位接诊医生"), "waiting": ("12", "有效挂号"), "completed": ("6", "当前空号")}),
|
|
({"schedule_mode": "roster", "summary": {"waiting": 6, "completed": 3, "missed": 1}},
|
|
99, "与排班合并", {"total": ("10", "0 位接诊医生"), "waiting": ("6", "有效挂号"), "completed": ("0", "当前空号")}),
|
|
({}, 4, "与排班合并", {"total": ("4", "0 位接诊医生"), "waiting": ("4", "有效挂号"), "completed": ("0", "当前空号")}),
|
|
],
|
|
ids=["self-summary", "ownership-total-fallback", "explicit-zero-wins", "roster-empty-slots", "roster-summary-fallback", "missing-extend"],
|
|
)
|
|
def test_overview_preserves_mode_fallbacks_and_fixed_completed_caption(
|
|
workspace_factory, extend, total, expected_mode, expected
|
|
) -> None:
|
|
workspace, repository = workspace_factory()
|
|
repository.result.update(count=total, extend=extend)
|
|
workspace.refresh()
|
|
assert _metrics(workspace) == expected
|
|
assert workspace.mode_label.text() == expected_mode
|
|
assert [label.text() for label in workspace.overview_card.findChildren(QLabel)
|
|
if label.text() in {"今日面诊总数", "待面诊", "已完成"}] == ["今日面诊总数", "待面诊", "已完成"]
|
|
assert workspace.queue_count.text() == f"共 {total} 人 · 每 15 秒刷新"
|
|
assert workspace.queue_table.rowCount() == 1 # Totals are server scope, not visible-row counts.
|
|
|
|
|
|
@pytest.mark.parametrize("week_schedule", [None, [], "invalid"], ids=["missing", "empty", "invalid-type"])
|
|
def test_schedule_fallback_is_seven_consecutive_days_with_blank_doctors(
|
|
workspace_factory, monkeypatch, week_schedule
|
|
) -> None:
|
|
# Sunday fixes the distinction between rolling seven days and a calendar week.
|
|
class Sunday:
|
|
@staticmethod
|
|
def currentDate():
|
|
return QDate(2026, 9, 6)
|
|
|
|
monkeypatch.setattr(patients_module, "QDate", Sunday)
|
|
workspace, repository = workspace_factory()
|
|
repository.result["extend"]["week_schedule"] = week_schedule
|
|
workspace.refresh()
|
|
rows = _texts(workspace.schedule_table)
|
|
assert [row[0] for row in rows] == ["09-06 周日", "09-07 周一", "09-08 周二", "09-09 周三", "09-10 周四", "09-11 周五", "09-12 周六"]
|
|
assert all(row[1:6] == ["0"] * 5 and row[6] == "" for row in rows)
|
|
|
|
|
|
def test_schedule_zero_priority_missing_fallback_and_doctor_names(workspace_factory) -> None:
|
|
workspace, repository = workspace_factory()
|
|
repository.result["extend"]["week_schedule"] = [
|
|
{"date_text": "09-05", "weekday": "周六", "doctor_count": 2, "total_appointments": 0,
|
|
"waiting_appointments": 0, "completed_appointments": 0, "total_slots": 11,
|
|
"booked_slots": 7, "empty_slots": 4, "missed_appointments": 1, "doctors": []},
|
|
{"date_text": "09-06", "weekday": "周日", "doctor_count": 2, "total_appointments": None,
|
|
"waiting_appointments": "", "total_slots": 11, "booked_slots": 7, "empty_slots": 4,
|
|
"missed_appointments": 0, "doctors": [{"doctor_name": "甲医生"}, {"name": "乙医生"}]},
|
|
{"date_text": "09-07", "weekday": "周一", "doctors": "invalid"},
|
|
]
|
|
workspace.refresh()
|
|
rows = {row[0]: row for row in _texts(workspace.schedule_table)}
|
|
assert rows["09-05 周六"] == ["09-05 周六", "2", "0", "0", "0", "1", ""]
|
|
assert rows["09-06 周日"] == ["09-06 周日", "2", "11", "7", "4", "0", "甲医生、乙医生"]
|
|
assert rows["09-07 周一"][6] == "—"
|
|
|
|
|
|
def test_demo_row_keeps_missing_queue_fields_and_empty_cells_visually_blank(
|
|
application, workspace_factory
|
|
) -> None:
|
|
repository = DemoDoctorRepository()
|
|
repository.login(repository.DEMO_ACCOUNT, repository.DEMO_PASSWORD)
|
|
workspace, _ = workspace_factory(repository=repository)
|
|
assert _texts(workspace.queue_table) == [["—", "林晓岚", "陈医生(演示)", "09:00", "0 位", "—"]]
|
|
assert _metrics(workspace) == {"total": ("1", "0 位接诊医生"), "waiting": ("1", "本人归属患者"), "completed": ("0", "已过号 0 人")}
|
|
assert workspace.scope == "演示医生本人患者"
|
|
assert workspace.pager.summary_label.text() == "共 1 条 · 已全部加载"
|
|
assert not workspace.pager.has_more
|
|
table = workspace.schedule_table
|
|
table.scrollToItem(table.item(0, 6))
|
|
_settle(application)
|
|
rect = table.visualItemRect(table.item(0, 6)).adjusted(3, 3, -3, -3)
|
|
image = table.viewport().grab(rect).toImage()
|
|
assert not image.isNull()
|
|
# Read the actual delegate output, not just the model's DisplayRole: blank is not “—”.
|
|
assert all(image.pixelColor(x, y).lightness() > 160
|
|
for x in range(image.width()) for y in range(image.height()))
|
|
dash_table = workspace.queue_table
|
|
dash_image = dash_table.viewport().grab(dash_table.visualItemRect(dash_table.item(0, 0))).toImage()
|
|
assert any(dash_image.pixelColor(x, y).lightness() < 100
|
|
for x in range(dash_image.width()) for y in range(dash_image.height()))
|
|
|
|
|
|
def test_queue_waiting_statuses_keep_source_counts_and_no_status_desc_fallback(workspace_factory) -> None:
|
|
workspace, repository = workspace_factory()
|
|
examples = [
|
|
({"queue_status": "consulting", "queue_status_text": "就诊中", "ahead_count": 12}, "0(进行中)", "就诊中"),
|
|
({"queue_status": "next", "queue_status_text": "待确认", "ahead_count": 12}, "0(待接诊)", "待确认"),
|
|
({"queue_status": "waiting", "queue_status_text": "等待中", "ahead_count": "3"}, "3 位 · 约 45 分钟", "等待中"),
|
|
({"queue_status": "waiting", "ahead_count": 2, "estimated_wait_minutes": 7}, "2 位 · 约 7 分钟", "—"),
|
|
({"queue_status": "missed", "queue_status_text": "已过号", "ahead_count": 2, "estimated_wait_minutes": -5}, "2 位 · 约 0 分钟", "已过号"),
|
|
({"queue_status": "completed", "queue_status_text": "已完成", "ahead_count": -8}, "0 位", "已完成"),
|
|
({"status_desc": "待接诊", "diagnosis_confirmed": 1, "ahead_count": "bad"}, "0 位", "—"),
|
|
]
|
|
base = repository.result["lists"][0]
|
|
repository.result["lists"] = [dict(base, **fields, id=100 + index, queue_no=40 + index)
|
|
for index, (fields, _, _) in enumerate(examples)]
|
|
workspace.refresh()
|
|
table = workspace.queue_table
|
|
for row_index in range(table.rowCount()):
|
|
row = table.item(row_index, 0).data(Qt.ItemDataRole.UserRole)
|
|
fields, expected_wait, expected_status = examples[row["id"] - 100]
|
|
assert table.item(row_index, 0).text() == str(row["queue_no"])
|
|
assert table.item(row_index, 4).text() == expected_wait
|
|
assert table.item(row_index, 5).text() == expected_status
|
|
assert all(table.item(row_index, col).data(Qt.ItemDataRole.UserRole) == row for col in range(6))
|
|
assert table.selectionMode() == QAbstractItemView.SelectionMode.SingleSelection
|
|
assert table.editTriggers() == QAbstractItemView.EditTrigger.NoEditTriggers
|
|
assert table.isSortingEnabled()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("permissions", "expected"),
|
|
[(('tcm.diagnosis/edit', 'tcm.diagnosis/readonlyDetail'), "edit"),
|
|
(("tcm.diagnosis/readonlyDetail",), "read"), ((), "denied"),
|
|
(("tcm.diagnosis.edit", "tcm.diagnosis.readonlyDetail"), "denied")],
|
|
ids=["edit-preferred", "readonly", "no-permission", "noncanonical-permission"],
|
|
)
|
|
def test_sorted_double_click_uses_selected_diagnosis_and_canonical_permissions(
|
|
application, workspace_factory, monkeypatch, permissions, expected
|
|
) -> None:
|
|
page, repository = workspace_factory(page=True, width=1328, height=884, permissions=permissions)
|
|
workspace = page.progress_workspace
|
|
repository.result["lists"] = [dict(repository.result["lists"][0], id=101 + index,
|
|
diagnosis_id=501 + index, patient_id=301 + index,
|
|
patient_name=name, queue_no=index + 1)
|
|
for index, name in enumerate(("阿青", "林青", "赵青"))]
|
|
workspace.refresh()
|
|
calls, toasts = [], []
|
|
dialog = SimpleNamespace(
|
|
open_for=lambda resource, **kwargs: calls.append(("edit", resource, kwargs)),
|
|
open_view_only=lambda resource, **kwargs: calls.append(("read", resource, kwargs)),
|
|
)
|
|
monkeypatch.setattr(page, "_ensure_diagnosis_dialog", lambda: dialog)
|
|
monkeypatch.setattr(patients_module, "show_toast", lambda _parent, text, *_args: toasts.append(text))
|
|
table = workspace.queue_table
|
|
table.sortItems(0, Qt.SortOrder.DescendingOrder)
|
|
table.selectRow(0)
|
|
table.scrollToItem(table.item(0, 1))
|
|
workspace.scroll.ensureWidgetVisible(table, 0, 0)
|
|
_settle(application)
|
|
row = table.item(0, 0).data(Qt.ItemDataRole.UserRole)
|
|
assert row["id"] == 103 # A sorted row must not resolve to the first source row (101).
|
|
point = table.visualItemRect(table.item(0, 1)).center()
|
|
QTest.mouseClick(table.viewport(), Qt.MouseButton.LeftButton, pos=point)
|
|
QTest.mouseDClick(table.viewport(), Qt.MouseButton.LeftButton, pos=point)
|
|
_settle(application)
|
|
if expected == "denied":
|
|
assert calls == [] and toasts == ["当前账号没有诊单查看权限。"]
|
|
else:
|
|
assert len(calls) == 1 and calls[0][0:2] == (expected, row["diagnosis_id"])
|
|
assert calls[0][1] != row["patient_id"] and calls[0][2]["seed"] == row
|
|
assert not toasts
|
|
|
|
|
|
def test_selected_row_id_fallback_and_invalid_diagnosis_never_use_patient_id(
|
|
workspace_factory, monkeypatch
|
|
) -> None:
|
|
page, repository = workspace_factory(page=True, permissions=("tcm.diagnosis/edit",))
|
|
workspace = page.progress_workspace
|
|
calls, toasts = [], []
|
|
monkeypatch.setattr(page, "_ensure_diagnosis_dialog", lambda: SimpleNamespace(
|
|
open_for=lambda resource, **kwargs: calls.append(resource)))
|
|
monkeypatch.setattr(patients_module, "show_toast", lambda _parent, text, *_args: toasts.append(text))
|
|
for row, expected in [({"id": 810, "patient_id": 910}, 810),
|
|
({"id": 811, "diagnosis_id": 0, "patient_id": 911}, None),
|
|
({"patient_id": 912}, None)]:
|
|
repository.result["lists"] = [row]
|
|
workspace.refresh()
|
|
workspace.queue_table.selectRow(0)
|
|
before = len(calls)
|
|
workspace.queue_table.itemDoubleClicked.emit(workspace.queue_table.item(0, 0))
|
|
assert calls[before:] == ([] if expected is None else [expected])
|
|
assert toasts == ["患者诊单信息不完整。", "患者诊单信息不完整。"]
|
|
|
|
|
|
def test_query_captures_today_status_and_page_before_worker_runs(
|
|
application, workspace_factory, monkeypatch
|
|
) -> None:
|
|
workspace, repository = workspace_factory()
|
|
queued = []
|
|
monkeypatch.setattr(patients_module, "run_async", lambda function, **callbacks: queued.append((function, callbacks)))
|
|
today = QDate.currentDate().toString("yyyy-MM-dd")
|
|
workspace.refresh(silent=True)
|
|
queued.pop()[1]["on_success"](deepcopy(repository.result))
|
|
workspace.pager.load_more()
|
|
workspace.pager.load_more()
|
|
assert len(queued) == 1 # Repeated bottom events share the in-flight request.
|
|
assert not workspace.banner.isVisible()
|
|
# Later GUI changes must not reach the pending worker's immutable query.
|
|
workspace._page = 9
|
|
queued[0][0]()
|
|
assert repository.queries[-1:] == [
|
|
{"status": 1, "start_date": today, "end_date": today, "page_no": 2, "page_size": 15},
|
|
]
|
|
assert "page" not in repository.queries[-1]
|
|
assert workspace.pager.page_size == 15
|
|
|
|
|
|
def test_stale_success_error_and_latest_error_preserve_the_last_good_data(
|
|
application, workspace_factory, monkeypatch
|
|
) -> None:
|
|
workspace, repository = workspace_factory()
|
|
pending = []
|
|
monkeypatch.setattr(patients_module, "run_async", lambda _function, **callbacks: pending.append(callbacks))
|
|
workspace.refresh()
|
|
# A date rollover changes the query; repeated same-day polls reuse in-flight work.
|
|
tomorrow = patients_module.QDate.currentDate().addDays(1)
|
|
monkeypatch.setattr(patients_module, "QDate", SimpleNamespace(currentDate=lambda: tomorrow))
|
|
workspace.refresh()
|
|
newer = deepcopy(repository.result)
|
|
newer["lists"][0].update(id=202, patient_name="最新结果")
|
|
newer["count"] = 22
|
|
newer["extend"].update(scope={"label": "新范围"}, summary={"waiting": 22, "completed": 4, "missed": 2})
|
|
pending[1]["on_success"](newer)
|
|
_settle(application)
|
|
snapshot = (_texts(workspace.queue_table), _texts(workspace.schedule_table), _metrics(workspace),
|
|
workspace.scope, workspace.queue_count.text(), workspace.pager.total, workspace.queue_stack.currentIndex())
|
|
pending[0]["on_success"]({"lists": [], "count": 0})
|
|
pending[0]["on_error"](RuntimeError("obsolete failure"))
|
|
_settle(application)
|
|
assert not workspace.banner.isVisible()
|
|
assert snapshot == (_texts(workspace.queue_table), _texts(workspace.schedule_table), _metrics(workspace),
|
|
workspace.scope, workspace.queue_count.text(), workspace.pager.total, workspace.queue_stack.currentIndex())
|
|
workspace.refresh(silent=True)
|
|
assert not workspace.banner.isVisible()
|
|
pending[2]["on_error"](RuntimeError("连接失败,请稍后刷新重试。"))
|
|
_settle(application)
|
|
assert workspace.banner.isVisible() and workspace.banner.property("kind") == "danger"
|
|
assert "连接失败" in workspace.banner.label.text()
|
|
assert snapshot == (_texts(workspace.queue_table), _texts(workspace.schedule_table), _metrics(workspace),
|
|
workspace.scope, workspace.queue_count.text(), workspace.pager.total, workspace.queue_stack.currentIndex())
|
|
|
|
|
|
def test_empty_list_keeps_total_week_and_compact_status_visible(
|
|
application, workspace_factory
|
|
) -> None:
|
|
workspace, repository = workspace_factory()
|
|
repository.result.update(lists=[], count=37)
|
|
workspace.refresh()
|
|
_settle(application)
|
|
assert workspace.queue_stack.currentIndex() == 1
|
|
assert not workspace.queue_table.isVisible() and workspace.pager.isVisible()
|
|
assert workspace.pager.height() == 24
|
|
assert workspace.schedule_table.rowCount() == 7
|
|
assert workspace.queue_count.text() == "共 37 人 · 每 15 秒刷新"
|
|
labels = [label.text() for label in workspace.queue_stack.currentWidget().findChildren(QLabel)]
|
|
assert "今日暂无候诊患者" in labels
|
|
assert "当前权限范围内没有有效挂号。" in labels
|
|
|
|
|
|
@pytest.mark.parametrize(("width", "height"), [(1536, 960), (1366, 768), (1024, 768)])
|
|
def test_actual_shell_keeps_all_columns_rows_and_load_status_reachable(
|
|
application, workspace_factory, width, height
|
|
) -> None:
|
|
shell, _ = workspace_factory(shell=True, width=width, height=height)
|
|
page = shell.pages["patients"]
|
|
page.filter_disclosure.set_expanded(True)
|
|
_settle(application)
|
|
workspace = page.progress_workspace
|
|
assert (shell.width(), shell.height()) == (width, height)
|
|
assert not page.scope_badge.isVisible() and workspace.scope_label.isVisible()
|
|
schedule, queue = workspace.schedule_table, workspace.queue_table
|
|
assert (schedule.rowCount(), schedule.columnCount(), queue.columnCount()) == (7, 7, 6)
|
|
if width == 1536:
|
|
assert schedule.verticalScrollBar().value() == 0
|
|
assert schedule.rowViewportPosition(6) + schedule.rowHeight(6) <= schedule.viewport().height()
|
|
assert workspace.scroll.viewport().rect().contains(_rect_in(schedule, workspace.scroll.viewport()))
|
|
for table in (schedule, queue):
|
|
workspace.scroll.ensureWidgetVisible(table, 0, 0)
|
|
_settle(application)
|
|
assert workspace.scroll.viewport().rect().contains(_rect_in(table, workspace.scroll.viewport()))
|
|
for column in range(table.columnCount()):
|
|
item = table.item(table.rowCount() - 1, column)
|
|
table.scrollToItem(item, QAbstractItemView.ScrollHint.PositionAtCenter)
|
|
_settle(application)
|
|
assert not table.isColumnHidden(column)
|
|
assert table.viewport().rect().contains(table.visualItemRect(item)), (width, column)
|
|
workspace.scroll.ensureWidgetVisible(workspace.pager, 0, 0)
|
|
_settle(application)
|
|
assert workspace.scroll.viewport().rect().contains(_rect_in(workspace.pager, workspace.scroll.viewport()))
|
|
assert workspace.pager.height() == 24
|
|
for child in (workspace.pager.summary_label,):
|
|
assert workspace.pager.rect().contains(_rect_in(child, workspace.pager))
|
|
if width == 1024:
|
|
assert schedule.horizontalScrollBar().maximum() > 0
|
|
assert queue.horizontalScrollBar().maximum() > 0
|
|
assert workspace.scroll.verticalScrollBar().maximum() > 0
|
|
|
|
|
|
def test_splitter_mouse_drag_reallocates_space_but_cannot_collapse_panels(
|
|
application, workspace_factory
|
|
) -> None:
|
|
workspace, _ = workspace_factory()
|
|
splitter = workspace.splitter
|
|
before = splitter.sizes()
|
|
handle = splitter.handle(1)
|
|
point = handle.rect().center()
|
|
QTest.mousePress(handle, Qt.MouseButton.LeftButton, pos=point)
|
|
QTest.mouseMove(handle, point + QPoint(0, -60))
|
|
QTest.mouseRelease(handle, Qt.MouseButton.LeftButton, pos=point + QPoint(0, -60))
|
|
_settle(application)
|
|
assert splitter.sizes()[0] < before[0]
|
|
assert not splitter.childrenCollapsible()
|
|
for sizes in ([0, 1000], [1000, 0]):
|
|
splitter.setSizes(sizes)
|
|
_settle(application)
|
|
assert splitter.sizes()[0] >= workspace.schedule_card.minimumHeight() > 0
|
|
assert splitter.sizes()[1] >= workspace.queue_card.minimumHeight() > 0
|
|
|
|
|
|
def test_typography_and_styles_are_local_to_the_progress_workspace(
|
|
application, workspace_factory
|
|
) -> None:
|
|
app_style = application.styleSheet()
|
|
unrelated = QLabel("外部标签")
|
|
unrelated.ensurePolished()
|
|
original_font = unrelated.font()
|
|
workspace, _ = workspace_factory()
|
|
assert workspace.schedule_table.font().pixelSize() == 14
|
|
assert workspace.queue_table.font().pixelSize() == 14
|
|
assert workspace.scope_label.font().pixelSize() == 13
|
|
assert workspace.mode_label.font().pixelSize() == 13
|
|
assert workspace.queue_count.font().pixelSize() == 13
|
|
for value, hint in workspace.overview.values():
|
|
assert value.font().pixelSize() == 18 and hint.font().pixelSize() == 13
|
|
for label in workspace.findChildren(QLabel):
|
|
if label.property("role") == "sectionTitle":
|
|
assert label.font().pixelSize() == 14
|
|
assert application.styleSheet() == app_style
|
|
assert "ProgressWorkspace" not in app_style
|
|
assert unrelated.font() == original_font and not unrelated.styleSheet()
|
|
unrelated.deleteLater()
|