feat(android): add state conditions and measured gesture feedback
Add optional ensure_state, gesture region and final value feedback shared by Flash and Pro. Preserve completed inputs when verification fails and stop repeated ineffective strategies. Regenerate pinned tool schemas for the new optional arguments.
This commit is contained in:
1 parent
6a69a595ef
commit
4ea7939d9c
20 files changed
+2779
-33
No files matched your search
@@ -71,6 +71,81 @@ async def run(name, args, native):
|
||||
return await execute_native(name, args, 400, 800, native)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"condition,fields",
|
||||
[
|
||||
({"state": "unchecked"}, {"checked": False}),
|
||||
({"state": "text", "expected_text": " exact "}, {"text": " exact "}),
|
||||
({"state": "content_desc", "expected_text": "播放"}, {"content_desc": "播放"}),
|
||||
({"state": "value", "expected_value": 3.0}, {}),
|
||||
],
|
||||
)
|
||||
async def test_ensure_state_already_satisfied_never_toggles(condition, fields):
|
||||
native = AsyncMock(return_value=observed(**fields))
|
||||
result = await run(
|
||||
"control_action",
|
||||
{"selector": SELECTOR, "action": "click", "ensure_state": condition},
|
||||
native,
|
||||
)
|
||||
assert result["success"] and result["skipped"]
|
||||
assert result["execution"] == "observed"
|
||||
assert [call.args[0] for call in native.await_args_list] == ["inspect_control"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ensure_state_missing_native_boolean_refuses_input():
|
||||
native = AsyncMock(return_value=observed(checked=None))
|
||||
result = await run(
|
||||
"control_action",
|
||||
{"selector": SELECTOR, "action": "click", "ensure_state": {"state": "unchecked"}},
|
||||
native,
|
||||
)
|
||||
assert not result["success"] and result["execution"] == "not_started"
|
||||
native.assert_awaited_once()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ensure_state_writes_once_then_reads_actual_state():
|
||||
native = AsyncMock(side_effect=[observed(checked=True), completed(), observed(checked=False)])
|
||||
result = await run(
|
||||
"control_action",
|
||||
{"selector": SELECTOR, "action": "click", "ensure_state": {"state": "unchecked"}},
|
||||
native,
|
||||
)
|
||||
assert result["success"] and result["verification"] == "condition_satisfied"
|
||||
assert [call.args[0] for call in native.await_args_list] == [
|
||||
"inspect_control",
|
||||
"control_action",
|
||||
"inspect_control",
|
||||
]
|
||||
args = native.await_args_list[1].args[1]
|
||||
assert "ensure_state" not in args and "verify_timeout_ms" not in args
|
||||
assert args["expected_token"] == TOKEN
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_failed_postcondition_preserves_completed_input_without_retoggling(clock):
|
||||
async def native(command, args, **kwargs):
|
||||
return completed() if command == "control_action" else observed(checked=True)
|
||||
|
||||
native = AsyncMock(side_effect=native)
|
||||
result = await run(
|
||||
"control_action",
|
||||
{
|
||||
"selector": SELECTOR,
|
||||
"action": "click",
|
||||
"ensure_state": {"state": "unchecked"},
|
||||
"verify_timeout_ms": 400,
|
||||
},
|
||||
native,
|
||||
)
|
||||
assert not result["success"] and result["code"] == "TIMEOUT"
|
||||
assert result["execution"] == "completed"
|
||||
assert result["native_receipt"]["success"]
|
||||
assert sum(c.args[0] == "control_action" for c in native.await_args_list) == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("name", ["pinch", "touch_gesture"])
|
||||
async def test_all_gesture_geometry_is_validated_before_a_single_native_dispatch(name):
|
||||
|
||||
Reference in new issue
Block a user