Files
Artemis-Eapil/tests/unit/test_input_state.py
T
chenmingxuan fac4ccb760 feat: prepare 2.2.0 with UI and execution fixes
Add dark theme, richer Markdown and readable plugin results.
Improve Android input evidence, response integrity and diagnostics.
Unify lifecycle, guidance receipts and evidence sources across clients.
Keep direct action dispatch without an added AI quota approval layer.
Align version metadata at 2.2.0 and bundle Helper 1.2.2.

Validation: 3063 backend tests before the version bump; 44 after.
Frontend: 317 tests and production build passed.
2026-10-26 00:44:13 +08:00

94 lines
3.5 KiB
Python

"""A hint, OCR label and real editable value are different kinds of proof."""
from types import SimpleNamespace
import pytest
from artemis.drivers.android.hierarchy import parse_ui_hierarchy
from artemis.utils.action_target import observation_anchor
from artemis.utils.input_state import input_state, verify_input
from artemis.utils.ui_filter import filter_ui_hierarchy
from artemis.utils.visualization import format_minimal_list_with_elements
@pytest.mark.parametrize(
("extra", "status"),
[
({"text": "example", "hint": "example", "showing-hint-text": "true"}, "empty"),
({"text": "example", "hint": "example"}, "unknown"),
({"text": "example", "hint": "example", "showing-hint-text": "false"}, "present"),
({"text": "", "hint": "example"}, "empty"),
({"hint": "example"}, "unknown"),
({"text": "actual"}, "present"),
],
)
def test_editable_value_evidence(extra, status):
evidence = input_state({"class": "android.widget.EditText", **extra})
assert evidence["input_value_state"] == status
if status != "present":
assert evidence["input_value"] in (None, "")
def test_static_json_and_ocr_cannot_prove_input():
static = {"class": "android.widget.TextView", "text": '{"pairing":"example"}'}
assert input_state(static) == {}
node = {
"class": "android.widget.EditText",
"text": "",
"hint": "example",
"bounds": "[0,0][100,100]",
"ocr_elements": [{"text": "example", "bounds": [0, 0, 100, 100]}],
}
lines, indexed, _ = format_minimal_list_with_elements([node], 100, 100)
assert len(indexed) == 1
assert indexed[0]["input_value_state"] == "empty"
assert "Hint:" in lines
def test_filter_keeps_empty_input_and_does_not_promote_child_hint():
node = {
"class": "android.widget.EditText",
"editable": True,
"text": "",
"bounds": "[0,0][100,100]",
"children": [
{"class": "android.widget.TextView", "text": "example", "bounds": "[0,0][100,100]"}
],
}
filtered = filter_ui_hierarchy([node])
assert filtered[0]["text"] == ""
assert input_state(filtered[0])["input_value_state"] == "empty"
def test_fresh_value_must_belong_to_the_actual_input():
node = {
"class": "android.widget.EditText",
"resource-id": "pair",
"text": "actual",
"focused": True,
}
assert verify_input([node], {"target_resource_id": "pair"}) == "present"
assert verify_input([node], {"target_resource_id": "other"}) == "unknown"
assert verify_input([{**node, "password": True}]) == "unknown"
def test_driver_parser_keeps_showing_hint_and_empty_editable_nodes():
nodes = parse_ui_hierarchy(
'<hierarchy><node class="android.widget.EditText" bounds="[0,0][100,100]" editable="true" text="example" hint="example" showing-hint-text="true" focused="true"/></hierarchy>'
)
assert len(nodes) == 1
assert nodes[0]["input_value"] == ""
assert nodes[0]["input_value_state"] == "empty"
assert nodes[0]["focused"] is True
def test_past_anchor_does_not_change_when_index_is_reused():
state = SimpleNamespace(
latest_screenshot="before.png", indexed_elements=[{"text": "Connect", "center": [1, 2]}]
)
old = observation_anchor(state)
state.latest_screenshot = "after.png"
state.indexed_elements = [{"text": "Keyboard key", "center": [3, 4]}]
assert old["target_image_name"] == "before"
assert observation_anchor(state)["target_snapshot_id"] != old["target_snapshot_id"]