Skip to content

Commit

Permalink
add touch_perform
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Jan 8, 2024
1 parent f823537 commit 3972b36
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 35 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ c.volume_down()
# tap x:100, y:200
c.tap(100, 200)

from wdapy import Gesture, GestureOption as Option
c.touch_perform([
Gesture("press", Option(x=100, y=200)),
Gesture("wait", Option(ms=100)), # ms shoud > 17
Gesture("moveTo", Option(x=100, y = 100)),
Gesture("release")
])
# dismiss keyboard
# by tap keyboard button to dismiss, default keyNames are ["前往", "发送", "Send", "Done", "Return"]
c.keyboard_dismiss(["Done", "Return"])
Expand Down
11 changes: 10 additions & 1 deletion tests/test_common_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from unittest.mock import MagicMock
import wdapy
from wdapy import AppiumClient
from wdapy._types import BatteryState
from wdapy._types import BatteryState, Gesture, GestureAction


class SimpleTest(unittest.TestCase):
Expand Down Expand Up @@ -134,6 +134,15 @@ def test_keyboard_dismiss(self):
payload = self._client.session_request.call_args.args[-1]
self.assertEqual(["Done"], payload['keyNames'])

def test_touch_perform(self):
self._client.session_request = MagicMock(return_value={"value": None})
gestures = [
Gesture(GestureAction.TAP, options={"x": 100, "y": 200})
]
self._client.touch_perform([{"action": "tap", "options": {"x": 100, "y": 200}}])
payload = self._client.session_request.call_args.args[-1]
self.assertEqual([{"action": "tap", "options": {"x": 100, "y": 200}}], payload['actions'])


if __name__ == "__main__":
unittest.main()
53 changes: 51 additions & 2 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,57 @@
# coding: utf-8
#

from wdapy._utils import camel_to_snake

from wdapy._utils import camel_to_snake, json_dumps_omit_empty
from wdapy._proto import *
from wdapy._types import *

def test_camel_to_snake():
assert "this_is_my_string" == camel_to_snake("ThisIsMyString")



def test_json_dumps_omit_empty():
# Test with a mix of None and non-None values
data = {
"a": 1,
"b": None,
"c": "test",
"d": [1, 2, 3],
"e": [{
"f": 1,
"g": None
}]
}
expected_json = '{"a": 1, "c": "test", "d": [1, 2, 3], "e": [{"f": 1}]}'
assert json_dumps_omit_empty(data) == expected_json

data = [Gesture(
action=GestureAction.TAP,
options=GestureOption(
x=100,
y=200
)
)]
expected_json = '[{"action": "tap", "options": {"x": 100, "y": 200}}]'
assert json_dumps_omit_empty(data) == expected_json

# Test with all values as None
data_all_none = {
"a": None,
"b": None
}
expected_json_all_none = '{}'
assert json_dumps_omit_empty(data_all_none) == expected_json_all_none

# Test with no None values
data_no_none = {
"a": 1,
"b": "test"
}
expected_json_no_none = '{"a": 1, "b": "test"}'
assert json_dumps_omit_empty(data_no_none) == expected_json_no_none

# Test with empty dictionary
data_empty = {}
expected_json_empty = '{}'
assert json_dumps_omit_empty(data_empty) == expected_json_empty
7 changes: 4 additions & 3 deletions wdapy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from ._wdapy import (AppiumClient, AppiumUSBClient, NanoClient, NanoUSBClient)

from . import exceptions
from . import _types as types
from ._proto import *
from wdapy import exceptions
from wdapy import _types as types
from wdapy._proto import *
from wdapy._types import *
6 changes: 3 additions & 3 deletions wdapy/_alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

import typing
from wdapy.exceptions import RequestError
from ._proto import *
from ._base import BaseClient
from ._logger import logger
from wdapy._proto import *
from wdapy._base import BaseClient
from wdapy._logger import logger


class Alert:
Expand Down
10 changes: 5 additions & 5 deletions wdapy/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
import simplejson as json
from retry import retry

from ._logger import logger
from ._proto import *
from ._types import Recover, StatusInfo
from .exceptions import *
from .usbmux import MuxError, requests_usbmux
from wdapy._logger import logger
from wdapy._proto import *
from wdapy._types import Recover, StatusInfo
from wdapy.exceptions import *
from wdapy.usbmux import MuxError, requests_usbmux


class BaseClient:
Expand Down
2 changes: 1 addition & 1 deletion wdapy/_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""

import logging
from ._proto import NAME
from wdapy._proto import NAME
from logzero import setup_logger

logger = setup_logger(NAME, level=logging.INFO)
15 changes: 15 additions & 0 deletions wdapy/_proto.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,18 @@ class Keycode(str, enum.Enum):
POWER = "power"
SNAPSHOT = "snapshot"
POWER_PLUS_HOME = "power_plus_home"


