diff --git a/apps/device-host-agent/tests/test_e2e.py b/apps/device-host-agent/tests/test_e2e.py index 068c986..82848f0 100644 --- a/apps/device-host-agent/tests/test_e2e.py +++ b/apps/device-host-agent/tests/test_e2e.py @@ -65,6 +65,9 @@ class FakeDriver(Driver): ) -> None: return None + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + return None + def input(self, text: str) -> None: return None diff --git a/apps/device-host-agent/tests/test_execution.py b/apps/device-host-agent/tests/test_execution.py index fb902b1..a3bb323 100644 --- a/apps/device-host-agent/tests/test_execution.py +++ b/apps/device-host-agent/tests/test_execution.py @@ -51,6 +51,9 @@ class FakeDriver(Driver): ) -> None: return None + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + return None + def input(self, text: str) -> None: return None diff --git a/driver/android_driver.py b/driver/android_driver.py index cbc52f7..bc226de 100644 --- a/driver/android_driver.py +++ b/driver/android_driver.py @@ -98,6 +98,15 @@ class AndroidDriver(Driver): except Exception as exc: raise DriverError("long press failed") from exc + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + from driver._w3c_actions import build_double_tap_actions, perform_actions + + client = self._require_client() + try: + perform_actions(client, build_double_tap_actions(x, y, interval_ms)) + except Exception as exc: + raise DriverError("double tap failed") from exc + def swipe( self, start_x: float, diff --git a/driver/base.py b/driver/base.py index d8bee23..c203960 100644 --- a/driver/base.py +++ b/driver/base.py @@ -51,6 +51,10 @@ class Driver(ABC): ) -> None: """Swipe through a sequence of waypoints in one continuous touch.""" + @abstractmethod + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + """Tap twice at the given coordinates with ``interval_ms`` between.""" + @abstractmethod def input(self, text: str) -> None: """Input text into the current focused field.""" diff --git a/driver/wda_driver.py b/driver/wda_driver.py index 70d06ea..5f8c05b 100644 --- a/driver/wda_driver.py +++ b/driver/wda_driver.py @@ -90,6 +90,15 @@ class WDADriver(Driver): except Exception as exc: raise DriverError("long press failed") from exc + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + from driver._w3c_actions import build_double_tap_actions, perform_actions + + client = self._require_client() + try: + perform_actions(client, build_double_tap_actions(x, y, interval_ms)) + except Exception as exc: + raise DriverError("double tap failed") from exc + def swipe( self, start_x: float, diff --git a/tests/fakes.py b/tests/fakes.py index 4533675..038ea5b 100644 --- a/tests/fakes.py +++ b/tests/fakes.py @@ -69,6 +69,9 @@ class FakeDriver(Driver): ) -> None: self.calls.append(("swipe_path", (tuple(waypoints), duration_ms))) + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + self.calls.append(("double_tap", (x, y, interval_ms))) + def input(self, text: str) -> None: self.calls.append(("input", (text,))) diff --git a/tests/test_android_driver.py b/tests/test_android_driver.py index f53f1f1..09fc02d 100644 --- a/tests/test_android_driver.py +++ b/tests/test_android_driver.py @@ -105,6 +105,7 @@ def test_connect_failure_raises_device_offline_and_clears_client() -> None: ("disconnect", {}), ("long_press", {"x": 1, "y": 2}), ("swipe_path", {"waypoints": [(0, 0), (1, 1)], "duration_ms": 100}), + ("double_tap", {"x": 1, "y": 2}), ], ) def test_operation_before_connect_raises_device_offline( @@ -247,6 +248,22 @@ def test_swipe_path_wraps_exception_into_driver_error() -> None: driver.swipe_path([(0, 0), (1, 1)], 100) +def test_double_tap_sends_w3c_actions() -> None: + from driver._w3c_actions import build_double_tap_actions + + driver = _connected_driver() + driver.double_tap(15, 25, interval_ms=90) + args = driver._client.execute.call_args.args + assert args[1] == {"actions": build_double_tap_actions(15, 25, 90)} + + +def test_double_tap_wraps_exception_into_driver_error() -> None: + driver = _connected_driver() + driver._client.execute.side_effect = RuntimeError("boom") + with pytest.raises(DriverError): + driver.double_tap(1, 2) + + def test_swipe_uses_drag_gesture_with_converted_speed() -> None: driver = _connected_driver() # 100px horizontal drag over 100ms => 100 / 0.1 = 1000 px/s diff --git a/tests/test_describe_screen.py b/tests/test_describe_screen.py index 99aad5f..0a6ded2 100644 --- a/tests/test_describe_screen.py +++ b/tests/test_describe_screen.py @@ -51,6 +51,9 @@ class TreeFailingDriver(Driver): ) -> None: return None + def double_tap(self, x: float, y: float, interval_ms: int = 80) -> None: + return None + def input(self, text: str) -> None: return None