Files
Artemis-Eapil/tests/unit/test_input_clearing.py
T
chenmingxuan be6e8c2abd feat: release 2.2.0 with PC-only tests and execution reliability fixes
Unify task scheduling and queue state, fix Android input and observation recovery, preserve screenshot paths, and match final review criteria accurately. Add collapsible copyable results and publish the complete 2.1.0 upgrade notes. Validation: 3189 backend tests, 326 frontend tests, 24 client tests, production build and quality gates.
2026-10-27 01:13:41 +08:00

100 lines
3.5 KiB
Python

"""Whole-field clearing must not depend on text length or keyboard shortcuts."""
from unittest.mock import Mock
import pytest
from artemis.controllers.unified_controller import UnifiedMobileController
from artemis.drivers.android.adb_driver import AndroidAdbDriver
from artemis.drivers.android.input_ime import AndroidInputIME
@pytest.mark.asyncio
@pytest.mark.parametrize(
"value",
["ARTEMIS_PRO_R3_20261026", "长文本🙂\n" * 80, ""],
ids=["23-chars", "long-unicode-multiline", "empty"],
)
async def test_full_clear_replaces_the_whole_field(value):
field = {"text": value}
device, ui = Mock(), Mock()
def shell(command, **kwargs):
# A field that ignores Ctrl+A: the previous implementation leaves AR.
if "67" in command:
field["text"] = field["text"][: -command.count("67")]
return ""
def clear():
field["text"] = ""
return True
device.shell.side_effect = shell
ui.clear_text.side_effect = clear
driver = AndroidAdbDriver("test-device", Mock(), ui_adb_client=ui)
driver._device = device
controller = object.__new__(UnifiedMobileController)
controller._driver = driver
assert await controller.erase_text()
assert field["text"] == ""
ui.clear_text.assert_called_once_with()
device.shell.assert_not_called()
ui.set_clipboard.assert_not_called()
@pytest.mark.asyncio
@pytest.mark.parametrize("implementation", ["driver", "ime"])
@pytest.mark.parametrize("failure", [False, RuntimeError("write reply lost")])
async def test_failed_clear_never_types_or_retries(implementation, failure):
device, ui = Mock(), Mock()
if isinstance(failure, Exception):
ui.clear_text.side_effect = failure
else:
ui.clear_text.return_value = failure
if implementation == "driver":
driver = AndroidAdbDriver("test-device", Mock(), ui_adb_client=ui)
driver._device = device
result = await driver.input_text("replacement", clear_existing=True)
else:
result = await AndroidInputIME(device, ui).type_text("replacement", clear_existing=True)
assert result is False
ui.clear_text.assert_called_once_with()
device.shell.assert_not_called()
ui.set_clipboard.assert_not_called()
ui.send_text.assert_not_called()
@pytest.mark.asyncio
async def test_missing_clear_backend_is_not_reported_as_success():
driver = AndroidAdbDriver("test-device", Mock())
driver._device = Mock()
assert not await driver.input_text("replacement", clear_existing=True)
driver._device.shell.assert_not_called()
@pytest.mark.asyncio
@pytest.mark.parametrize("implementation", ["driver", "ime"])
async def test_empty_append_does_not_paste_or_send_keys(implementation):
device, ui = Mock(), Mock()
if implementation == "driver":
driver = AndroidAdbDriver("test-device", Mock(), ui_adb_client=ui)
driver._device = device
result = await driver.input_text("", clear_existing=False)
else:
result = await AndroidInputIME(device, ui).type_text("", clear_existing=False)
assert result is True
device.shell.assert_not_called()
ui.clear_text.assert_not_called()
ui.set_clipboard.assert_not_called()
@pytest.mark.asyncio
async def test_partial_delete_stops_at_first_failed_key():
from unittest.mock import AsyncMock
controller = object.__new__(UnifiedMobileController)
controller._driver = Mock()
controller._driver.press_key = AsyncMock(return_value=False)
assert not await controller.erase_text(nb_chars=3)
controller._driver.press_key.assert_awaited_once_with("delete")