summaryrefslogtreecommitdiff
path: root/tools/tests/test_ui_flow.py
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-19 22:28:09 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-19 22:28:09 +0200
commit2373b6f02ef027a423762d5d6349cd9e0f36090e (patch)
tree7509b0b1905d7c593074bba8ed4c94e30e437604 /tools/tests/test_ui_flow.py
parent35042ff04ffc50e55ff59a385849442be4429d6b (diff)
test(ui): drive the simulator with a scripted flow and check every screen
tools/ui_flow.py clicks buttons and taps from the device files, waits for DIAG shown lines and compares 10 screens per watch with goldens on four test and three spot-check devices. Adds the fixture-wrap generator, UI logic tests and driver tests; the launcher opens apps from their glance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/tests/test_ui_flow.py')
-rw-r--r--tools/tests/test_ui_flow.py73
1 files changed, 73 insertions, 0 deletions
diff --git a/tools/tests/test_ui_flow.py b/tools/tests/test_ui_flow.py
new file mode 100644
index 0000000..7e0d336
--- /dev/null
+++ b/tools/tests/test_ui_flow.py
@@ -0,0 +1,73 @@
+# Author: cirakg & claude ai
+# Summary: Tests for the UI flow driver's pure parts: device key layout, JSON lookup and the app-log reader.
+from __future__ import annotations
+
+import json
+import threading
+import time
+from pathlib import Path
+
+import pytest
+import ui_flow
+
+
+def write_sim(tmp_path: Path, config: dict[str, object]) -> Path:
+ (tmp_path / "simulator.json").write_text(json.dumps(config), encoding="utf-8")
+ return tmp_path
+
+
+def test_load_keys_gives_button_centres(tmp_path: Path) -> None:
+ device = write_sim(
+ tmp_path,
+ {
+ "keys": [
+ {"id": "enter", "behavior": "onSelect", "location": {"x": 10, "y": 20, "width": 30, "height": 40}},
+ {
+ "id": "menu",
+ "behavior": "onMenu",
+ "isHold": True,
+ "location": {"x": 0, "y": 0, "width": 2, "height": 2},
+ },
+ {"id": "start", "location": {"x": 5, "y": 5, "width": 10, "height": 10}},
+ ]
+ },
+ )
+ keys = ui_flow.load_keys(device)
+ assert keys["enter"] == ui_flow.Key(25, 40, "onSelect", False)
+ assert keys["menu"].hold
+ assert keys["start"].behavior == ""
+
+
+def test_find_key_searches_nested_structures() -> None:
+ data = {"display": {"isTouch": True}, "glance": [{"contentArea": {"x": 1}}]}
+ assert ui_flow.find_key(data, "isTouch") is True
+ assert ui_flow.find_key(data, "contentArea") == {"x": 1}
+ assert ui_flow.find_key(data, "missing") is None
+
+
+def test_app_log_waits_only_for_lines_after_the_mark(tmp_path: Path) -> None:
+ log_path = tmp_path / "app.log"
+ log_path.write_text("DIAG shown list\n", encoding="utf-8")
+ log = ui_flow.AppLog(log_path)
+ assert log.wait_for(r"DIAG shown list\s*$", 1).group(0).startswith("DIAG shown list")
+ log.set_mark()
+
+ def append_later() -> None:
+ time.sleep(0.2)
+ with log_path.open("a", encoding="utf-8") as f:
+ f.write("DIAG shown otp\n")
+
+ writer = threading.Thread(target=append_later)
+ writer.start()
+ assert log.wait_for(r"DIAG shown otp", 3)
+ writer.join()
+ with pytest.raises(ui_flow.FlowError):
+ log.wait_for(r"DIAG shown list", 0.3) # only before the mark
+
+
+def test_app_log_latest_returns_the_last_match(tmp_path: Path) -> None:
+ log_path = tmp_path / "app.log"
+ log_path.write_text("DIAG list_focus y=10\nother\nDIAG list_focus y=42\n", encoding="utf-8")
+ match = ui_flow.AppLog(log_path).latest(r"DIAG list_focus y=(\d+)")
+ assert match is not None and match.group(1) == "42"
+ assert ui_flow.AppLog(tmp_path / "missing.log").latest("x") is None