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.
94 lines
3.5 KiB
Python
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"]
|