All checks were successful
技能自动化发布 / release (push) Successful in 15s
基于 skill-template 脚手架开发,包含: - 5 张业务表(教职工、请假政策、考勤日历、请假记录、假期余额) - 请假核算引擎(扣薪计算、审批层级判定、年假抵扣) - CLI 入口(import-employees / calculate-leave / render-html) - HTML 审批与扣薪报告生成 - 示例数据与完整测试套件(295 tests passed)
100 lines
3.9 KiB
Python
100 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "scripts"))
|
|
|
|
from service.desktop_session import ( # noqa: E402
|
|
DesktopRpaError,
|
|
WindowCandidate,
|
|
activate_window,
|
|
resolve_unique_window,
|
|
)
|
|
from service import uia_locator # noqa: E402
|
|
from util.selectors import ElementSpec, MAIN_WINDOW # noqa: E402
|
|
|
|
|
|
class SelectorContractTest(unittest.TestCase):
|
|
def test_unique_window_is_required(self) -> None:
|
|
candidate = WindowCandidate(1, 10, "WXWork", "WeWorkWindow", "企业微信", True)
|
|
self.assertEqual(
|
|
resolve_unique_window(MAIN_WINDOW, candidates=[candidate]),
|
|
candidate,
|
|
)
|
|
|
|
def test_duplicate_windows_fail_instead_of_taking_first(self) -> None:
|
|
candidates = [
|
|
WindowCandidate(1, 10, "WXWork", "WeWorkWindow", "企业微信", True),
|
|
WindowCandidate(2, 11, "WXWork", "WeWorkWindow", "企业微信", True),
|
|
]
|
|
with self.assertRaises(DesktopRpaError) as ctx:
|
|
resolve_unique_window(MAIN_WINDOW, candidates=candidates)
|
|
self.assertEqual(ctx.exception.code, "ELEMENT_AMBIGUOUS")
|
|
|
|
def test_hidden_window_is_not_a_candidate(self) -> None:
|
|
hidden = WindowCandidate(1, 10, "WXWork", "WeWorkWindow", "企业微信", False)
|
|
with self.assertRaises(DesktopRpaError) as ctx:
|
|
resolve_unique_window(MAIN_WINDOW, candidates=[hidden])
|
|
self.assertEqual(ctx.exception.code, "WINDOW_NOT_FOUND")
|
|
|
|
def test_activate_without_maximize_skips_restore_when_zoomed(self) -> None:
|
|
window = WindowCandidate(42, 10, "WXWork", "WeWorkWindow", "企业微信", True)
|
|
with patch("service.desktop_session.user32") as user32:
|
|
user32.IsIconic.return_value = False
|
|
user32.IsZoomed.return_value = True
|
|
user32.SetForegroundWindow.return_value = True
|
|
activate_window(window, maximize=False)
|
|
user32.ShowWindow.assert_not_called()
|
|
user32.SetForegroundWindow.assert_called_once_with(42)
|
|
|
|
def test_activate_maximize_when_not_zoomed(self) -> None:
|
|
window = WindowCandidate(42, 10, "WXWork", "WeWorkWindow", "企业微信", True)
|
|
with patch("service.desktop_session.user32") as user32:
|
|
user32.IsIconic.return_value = False
|
|
user32.IsZoomed.return_value = False
|
|
user32.SetForegroundWindow.return_value = True
|
|
activate_window(window, maximize=True)
|
|
user32.ShowWindow.assert_called()
|
|
user32.SetForegroundWindow.assert_called_once_with(42)
|
|
|
|
def test_locate_unique_matches_child_value(self) -> None:
|
|
row = MagicMock()
|
|
row.Name = "status_layout"
|
|
row.AutomationId = ""
|
|
row.ControlTypeName = "TreeControl"
|
|
row.ClassName = ""
|
|
label = MagicMock()
|
|
label.Name = "main_chattext"
|
|
label.GetChildren.return_value = []
|
|
row.GetChildren.return_value = [label]
|
|
|
|
root = MagicMock()
|
|
root.Name = "wlist_view"
|
|
root.AutomationId = ""
|
|
root.ControlTypeName = "TreeControl"
|
|
root.ClassName = ""
|
|
root.GetChildren.return_value = [row]
|
|
|
|
with patch.object(uia_locator, "control_value", side_effect=lambda c: "新的客户" if c is label else ""):
|
|
spec = ElementSpec(
|
|
"new_customer_row",
|
|
name="status_layout",
|
|
ancestor_name="wlist_view",
|
|
child_value_name="main_chattext",
|
|
value="新的客户",
|
|
)
|
|
# ancestor is root itself when searching from root with name wlist_view
|
|
found = uia_locator.locate_unique(root, ElementSpec("wlist", name="wlist_view"))
|
|
self.assertIs(found, root)
|
|
found_row = uia_locator.locate_unique(root, spec)
|
|
self.assertIs(found_row, row)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|