class GestureAction(str, enum.Enum):
TAP = "tap"
PRESS = "press"
MOVE_TO = "moveTo"
WAIT = "wait"
RELEASE = "release"


class BatteryState(enum.IntEnum):
Unknown = 0
Unplugged = 1
Charging = 2
Full = 3
32 changes: 21 additions & 11 deletions wdapy/_types.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# coding: utf-8
#
__all__ = ["Recover", "StatusInfo", "AppInfo", "DeviceInfo", "BatteryInfo", "SourceTree",
"StatusBarSize", "AppList",
"Gesture", "GestureOption", "GestureAction"]

__all__ = ["Recover", "StatusInfo", "AppInfo", "DeviceInfo", "BatteryInfo", "SourceTree", "StatusBarSize", "AppList"]

import abc
import enum
import typing
import abc
from ._utils import camel_to_snake
from dataclasses import dataclass
from typing import Optional, Union

from wdapy._proto import *
from wdapy._utils import camel_to_snake


def smart_value_of(obj, data: dict):
Expand Down Expand Up @@ -76,13 +81,6 @@ class DeviceInfo(_Base):
is_simulator: bool


class BatteryState(enum.IntEnum):
Unknown = 0
Unplugged = 1
Charging = 2
Full = 3


class BatteryInfo(_Base):
level: float
state: BatteryState
Expand All @@ -109,5 +107,17 @@ class AppList(_Base):
bundle_id: str


@dataclass
class GestureOption:
element: Optional[str] = None
x: Optional[int] = None
y: Optional[int] = None
count: Optional[int] = None
ms: Optional[int] = None # action:wait duration


@dataclass
class Gesture:
action: Union[str, GestureAction]
options: Optional[Union[dict, GestureOption]] = None

30 changes: 30 additions & 0 deletions wdapy/_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# coding: utf-8
#

from __future__ import annotations

import dataclasses
import json


def camel_to_snake(s: str) -> str:
return ''.join(['_'+c.lower() if c.isupper() else c for c in s]).lstrip("_")


def omit_empty(d: list | dict | dataclasses.dataclass | None):
if isinstance(d, list):
return [omit_empty(v) for v in d]
elif isinstance(d, dict):
return {k: omit_empty(v) for k, v in d.items() if v is not None}
elif dataclasses.is_dataclass(d):
return omit_empty(dataclasses.asdict(d))
else:
return d


def json_dumps_omit_empty(data: dict) -> str:
"""
Convert a dictionary to a JSON string, omitting any items with a value of None.
Parameters:
data (dict): The dictionary to convert to a JSON string.
Returns:
str: A JSON string representation of the dictionary with None values omitted.
"""
return json.dumps(omit_empty(data))
28 changes: 21 additions & 7 deletions wdapy/_wdapy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# coding: utf-8

from __future__ import annotations

import atexit
import base64
import io
Expand All @@ -17,13 +19,14 @@
from logzero import setup_logger
from PIL import Image

from ._alert import Alert
from ._base import BaseClient
from ._logger import logger
from ._proto import *
from ._types import *
from .exceptions import *
from .usbmux import requests_usbmux, usbmux
from wdapy._alert import Alert
from wdapy._base import BaseClient
from wdapy._logger import logger
from wdapy._proto import *
from wdapy._types import *
from wdapy.exceptions import *
from wdapy._utils import omit_empty
from wdapy.usbmux import requests_usbmux, usbmux


class HTTPResponse:
Expand Down Expand Up @@ -239,6 +242,17 @@ def press_duration(self, name: Keycode, duration: float):
"duration": duration
}
return self.session_request(POST, "/wda/performIoHidEvent", payload)

def touch_perform(self, gestures: list[Gesture]):
""" perform touch actions
Ref:
https://appium.readthedocs.io/en/latest/en/commands/interactions/touch/touch-perform/
"""
payload = {
"actions": omit_empty(gestures)
}
self.session_request(POST, "/wda/touch/perform", payload)

def volume_up(self):
self.press(Keycode.VOLUME_UP)
Expand Down
2 changes: 1 addition & 1 deletion wdapy/usbmux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
"""Created on Thu Dec 09 2021 09:56:30 by codeskyblue
"""

from .usbmux import MuxError, MuxConnectError
from wdapy.usbmux.usbmux import MuxError, MuxConnectError
2 changes: 1 addition & 1 deletion wdapy/usbmux/requests_usbmux.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import urllib3
from requests.adapters import HTTPAdapter

from . import usbmux
from wdapy.usbmux import usbmux

try:
import http.client as httplib
Expand Down

0 comments on commit 3972b36

Please sign in to comment.