From fea88d1397d2721fa4da8a48a1a1a5cd6bbde6c7 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Wed, 25 Sep 2024 00:17:58 -0700 Subject: [PATCH 1/5] test: cleanup test more (#1032) * test: cleanup duplicated tests more * test: just remove existing ones --- .github/workflows/functional-test.yml | 14 ++--- test/functional/android/common_tests.py | 58 ------------------- test/functional/android/hw_actions_tests.py | 29 ---------- .../android/network_connection_tests.py | 34 ----------- test/functional/android/webelement_tests.py | 52 ----------------- test/unit/webdriver/device/common_test.py | 6 +- test/unit/webdriver/webelement_test.py | 25 ++++++++ 7 files changed, 34 insertions(+), 184 deletions(-) delete mode 100644 test/functional/android/common_tests.py delete mode 100644 test/functional/android/hw_actions_tests.py delete mode 100644 test/functional/android/network_connection_tests.py delete mode 100644 test/functional/android/webelement_tests.py diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index b77088fb..ada561b9 100644 --- a/.github/workflows/functional-test.yml +++ b/.github/workflows/functional-test.yml @@ -95,16 +95,12 @@ jobs: name: func_test_android1 - target: test/functional/android/keyboard_tests.py test/functional/android/location_tests.py name: func_test_android2 - - target: test/functional/android/appium_service_tests.py + - target: test/functional/android/appium_service_tests.py test/functional/android/chrome_tests.py name: func_test_android3 - - target: test/functional/android/finger_print_tests.py test/functional/android/screen_record_tests.py test/functional/android/settings_tests.py test/functional/android/chrome_tests.py + - target: test/functional/android/finger_print_tests.py test/functional/android/screen_record_tests.py test/functional/android/settings_tests.py name: func_test_android4 - - target: test/functional/android/remote_fs_tests.py + - target: test/functional/android/remote_fs_tests.py test/functional/android/log_event_tests.py name: func_test_android5 - - target: test/functional/android/common_tests.py test/functional/android/webelement_tests.py - name: func_test_android6 - - target: test/functional/android/network_connection_tests.py test/functional/android/log_event_tests.py test/functional/android/hw_actions_tests.py - name: func_test_android8 runs-on: ubuntu-latest @@ -234,7 +230,7 @@ jobs: echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules sudo udevadm trigger --name-match=kvm - + - name: Set up Python 3.12 uses: actions/setup-python@v3 with: @@ -247,7 +243,7 @@ jobs: - name: Install Appium run: npm install --location=global appium - + - name: Install Android drivers and Run Appium if: matrix.e2e-tests == 'flutter-android' run: | diff --git a/test/functional/android/common_tests.py b/test/functional/android/common_tests.py deleted file mode 100644 index 66aea810..00000000 --- a/test/functional/android/common_tests.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from time import sleep - -import pytest -from selenium.common.exceptions import NoSuchElementException - -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.test_helper import wait_for_element - -from ..test_helper import is_ci -from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase - - -class TestCommon(BaseTestCase): - def test_current_package(self) -> None: - assert APIDEMO_PKG_NAME == self.driver.current_package - - # TODO Due to unexpected dialog, "System UI isn't responding" - @pytest.mark.skipif(condition=is_ci(), reason='Need to fix flaky test during running on CI.') - def test_open_notifications(self) -> None: - for word in ['App', 'Notification', 'Status Bar', ':-|']: - wait_for_element(self.driver, AppiumBy.ANDROID_UIAUTOMATOR, f'new UiSelector().text("{word}")').click() - - self.driver.open_notifications() - sleep(1) - with pytest.raises(NoSuchElementException): - self.driver.find_element(by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().text(":-|")') - - els = self.driver.find_element(by=AppiumBy.CLASS_NAME, value='android.widget.TextView') - # sometimes numbers shift - title = False - body = False - for el in els: - text = el.text - if text == 'Mood ring': - title = True - elif text == 'I am ok': - body = True - assert title - assert body - - self.driver.keyevent(4) - sleep(1) - self.driver.find_element(by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().text(":-|")') diff --git a/test/functional/android/hw_actions_tests.py b/test/functional/android/hw_actions_tests.py deleted file mode 100644 index 4646d254..00000000 --- a/test/functional/android/hw_actions_tests.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from time import sleep - -from .helper.test_helper import BaseTestCase - - -class TestHwActions(BaseTestCase): - def test_lock(self) -> None: - self.driver.lock(-1) - sleep(10) - try: - assert self.driver.is_locked() - finally: - self.driver.unlock() - assert not self.driver.is_locked() diff --git a/test/functional/android/network_connection_tests.py b/test/functional/android/network_connection_tests.py deleted file mode 100644 index db1ce63b..00000000 --- a/test/functional/android/network_connection_tests.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - -from appium.webdriver.connectiontype import ConnectionType - -from ..test_helper import is_ci -from .helper.test_helper import BaseTestCase - - -class TestNetworkConnection(BaseTestCase): - def test_get_network_connection(self) -> None: - nc = self.driver.network_connection - assert isinstance(nc, int) - - @pytest.mark.skipif(condition=is_ci(), reason='Need to fix flaky test during running on CI') - def test_set_network_connection(self) -> None: - try: - self.driver.set_network_connection(ConnectionType.DATA_ONLY) - except Exception: - assert False, "Should not raise any exceptions" diff --git a/test/functional/android/webelement_tests.py b/test/functional/android/webelement_tests.py deleted file mode 100644 index 9a8ba1fd..00000000 --- a/test/functional/android/webelement_tests.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.test_helper import wait_for_element - -from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase - - -class TestWebelement(BaseTestCase): - def test_element_location_in_view(self) -> None: - el = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Content') - loc = el.location_in_view - assert loc['x'] is not None - assert loc['y'] is not None - - def test_set_text(self) -> None: - self.driver.find_element( - by=AppiumBy.ANDROID_UIAUTOMATOR, - value='new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("Views").instance(0));', - ).click() - - wait_for_element(self.driver, AppiumBy.ACCESSIBILITY_ID, 'Controls').click() - wait_for_element(self.driver, AppiumBy.ACCESSIBILITY_ID, '1. Light Theme').click() - - el = wait_for_element(self.driver, AppiumBy.CLASS_NAME, 'android.widget.EditText') - el.send_keys('original text') - el.clear() - el.send_keys('new text') - - assert 'new text' == el.text - - def test_send_keys(self) -> None: - for text in ['App', 'Activity', 'Custom Title']: - wait_for_element(self.driver, AppiumBy.XPATH, f'//android.widget.TextView[@text=\'{text}\']').click() - - el = wait_for_element(self.driver, AppiumBy.ID, '{}:id/left_text_edit'.format(APIDEMO_PKG_NAME)) - el.send_keys(' text') - - assert 'Left is best text' == el.text diff --git a/test/unit/webdriver/device/common_test.py b/test/unit/webdriver/device/common_test.py index c4065e47..7bf99153 100644 --- a/test/unit/webdriver/device/common_test.py +++ b/test/unit/webdriver/device/common_test.py @@ -15,16 +15,18 @@ import httpretty from appium.webdriver.webdriver import WebDriver -from test.unit.helper.test_helper import android_w3c_driver, appium_command +from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body class TestWebDriverCommon(object): @httpretty.activate def test_open_notifications(self): driver = android_w3c_driver() - httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/appium/device/open_notifications')) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.open_notifications(), WebDriver) + assert {'args': [], 'script': 'mobile: openNotifications'} == get_httpretty_request_body( + httpretty.last_request() + ) @httpretty.activate def test_current_package(self): diff --git a/test/unit/webdriver/webelement_test.py b/test/unit/webdriver/webelement_test.py index 57cb317f..14b6abb6 100644 --- a/test/unit/webdriver/webelement_test.py +++ b/test/unit/webdriver/webelement_test.py @@ -62,6 +62,14 @@ def test_send_key_with_file(self): d = get_httpretty_request_body(httpretty.last_request()) assert d['text'] == ''.join(d['value']) + @httpretty.activate + def test_clear(self): + driver = android_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/element/element_id/clear')) + + element = MobileWebElement(driver, 'element_id') + element.clear() + @httpretty.activate def test_get_attribute_with_dict(self): driver = android_w3c_driver() @@ -79,3 +87,20 @@ def test_get_attribute_with_dict(self): assert isinstance(ef, dict) assert ef == rect_dict + + @httpretty.activate + def test_element_location_in_view(self): + driver = android_w3c_driver() + location_in_view = {'y': 200, 'x': 100} + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/element/element_id/location_in_view'), + body=json.dumps({"value": location_in_view}), + ) + + element = MobileWebElement(driver, 'element_id') + loc = element.location_in_view + + httpretty.last_request() + + assert loc == location_in_view From 9a3a6337c375d3ece124df459e231fbfd0f2d8b1 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Wed, 25 Sep 2024 22:39:47 -0700 Subject: [PATCH 2/5] test: cleanup tests more (#1033) * test: remove some functional test which is tested in unit tets * test: remvoe location tests * remove finger * remove more * more * more * cleanup more --- .github/workflows/functional-test.yml | 13 +-- test/functional/android/device_time_tests.py | 25 ------ .../android/file/find_by_image_failure.png | Bin 25258 -> 0 bytes .../android/file/find_by_image_success.png | Bin 2572 -> 0 bytes test/functional/android/file/test_file.txt | 1 - test/functional/android/file/test_image.jpg | Bin 21422 -> 0 bytes test/functional/android/finger_print_tests.py | 24 ----- test/functional/android/keyboard_tests.py | 26 ------ test/functional/android/location_tests.py | 21 ----- test/functional/android/log_event_tests.py | 24 ----- test/functional/android/remote_fs_tests.py | 62 ------------- .../functional/android/screen_record_tests.py | 26 ------ .../android/search_context/__init__.py | 0 .../find_by_accessibility_id_tests.py | 51 ----------- .../search_context/find_by_image_tests.py | 83 ------------------ .../find_by_uiautomator_tests.py | 54 ------------ .../find_by_view_matcher_tests.py | 72 --------------- test/functional/android/settings_tests.py | 27 ------ 18 files changed, 2 insertions(+), 507 deletions(-) delete mode 100644 test/functional/android/device_time_tests.py delete mode 100644 test/functional/android/file/find_by_image_failure.png delete mode 100644 test/functional/android/file/find_by_image_success.png delete mode 100644 test/functional/android/file/test_file.txt delete mode 100644 test/functional/android/file/test_image.jpg delete mode 100644 test/functional/android/finger_print_tests.py delete mode 100644 test/functional/android/keyboard_tests.py delete mode 100644 test/functional/android/location_tests.py delete mode 100644 test/functional/android/log_event_tests.py delete mode 100644 test/functional/android/remote_fs_tests.py delete mode 100644 test/functional/android/screen_record_tests.py delete mode 100644 test/functional/android/search_context/__init__.py delete mode 100644 test/functional/android/search_context/find_by_accessibility_id_tests.py delete mode 100644 test/functional/android/search_context/find_by_image_tests.py delete mode 100644 test/functional/android/search_context/find_by_uiautomator_tests.py delete mode 100644 test/functional/android/search_context/find_by_view_matcher_tests.py delete mode 100644 test/functional/android/settings_tests.py diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index ada561b9..06c17ede 100644 --- a/.github/workflows/functional-test.yml +++ b/.github/workflows/functional-test.yml @@ -91,16 +91,8 @@ jobs: fail-fast: false matrix: test_targets: - - target: test/functional/android/device_time_tests.py test/functional/android/search_context/find_by_*.py - name: func_test_android1 - - target: test/functional/android/keyboard_tests.py test/functional/android/location_tests.py - name: func_test_android2 - target: test/functional/android/appium_service_tests.py test/functional/android/chrome_tests.py - name: func_test_android3 - - target: test/functional/android/finger_print_tests.py test/functional/android/screen_record_tests.py test/functional/android/settings_tests.py - name: func_test_android4 - - target: test/functional/android/remote_fs_tests.py test/functional/android/log_event_tests.py - name: func_test_android5 + name: func_test_android1 runs-on: ubuntu-latest @@ -126,9 +118,8 @@ jobs: - run: | appium driver install uiautomator2 appium driver install espresso - appium plugin install images appium plugin install execute-driver - nohup appium --use-plugins=images,execute-driver --relaxed-security --log-timestamp --log-no-colors 2>&1 > appium.log & + nohup appium --use-plugins=execute-driver --relaxed-security --log-timestamp --log-no-colors 2>&1 > appium.log & - name: Enable KVM group perms run: | diff --git a/test/functional/android/device_time_tests.py b/test/functional/android/device_time_tests.py deleted file mode 100644 index b5d576fb..00000000 --- a/test/functional/android/device_time_tests.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from dateutil.parser import parse - -from .helper.test_helper import BaseTestCase - - -class TestDeviceTime(BaseTestCase): - def test_device_time(self) -> None: - date_time = self.driver.device_time - # convert to date ought to work - parse(date_time) diff --git a/test/functional/android/file/find_by_image_failure.png b/test/functional/android/file/find_by_image_failure.png deleted file mode 100644 index 5ea155cc48228ab46a40a4b37708a6cb9efa92f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25258 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 QHz<%)6g1@PV#0F5004-g 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 diff --git a/test/functional/android/file/test_file.txt b/test/functional/android/file/test_file.txt deleted file mode 100644 index 4fac12a9..00000000 --- a/test/functional/android/file/test_file.txt +++ /dev/null @@ -1 +0,0 @@ -HEllo diff --git a/test/functional/android/file/test_image.jpg b/test/functional/android/file/test_image.jpg deleted file mode 100644 index 6434234fb11467c62823873c58456aa5df3013c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21422 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 AqyPW_ diff --git a/test/functional/android/finger_print_tests.py b/test/functional/android/finger_print_tests.py deleted file mode 100644 index 23fe9d3a..00000000 --- a/test/functional/android/finger_print_tests.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .helper.test_helper import BaseTestCase - - -class TestFingerPrint(BaseTestCase): - def test_finger_print(self) -> None: - try: - self.driver.finger_print(1) - except Exception: - assert False, "Sould not raise any exceptions" diff --git a/test/functional/android/keyboard_tests.py b/test/functional/android/keyboard_tests.py deleted file mode 100644 index 5e1ebe6a..00000000 --- a/test/functional/android/keyboard_tests.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .helper.test_helper import BaseTestCase - - -class TestKeyboard(BaseTestCase): - def test_press_keycode(self) -> None: - # TODO not sure how to test this. - self.driver.press_keycode(176) - - def test_long_press_keycode(self) -> None: - # TODO not sure how to test this. - self.driver.long_press_keycode(176) diff --git a/test/functional/android/location_tests.py b/test/functional/android/location_tests.py deleted file mode 100644 index 30cd7880..00000000 --- a/test/functional/android/location_tests.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .helper.test_helper import BaseTestCase - - -class TestLocation(BaseTestCase): - def test_toggle_location_services(self) -> None: - self.driver.toggle_location_services() # TODO Add assert diff --git a/test/functional/android/log_event_tests.py b/test/functional/android/log_event_tests.py deleted file mode 100644 index 9ad0b53d..00000000 --- a/test/functional/android/log_event_tests.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .helper.test_helper import BaseTestCase - - -class TestLogEvent(BaseTestCase): - def test_log_event(self) -> None: - vendor = 'appium' - event = 'funEvent' - self.driver.log_event(vendor, event) - assert f'{vendor}:{event}' in self.driver.get_events().keys() diff --git a/test/functional/android/remote_fs_tests.py b/test/functional/android/remote_fs_tests.py deleted file mode 100644 index c11d4c2b..00000000 --- a/test/functional/android/remote_fs_tests.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import base64 -import os -import random -from io import BytesIO -from zipfile import ZipFile - -from .helper.test_helper import BaseTestCase - - -class TestRemoteFs(BaseTestCase): - def test_push_pull_file(self) -> None: - dest_path = '/data/local/tmp/test_push_file.txt' - data = bytes('This is the contents of the file to push to the device.', 'utf-8') - - self.driver.push_file(dest_path, base64.b64encode(data).decode('utf-8')) - data_ret = base64.b64decode(self.driver.pull_file(dest_path)) - - assert data == data_ret - - def test_pull_folder(self) -> None: - data = bytes('random string data {}'.format(random.randint(0, 1000)), 'utf-8') - dest_dir = '/data/local/tmp/' - - for filename in ['1.txt', '2.txt']: - self.driver.push_file(os.path.join(dest_dir, filename), base64.b64encode(data).decode('utf-8')) - - folder = self.driver.pull_folder(dest_dir) - - with ZipFile(BytesIO(base64.b64decode(folder))) as fzip: - for filename in ['tmp/1.txt', 'tmp/2.txt']: - # e.g. in the fzip.namelist(): - # ['tmp/', 'tmp/.studio/', 'tmp/.studio/process-tracker', 'tmp/1.txt', 'tmp/2.txt', - # 'tmp/chrome-command-line', 'tmp/espresso.apppackage', 'tmp/remote.txt', - # 'tmp/test_file.txt', 'tmp/test_image.jpg', 'tmp/test_push_file.txt'] - assert filename in fzip.namelist() - - def test_push_file_with_src_path(self) -> None: - test_files = ['test_image.jpg', 'test_file.txt'] - for file_name in test_files: - src_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'file', file_name) - dest_path = os.path.join('/data/local/tmp/', file_name) - - with open(src_path, 'rb') as fr: - original_data = fr.read() - - self.driver.push_file(dest_path, source_path=src_path) - new_data = base64.b64decode(self.driver.pull_file(dest_path)) - assert original_data == new_data diff --git a/test/functional/android/screen_record_tests.py b/test/functional/android/screen_record_tests.py deleted file mode 100644 index 49618a89..00000000 --- a/test/functional/android/screen_record_tests.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from time import sleep - -from .helper.test_helper import BaseTestCase - - -class TestScreenRecord(BaseTestCase): - def test_screen_record(self) -> None: - self.driver.start_recording_screen(timeLimit=10, forcedRestart=True) - sleep(10) - result = self.driver.stop_recording_screen() - assert len(result) > 0 diff --git a/test/functional/android/search_context/__init__.py b/test/functional/android/search_context/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/functional/android/search_context/find_by_accessibility_id_tests.py b/test/functional/android/search_context/find_by_accessibility_id_tests.py deleted file mode 100644 index 4f9dc3fe..00000000 --- a/test/functional/android/search_context/find_by_accessibility_id_tests.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - -from appium.webdriver.common.appiumby import AppiumBy -from appium.webdriver.webelement import WebElement -from test.functional.android.helper.test_helper import BaseTestCase, is_ci -from test.functional.test_helper import wait_for_element - - -class TestFindByAccessibilityID(BaseTestCase): - def test_find_single_element(self) -> None: - wait_for_element(self.driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Accessibility")').click() - wait_for_element( - self.driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Accessibility Node Querying")' - ).click() - el = wait_for_element(self.driver, AppiumBy.ACCESSIBILITY_ID, 'Task Take out Trash') - assert el is not None - - def test_find_multiple_elements(self) -> None: - els = self.driver.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value='Accessibility') - assert isinstance(els, list) - - @pytest.mark.skipif(condition=is_ci(), reason='Need to fix flaky test during running on CI') - def test_element_find_single_element(self) -> None: - wait_for_element(self.driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Accessibility")').click() - wait_for_element( - self.driver, AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Accessibility Node Querying")' - ).click() - el = wait_for_element(self.driver, AppiumBy.CLASS_NAME, 'android.widget.ListView') - - sub_el: WebElement = el.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Task Take out Trash') - assert sub_el is not None - - def test_element_find_multiple_elements(self) -> None: - wait_for_element(self.driver, AppiumBy.CLASS_NAME, 'android.widget.ListView') - el = self.driver.find_element(by=AppiumBy.CLASS_NAME, value='android.widget.ListView') - sub_els: list = el.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value='Animation') - assert isinstance(sub_els, list) diff --git a/test/functional/android/search_context/find_by_image_tests.py b/test/functional/android/search_context/find_by_image_tests.py deleted file mode 100644 index 94970fa9..00000000 --- a/test/functional/android/search_context/find_by_image_tests.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import base64 - -import pytest -from selenium.common.exceptions import TimeoutException - -from appium import webdriver -from appium.options.common import AppiumOptions -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.android.helper import desired_capabilities -from test.functional.test_helper import wait_for_element -from test.helpers.constants import SERVER_URL_BASE - - -class TestFindByImage(object): - def setup_method(self) -> None: - caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip') - self.driver = webdriver.Remote(SERVER_URL_BASE, options=AppiumOptions().load_capabilities(caps)) - - # relax template matching - self.driver.update_settings( - { - 'fixImageFindScreenshotDims': False, - 'fixImageTemplateSize': True, - 'autoUpdateImageElementPosition': True, - 'fixImageTemplateScale': True, - 'imageMatchThreshold': 0.8, - } - ) - - def teardown_method(self) -> None: - self.driver.quit() - - def test_find_based_on_image_template(self) -> None: - image_path = desired_capabilities.PATH('file/find_by_image_success.png') - with open(image_path, 'rb') as png_file: - b64_data = base64.b64encode(png_file.read()).decode('UTF-8') - - el = wait_for_element(self.driver, AppiumBy.IMAGE, b64_data) - size = el.size - assert size['width'] is not None - assert size['height'] is not None - loc = el.location - assert loc['x'] is not None - assert loc['y'] is not None - rect = el.rect - assert rect['width'] is not None - assert rect['height'] is not None - assert rect['x'] is not None - assert rect['y'] is not None - assert el.is_displayed() - el.click() - wait_for_element(self.driver, AppiumBy.ACCESSIBILITY_ID, 'Alarm') - - def test_find_multiple_elements_by_image_just_returns_one(self) -> None: - wait_for_element(self.driver, AppiumBy.ACCESSIBILITY_ID, 'App') - image_path = desired_capabilities.PATH('file/find_by_image_success.png') - with open(image_path, 'rb') as png_file: - b64_data = base64.b64encode(png_file.read()).decode('UTF-8') - els = self.driver.find_elements(AppiumBy.IMAGE, b64_data) - els[0].click() - wait_for_element(self.driver, AppiumBy.ACCESSIBILITY_ID, 'Alarm') - - def test_find_throws_no_such_element(self) -> None: - image_path = desired_capabilities.PATH('file/find_by_image_failure.png') - with open(image_path, 'rb') as png_file: - b64_data = base64.b64encode(png_file.read()).decode('UTF-8') - - with pytest.raises(TimeoutException): - wait_for_element(self.driver, AppiumBy.IMAGE, b64_data, timeout_sec=3) diff --git a/test/functional/android/search_context/find_by_uiautomator_tests.py b/test/functional/android/search_context/find_by_uiautomator_tests.py deleted file mode 100644 index a8819bc0..00000000 --- a/test/functional/android/search_context/find_by_uiautomator_tests.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pytest - -from appium.webdriver.common.appiumby import AppiumBy -from appium.webdriver.webelement import WebElement -from test.functional.android.helper.test_helper import BaseTestCase - - -@pytest.mark.skip(reason='Need to fix flaky test') -class TestFindByUIAutomator(BaseTestCase): - def test_find_single_element(self) -> None: - el = self.driver.find_element(by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().text("Animation")') - assert el is not None - - def test_find_multiple_elements(self) -> None: - els = self.driver.find_elements(by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().clickable(true)') - assert isinstance(els, list) - - def test_element_find_single_element(self) -> None: - el = self.driver.find_element(by=AppiumBy.CLASS_NAME, value='android.widget.ListView') - - sub_el: WebElement = el.find_element( - by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().description("Animation")' - ) - assert sub_el is not None - - def test_element_find_multiple_elements(self) -> None: - el = self.driver.find_element(by=AppiumBy.CLASS_NAME, value='android.widget.ListView') - - sub_els = el.find_elements( - by=AppiumBy.ANDROID_UIAUTOMATOR, value='new UiSelector().clickable(true)' - ) # type: list - assert isinstance(sub_els, list) - - def test_scroll_into_view(self) -> None: - el = self.driver.find_element( - by=AppiumBy.ANDROID_UIAUTOMATOR, - value='new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("Views").instance(0));', - ) - el.click() - # TODO Add assert diff --git a/test/functional/android/search_context/find_by_view_matcher_tests.py b/test/functional/android/search_context/find_by_view_matcher_tests.py deleted file mode 100644 index 47a2eb8d..00000000 --- a/test/functional/android/search_context/find_by_view_matcher_tests.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json - -import pytest -from selenium.common.exceptions import WebDriverException - -from appium import webdriver -from appium.options.common import AppiumOptions -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.android.helper.test_helper import BaseTestCase, desired_capabilities, is_ci -from test.helpers.constants import SERVER_URL_BASE - - -class TestFindByViewMatcher(BaseTestCase): - # Override - def setup_method(self, method) -> None: # type: ignore - caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip') - caps['automationName'] = 'Espresso' - self.driver = webdriver.Remote(SERVER_URL_BASE, options=AppiumOptions().load_capabilities(caps)) - if is_ci(): - self.driver.start_recording_screen() - - def test_find_single_element(self) -> None: - el = self.driver.find_element( - by=AppiumBy.ANDROID_VIEW_MATCHER, - value=json.dumps({'name': 'withText', 'args': ['Accessibility'], 'class': 'ViewMatchers'}), - ) - assert el.text == 'Accessibility' - - def test_find_single_element_ful_class_name(self) -> None: - el = self.driver.find_element( - by=AppiumBy.ANDROID_VIEW_MATCHER, - value=json.dumps( - {'name': 'withText', 'args': ['Accessibility'], 'class': 'androidx.test.espresso.matcher.ViewMatchers'} - ), - ) - assert el.text == 'Accessibility' - - def test_find_single_element_using_hamcrest_matcher(self) -> None: - el = self.driver.find_element( - by=AppiumBy.ANDROID_VIEW_MATCHER, - value=json.dumps( - { - 'name': 'withText', - 'args': {'name': 'containsString', 'args': 'Animati', 'class': 'org.hamcrest.Matchers'}, - 'class': 'ViewMatchers', - } - ), - ) - assert el.text == 'Animation' - - # androidx.test.espresso.AmbiguousViewMatcherException: - # 'with text: a string containing "Access"' matches multiple views in the hierarchy. - def test_find_multiple_elements(self) -> None: - el = self.driver.find_element( - by=AppiumBy.ANDROID_VIEW_MATCHER, - value=json.dumps({'name': 'withSubstring', 'args': ['Access'], 'class': 'ViewMatchers'}), - ) - assert el.text == "Access'ibility" diff --git a/test/functional/android/settings_tests.py b/test/functional/android/settings_tests.py deleted file mode 100644 index 66afa953..00000000 --- a/test/functional/android/settings_tests.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .helper.test_helper import BaseTestCase - - -class TestSettings(BaseTestCase): - def test_get_settings(self) -> None: - settings = self.driver.get_settings() - assert settings is not None - - def test_update_settings(self) -> None: - self.driver.update_settings({'waitForIdleTimeout': 10001}) - settings = self.driver.get_settings() - assert settings['waitForIdleTimeout'] == 10001 From 877335152c0e0e705e36867d4449631d5385925a Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Thu, 26 Sep 2024 23:28:30 -0700 Subject: [PATCH 3/5] test: cleanup ios (#1034) --- .github/workflows/functional-test.yml | 4 +- test/functional/ios/applications_tests.py | 33 ----- test/functional/ios/hw_actions_tests.py | 38 ------ test/functional/ios/keyboard_tests.py | 87 ------------- test/unit/webdriver/app_test.py | 137 +++++++++++++++++++- test/unit/webdriver/device/keyboard_test.py | 39 +++++- test/unit/webdriver/device/lock_test.py | 78 ++++++++++- 7 files changed, 249 insertions(+), 167 deletions(-) delete mode 100644 test/functional/ios/applications_tests.py delete mode 100644 test/functional/ios/hw_actions_tests.py delete mode 100644 test/functional/ios/keyboard_tests.py diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index 06c17ede..bfa1b713 100644 --- a/.github/workflows/functional-test.yml +++ b/.github/workflows/functional-test.yml @@ -20,10 +20,8 @@ jobs: test_targets: - target: test/functional/ios/search_context/find_by_*.py test/functional/ios/remote_fs_tests.py test/functional/ios/safari_tests.py test/functional/ios/execute_driver_tests.py name: func_test_ios1 - - target: test/functional/ios/applications_tests.py test/functional/ios/hw_actions_tests.py test/functional/ios/keyboard_tests.py - name: func_test_ios2 - target: test/functional/ios/screen_record_tests.py test/functional/ios/webdriver_tests.py - name: func_test_ios3 + name: func_test_ios2 runs-on: macos-14 diff --git a/test/functional/ios/applications_tests.py b/test/functional/ios/applications_tests.py deleted file mode 100644 index 072059bf..00000000 --- a/test/functional/ios/applications_tests.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from appium.webdriver.applicationstate import ApplicationState -from test.functional.ios.helper.test_helper import BaseTestCase -from test.functional.test_helper import wait_for_condition - -from .helper import desired_capabilities - - -class TestWebDriver(BaseTestCase): - def test_app_management(self) -> None: - # this only works in Xcode9+ - if float(desired_capabilities.get_desired_capabilities(desired_capabilities.BUNDLE_ID)['platformVersion']) < 11: - return - assert self.driver.query_app_state(desired_capabilities.BUNDLE_ID) == ApplicationState.RUNNING_IN_FOREGROUND - self.driver.background_app(-1) - assert wait_for_condition( - lambda: self.driver.query_app_state(desired_capabilities.BUNDLE_ID) < ApplicationState.RUNNING_IN_FOREGROUND - ), 'The app didn\'t go to background.' - self.driver.activate_app(desired_capabilities.BUNDLE_ID) - assert self.driver.query_app_state(desired_capabilities.BUNDLE_ID) == ApplicationState.RUNNING_IN_FOREGROUND diff --git a/test/functional/ios/hw_actions_tests.py b/test/functional/ios/hw_actions_tests.py deleted file mode 100644 index 86264b37..00000000 --- a/test/functional/ios/hw_actions_tests.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestHwActions(BaseTestCase): - def test_lock(self) -> None: - self.driver.lock(-1) - try: - assert self.driver.is_locked() - finally: - self.driver.unlock() - assert not self.driver.is_locked() - - def test_shake(self) -> None: - # TODO what can we assert about this? - self.driver.shake() - - def test_touch_id(self) -> None: - # nothing to assert, just verify that it doesn't blow up - self.driver.touch_id(True) - self.driver.touch_id(False) - - def test_toggle_touch_id_enrollment(self) -> None: - # nothing to assert, just verify that it doesn't blow up - self.driver.toggle_touch_id_enrollment() diff --git a/test/functional/ios/keyboard_tests.py b/test/functional/ios/keyboard_tests.py deleted file mode 100644 index d07e5e70..00000000 --- a/test/functional/ios/keyboard_tests.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import TYPE_CHECKING - -import pytest -from selenium.common.exceptions import NoSuchElementException - -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.ios.helper.test_helper import BaseTestCase - -if TYPE_CHECKING: - from appium.webdriver.webelement import WebElement - - -class TestKeyboard(BaseTestCase): - def test_hide_keyboard(self) -> None: - self._move_to_textbox() - - el = self.driver.find_elements(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeTextField')[0] - el.click() - el.send_keys('Testing') - - assert self._get_keyboard_el().is_displayed() - - self.driver.hide_keyboard(key_name='Done') - - with pytest.raises(NoSuchElementException): - self._get_keyboard_el() - - def test_hide_keyboard_presskey_strategy(self) -> None: - self._move_to_textbox() - - el = self.driver.find_elements(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeTextField')[0] - el.click() - el.send_keys('Testing') - - assert self._get_keyboard_el().is_displayed() - - self.driver.hide_keyboard(strategy='pressKey', key='Done') - - with pytest.raises(NoSuchElementException): - self._get_keyboard_el() - - def test_hide_keyboard_no_key_name(self) -> None: - self._move_to_textbox() - - el = self.driver.find_elements(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeTextField')[0] - el.click() - el.send_keys('Testing') - - assert self._get_keyboard_el().is_displayed() - - self.driver.hide_keyboard() - - with pytest.raises(NoSuchElementException): - self._get_keyboard_el() - - def test_is_keyboard_shown(self) -> None: - self._move_to_textbox() - - el = self.driver.find_elements(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeTextField')[0] - el.click() - el.send_keys('Testing') - assert self.driver.is_keyboard_shown() - - def _get_keyboard_el(self) -> 'WebElement': - return self.driver.find_element(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeKeyboard') - - def _move_to_textbox(self) -> None: - el1 = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Sliders') - el2 = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Buttons') - self.driver.scroll(el1, el2) - - # Click text fields - self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Text Fields').click() diff --git a/test/unit/webdriver/app_test.py b/test/unit/webdriver/app_test.py index 45367141..6fa773ee 100644 --- a/test/unit/webdriver/app_test.py +++ b/test/unit/webdriver/app_test.py @@ -16,10 +16,10 @@ from appium.webdriver.applicationstate import ApplicationState from appium.webdriver.webdriver import WebDriver -from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body +from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body, ios_w3c_driver -class TestWebDriverApp(object): +class TestWebDriverAppAndroid(object): @httpretty.activate def test_install_app(self): driver = android_w3c_driver() @@ -150,3 +150,136 @@ def test_app_strings_with_lang_and_file(self): 'script': 'mobile: getAppStrings', } == get_httpretty_request_body(httpretty.last_request()) assert 'You can\'t wipe my data, you are a monkey!' == result['monkey_wipe_data'], result + + +class TestWebDriverAppIOS(object): + @httpretty.activate + def test_install_app(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') + result = driver.install_app('path/to/app') + + assert { + 'args': [{'app': 'path/to/app', 'appPath': 'path/to/app'}], + 'script': 'mobile: installApp', + } == get_httpretty_request_body(httpretty.last_request()) + assert isinstance(result, WebDriver) + + @httpretty.activate + def test_remove_app(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') + result = driver.remove_app('com.app.id') + + assert { + 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], + 'script': 'mobile: removeApp', + } == get_httpretty_request_body(httpretty.last_request()) + assert isinstance(result, WebDriver) + + @httpretty.activate + def test_app_installed(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}' + ) + result = driver.is_app_installed("com.app.id") + + assert { + 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], + 'script': 'mobile: isAppInstalled', + } == get_httpretty_request_body(httpretty.last_request()) + assert result is True + + @httpretty.activate + def test_terminate_app(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}' + ) + result = driver.terminate_app("com.app.id") + + assert { + 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], + 'script': 'mobile: terminateApp', + } == get_httpretty_request_body(httpretty.last_request()) + assert result is True + + @httpretty.activate + def test_activate_app(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') + result = driver.activate_app("com.app.id") + + assert { + 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], + 'script': 'mobile: activateApp', + } == get_httpretty_request_body(httpretty.last_request()) + assert isinstance(result, WebDriver) + + @httpretty.activate + def test_background_app(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') + result = driver.background_app(0) + + assert {'args': [{'seconds': 0}], 'script': 'mobile: backgroundApp'} == get_httpretty_request_body( + httpretty.last_request() + ) + assert isinstance(result, WebDriver) + + @httpretty.activate + def test_query_app_state(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": 3}') + result = driver.query_app_state('com.app.id') + + assert { + 'args': [{'appId': 'com.app.id', 'bundleId': 'com.app.id'}], + 'script': 'mobile: queryAppState', + } == get_httpretty_request_body(httpretty.last_request()) + assert result is ApplicationState.RUNNING_IN_BACKGROUND + + @httpretty.activate + def test_app_strings(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/execute/sync'), + body='{"value": {"monkey_wipe_data": "You can\'t wipe my data, you are a monkey!"} }', + ) + result = driver.app_strings() + + assert {'args': [{}], 'script': 'mobile: getAppStrings'} == get_httpretty_request_body(httpretty.last_request()) + assert 'You can\'t wipe my data, you are a monkey!' == result['monkey_wipe_data'], result + + @httpretty.activate + def test_app_strings_with_lang(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/execute/sync'), + body='{"value": {"monkey_wipe_data": "You can\'t wipe my data, you are a monkey!"} }', + ) + result = driver.app_strings('en') + + assert {'args': [{'language': 'en'}], 'script': 'mobile: getAppStrings'} == get_httpretty_request_body( + httpretty.last_request() + ) + assert 'You can\'t wipe my data, you are a monkey!' == result['monkey_wipe_data'], result + + @httpretty.activate + def test_app_strings_with_lang_and_file(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/execute/sync'), + body='{"value": {"monkey_wipe_data": "You can\'t wipe my data, you are a monkey!"} }', + ) + result = driver.app_strings('en', 'some_file') + + assert { + 'args': [{'language': 'en', 'stringFile': 'some_file'}], + 'script': 'mobile: getAppStrings', + } == get_httpretty_request_body(httpretty.last_request()) + assert 'You can\'t wipe my data, you are a monkey!' == result['monkey_wipe_data'], result diff --git a/test/unit/webdriver/device/keyboard_test.py b/test/unit/webdriver/device/keyboard_test.py index e3401839..79579fb9 100644 --- a/test/unit/webdriver/device/keyboard_test.py +++ b/test/unit/webdriver/device/keyboard_test.py @@ -15,10 +15,10 @@ import httpretty from appium.webdriver.webdriver import WebDriver -from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body +from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body, ios_w3c_driver -class TestWebDriverKeyboard(object): +class TestWebDriverKeyboardAndroid(object): @httpretty.activate def test_hide_keyboard(self): driver = android_w3c_driver() @@ -96,3 +96,38 @@ def test_long_press_keycode_with_flags(self): driver.long_press_keycode(86, metastate=0x00000001 | 0x00200000, flags=0x20 | 0x00000004 | 0x00000008), WebDriver, ) + + +class TestWebDriverKeyboardIOS(object): + @httpretty.activate + def test_hide_keyboard(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + assert isinstance(driver.hide_keyboard(), WebDriver) + assert {'args': [{}], 'script': 'mobile: hideKeyboard'} == get_httpretty_request_body(httpretty.last_request()) + + @httpretty.activate + def test_hide_keyboard_with_key(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + assert isinstance(driver.hide_keyboard(key_name='Done'), WebDriver) + assert {'args': [{'keys': ['Done']}], 'script': 'mobile: hideKeyboard'} == get_httpretty_request_body( + httpretty.last_request() + ) + + @httpretty.activate + def test_hide_keyboard_with_key_and_strategy(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + assert isinstance(driver.hide_keyboard(strategy='pressKey', key='Done'), WebDriver) + # only 'keys' works + assert {'args': [{'keys': ['Done']}], 'script': 'mobile: hideKeyboard'} == get_httpretty_request_body( + httpretty.last_request() + ) + + @httpretty.activate + def test_is_keyboard_shown(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + driver.is_keyboard_shown(), WebDriver + assert {'script': 'mobile: isKeyboardShown', 'args': []} == get_httpretty_request_body(httpretty.last_request()) diff --git a/test/unit/webdriver/device/lock_test.py b/test/unit/webdriver/device/lock_test.py index 1e8ce09c..7a357a70 100644 --- a/test/unit/webdriver/device/lock_test.py +++ b/test/unit/webdriver/device/lock_test.py @@ -15,10 +15,10 @@ import httpretty from appium.webdriver.webdriver import WebDriver -from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body +from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body, ios_w3c_driver -class TestWebDriverLock(object): +class TestWebDriverLockAndroid(object): @httpretty.activate def test_lock(self): driver = android_w3c_driver() @@ -71,3 +71,77 @@ def test_unlock(self): ) httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) assert isinstance(driver.unlock(), WebDriver) + + +class TestWebDriverLockIOS(object): + @httpretty.activate + def test_lock(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/lock'), body='{"value": ""}' + ) + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') + driver.lock(1) + + d = get_httpretty_request_body(httpretty.last_request()) + assert d.get('seconds', d['args'][0]['seconds']) == 1 + + @httpretty.activate + def test_lock_no_args(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/lock'), body='{"value": ""}' + ) + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": ""}') + driver.lock() + + @httpretty.activate + def test_islocked_false(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/is_locked'), body='{"value": false}' + ) + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": false}' + ) + assert driver.is_locked() is False + + @httpretty.activate + def test_islocked_true(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/appium/device/is_locked'), body='{"value": true}' + ) + httpretty.register_uri( + httpretty.POST, appium_command('/session/1234567890/execute/sync'), body='{"value": true}' + ) + assert driver.is_locked() is True + + @httpretty.activate + def test_unlock(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/device/unlock'), + ) + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + assert isinstance(driver.unlock(), WebDriver) + + @httpretty.activate + def test_touch_id(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + assert isinstance(driver.touch_id(True), WebDriver) + assert { + 'script': 'mobile: sendBiometricMatch', + 'args': [{'match': True, 'type': 'touchId'}], + } == get_httpretty_request_body(httpretty.last_request()) + + @httpretty.activate + def test_touch_id(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + assert isinstance(driver.toggle_touch_id_enrollment(), WebDriver) + assert {'script': 'mobile: enrollBiometric', 'args': [{'isEnabled': True}]} == get_httpretty_request_body( + httpretty.last_request() + ) From 2b48a09a707e669b1d8caa9d48ca578ecc34f3e4 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 30 Sep 2024 00:28:06 -0700 Subject: [PATCH 4/5] test: cleanup func tests for ios more (#1036) --- .github/workflows/functional-test.yml | 4 +- test/functional/ios/execute_driver_tests.py | 43 ------- test/functional/ios/file/test_file.txt | 1 - test/functional/ios/file/test_image.jpg | Bin 21422 -> 0 bytes test/functional/ios/remote_fs_tests.py | 26 ---- test/functional/ios/screen_record_tests.py | 25 ---- .../functional/ios/search_context/__init__.py | 0 .../find_by_element_webelement_tests.py | 31 ----- .../find_by_ios_class_chain_tests.py | 29 ----- .../find_by_ios_predicate_tests.py | 50 -------- test/functional/ios/webdriver_tests.py | 76 ----------- test/unit/webdriver/device/keyboard_test.py | 9 ++ test/unit/webdriver/screen_record_test.py | 35 ++++- .../unit/webdriver/search_context/ios_test.py | 121 ++++++++++++++++++ 14 files changed, 164 insertions(+), 286 deletions(-) delete mode 100644 test/functional/ios/execute_driver_tests.py delete mode 100644 test/functional/ios/file/test_file.txt delete mode 100644 test/functional/ios/file/test_image.jpg delete mode 100644 test/functional/ios/remote_fs_tests.py delete mode 100644 test/functional/ios/screen_record_tests.py delete mode 100644 test/functional/ios/search_context/__init__.py delete mode 100644 test/functional/ios/search_context/find_by_element_webelement_tests.py delete mode 100644 test/functional/ios/search_context/find_by_ios_class_chain_tests.py delete mode 100644 test/functional/ios/search_context/find_by_ios_predicate_tests.py delete mode 100644 test/functional/ios/webdriver_tests.py create mode 100644 test/unit/webdriver/search_context/ios_test.py diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index bfa1b713..e39e3e8a 100644 --- a/.github/workflows/functional-test.yml +++ b/.github/workflows/functional-test.yml @@ -18,10 +18,8 @@ jobs: fail-fast: false matrix: test_targets: - - target: test/functional/ios/search_context/find_by_*.py test/functional/ios/remote_fs_tests.py test/functional/ios/safari_tests.py test/functional/ios/execute_driver_tests.py + - target: test/functional/ios/safari_tests.py name: func_test_ios1 - - target: test/functional/ios/screen_record_tests.py test/functional/ios/webdriver_tests.py - name: func_test_ios2 runs-on: macos-14 diff --git a/test/functional/ios/execute_driver_tests.py b/test/functional/ios/execute_driver_tests.py deleted file mode 100644 index 5e0e5009..00000000 --- a/test/functional/ios/execute_driver_tests.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import textwrap - -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestExecuteDriver(BaseTestCase): - def test_batch(self) -> None: - script = """ - const status = await driver.status(); - console.warn('warning message'); - return status; - """ - - response = self.driver.execute_driver(script=textwrap.dedent(script)) - assert response.result['build'] - assert response.logs['warn'] == ['warning message'] - - def test_batch_combination_python_script(self) -> None: - script = """ - console.warn('warning message'); - const element = await driver.findElement('accessibility id', 'Buttons'); - const rect = await driver.getElementRect(element.ELEMENT); - return [element, rect]; - """ - - response = self.driver.execute_driver(script=textwrap.dedent(script)) - r = response.result[0].rect - - assert r == response.result[1] diff --git a/test/functional/ios/file/test_file.txt b/test/functional/ios/file/test_file.txt deleted file mode 100644 index aca4a3b3..00000000 --- a/test/functional/ios/file/test_file.txt +++ /dev/null @@ -1 +0,0 @@ -"We have to stop optimizing for programmers and start optimizing for users." - Jeff Atwood \ No newline at end of file diff --git a/test/functional/ios/file/test_image.jpg b/test/functional/ios/file/test_image.jpg deleted file mode 100644 index 6434234fb11467c62823873c58456aa5df3013c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21422 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 AqyPW_ diff --git a/test/functional/ios/remote_fs_tests.py b/test/functional/ios/remote_fs_tests.py deleted file mode 100644 index 5e051d73..00000000 --- a/test/functional/ios/remote_fs_tests.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os - -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestRemoteFs(BaseTestCase): - def test_push_file(self) -> None: - file_name = 'test_image.jpg' - source_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'file', file_name) - destination_path = file_name - - self.driver.push_file(destination_path, source_path=source_path) diff --git a/test/functional/ios/screen_record_tests.py b/test/functional/ios/screen_record_tests.py deleted file mode 100644 index b821f6d3..00000000 --- a/test/functional/ios/screen_record_tests.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from time import sleep - -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestScreenRecord(BaseTestCase): - def test_screen_record(self) -> None: - self.driver.start_recording_screen() - sleep(10) - result = self.driver.stop_recording_screen() - assert len(result) > 0 diff --git a/test/functional/ios/search_context/__init__.py b/test/functional/ios/search_context/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/test/functional/ios/search_context/find_by_element_webelement_tests.py b/test/functional/ios/search_context/find_by_element_webelement_tests.py deleted file mode 100644 index 163c2320..00000000 --- a/test/functional/ios/search_context/find_by_element_webelement_tests.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestFindByElementWebelement(BaseTestCase): - def test_find_element_by_path(self) -> None: - el = self.driver.find_element(by=AppiumBy.IOS_PREDICATE, value='wdName == "UIKitCatalog"') - assert self.IOS_UICATALOG_APP_NAME == el.get_attribute('name') - - c_el = el.find_elements(by=AppiumBy.IOS_PREDICATE, value='label == "UIKitCatalog"') # type: list - assert self.IOS_UICATALOG_APP_NAME == c_el[0].get_attribute('name') - - c_el = el.find_elements(by=AppiumBy.IOS_CLASS_CHAIN, value='**/XCUIElementTypeStaticText') - assert self.IOS_UICATALOG_APP_NAME == c_el[0].get_attribute('name') - - c_el = el.find_elements(by=AppiumBy.ACCESSIBILITY_ID, value='UIKitCatalog') - assert self.IOS_UICATALOG_APP_NAME == c_el[0].get_attribute('name') diff --git a/test/functional/ios/search_context/find_by_ios_class_chain_tests.py b/test/functional/ios/search_context/find_by_ios_class_chain_tests.py deleted file mode 100644 index 6836ffc7..00000000 --- a/test/functional/ios/search_context/find_by_ios_class_chain_tests.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestFindByIOClassChain(BaseTestCase): - def test_find_element_by_path(self) -> None: - el = self.driver.find_element(by=AppiumBy.IOS_CLASS_CHAIN, value='**/XCUIElementTypeNavigationBar') - assert self.IOS_UICATALOG_APP_NAME == el.get_attribute('name') - - def test_find_multiple_elements_by_path(self) -> None: - els = self.driver.find_elements( - by=AppiumBy.IOS_CLASS_CHAIN, value='XCUIElementTypeWindow/**/XCUIElementTypeStaticText' - ) - assert 37 == len(els) - assert self.IOS_UICATALOG_APP_NAME == els[0].get_attribute('name') diff --git a/test/functional/ios/search_context/find_by_ios_predicate_tests.py b/test/functional/ios/search_context/find_by_ios_predicate_tests.py deleted file mode 100644 index f98b8f30..00000000 --- a/test/functional/ios/search_context/find_by_ios_predicate_tests.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.ios.helper.test_helper import BaseTestCase - - -class TestFindByIOSPredicate(BaseTestCase): - def test_find_element_by_name(self) -> None: - # Will throw exception if element is not found - self.driver.find_element(by=AppiumBy.IOS_PREDICATE, value='wdName == "Buttons"') - - def test_find_multiple_element_by_type(self) -> None: - e = self.driver.find_elements(by=AppiumBy.IOS_PREDICATE, value='wdType == "XCUIElementTypeStaticText"') - assert len(e) != 0 - - def test_find_element_by_label(self) -> None: - # Will throw exception if element is not found - self.driver.find_element(by=AppiumBy.IOS_PREDICATE, value='label == "Buttons"') - - def test_find_element_by_value(self) -> None: - # Will throw exception if element is not found - self.driver.find_element(by=AppiumBy.IOS_PREDICATE, value='wdValue == "Buttons"') - - def test_find_element_by_isvisible(self) -> None: - # Will throw exception if element is not found - self.driver.find_element(by=AppiumBy.IOS_PREDICATE, value='wdValue == "Buttons" AND visible == 1') - - # Should not find any elements - e = self.driver.find_elements(by=AppiumBy.IOS_PREDICATE, value='wdValue == "Buttons" AND visible == 0') - assert len(e) == 0 - - def test_find_element_by_isenabled(self) -> None: - # Will throw exception if element is not found - self.driver.find_element(by=AppiumBy.IOS_PREDICATE, value='wdValue == "Buttons" AND enabled == 1') - - # Should not find any elements - e = self.driver.find_elements(by=AppiumBy.IOS_PREDICATE, value='wdValue == "Buttons" AND enabled == 0') - assert len(e) == 0 diff --git a/test/functional/ios/webdriver_tests.py b/test/functional/ios/webdriver_tests.py deleted file mode 100644 index 9cb8c62d..00000000 --- a/test/functional/ios/webdriver_tests.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from appium.webdriver.applicationstate import ApplicationState -from appium.webdriver.common.appiumby import AppiumBy -from test.functional.ios.helper.test_helper import BaseTestCase -from test.functional.test_helper import wait_for_condition - -from .helper import desired_capabilities - - -class TestWebDriver(BaseTestCase): - def test_app_management(self) -> None: - # this only works in Xcode9+ - if float(desired_capabilities.get_desired_capabilities(desired_capabilities.BUNDLE_ID)['platformVersion']) < 11: - return - assert self.driver.query_app_state(desired_capabilities.BUNDLE_ID) == ApplicationState.RUNNING_IN_FOREGROUND - self.driver.background_app(-1) - print(self.driver.query_app_state(desired_capabilities.BUNDLE_ID) < ApplicationState.RUNNING_IN_FOREGROUND) - assert wait_for_condition( - lambda: self.driver.query_app_state(desired_capabilities.BUNDLE_ID) - < ApplicationState.RUNNING_IN_FOREGROUND, - ), 'The app didn\'t go to background.' - self.driver.activate_app(desired_capabilities.BUNDLE_ID) - assert self.driver.query_app_state(desired_capabilities.BUNDLE_ID) == ApplicationState.RUNNING_IN_FOREGROUND - - def test_clear(self) -> None: - self._move_to_textbox() - - el = self.driver.find_elements(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeTextField')[0] - - # Verify default text - def_text = 'Placeholder text' - text = el.get_attribute('value') - assert text == def_text - - # Input some text, verify - input_text = 'blah' - el.click() - el.send_keys(input_text) - self.driver.hide_keyboard() - - # TODO Needs to get the element again to update value in the element. Remove below one line when it's fixed. - el = self.driver.find_elements(by=AppiumBy.CLASS_NAME, value='XCUIElementTypeTextField')[0] - text = el.get_attribute('value') - assert text == input_text - - # Clear text, verify - el.clear() - text = el.get_attribute('value') - assert text == def_text - - def test_press_button(self) -> None: - self.driver.press_button('Home') - if float(desired_capabilities.get_desired_capabilities(desired_capabilities.BUNDLE_ID)['platformVersion']) < 11: - return - assert self.driver.query_app_state(desired_capabilities.BUNDLE_ID) == ApplicationState.RUNNING_IN_FOREGROUND - - def _move_to_textbox(self) -> None: - el1 = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Sliders') - el2 = self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Buttons') - self.driver.scroll(el1, el2) - - # Click text fields - self.driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='Text Fields').click() diff --git a/test/unit/webdriver/device/keyboard_test.py b/test/unit/webdriver/device/keyboard_test.py index 79579fb9..40e3e289 100644 --- a/test/unit/webdriver/device/keyboard_test.py +++ b/test/unit/webdriver/device/keyboard_test.py @@ -131,3 +131,12 @@ def test_is_keyboard_shown(self): httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) driver.is_keyboard_shown(), WebDriver assert {'script': 'mobile: isKeyboardShown', 'args': []} == get_httpretty_request_body(httpretty.last_request()) + + @httpretty.activate + def test_press_button(self): + driver = ios_w3c_driver() + httpretty.register_uri(httpretty.POST, appium_command('/session/1234567890/execute/sync')) + driver.press_button('Home') + assert {'script': 'mobile: pressButton', 'args': [{'name': 'Home'}]} == get_httpretty_request_body( + httpretty.last_request() + ) diff --git a/test/unit/webdriver/screen_record_test.py b/test/unit/webdriver/screen_record_test.py index 38054553..da470ceb 100644 --- a/test/unit/webdriver/screen_record_test.py +++ b/test/unit/webdriver/screen_record_test.py @@ -9,10 +9,10 @@ import httpretty -from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body +from test.unit.helper.test_helper import android_w3c_driver, appium_command, get_httpretty_request_body, ios_w3c_driver -class TestWebDriverScreenRecord(object): +class TestWebDriverScreenRecordAndroid(object): @httpretty.activate def test_start_recording_screen(self): driver = android_w3c_driver() @@ -41,3 +41,34 @@ def test_stop_recording_screen(self): assert d['options']['user'] == 'userA' assert d['options']['pass'] == '12345' assert 'password' not in d['options'].keys() + + +class TestWebDriverScreenRecordIOS(object): + @httpretty.activate + def test_start_recording_screen(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/start_recording_screen'), + ) + assert driver.start_recording_screen(user='userA', password='12345') is None + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['options']['user'] == 'userA' + assert d['options']['pass'] == '12345' + assert 'password' not in d['options'].keys() + + @httpretty.activate + def test_stop_recording_screen(self): + driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/stop_recording_screen'), + body='{"value": "b64_video_data"}', + ) + assert driver.stop_recording_screen(user='userA', password='12345') == 'b64_video_data' + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['options']['user'] == 'userA' + assert d['options']['pass'] == '12345' + assert 'password' not in d['options'].keys() diff --git a/test/unit/webdriver/search_context/ios_test.py b/test/unit/webdriver/search_context/ios_test.py new file mode 100644 index 00000000..d6b288a4 --- /dev/null +++ b/test/unit/webdriver/search_context/ios_test.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import httpretty + +from appium.webdriver.common.appiumby import AppiumBy +from appium.webdriver.webelement import WebElement as MobileWebElement +from test.unit.helper.test_helper import appium_command, get_httpretty_request_body, ios_w3c_driver + + +class TestWebDriverIOSSearchContext(object): + @httpretty.activate + def test_find_element_by_ios_predicate(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element'), + body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "element-id"}}', + ) + el = driver.find_element(by=AppiumBy.IOS_PREDICATE, value='wdName == "UIKitCatalog"') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == '-ios predicate string' + assert d['value'] == 'wdName == "UIKitCatalog"' + assert el.id == 'element-id' + + @httpretty.activate + def test_find_elements_by_ios_predicate(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/elements'), + body='{"value": [{"element-6066-11e4-a52e-4f735466cecf": "element-id1"}, ' + '{"element-6066-11e4-a52e-4f735466cecf": "element-id2"}]}', + ) + els = driver.find_elements(by=AppiumBy.IOS_PREDICATE, value='wdName == "UIKitCatalog"') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == '-ios predicate string' + assert d['value'] == 'wdName == "UIKitCatalog"' + assert els[0].id == 'element-id1' + assert els[1].id == 'element-id2' + + @httpretty.activate + def test_find_child_elements_by_ios_predicate(self): + driver = ios_w3c_driver() + element = MobileWebElement(driver, 'element_id') + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element/element_id/elements'), + body='{"value": [{"element-6066-11e4-a52e-4f735466cecf": "child-element-id1"}, ' + '{"element-6066-11e4-a52e-4f735466cecf": "child-element-id2"}]}', + ) + els = element.find_elements(by=AppiumBy.IOS_PREDICATE, value='wdName == "UIKitCatalog"') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == '-ios predicate string' + assert d['value'] == 'wdName == "UIKitCatalog"' + assert els[0].id == 'child-element-id1' + assert els[1].id == 'child-element-id2' + + @httpretty.activate + def test_find_element_by_ios_class_chain(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element'), + body='{"value": {"element-6066-11e4-a52e-4f735466cecf": "element-id"}}', + ) + el = driver.find_element(by=AppiumBy.IOS_CLASS_CHAIN, value='**/XCUIElementTypeStaticText') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == '-ios class chain' + assert d['value'] == '**/XCUIElementTypeStaticText' + assert el.id == 'element-id' + + @httpretty.activate + def test_find_elements_by_ios_class_chain(self): + driver = ios_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/elements'), + body='{"value": [{"element-6066-11e4-a52e-4f735466cecf": "element-id1"}, ' + '{"element-6066-11e4-a52e-4f735466cecf": "element-id2"}]}', + ) + els = driver.find_elements(by=AppiumBy.IOS_CLASS_CHAIN, value='**/XCUIElementTypeStaticText') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == '-ios class chain' + assert d['value'] == '**/XCUIElementTypeStaticText' + assert els[0].id == 'element-id1' + assert els[1].id == 'element-id2' + + @httpretty.activate + def test_find_child_elements_by_ios_class_chain(self): + driver = ios_w3c_driver() + element = MobileWebElement(driver, 'element_id') + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element/element_id/elements'), + body='{"value": [{"element-6066-11e4-a52e-4f735466cecf": "child-element-id1"}, ' + '{"element-6066-11e4-a52e-4f735466cecf": "child-element-id2"}]}', + ) + els = element.find_elements(by=AppiumBy.IOS_CLASS_CHAIN, value='**/XCUIElementTypeStaticText') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['using'] == '-ios class chain' + assert d['value'] == '**/XCUIElementTypeStaticText' + assert els[0].id == 'child-element-id1' + assert els[1].id == 'child-element-id2' From e4b40aefc573429d40d51b60ac03ca2961b3313e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:14:51 -0700 Subject: [PATCH 5/5] chore(deps-dev): update tox requirement from ~=4.20 to ~=4.21 (#1037) Updates the requirements on [tox](https://github.com/tox-dev/tox) to permit the latest version. - [Release notes](https://github.com/tox-dev/tox/releases) - [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/4.20.0...4.21.0) --- updated-dependencies: - dependency-name: tox dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Pipfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index a6e31095..461732aa 100644 --- a/Pipfile +++ b/Pipfile @@ -15,7 +15,7 @@ pylint-quotes = "~=0.2.3" pytest = "~=8.3" pytest-cov = "~=4.1" python-dateutil = "~=2.9" -tox = "~=4.20" +tox = "~=4.21" types-python-dateutil = "~=2.9" [packages]