diff --git a/.github/workflows/functional-test.yml b/.github/workflows/functional-test.yml index bfa1b7133..e39e3e8a2 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 5e0e5009e..000000000 --- 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 aca4a3b3e..000000000 --- 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 6434234fb..000000000 Binary files a/test/functional/ios/file/test_image.jpg and /dev/null differ diff --git a/test/functional/ios/remote_fs_tests.py b/test/functional/ios/remote_fs_tests.py deleted file mode 100644 index 5e051d732..000000000 --- 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 b821f6d30..000000000 --- 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 e69de29bb..000000000 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 163c23205..000000000 --- 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 6836ffc71..000000000 --- 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 f98b8f30d..000000000 --- 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 9cb8c62dd..000000000 --- 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 79579fb99..40e3e289a 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 380545531..da470cebf 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 000000000..d6b288a41 --- /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'