From 2373b6f02ef027a423762d5d6349cd9e0f36090e Mon Sep 17 00:00:00 2001 From: cirakg Date: Mon, 19 Oct 2026 22:28:09 +0200 Subject: 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 --- tools/tests/test_ui_flow.py | 73 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tools/tests/test_ui_flow.py (limited to 'tools/tests/test_ui_flow.py') 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 -- cgit v1.2.3