diff --git a/airtest/core/android/android.py b/airtest/core/android/android.py index 84abb1c2..29129a2b 100644 --- a/airtest/core/android/android.py +++ b/airtest/core/android/android.py @@ -942,6 +942,14 @@ def set_clipboard(self, text): """ self.yosemite_ext.set_clipboard(text) + def paste(self): + """ + Paste the clipboard content + Returns: + + """ + self.text(self.get_clipboard()) + def _register_rotation_watcher(self): """ Register callbacks for Android and minicap when rotation of screen has changed diff --git a/airtest/core/android/ime.py b/airtest/core/android/ime.py index 591913b0..9eae1c60 100644 --- a/airtest/core/android/ime.py +++ b/airtest/core/android/ime.py @@ -2,6 +2,7 @@ import re from airtest.core.android.yosemite import Yosemite from airtest.core.error import AdbError +from airtest.utils.snippet import escape_special_char from .constant import YOSEMITE_IME_SERVICE from six import text_type @@ -113,7 +114,7 @@ def text(self, value): Input text with Yosemite input method Args: - value: text to be inputted, will automatically replace single quotes with double quotes + value: text to be inputted Returns: output form `adb shell` command @@ -123,10 +124,8 @@ def text(self, value): self.start() # 更多的输入用法请见 https://github.com/macacajs/android-unicode#use-in-adb-shell value = ensure_unicode(value) - if '\'' in value: - # 如果value中有特殊符号,必须用单引号包裹内容,因此将单引号替换为双引号 - value = value.replace('\'', '"') - self.adb.shell(f"am broadcast -a ADB_INPUT_TEXT --es msg '{value}'") + value = escape_special_char(value) + self.adb.shell(f"am broadcast -a ADB_INPUT_TEXT --es msg {value}") def code(self, code): """ diff --git a/airtest/core/android/yosemite_ext.py b/airtest/core/android/yosemite_ext.py index 475c14c0..f37fb924 100644 --- a/airtest/core/android/yosemite_ext.py +++ b/airtest/core/android/yosemite_ext.py @@ -1,7 +1,9 @@ +import re + from .constant import YOSEMITE_APK, YOSEMITE_PACKAGE from airtest.core.android.yosemite import Yosemite from airtest.core.error import AirtestError -from airtest.utils.snippet import on_method_ready +from airtest.utils.snippet import on_method_ready, escape_special_char from airtest.utils.logger import get_logger LOGGING = get_logger(__name__) @@ -48,7 +50,7 @@ def get_clipboard(self): def set_clipboard(self, text): """ - Set clipboard content, will automatically replace single quotes with double quotes + Set clipboard content Args: text: text to be set @@ -57,11 +59,15 @@ def set_clipboard(self, text): None """ - if "\'" in text: - text = text.replace("\'", "\"") - ret = self.device_op("clipboard", f"--TEXT '{text}'") - if ret and "Exception" in ret: - raise AirtestError("set clipboard failed: %s" % ret) + text = escape_special_char(text) + + try: + ret = self.device_op("clipboard", f'--TEXT {text}') + except Exception as e: + raise AirtestError("set clipboard failed, %s" % repr(e)) + else: + if ret and "Exception" in ret: + raise AirtestError("set clipboard failed: %s" % ret) def change_lang(self, lang): """ diff --git a/airtest/core/api.py b/airtest/core/api.py index 7f2914b4..a26bb87e 100644 --- a/airtest/core/api.py +++ b/airtest/core/api.py @@ -697,6 +697,23 @@ def set_clipboard(content, *args, **kwargs): """ G.DEVICE.set_clipboard(content, *args, **kwargs) + +@logwrap +def paste(*args, **kwargs): + """ + Paste the content from the clipboard. + + :platforms: Android, iOS + :return: None + :Example: + + >>> set_clipboard("content") + >>> paste() # will paste "content" to the device + + """ + G.DEVICE.paste(*args, **kwargs) + + """ Assertions: see airtest/core/assertions.py """ diff --git a/airtest/core/ios/ios.py b/airtest/core/ios/ios.py index 6eccc851..a88b5781 100644 --- a/airtest/core/ios/ios.py +++ b/airtest/core/ios/ios.py @@ -971,6 +971,21 @@ def set_clipboard(self, content, wda_bundle_id=None, *args, **kwargs): else: LOGGING.warning("we can't switch back to the app before, becasue can't get bundle id.") + def paste(self, wda_bundle_id=None, *args, **kwargs): + """ + Paste the current clipboard content on the device. + + Args: + wda_bundle_id (str, optional): The bundle ID of the WDA app. Defaults to None. + + Raises: + RuntimeError: If the device is remote and the wda_bundle_id parameter is not provided. + + Returns: + None + """ + self.text(self.get_clipboard(wda_bundle_id=wda_bundle_id)) + def get_ip_address(self): """Get ip address from WDA. diff --git a/airtest/utils/snippet.py b/airtest/utils/snippet.py index c93b8401..184d16eb 100644 --- a/airtest/utils/snippet.py +++ b/airtest/utils/snippet.py @@ -1,5 +1,6 @@ # _*_ coding:UTF-8 _*_ import os +import re import sys import stat import threading @@ -172,3 +173,16 @@ def parse_device_uri(uri): if host: params["host"] = host.split(":") return platform, uuid, params + + +def escape_special_char(string): + """ + Escape special characters in a string. + + Args: + string (str): The input string, e.g. 'testing !@#$%^&*()_+' + + Returns: + str: The string with special characters escaped. e.g. 'testing \!\@\#\$\%\^\&\*\(\)_\+' + """ + return re.sub(r'([!@#\$%\^&\*\(\)_\+\\|;:"\'<>\?\{\}\[\]#\~\^ ])', r'\\\1', string) diff --git a/tests/test_android.py b/tests/test_android.py index 1cefb002..3eb4aa96 100644 --- a/tests/test_android.py +++ b/tests/test_android.py @@ -140,6 +140,19 @@ def test_text(self): self.android.ime_method = IME_METHOD.YOSEMITEIME self.android.text(u'你好') + def test_clipboard(self): + text1 = "test clipboard" + self.android.set_clipboard(text1) + self.assertEqual(self.android.get_clipboard(), text1) + + self.android.paste() + + # test escape special char + text2 = "test clipboard with $pecial char #@!#%$#^&*()'" + self.android.set_clipboard(text2) + self.assertEqual(self.android.get_clipboard(), text2) + self.android.paste() + def test_touch(self): for i in (TOUCH_METHOD.ADBTOUCH, TOUCH_METHOD.MINITOUCH, TOUCH_METHOD.MAXTOUCH): self.android.touch_method = i diff --git a/tests/test_yosemite.py b/tests/test_yosemite.py index 749a51f7..a245feca 100644 --- a/tests/test_yosemite.py +++ b/tests/test_yosemite.py @@ -52,6 +52,10 @@ def test_text(self): self.ime.text("nimei") self.ime.text("你妹") + def test_escape_text(self): + self.ime.text("$call org/org->create_org($id,'test123')") + self.ime.text("#@$%^&&*)_+!") + def test_code(self): self.ime.text("test code") self.ime.code("2")