chore: archive tools/ui_inspector snapshot before migration removal.
Keep the last in-template copy on main so history can restore it; product continues in jiangchang-ui-recorder. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Smoke test for flow document API + step labeling + kit_wake helpers (v0.8.0)."""
|
||||
"""Smoke test for flow document API + surface routing + kit helpers (v0.10)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -11,7 +11,30 @@ if str(_HERE) not in sys.path:
|
||||
sys.path.insert(0, str(_HERE))
|
||||
|
||||
import artifacts as art
|
||||
import desktop_uia as uia
|
||||
import kit_browser
|
||||
import kit_wake
|
||||
import skip_policy
|
||||
import target
|
||||
import user_prefs
|
||||
from surfaces.registry import get_driver
|
||||
from drivers.mobile import MobileDriver
|
||||
from jiangchang_skill_core.rpa.page_dump import build_page_dump_result
|
||||
|
||||
|
||||
def _assert_selection_api() -> None:
|
||||
assert callable(uia.window_info_under_cursor)
|
||||
assert callable(uia.find_wecom_search_result_window)
|
||||
assert callable(uia.resolve_capture_target)
|
||||
try:
|
||||
_info, _cands, meta = uia.resolve_capture_target(
|
||||
under_cursor=False,
|
||||
prefer_wecom_search_popup=False,
|
||||
)
|
||||
assert "strategy" in meta
|
||||
except RuntimeError as exc:
|
||||
assert "未找到可拾取" in str(exc)
|
||||
print("OK selection api")
|
||||
|
||||
|
||||
def _assert_kit_wake_detect() -> None:
|
||||
@@ -23,6 +46,204 @@ def _assert_kit_wake_detect() -> None:
|
||||
print("OK kit_wake detect")
|
||||
|
||||
|
||||
def _assert_surface_routing() -> None:
|
||||
assert (
|
||||
target.resolve_surface(process_name="chrome.exe", class_name="Chrome_WidgetWin_1")
|
||||
== target.SURFACE_BROWSER
|
||||
)
|
||||
assert (
|
||||
target.resolve_surface(process_name="msedge.exe", class_name="Chrome_WidgetWin_1")
|
||||
== target.SURFACE_BROWSER
|
||||
)
|
||||
assert (
|
||||
target.resolve_surface(process_name="WXWork.exe", class_name="WeWorkWindow")
|
||||
== target.SURFACE_WINDOWS
|
||||
)
|
||||
assert get_driver(target.SURFACE_WINDOWS).driver_name == "uiautomation"
|
||||
assert get_driver(target.SURFACE_BROWSER).driver_name == "playwright"
|
||||
mobile = MobileDriver(surface=target.SURFACE_ANDROID)
|
||||
try:
|
||||
mobile.dump(info=None, selection_meta={})
|
||||
raise AssertionError("mobile dump should raise")
|
||||
except RuntimeError as exc:
|
||||
assert "SURFACE_UNSUPPORTED" in str(exc)
|
||||
print("OK surface routing")
|
||||
|
||||
|
||||
def _assert_user_prefs() -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="ui_prefs_") as tmp:
|
||||
path = Path(tmp) / "user_prefs.json"
|
||||
# monkeypatch prefs path
|
||||
old = user_prefs._PREFS_PATH
|
||||
try:
|
||||
user_prefs._PREFS_PATH = path
|
||||
user_prefs.save_prefs(mode="windows", start_url="https://example.com")
|
||||
loaded = user_prefs.load_prefs()
|
||||
assert loaded["mode"] == "windows"
|
||||
assert loaded["start_url"] == "https://example.com"
|
||||
user_prefs.save_prefs(mode="browser", start_url="")
|
||||
loaded_empty = user_prefs.load_prefs()
|
||||
assert loaded_empty["start_url"] == ""
|
||||
user_prefs.save_prefs(mode="mobile", start_url="https://a.test")
|
||||
loaded2 = user_prefs.load_prefs()
|
||||
assert loaded2["mode"] == target.SURFACE_ANDROID
|
||||
finally:
|
||||
user_prefs._PREFS_PATH = old
|
||||
print("OK user_prefs")
|
||||
|
||||
|
||||
def _assert_skip_policy() -> None:
|
||||
# blank + empty locatable → skip
|
||||
assert (
|
||||
skip_policy.should_skip_empty_dump(
|
||||
surface="browser",
|
||||
url="about:blank",
|
||||
locatable_count=0,
|
||||
nodes_kept=1,
|
||||
sparse=True,
|
||||
)
|
||||
== "blank_or_newtab_empty"
|
||||
)
|
||||
# blank URL but rich content (nav race like step05) → keep
|
||||
assert (
|
||||
skip_policy.should_skip_empty_dump(
|
||||
surface="browser",
|
||||
url="about:blank",
|
||||
locatable_count=74,
|
||||
nodes_kept=97,
|
||||
sparse=False,
|
||||
)
|
||||
is None
|
||||
)
|
||||
# real page → keep
|
||||
assert (
|
||||
skip_policy.should_skip_empty_dump(
|
||||
surface="browser",
|
||||
url="https://www.douyin.com/jingxuan",
|
||||
locatable_count=150,
|
||||
nodes_kept=160,
|
||||
sparse=False,
|
||||
)
|
||||
is None
|
||||
)
|
||||
# windows never skipped by this policy
|
||||
assert (
|
||||
skip_policy.should_skip_empty_dump(
|
||||
surface="windows",
|
||||
url="",
|
||||
locatable_count=0,
|
||||
nodes_kept=1,
|
||||
sparse=True,
|
||||
)
|
||||
is None
|
||||
)
|
||||
# same URL with content is NOT skipped (scroll-safe)
|
||||
assert (
|
||||
skip_policy.should_skip_empty_dump(
|
||||
surface="browser",
|
||||
url="https://www.douyin.com/jingxuan",
|
||||
locatable_count=20,
|
||||
nodes_kept=30,
|
||||
sparse=False,
|
||||
)
|
||||
is None
|
||||
)
|
||||
print("OK skip_policy")
|
||||
|
||||
|
||||
def _assert_active_page_resolve() -> None:
|
||||
"""Unit-level: visible tab wins over sticky pages[0]."""
|
||||
|
||||
class _FakePage:
|
||||
def __init__(self, url: str, visible: bool, *, closed: bool = False) -> None:
|
||||
self.url = url
|
||||
self._visible = visible
|
||||
self._closed = closed
|
||||
|
||||
def is_closed(self) -> bool:
|
||||
return self._closed
|
||||
|
||||
def evaluate(self, _script: str) -> str:
|
||||
return "visible" if self._visible else "hidden"
|
||||
|
||||
def title(self) -> str:
|
||||
return self.url
|
||||
|
||||
class _FakeCtx:
|
||||
def __init__(self, pages: list) -> None:
|
||||
self.pages = pages
|
||||
|
||||
search = _FakePage("https://www.douyin.com/search/foo", visible=False)
|
||||
detail = _FakePage("https://www.douyin.com/video/123", visible=True)
|
||||
ctx = _FakeCtx([search, detail])
|
||||
|
||||
page, meta = kit_browser.resolve_active_page(ctx, preferred=search)
|
||||
assert page is detail, (page.url, meta)
|
||||
assert meta["resolve"] == "visibility"
|
||||
assert meta["page_count"] == 2
|
||||
assert meta["visible_count"] == 1
|
||||
|
||||
# No visible → preferred if still open
|
||||
both_hidden = _FakeCtx(
|
||||
[
|
||||
_FakePage("https://a.test/1", visible=False),
|
||||
_FakePage("https://a.test/2", visible=False),
|
||||
]
|
||||
)
|
||||
preferred = both_hidden.pages[0]
|
||||
page2, meta2 = kit_browser.resolve_active_page(both_hidden, preferred=preferred)
|
||||
assert page2 is preferred
|
||||
assert meta2["resolve"] == "preferred_fallback"
|
||||
|
||||
# Closed preferred → first open
|
||||
closed_pref = _FakePage("https://gone", visible=False, closed=True)
|
||||
open_only = _FakePage("https://alive", visible=False)
|
||||
page3, meta3 = kit_browser.resolve_active_page(
|
||||
_FakeCtx([closed_pref, open_only]), preferred=closed_pref
|
||||
)
|
||||
assert page3 is open_only
|
||||
assert meta3["resolve"] == "first_open"
|
||||
print("OK active page resolve")
|
||||
|
||||
|
||||
def _assert_page_dump_kit() -> None:
|
||||
if not kit_browser.kit_page_dump_available():
|
||||
raise RuntimeError(
|
||||
"jiangchang_skill_core.rpa.page_dump 不可用;"
|
||||
"请 pip install -e jiangchang-platform-kit>=1.2.11"
|
||||
)
|
||||
result = build_page_dump_result(
|
||||
url="https://example.com",
|
||||
title="Example",
|
||||
a11y_snapshot={
|
||||
"role": "RootWebArea",
|
||||
"name": "Example",
|
||||
"children": [{"role": "button", "name": "OK", "children": []}],
|
||||
},
|
||||
dom_nodes=[
|
||||
{
|
||||
"tag": "button",
|
||||
"role": "button",
|
||||
"id": "",
|
||||
"name_attr": "",
|
||||
"testid": "",
|
||||
"aria_label": "OK",
|
||||
"placeholder": "",
|
||||
"title": "",
|
||||
"alt": "",
|
||||
"href": "",
|
||||
"type": "submit",
|
||||
"text": "OK",
|
||||
"disabled": False,
|
||||
"rect": {},
|
||||
}
|
||||
],
|
||||
)
|
||||
assert result["stats"]["locatable_count"] >= 1
|
||||
assert result["locatable"][0].get("primary_locator")
|
||||
print("OK page_dump kit builder")
|
||||
|
||||
|
||||
def _assert_label() -> None:
|
||||
msg_page = [
|
||||
{"control_type": "TreeItemControl", "name": "groupbuddyname", "value": "某群"},
|
||||
@@ -83,7 +304,13 @@ def _assert_label() -> None:
|
||||
|
||||
|
||||
def main() -> int:
|
||||
_assert_selection_api()
|
||||
_assert_kit_wake_detect()
|
||||
_assert_surface_routing()
|
||||
_assert_user_prefs()
|
||||
_assert_skip_policy()
|
||||
_assert_active_page_resolve()
|
||||
_assert_page_dump_kit()
|
||||
_assert_label()
|
||||
with tempfile.TemporaryDirectory(prefix="ui_flow_smoke_") as tmp:
|
||||
root = Path(tmp)
|
||||
@@ -111,7 +338,15 @@ def main() -> int:
|
||||
)
|
||||
assert idx == 1
|
||||
assert "陈德炼" in name, name
|
||||
art.write_json(step_dir / "meta.json", {"ok": True})
|
||||
art.write_json(
|
||||
step_dir / "meta.json",
|
||||
art.build_meta(
|
||||
window={"title": "企业微信", "class_name": "WeWorkWindow", "pid": 1, "hwnd": 2},
|
||||
scene="demo",
|
||||
surface="windows",
|
||||
driver="uiautomation",
|
||||
),
|
||||
)
|
||||
art.register_step(
|
||||
flow_dir,
|
||||
flow,
|
||||
@@ -120,9 +355,25 @@ def main() -> int:
|
||||
window={"title": "企业微信", "class_name": "WeWorkWindow", "pid": 1, "hwnd": 2},
|
||||
stats={"findall_count": 10, "locatable_count": 5, "nodes_kept": 3, "uia_sparse": False},
|
||||
label=label,
|
||||
surface="windows",
|
||||
)
|
||||
# browser-shaped step registration
|
||||
blow = art.load_flow(flow_dir)
|
||||
art.register_step(
|
||||
flow_dir,
|
||||
blow,
|
||||
step_index=2,
|
||||
step_name="02_browser-demo",
|
||||
window={"title": "精选", "class_name": "PlaywrightPage", "pid": 3, "hwnd": 4},
|
||||
stats={"findall_count": 20, "locatable_count": 12, "nodes_kept": 20, "uia_sparse": False},
|
||||
label="浏览器精选",
|
||||
surface="browser",
|
||||
url="https://www.douyin.com/jingxuan",
|
||||
)
|
||||
flow = art.end_flow(flow_dir, art.load_flow(flow_dir))
|
||||
assert flow["status"] == "ended"
|
||||
summary = (flow_dir / "SUMMARY.md").read_text(encoding="utf-8")
|
||||
assert "surface: `browser`" in summary or "surface: `windows`" in summary
|
||||
print(f"OK flow={flow_dir.name} step={name}")
|
||||
print("OK ui_inspector flow smoke passed")
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user