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.
This commit is contained in:
1 parent
83bcfd54d5
commit
fac4ccb760
146 files changed
+5689
-2033
No files matched your search
@@ -0,0 +1,93 @@
|
||||
"""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"]
|
||||
Reference in new issue
Block a user