更新
This commit is contained in:
@@ -0,0 +1,399 @@
|
||||
"""Offline rendering and data-integrity checks for the prescription workspace."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import socket
|
||||
from copy import deepcopy
|
||||
from typing import Any
|
||||
|
||||
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
||||
|
||||
import pytest
|
||||
from PySide6.QtCore import QEvent, QObject, Qt
|
||||
from PySide6.QtTest import QTest
|
||||
from PySide6.QtWidgets import QApplication, QVBoxLayout, QWidget
|
||||
|
||||
from doctor_workstation.ui.dialogs.issued_prescription_ai_comparison import (
|
||||
PrescriptionComparisonPanel,
|
||||
)
|
||||
|
||||
|
||||
def saved_row(name: str = "黄芪", *, doctor: Any = 30, candidate: Any = 15, unit: str = "g", basis: str = "per_dose", formula: str = "主方", processing: str = "生品") -> dict[str, Any]:
|
||||
identity = {"name": name, "processing": processing, "formula_type": formula, "administration_route": "口服", "group": "", "unit": unit, "dose_basis": basis}
|
||||
return {**identity, "key": "a1b2c3d4" * 8, "doctor": {**identity, "dosage": doctor}, "candidate": {**identity, "dosage": candidate}, "doctor_dosage": doctor, "candidate_dosage": candidate, "match_type": "matched"}
|
||||
|
||||
|
||||
def saved_batch(rows: list[dict[str, Any]] | None = None) -> dict[str, Any]:
|
||||
rows = rows if rows is not None else [saved_row(), saved_row("白术", doctor=9, candidate=12)]
|
||||
herbs = []
|
||||
for row in rows:
|
||||
if row.get("candidate"):
|
||||
original = deepcopy(row["candidate"])
|
||||
original.pop("source_rows", None)
|
||||
row["candidate"].setdefault("source_rows", [len(herbs)])
|
||||
herbs.append(original)
|
||||
candidate = {"status": "available_for_review", "prescription_name": "补中益气汤加减", "herbs": herbs, "usage_instruction": "水煎温服", "times_per_day": 2, "usage_days": 7}
|
||||
qwen = {"status": "succeeded", "candidate": candidate, "comparison": {"status": "comparable", "score": 68.5, "herb_score": 100, "rows": rows}}
|
||||
openai = deepcopy(qwen)
|
||||
openai["candidate"]["prescription_name"] = "益气健脾方"
|
||||
return {"id": 44, "validity": "current", "status": "completed", "models": {"qwen": qwen, "openai": openai}}
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def application() -> QApplication:
|
||||
return QApplication.instance() or QApplication([])
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def no_network(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
def denied(*_args: Any, **_kwargs: Any) -> None:
|
||||
pytest.fail("The comparison panel must never contact external systems")
|
||||
monkeypatch.setattr(socket.socket, "connect", denied)
|
||||
monkeypatch.setattr(socket.socket, "connect_ex", denied)
|
||||
monkeypatch.setattr(socket, "create_connection", denied)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def panel(application: QApplication):
|
||||
host = QWidget()
|
||||
host.resize(1120, 400)
|
||||
layout = QVBoxLayout(host)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
widget = PrescriptionComparisonPanel(host)
|
||||
layout.addWidget(widget)
|
||||
host.show()
|
||||
application.processEvents()
|
||||
yield widget
|
||||
host.close()
|
||||
host.deleteLater()
|
||||
application.processEvents()
|
||||
|
||||
|
||||
def test_saved_snapshot_is_readable_and_model_switch_is_explicit(panel: PrescriptionComparisonPanel, application: QApplication) -> None:
|
||||
data = saved_batch()
|
||||
before = deepcopy(data)
|
||||
panel.set_batch(data)
|
||||
application.processEvents()
|
||||
assert data == before
|
||||
assert panel.selected_model == "qwen"
|
||||
assert panel.model_buttons["qwen"].isChecked()
|
||||
assert panel.name_label.text() == "补中益气汤加减"
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
assert panel.herb_table.item(0, 1).text() == "30 克 / 每剂"
|
||||
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||
assert "主方" in panel.herb_table.item(0, 0).text()
|
||||
assert "a1b2c3d4" not in panel.chart.accessibleDescription()
|
||||
assert "药味剂量一致度" not in panel.chart.accessibleDescription()
|
||||
assert "水煎温服" in panel.usage_label.text()
|
||||
assert panel.chart.bars_enabled
|
||||
assert panel.chart.groups[0][0] == ("克", "每剂")
|
||||
panel.model_buttons["openai"].click()
|
||||
assert panel.selected_model == "openai"
|
||||
assert panel.name_label.text() == "益气健脾方"
|
||||
assert panel.herb_table.horizontalHeaderItem(2).text() == "OpenAI剂量"
|
||||
assert panel.chart.model_key == "openai"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("candidate_key", ["candidate_dose", "candidate_dosage", "ai_dose"])
|
||||
def test_legacy_flat_doses_remain_supported(panel: PrescriptionComparisonPanel, candidate_key: str) -> None:
|
||||
data = saved_batch()
|
||||
data["models"]["qwen"]["comparison"]["rows"] = [{"name": "黄芪", "doctor_dose": 0, candidate_key: "12.50", "unit": "g", "dose_basis": "每剂", "formula_type": "主方"}]
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.item(0, 1).text() == "0 克 / 每剂"
|
||||
assert panel.herb_table.item(0, 2).text() == "12.50 克 / 每剂"
|
||||
assert panel.chart.rows[0].doctor.number == 0
|
||||
|
||||
|
||||
def test_absent_historical_doctor_is_never_replaced_with_current_prescription(panel: PrescriptionComparisonPanel) -> None:
|
||||
data = saved_batch()
|
||||
data["models"]["qwen"]["comparison"]["rows"] = []
|
||||
data["prescription"] = {"herbs": [{"name": "黄芪", "dosage": 99, "unit": "g", "dose_basis": "per_dose"}]}
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
assert panel.herb_table.item(0, 1).text() == "—"
|
||||
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||
assert not panel.chart.bars_enabled
|
||||
assert "历史处方" in panel.status_label.text()
|
||||
assert "99" not in panel.chart.accessibleDescription()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("rows", [None, {}, "unavailable"])
|
||||
def test_malformed_comparison_keeps_candidate_list_without_claiming_a_snapshot(panel: PrescriptionComparisonPanel, rows: Any) -> None:
|
||||
data = saved_batch()
|
||||
data["models"]["qwen"]["comparison"]["rows"] = rows
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
assert panel.herb_table.item(0, 1).text() == "—"
|
||||
assert not panel.chart.bars_enabled
|
||||
|
||||
|
||||
def test_explicit_missing_snapshot_and_nested_unknown_unit_override_flat_values(panel: PrescriptionComparisonPanel) -> None:
|
||||
row = saved_row()
|
||||
row["doctor"] = None
|
||||
row["candidate"]["unit"] = None
|
||||
panel.set_batch(saved_batch([row]))
|
||||
assert panel.herb_table.item(0, 1).text() == "—"
|
||||
assert "单位未注明" in panel.herb_table.item(0, 2).text()
|
||||
assert panel.chart.groups[0][0] is None
|
||||
assert "未明确" in panel.chart.accessibleDescription()
|
||||
|
||||
|
||||
def test_incomparable_report_retains_original_doses_without_bars(panel: PrescriptionComparisonPanel) -> None:
|
||||
data = saved_batch()
|
||||
data["models"]["qwen"]["comparison"].update(status="not_comparable", score=99, reason="dose_basis_mismatch")
|
||||
panel.set_batch(data)
|
||||
assert "剂量基准不同" in panel.status_label.text()
|
||||
assert not panel.chart.bars_enabled
|
||||
assert panel.herb_table.item(0, 1).text() == "30 克 / 每剂"
|
||||
assert all(scale is None for scale, _rows, _maximum in panel.chart.groups)
|
||||
assert "99" not in panel.chart.accessibleDescription()
|
||||
|
||||
|
||||
def test_units_and_bases_have_independent_scales_and_mismatched_rows_are_excluded(panel: PrescriptionComparisonPanel) -> None:
|
||||
rows = [saved_row("黄芪"), saved_row("白术", unit="mg", doctor=1000), saved_row("茯苓", basis="per_day", doctor=60), saved_row("甘草")]
|
||||
rows[-1]["candidate"]["dose_basis"] = "per_day"
|
||||
panel.set_batch(saved_batch(rows))
|
||||
assert [scale for scale, _members, _maximum in panel.chart.groups] == [("克", "每剂"), ("毫克", "每剂"), ("克", "每日"), None]
|
||||
assert panel.chart.groups[0][2] == 30
|
||||
assert panel.chart.groups[1][2] == 1000
|
||||
assert "单位或剂量基准不同" in panel.chart.accessibleDescription()
|
||||
assert "每日" in panel.herb_table.item(3, 2).text()
|
||||
|
||||
|
||||
def test_main_auxiliary_and_processing_rows_never_merge(panel: PrescriptionComparisonPanel) -> None:
|
||||
rows = [saved_row("甘草", formula="主方"), saved_row("甘草", formula="辅方"), saved_row("甘草", processing="炙品")]
|
||||
rows[2]["candidate"]["processing"] = "生品"
|
||||
panel.set_batch(saved_batch(rows))
|
||||
assert panel.herb_table.rowCount() == 3
|
||||
assert "主方" in panel.herb_table.item(0, 0).text()
|
||||
assert "辅方" in panel.herb_table.item(1, 0).text()
|
||||
assert panel.chart.rows[2].scale is None
|
||||
assert "炮制" in panel.chart.accessibleDescription()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("skipped_index", [0, 1])
|
||||
def test_zero_based_trace_preserves_candidate_herbs_skipped_during_normalization(panel: PrescriptionComparisonPanel, skipped_index: int) -> None:
|
||||
normalized = saved_row("黄芪")
|
||||
normalized["candidate"]["source_rows"] = [1 - skipped_index]
|
||||
data = saved_batch([normalized])
|
||||
original = deepcopy(data["models"]["qwen"]["candidate"]["herbs"][0])
|
||||
excluded = {**original, "name": "黄芪", "processing": "炮制待核对", "dosage": 8, "instructions": "先煎 30 分钟"}
|
||||
herbs = [original]
|
||||
herbs.insert(skipped_index, excluded)
|
||||
data["models"]["qwen"]["candidate"]["herbs"] = herbs
|
||||
# Even a contradictory comparable status must not grant the omitted raw herb a bar.
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||
assert "未纳入对比" in panel.herb_table.item(1, 0).text()
|
||||
assert "炮制待核对" in panel.herb_table.item(1, 0).text()
|
||||
assert panel.herb_table.item(1, 1).text() == "—"
|
||||
assert panel.herb_table.item(1, 2).text() == "8 克 / 每剂"
|
||||
assert panel.chart.rows[1].scale is None
|
||||
assert "先煎 30 分钟" in panel.chart.accessibleDescription()
|
||||
assert "原方 2 项" in panel.count_label.text() and "未纳入 1 项" in panel.count_label.text()
|
||||
|
||||
|
||||
def test_merged_source_rows_cover_each_original_once_and_keep_original_doses(panel: PrescriptionComparisonPanel) -> None:
|
||||
merged = saved_row("黄芪", candidate=10)
|
||||
merged["candidate"]["source_rows"] = [0, 1]
|
||||
data = saved_batch([merged])
|
||||
original = data["models"]["qwen"]["candidate"]["herbs"][0]
|
||||
data["models"]["qwen"]["candidate"]["herbs"] = [{**original, "name": "北芪", "dosage": 4}, {**original, "dosage": 6}, {**original, "name": "未识别药材", "dosage": 5}]
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
assert panel.herb_table.item(0, 2).text() == "10 克 / 每剂"
|
||||
tooltip = panel.herb_table.item(0, 0).toolTip()
|
||||
assert "第 1 项 北芪 4 克 / 每剂" in tooltip
|
||||
assert "第 2 项 黄芪 6 克 / 每剂" in tooltip
|
||||
assert "未识别药材" in panel.herb_table.item(1, 0).text()
|
||||
assert "未纳入对比" in panel.herb_table.item(1, 0).text()
|
||||
assert "原方 3 项" in panel.count_label.text() and "对比 1 项" in panel.count_label.text()
|
||||
panel.search.setText("北芪")
|
||||
assert panel.herb_table.rowCount() == 1
|
||||
assert "黄芪" in panel.herb_table.item(0, 0).text()
|
||||
|
||||
|
||||
def test_legacy_missing_trace_retains_full_original_separately_without_name_matching(panel: PrescriptionComparisonPanel) -> None:
|
||||
data = saved_batch([saved_row("黄芪")])
|
||||
model = data["models"]["qwen"]
|
||||
del model["comparison"]["rows"][0]["candidate"]["source_rows"]
|
||||
original = model["candidate"]["herbs"][0]
|
||||
model["candidate"]["herbs"] = [{**original, "dosage": 4}, {**original, "name": "炮制待核对药材", "dosage": 6}]
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.rowCount() == 3
|
||||
assert panel.herb_table.item(0, 2).text() == "15 克 / 每剂"
|
||||
for index, dose in ((1, "4 克 / 每剂"), (2, "6 克 / 每剂")):
|
||||
assert "原方附列" in panel.herb_table.item(index, 0).text()
|
||||
assert panel.herb_table.item(index, 1).text() == "—"
|
||||
assert panel.herb_table.item(index, 2).text() == dose
|
||||
assert panel.chart.rows[index].scale is None
|
||||
assert "对应关系未保存" in panel.status_label.text()
|
||||
assert "原方附列 2 项" in panel.count_label.text()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("trace", [[True], [1], [-1], ["0"], []])
|
||||
def test_invalid_source_trace_never_hides_an_original_herb(panel: PrescriptionComparisonPanel, trace: list[Any]) -> None:
|
||||
data = saved_batch([saved_row("黄芪")])
|
||||
data["models"]["qwen"]["comparison"]["rows"][0]["candidate"]["source_rows"] = trace
|
||||
panel.set_batch(data)
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
assert "原方附列" in panel.herb_table.item(1, 0).text()
|
||||
assert panel.chart.rows[1].scale is None
|
||||
|
||||
|
||||
def test_real_normalized_usage_keeps_all_special_instructions_visible_and_accessible(panel: PrescriptionComparisonPanel) -> None:
|
||||
row = saved_row("石膏")
|
||||
row["doctor"]["usage"] = {"decoction_instruction": "先煎 30 分钟", "special_usage": "布包煎", "usage_time": "饭后", "usage_way": "温服"}
|
||||
row["candidate"]["instructions"] = "后下 5 分钟"
|
||||
row["candidate"]["usage"] = {"instructions": "后下 5 分钟", "decoction_instruction": "另煎", "special_usage": "分次兑服", "usage_instruction": "服前摇匀", "usage_time": "睡前", "usage_way": "温服"}
|
||||
panel.set_batch(saved_batch([row]))
|
||||
name_item = panel.herb_table.item(0, 0)
|
||||
for instruction in ("先煎 30 分钟", "布包煎", "饭后", "温服", "后下 5 分钟", "另煎", "分次兑服", "服前摇匀", "睡前"):
|
||||
assert instruction in name_item.toolTip()
|
||||
assert instruction in name_item.data(Qt.ItemDataRole.AccessibleDescriptionRole)
|
||||
assert instruction in panel.chart.accessibleDescription()
|
||||
assert "先煎 30 分钟" in name_item.text() and "后下 5 分钟" in name_item.text()
|
||||
assert panel.chart.rows[0].candidate.instructions.count("后下 5 分钟") == 1
|
||||
assert panel.herb_table.rowHeight(0) >= panel.herb_table.fontMetrics().height() * 3 + 12
|
||||
|
||||
|
||||
def test_clicking_a_table_row_scrolls_to_its_chart_group(panel: PrescriptionComparisonPanel, application: QApplication) -> None:
|
||||
rows = [saved_row(f"药材{index}", unit="g" if index % 2 == 0 else "mg") for index in range(20)]
|
||||
panel.set_batch(saved_batch(rows))
|
||||
application.processEvents()
|
||||
item = panel.herb_table.item(11, 0)
|
||||
panel.herb_table.scrollToItem(item)
|
||||
application.processEvents()
|
||||
panel.chart_scroll.verticalScrollBar().setValue(0)
|
||||
QTest.mouseClick(panel.herb_table.viewport(), Qt.MouseButton.LeftButton, pos=panel.herb_table.visualItemRect(item).center())
|
||||
application.processEvents()
|
||||
assert panel.herb_table.currentRow() == 11
|
||||
# Ten gram rows precede the milligram group; the clicked row is its sixth member.
|
||||
expected_y = 8 + panel.chart.GROUP_HEIGHT * 2 + panel.chart.ROW_HEIGHT * 15
|
||||
assert panel.chart_scroll.verticalScrollBar().value() == expected_y
|
||||
|
||||
|
||||
@pytest.mark.parametrize("field", ["processing", "formula_type", "administration_route", "group"])
|
||||
def test_unrecognized_identity_values_do_not_become_equal_after_localization(panel: PrescriptionComparisonPanel, field: str) -> None:
|
||||
row = saved_row()
|
||||
row["doctor"][field] = "unknown_first"
|
||||
row["candidate"][field] = "unknown_second"
|
||||
panel.set_batch(saved_batch([row]))
|
||||
assert panel.chart.rows[0].scale is None
|
||||
assert "unknown_first" not in panel.chart.accessibleDescription()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("value", [None, "适量", -3, float("nan"), float("inf"), True])
|
||||
def test_invalid_or_missing_doses_never_produce_numeric_bars(panel: PrescriptionComparisonPanel, application: QApplication, value: Any) -> None:
|
||||
panel.set_batch(saved_batch([saved_row(doctor=value)]))
|
||||
assert panel.chart.rows[0].doctor.number is None
|
||||
assert panel.chart.groups[0][2] == 15
|
||||
application.processEvents()
|
||||
assert not panel.chart.grab().isNull()
|
||||
if value is None or value is True:
|
||||
assert panel.herb_table.item(0, 1).text() == "—"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("status", "expected"), [("running", "正在生成"), ("failed", "未完成"), ("future_state", "状态未确认")])
|
||||
def test_processing_failed_and_unknown_model_states_are_explicit(panel: PrescriptionComparisonPanel, status: str, expected: str) -> None:
|
||||
data = saved_batch()
|
||||
data["models"]["qwen"].update(status=status, candidate=None, comparison=None)
|
||||
panel.set_batch(data)
|
||||
assert expected in panel.status_label.text()
|
||||
assert panel.empty_label.isVisible()
|
||||
assert panel.herb_table.rowCount() == 0
|
||||
assert not panel.chart.bars_enabled
|
||||
|
||||
|
||||
@pytest.mark.parametrize("validity", ["stale", "source_updated", "future_validity"])
|
||||
def test_outdated_and_unknown_validity_only_show_saved_original_values(panel: PrescriptionComparisonPanel, validity: str) -> None:
|
||||
data = saved_batch()
|
||||
data["validity"] = validity
|
||||
panel.set_batch(data)
|
||||
assert not panel.chart.bars_enabled
|
||||
assert "历史原值" in panel.status_label.text()
|
||||
assert panel.herb_table.rowCount() == 2
|
||||
|
||||
|
||||
def test_refresh_preserves_search_model_selection_and_both_scroll_positions(panel: PrescriptionComparisonPanel, application: QApplication) -> None:
|
||||
data = saved_batch([saved_row(f"黄芪{index}") for index in range(40)])
|
||||
panel.set_batch(data)
|
||||
panel.model_buttons["openai"].click()
|
||||
panel.search.setText("黄芪")
|
||||
panel.herb_table.selectRow(10)
|
||||
application.processEvents()
|
||||
panel.herb_table.verticalScrollBar().setValue(180)
|
||||
panel.chart_scroll.verticalScrollBar().setValue(250)
|
||||
before = (panel.herb_table.verticalScrollBar().value(), panel.chart_scroll.verticalScrollBar().value())
|
||||
for _ in range(3):
|
||||
panel.set_batch(deepcopy(data))
|
||||
application.processEvents()
|
||||
assert panel.search.text() == "黄芪"
|
||||
assert panel.selected_model == "openai"
|
||||
assert panel.herb_table.currentRow() == 10
|
||||
assert before == (panel.herb_table.verticalScrollBar().value(), panel.chart_scroll.verticalScrollBar().value())
|
||||
changed = deepcopy(data)
|
||||
changed["models"]["qwen"]["progress"] = {"stage": "completed"}
|
||||
panel.set_batch(changed)
|
||||
application.processEvents()
|
||||
assert panel.search.text() == "黄芪" and panel.selected_model == "openai"
|
||||
assert before == (panel.herb_table.verticalScrollBar().value(), panel.chart_scroll.verticalScrollBar().value())
|
||||
panel.search.setText("黄芪39")
|
||||
assert panel.herb_table.rowCount() == 1
|
||||
assert len(panel.chart.rows) == 1
|
||||
panel.search.setText("未匹配")
|
||||
assert "没有匹配" in panel.empty_label.text()
|
||||
|
||||
|
||||
def test_parent_owned_controls_do_not_flash_windows_on_update(application: QApplication) -> None:
|
||||
shown_windows = []
|
||||
|
||||
class WindowObserver(QObject):
|
||||
def eventFilter(self, watched: QObject, event: QEvent) -> bool:
|
||||
if event.type() == QEvent.Type.Show and isinstance(watched, QWidget) and watched.isWindow():
|
||||
shown_windows.append(watched)
|
||||
return False
|
||||
|
||||
observer = WindowObserver()
|
||||
application.installEventFilter(observer)
|
||||
host = QWidget()
|
||||
try:
|
||||
layout = QVBoxLayout(host)
|
||||
widget = PrescriptionComparisonPanel(host)
|
||||
layout.addWidget(widget)
|
||||
widget.set_batch(saved_batch())
|
||||
host.show()
|
||||
application.processEvents()
|
||||
assert shown_windows == [host]
|
||||
shown_windows.clear()
|
||||
widget.set_batch(saved_batch([saved_row("当归")]))
|
||||
widget.model_buttons["openai"].click()
|
||||
widget.set_batch({})
|
||||
application.processEvents()
|
||||
assert shown_windows == []
|
||||
assert all(child.parentWidget() is not None for child in widget.findChildren(QWidget))
|
||||
assert widget.herb_table.rowCount() == 0
|
||||
assert widget.chart.rows == []
|
||||
finally:
|
||||
application.removeEventFilter(observer)
|
||||
host.close()
|
||||
host.deleteLater()
|
||||
application.processEvents()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("size", [(1120, 400), (940, 340)])
|
||||
def test_compact_sizes_keep_table_and_chart_scrollable(panel: PrescriptionComparisonPanel, application: QApplication, size: tuple[int, int]) -> None:
|
||||
panel.parentWidget().resize(*size)
|
||||
panel.set_batch(saved_batch([saved_row(f"药材{index}") for index in range(60)]))
|
||||
application.processEvents()
|
||||
assert panel.width() <= size[0] and panel.height() <= size[1]
|
||||
assert panel.herb_table.viewport().height() >= 65
|
||||
assert panel.herb_table.viewport().width() >= 320
|
||||
assert panel.chart_scroll.viewport().height() >= 90
|
||||
assert panel.herb_table.verticalScrollBar().maximum() > 0
|
||||
assert panel.chart_scroll.verticalScrollBar().maximum() > 0
|
||||
assert not panel.grab().isNull()
|
||||
assert panel.herb_table.item(0, 2).data(Qt.ItemDataRole.AccessibleTextRole) == "15 克 / 每剂"
|
||||
assert "药材59" in panel.chart.accessibleDescription()
|
||||
Reference in New Issue
Block a user