"""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")