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>
121 lines
3.6 KiB
Python
121 lines
3.6 KiB
Python
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
_HERE = Path(__file__).resolve().parents[2]
|
|
if str(_HERE) not in sys.path:
|
|
sys.path.insert(0, str(_HERE))
|
|
|
|
from app import (
|
|
_DEFAULT_FLOW_NAME_SLUGS,
|
|
_default_window_geometry,
|
|
_flow_name_for_mode,
|
|
_format_geometry,
|
|
_format_live_action,
|
|
_merge_current_live_action,
|
|
)
|
|
import artifacts as art
|
|
import target
|
|
import user_prefs
|
|
|
|
|
|
def test_default_geometry_is_narrow_tall_and_leaves_title_bar_clear() -> None:
|
|
width, height, x, y = _default_window_geometry((0, 0, 1920, 1040))
|
|
|
|
assert (width, height) == (576, 951)
|
|
assert (x, y) == (1328, 73)
|
|
assert y >= 64
|
|
assert x + width == 1920 - 16
|
|
|
|
|
|
def test_default_geometry_supports_small_and_left_side_monitors() -> None:
|
|
width, height, x, y = _default_window_geometry(
|
|
(-1366, 0, 0, 728), side="left"
|
|
)
|
|
|
|
assert (width, height) == (560, 648)
|
|
assert (x, y) == (-1350, 64)
|
|
assert _format_geometry(width, height, x, y) == "560x648-1350+64"
|
|
|
|
|
|
def test_flow_name_defaults_follow_pick_mode() -> None:
|
|
assert _flow_name_for_mode(target.SURFACE_BROWSER) == "browser"
|
|
assert _flow_name_for_mode(target.SURFACE_WINDOWS) == "desktop"
|
|
assert _flow_name_for_mode(target.SURFACE_ANDROID) == "mobile"
|
|
assert _DEFAULT_FLOW_NAME_SLUGS == frozenset({"browser", "desktop", "mobile"})
|
|
|
|
|
|
def test_local_ts_is_compact_yyyymmddhhmmss() -> None:
|
|
stamp = art._local_ts()
|
|
assert len(stamp) == 14
|
|
assert stamp.isdigit()
|
|
assert "T" not in stamp
|
|
assert "+" not in stamp
|
|
|
|
|
|
def test_create_flow_dir_uses_compact_timestamp(tmp_path: Path) -> None:
|
|
flow_dir = art.create_flow(tmp_path, flow_name="browser")
|
|
name = flow_dir.name
|
|
assert name.endswith("_browser")
|
|
stamp = name[: -len("_browser")]
|
|
assert len(stamp) == 14
|
|
assert stamp.isdigit()
|
|
|
|
|
|
def test_hidden_legacy_recording_preference_migrates_to_auto(
|
|
tmp_path: Path, monkeypatch
|
|
) -> None:
|
|
prefs_path = tmp_path / "user_prefs.json"
|
|
prefs_path.write_text(
|
|
json.dumps(
|
|
{
|
|
"mode": "browser",
|
|
"start_url": "https://example.com",
|
|
"recording_mode": "legacy_interval",
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setattr(user_prefs, "_PREFS_PATH", prefs_path)
|
|
|
|
assert user_prefs.load_prefs()["recording_mode"] == "auto"
|
|
|
|
|
|
def test_live_action_formatter_is_surface_neutral_and_two_phase() -> None:
|
|
text = _format_live_action(
|
|
{
|
|
"sequence": 3,
|
|
"action_type": "click",
|
|
"phase": "captured",
|
|
"surface": "browser",
|
|
"target_name": "提交",
|
|
"target_type": "button",
|
|
}
|
|
)
|
|
assert "#3 点击" in text
|
|
assert "browser" in text
|
|
assert "证据采集中" in text
|
|
assert "等待异步证据" in text
|
|
|
|
|
|
def test_current_live_action_overwrites_without_allowing_late_evidence_to_regress() -> None:
|
|
first = {"id": "action-1", "sequence": 1, "phase": "captured"}
|
|
first_evidence = {"id": "action-1", "sequence": 1, "phase": "evidence"}
|
|
second = {"id": "action-2", "sequence": 2, "phase": "captured"}
|
|
|
|
current = _merge_current_live_action(None, first)
|
|
current = _merge_current_live_action(current, first_evidence)
|
|
assert current == first_evidence
|
|
|
|
current = _merge_current_live_action(current, second)
|
|
assert current == second
|
|
|
|
current = _merge_current_live_action(current, first_evidence)
|
|
assert current == second
|
|
|
|
|
|
def test_current_live_action_ignores_updates_without_an_action_id() -> None:
|
|
current = {"id": "action-2", "sequence": 2, "phase": "captured"}
|
|
|
|
assert _merge_current_live_action(current, {"sequence": 3}) is current
|