From 6e1a2a7314a75a26be5eadcef4b26590bf5addc3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Jan 2020 20:41:40 +0900 Subject: [PATCH 01/25] Update pytest-cov requirement from ~=2.6 to ~=2.8 (#489) Updates the requirements on [pytest-cov](https://github.com/pytest-dev/pytest-cov) to permit the latest version. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.6.0...v2.8.1) Signed-off-by: dependabot-preview[bot] --- Pipfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 793690c1..1a6d5c88 100644 --- a/Pipfile +++ b/Pipfile @@ -12,7 +12,7 @@ selenium = "~=3.141" autopep8 = "~=1.4" pytest = "~=4.0" -pytest-cov = "~=2.6" +pytest-cov = "~=2.8" tox = "~=3.6" tox-travis = "~=0.11" From 53dc75f63395a750e1a37726122ba8b98d1a93e7 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Jan 2020 20:43:47 +0900 Subject: [PATCH 02/25] Update autopep8 requirement from ~=1.4 to ~=1.5 (#490) Updates the requirements on [autopep8](https://github.com/hhatto/autopep8) to permit the latest version. - [Release notes](https://github.com/hhatto/autopep8/releases) - [Commits](https://github.com/hhatto/autopep8/compare/v1.4...v1.5) Signed-off-by: dependabot-preview[bot] --- Pipfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 1a6d5c88..76898d3b 100644 --- a/Pipfile +++ b/Pipfile @@ -9,7 +9,7 @@ pre-commit = "~=1.13" [packages] selenium = "~=3.141" -autopep8 = "~=1.4" +autopep8 = "~=1.5" pytest = "~=4.0" pytest-cov = "~=2.8" From 3359a4476258474737e29d79e9f286bbd1050a99 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 26 Jan 2020 20:45:08 +0900 Subject: [PATCH 03/25] Update tox-travis requirement from ~=0.11 to ~=0.12 (#491) Updates the requirements on [tox-travis](https://github.com/tox-dev/tox-travis) to permit the latest version. - [Release notes](https://github.com/tox-dev/tox-travis/releases) - [Changelog](https://github.com/tox-dev/tox-travis/blob/master/HISTORY.rst) - [Commits](https://github.com/tox-dev/tox-travis/compare/0.11...0.12) Signed-off-by: dependabot-preview[bot] --- Pipfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 76898d3b..898b549b 100644 --- a/Pipfile +++ b/Pipfile @@ -15,7 +15,7 @@ pytest = "~=4.0" pytest-cov = "~=2.8" tox = "~=3.6" -tox-travis = "~=0.11" +tox-travis = "~=0.12" httpretty = "~=0.9" python-dateutil = "~=2.8" From 4d269f828ab835d0b01a3c3d1b38ff7f89bbabde Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2020 23:14:58 +0900 Subject: [PATCH 04/25] Update tox requirement from ~=3.6 to ~=3.14 (#494) 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/master/docs/changelog.rst) - [Commits](https://github.com/tox-dev/tox/compare/3.6.0...3.14.3) Signed-off-by: dependabot-preview[bot] --- Pipfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Pipfile b/Pipfile index 898b549b..7446b7b3 100644 --- a/Pipfile +++ b/Pipfile @@ -14,7 +14,7 @@ autopep8 = "~=1.5" pytest = "~=4.0" pytest-cov = "~=2.8" -tox = "~=3.6" +tox = "~=3.14" tox-travis = "~=0.12" httpretty = "~=0.9" From b95776d8296b0778133096fbfee8bb13eafcdecb Mon Sep 17 00:00:00 2001 From: Mori Atsushi Date: Fri, 31 Jan 2020 07:46:21 +0900 Subject: [PATCH 05/25] chore: Fix find_by_images_tests.py (#495) * chore: Fix find_by_images_tests.py * Add installation opencv4nodejs * Fix typo * Add taking screen record to find_by_image_test * Fix errors on the emulator * Remove unused imports --- ci-jobs/functional/run_android_test.yml | 2 ++ ci-jobs/functional/run_appium.yml | 3 +++ ci-jobs/functional_test.yml | 1 + .../android/file/find_by_image_success.png | Bin 6341 -> 5226 bytes .../search_context/find_by_image_tests.py | 23 +++++++----------- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ci-jobs/functional/run_android_test.yml b/ci-jobs/functional/run_android_test.yml index cedf2ac7..1786e8dc 100644 --- a/ci-jobs/functional/run_android_test.yml +++ b/ci-jobs/functional/run_android_test.yml @@ -7,6 +7,8 @@ jobs: CI: ${{ parameters.ci }} steps: - template: ./run_appium.yml + parameters: + OPENCV: ${{ parameters.opencv }} - script: bash ci-jobs/functional/start-emulator.sh displayName: Create and run Emulator - script: | diff --git a/ci-jobs/functional/run_appium.yml b/ci-jobs/functional/run_appium.yml index 42ebadd3..dbc773b1 100644 --- a/ci-jobs/functional/run_appium.yml +++ b/ci-jobs/functional/run_appium.yml @@ -5,6 +5,9 @@ steps: displayName: Install Node 11.x - script: npm install -g appium@beta --chromedriver_version='2.44' displayName: Install appium +- script: npm install -g opencv4nodejs + condition: and(succeeded(), eq('${{ parameters.opencv }}', true)) + displayName: Install opencv4nodejs - task: UsePythonVersion@0 inputs: versionSpec: '3.x' diff --git a/ci-jobs/functional_test.yml b/ci-jobs/functional_test.yml index cc807502..f1455f64 100644 --- a/ci-jobs/functional_test.yml +++ b/ci-jobs/functional_test.yml @@ -32,6 +32,7 @@ jobs: testFiles: 'device_time_tests.py search_context/find_by_*.py' sdkVer: ${{ parameters.androidSdkVer }} CI: ${{ parameters.ci }} + OPENCV: true - template: ./functional/run_android_test.yml parameters: name: 'func_test_android2' diff --git a/test/functional/android/file/find_by_image_success.png b/test/functional/android/file/find_by_image_success.png index a7f06ca9f90655f31e0e9fd9b135109705865f99..39435fd3a9c6d79c0dbd2bd4127a09d3264ea0a6 100644 GIT binary patch literal 5226 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 literal 6341 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 U%6O@h|M`?ql2?~2moX3eALyHPApigX diff --git a/test/functional/android/search_context/find_by_image_tests.py b/test/functional/android/search_context/find_by_image_tests.py index 9d628c28..0ba6f8a9 100644 --- a/test/functional/android/search_context/find_by_image_tests.py +++ b/test/functional/android/search_context/find_by_image_tests.py @@ -15,21 +15,21 @@ import base64 import unittest -import pytest from selenium.common.exceptions import NoSuchElementException, TimeoutException -from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from appium import webdriver +from appium.webdriver.common.mobileby import MobileBy from test.functional.android.helper import desired_capabilities +from ..helper.test_helper import wait_for_element + -@pytest.mark.skip(reason="Need to fix broken test") class FindByImageTests(unittest.TestCase): def setUp(self): - desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk') + desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip') self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) # relax template matching @@ -42,13 +42,10 @@ def tearDown(self): def test_find_based_on_image_template(self): image_path = desired_capabilities.PATH('file/find_by_image_success.png') - print(image_path) with open(image_path, 'rb') as png_file: b64_data = base64.b64encode(png_file.read()).decode('UTF-8') - el = WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((By.IMAGE, b64_data)) - ) + el = wait_for_element(self.driver, MobileBy.IMAGE, b64_data) size = el.size self.assertIsNotNone(size['width']) self.assertIsNotNone(size['height']) @@ -62,16 +59,14 @@ def test_find_based_on_image_template(self): self.assertIsNotNone(rect['y']) self.assertTrue(el.is_displayed()) el.click() - self.driver.find_element_by_accessibility_id("Alarm") + wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, "Alarm") def test_find_multiple_elements_by_image_just_returns_one(self): - WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((By.ACCESSIBILITY_ID, "App")) - ) + wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, "App") image_path = desired_capabilities.PATH('file/find_by_image_success.png') els = self.driver.find_elements_by_image(image_path) els[0].click() - self.driver.find_element_by_accessibility_id("Alarm") + wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, "Alarm") def test_find_throws_no_such_element(self): image_path = desired_capabilities.PATH('file/find_by_image_failure.png') @@ -80,7 +75,7 @@ def test_find_throws_no_such_element(self): with self.assertRaises(TimeoutException): WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((By.IMAGE, b64_data)) + EC.presence_of_element_located((MobileBy.IMAGE, b64_data)) ) with self.assertRaises(NoSuchElementException): self.driver.find_element_by_image(image_path) From f2bf259bd386406dfabbe26943b27a9e74090106 Mon Sep 17 00:00:00 2001 From: Mori Atsushi Date: Sun, 9 Feb 2020 22:25:55 +0900 Subject: [PATCH 06/25] feat: Add viewmatcher (#480) * Add android view matcher as strategy locator * Add docstring * Add functional test * Remove find_elements_by_android_data_matcher * Fix docstring * tweak docstring --- appium/webdriver/common/mobileby.py | 1 + .../extensions/search_context/android.py | 34 ++++++++ .../find_by_view_matcher_tests.py | 77 +++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 test/functional/android/search_context/find_by_view_matcher_tests.py diff --git a/appium/webdriver/common/mobileby.py b/appium/webdriver/common/mobileby.py index 34970a7b..2034275f 100644 --- a/appium/webdriver/common/mobileby.py +++ b/appium/webdriver/common/mobileby.py @@ -22,6 +22,7 @@ class MobileBy(By): ANDROID_UIAUTOMATOR = '-android uiautomator' ANDROID_VIEWTAG = '-android viewtag' ANDROID_DATA_MATCHER = '-android datamatcher' + ANDROID_VIEW_MATCHER = '-android viewmatcher' WINDOWS_UI_AUTOMATION = '-windows uiautomation' ACCESSIBILITY_ID = 'accessibility id' IMAGE = '-image' diff --git a/appium/webdriver/extensions/search_context/android.py b/appium/webdriver/extensions/search_context/android.py index 7c6f127a..942675c7 100644 --- a/appium/webdriver/extensions/search_context/android.py +++ b/appium/webdriver/extensions/search_context/android.py @@ -24,6 +24,39 @@ class AndroidSearchContext(BaseSearchContext): """Define search context for Android""" + def find_element_by_android_view_matcher(self, name=None, args=None, className=None): + """Finds element by [onView](https://developer.android.com/training/testing/espresso/basics) in Android + + It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). + + Args: + name (:obj:`str`, optional): The name of a method to invoke. + The method must return a Hamcrest + [Matcher](http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html) + args (:obj:`str`, optional): The args provided to the method + className (:obj:`str`, optional): The class name that the method is part of (defaults to `org.hamcrest.Matchers`). + Can be fully qualified by having the androidx.test.espresso.matcher. prefix. + If the prefix is not provided then it is going to be added implicitly. + (e.g.: `class=CursorMatchers` fully qualified is `class=androidx.test.espresso.matcher.CursorMatchers` + + Returns: + `appium.webdriver.webelement.WebElement`: The found element + + Raises: + TypeError - Raises a TypeError if the arguments are not validated for JSON format + + Usage: + driver.find_element_by_android_view_matcher(name='withText', args=['Accessibility'], className='ViewMatchers') + + # To enable auto completion in PyCharm(IDE) + :rtype: `appium.webdriver.webelement.WebElement` + """ + + return self.find_element( + by=MobileBy.ANDROID_VIEW_MATCHER, + value=self._build_data_matcher(name=name, args=args, className=className) + ) + def find_element_by_android_data_matcher(self, name=None, args=None, className=None): """Finds element by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android @@ -58,6 +91,7 @@ def find_element_by_android_data_matcher(self, name=None, args=None, className=N def find_elements_by_android_data_matcher(self, name=None, args=None, className=None): """Finds elements by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android + It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). Args: 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 new file mode 100644 index 00000000..0f8ed0d0 --- /dev/null +++ b/test/functional/android/search_context/find_by_view_matcher_tests.py @@ -0,0 +1,77 @@ +#!/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 +import unittest + +import pytest +from selenium.common.exceptions import WebDriverException + +from appium import webdriver +from appium.webdriver.common.mobileby import MobileBy +from appium.webdriver.extensions.search_context.android import ( + AndroidSearchContext +) +from test.functional.android.helper.test_helper import ( + desired_capabilities, + is_ci +) + + +class FindByViewMatcherTests(unittest.TestCase): + + def setUp(self): + desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip') + desired_caps['automationName'] = 'Espresso' + self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) + + def tearDown(self): + if is_ci(): + # Take the screenshot to investigate when tests failed only on CI + img_path = os.path.join(os.getcwd(), self._testMethodName + '.png') + self.driver.get_screenshot_as_file(img_path) + self.driver.quit() + + def test_find_single_element(self): + el = self.driver.find_element_by_android_view_matcher( + name='withText', args=['Accessibility'], className='ViewMatchers') + assert el.text == 'Accessibility' + + def test_find_single_element_ful_class_name(self): + el = self.driver.find_element_by_android_view_matcher( + name='withText', args=['Accessibility'], className='androidx.test.espresso.matcher.ViewMatchers') + assert el.text == 'Accessibility' + + def test_find_single_element_using_hamcrest_matcher(self): + el = self.driver.find_element_by_android_view_matcher( + name='withText', + args={ + 'name': 'containsString', + 'args': 'Animati', + 'class': 'org.hamcrest.Matchers'}, + className='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): + value = AndroidSearchContext()._build_data_matcher( + name='withSubstring', args=['Access'], className='ViewMatchers') + with pytest.raises(WebDriverException): + self.driver.find_elements(by=MobileBy.ANDROID_VIEW_MATCHER, value=value) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(FindByViewMatcherTests) + unittest.TextTestRunner(verbosity=2).run(suite) From 864b045149f0aa35b3e7cf262cb2f5847161fcc9 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 10 Feb 2020 22:31:19 +0900 Subject: [PATCH 07/25] Bump 0.50 --- appium/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appium/version.py b/appium/version.py index fda2e626..70897073 100644 --- a/appium/version.py +++ b/appium/version.py @@ -1 +1 @@ -version = '0.49' +version = '0.50' From f14291b45f586be11fc476178fe77a4b599cec42 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Mon, 10 Feb 2020 22:31:24 +0900 Subject: [PATCH 08/25] Update changelog for 0.50 --- CHANGELOG.rst | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f1a8ae78..a7b9fbaf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,100 @@ Changelog ========= +v0.50 (2020-02-10) +------------------ +- Bump 0.50. [Kazuaki Matsuo] +- Feat: Add viewmatcher (#480) [Mori Atsushi] + + * Add android view matcher as strategy locator + + * Add docstring + + * Add functional test + + * Remove find_elements_by_android_data_matcher + + * Fix docstring + + * tweak docstring +- Chore: Fix find_by_images_tests.py (#495) [Mori Atsushi] + + * chore: Fix find_by_images_tests.py + + * Add installation opencv4nodejs + + * Fix typo + + * Add taking screen record to find_by_image_test + + * Fix errors on the emulator + + * Remove unused imports +- Update tox requirement from ~=3.6 to ~=3.14 (#494) [dependabot- + preview[bot]] + + 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/master/docs/changelog.rst) + - [Commits](https://github.com/tox-dev/tox/compare/3.6.0...3.14.3) +- Update tox-travis requirement from ~=0.11 to ~=0.12 (#491) + [dependabot-preview[bot]] + + Updates the requirements on [tox-travis](https://github.com/tox-dev/tox-travis) to permit the latest version. + - [Release notes](https://github.com/tox-dev/tox-travis/releases) + - [Changelog](https://github.com/tox-dev/tox-travis/blob/master/HISTORY.rst) + - [Commits](https://github.com/tox-dev/tox-travis/compare/0.11...0.12) +- Update autopep8 requirement from ~=1.4 to ~=1.5 (#490) [dependabot- + preview[bot]] + + Updates the requirements on [autopep8](https://github.com/hhatto/autopep8) to permit the latest version. + - [Release notes](https://github.com/hhatto/autopep8/releases) + - [Commits](https://github.com/hhatto/autopep8/compare/v1.4...v1.5) +- Update pytest-cov requirement from ~=2.6 to ~=2.8 (#489) [dependabot- + preview[bot]] + + Updates the requirements on [pytest-cov](https://github.com/pytest-dev/pytest-cov) to permit the latest version. + - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) + - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) + - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.6.0...v2.8.1) +- Chore: add try/catch in release script (#479) [Kazuaki Matsuo] + + * Add m and try/catch in pushing + + * fix error message + + * remove -m since it does not work for this usage +- [CI] Run with iOS 13.3 and Xcode 11.3 (#477) [Mori Atsushi] + + * [CI] Run with iOS 13.3 and Xcode 11.3 + + * Skip the case which has problem on Xcode 11.3 + + * Update FyndByIOClassChainTests along to iOS13 + + * Update FyndByElementWebelementTests along to iOS13 + + * Update KeyboardTests along to iOS13 + + * Update webdriver_tests along to iOS13 + + * Run test_find_element_by_isvisible with simpleIsVisibleCheck caps + + * Run test_hide_keyboard_no_key_name + + * Remove unused codes + + * [Readme] py.test -> pytest +- Ci: Take screen record as evidence (#481) [Mori Atsushi] + + * Take screen record for android + + * Take screen record for iOS + + * Save screen record for iOS +- Update changelog for 0.49. [Kazuaki Matsuo] + + v0.49 (2019-12-24) ------------------ - Bump 0.49. [Kazuaki Matsuo] From 446b8c49b85bc8efb38070958dd1190138177c59 Mon Sep 17 00:00:00 2001 From: Mori Atsushi Date: Tue, 11 Feb 2020 00:04:12 +0900 Subject: [PATCH 09/25] Fix flaky functional tests (#473) * Run all tests * Fix apk file path * Skip find_element_by_image test cases * Skip context switching test * Skip multi tap test on CI * Change strategy for waiting element * Add functions for same steps * Restore unexpected changes * Fix touch_action_tests * Fix * Fix Fix test_driver_swipe * fix * Create _move_to_[target_view] * [test_driver_swipe] Add wait --- ci-jobs/functional_test.yml | 6 +- test/functional/android/applications_tests.py | 5 +- test/functional/android/common_tests.py | 3 - .../android/context_switching_tests.py | 7 +- test/functional/android/multi_action_tests.py | 60 ++++++------- .../android/network_connection_tests.py | 3 - .../find_by_accessibility_id_tests.py | 5 +- .../find_by_uiautomator_tests.py | 1 - test/functional/android/touch_action_tests.py | 89 +++++++------------ 9 files changed, 69 insertions(+), 110 deletions(-) diff --git a/ci-jobs/functional_test.yml b/ci-jobs/functional_test.yml index f1455f64..200b7f20 100644 --- a/ci-jobs/functional_test.yml +++ b/ci-jobs/functional_test.yml @@ -5,8 +5,6 @@ parameters: xcodeForIOS: 11.3 CI: true -# [Android] Need to fix and add flaky tests for activities_tests, find_by_uiautomator_tests - jobs: - template: ./functional/run_ios_test.yml parameters: @@ -70,7 +68,7 @@ jobs: name: 'func_test_android6' vmImage: ${{ parameters.vmImage }} pytestOpt: ${{ parameters.pytestOpt }} - testFiles: 'common_tests.py' + testFiles: 'common_tests.py multi_action_tests.py webelement_tests.py' sdkVer: ${{ parameters.androidSdkVer }} CI: ${{ parameters.ci }} - template: ./functional/run_android_test.yml @@ -86,6 +84,6 @@ jobs: name: 'func_test_android8' vmImage: ${{ parameters.vmImage }} pytestOpt: ${{ parameters.pytestOpt }} - testFiles: 'network_connection_tests.py log_event_tests.py' + testFiles: 'network_connection_tests.py log_event_tests.py activities_tests.py hw_actions_tests.py touch_action_tests.py' sdkVer: ${{ parameters.androidSdkVer }} CI: ${{ parameters.ci }} diff --git a/test/functional/android/applications_tests.py b/test/functional/android/applications_tests.py index 45ea52ca..9703fbc7 100644 --- a/test/functional/android/applications_tests.py +++ b/test/functional/android/applications_tests.py @@ -13,11 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import unittest from time import sleep from appium.webdriver.applicationstate import ApplicationState +from .helper.desired_capabilities import PATH from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase @@ -33,9 +35,8 @@ def test_is_app_installed(self): self.assertTrue(self.driver.is_app_installed(APIDEMO_PKG_NAME)) def test_install_app(self): - self.skipTest('This causes the server to crash. no idea why') self.assertFalse(self.driver.is_app_installed('io.selendroid.testapp')) - self.driver.install_app('/Users/isaac/code/python-client/test/apps/selendroid-test-app.apk') + self.driver.install_app(PATH(os.path.join('../..', 'apps', 'selendroid-test-app.apk'))) self.assertTrue(self.driver.is_app_installed('io.selendroid.testapp')) def test_remove_app(self): diff --git a/test/functional/android/common_tests.py b/test/functional/android/common_tests.py index cf5ea791..7c50150c 100644 --- a/test/functional/android/common_tests.py +++ b/test/functional/android/common_tests.py @@ -39,9 +39,6 @@ def test_end_test_coverage(self): sleep(5) def test_open_notifications(self): - if is_ci(): - # TODO Due to unexpected dialog, "System UI isn't responding" - self.skipTest('Need to fix flaky test during running on CI.') for word in ['App', 'Notification', 'Status Bar', ':-|']: wait_for_element(self.driver, MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("{}")'.format(word)).click() diff --git a/test/functional/android/context_switching_tests.py b/test/functional/android/context_switching_tests.py index ed4dcef9..2bc6d092 100644 --- a/test/functional/android/context_switching_tests.py +++ b/test/functional/android/context_switching_tests.py @@ -22,28 +22,31 @@ from .helper import desired_capabilities -@pytest.mark.skip(reason="Need to fix broken test") class ContextSwitchingTests(unittest.TestCase): def setUp(self): desired_caps = desired_capabilities.get_desired_capabilities('selendroid-test-app.apk') self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) def test_contexts_list(self): + self.skipTest('Need to replace with apk which has WEBVIEW as context') self._enter_webview() contexts = self.driver.contexts self.assertEqual(2, len(contexts)) def test_move_to_correct_context(self): + self.skipTest('Need to replace with apk which has WEBVIEW as context') self._enter_webview() self.assertEqual('WEBVIEW_io.selendroid.testapp', self.driver.current_context) def test_actually_in_webview(self): + self.skipTest('Need to replace with apk which has WEBVIEW as context') self._enter_webview() self.driver.find_element_by_css_selector('input[type=submit]').click() el = self.driver.find_element_by_xpath("//h1[contains(., 'This is my way')]") self.assertIsNot(None, el) def test_move_back_to_native_context(self): + self.skipTest('Need to replace with apk which has WEBVIEW as context') self._enter_webview() self.driver.switch_to.context(None) self.assertEqual('NATIVE_APP', self.driver.current_context) @@ -55,7 +58,7 @@ def tearDown(self): self.driver.quit() def _enter_webview(self): - btn = self.driver.find_element_by_name('buttonStartWebviewCD') + btn = self.driver.find_element_by_accessibility_id('buttonStartWebviewCD') btn.click() self.driver.switch_to.context('WEBVIEW') diff --git a/test/functional/android/multi_action_tests.py b/test/functional/android/multi_action_tests.py index 3d0be2b0..7ed47708 100644 --- a/test/functional/android/multi_action_tests.py +++ b/test/functional/android/multi_action_tests.py @@ -19,29 +19,13 @@ from appium.webdriver.common.multi_action import MultiAction from appium.webdriver.common.touch_action import TouchAction -from .helper.test_helper import BaseTestCase, wait_for_element +from .helper.test_helper import BaseTestCase, is_ci, wait_for_element class MultiActionTests(BaseTestCase): def test_parallel_actions(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - self.driver.scroll(el1, el2) - - el = self.driver.find_element_by_accessibility_id('Views') - action = TouchAction(self.driver) - action.tap(el).perform() - - # simulate a swipe/scroll - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') - action.press(el).move_to(x=100, y=-1000).release().perform() - el = self.driver.find_element_by_accessibility_id('Layouts') - action.press(el).move_to(x=100, y=-1000).release().perform() + self._move_to_splitting_touches_accros_views() - el = self.driver.find_element_by_accessibility_id('Splitting Touches across Views') - action.tap(el).perform() - - wait_for_element(self.driver, MobileBy.CLASS_NAME, 'android.widget.ListView') els = self.driver.find_elements_by_class_name('android.widget.ListView') a1 = TouchAction() a1.press(els[0]) \ @@ -56,24 +40,8 @@ def test_parallel_actions(self): ma.perform() def test_actions_with_waits(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - self.driver.scroll(el1, el2) - - el = self.driver.find_element_by_accessibility_id('Views') - action = TouchAction(self.driver) - action.tap(el).perform() - - # simulate a swipe/scroll - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') - action.press(el).move_to(x=100, y=-1000).release().perform() - el = self.driver.find_element_by_accessibility_id('Layouts') - action.press(el).move_to(x=100, y=-1000).release().perform() - - el = self.driver.find_element_by_accessibility_id('Splitting Touches across Views') - action.tap(el).perform() + self._move_to_splitting_touches_accros_views() - wait_for_element(self.driver, MobileBy.CLASS_NAME, 'android.widget.ListView') els = self.driver.find_elements_by_class_name('android.widget.ListView') a1 = TouchAction() a1.press(els[0]) \ @@ -95,7 +63,29 @@ def test_actions_with_waits(self): ma.add(a1, a2) ma.perform() + def _move_to_splitting_touches_accros_views(self): + el1 = self.driver.find_element_by_accessibility_id('Content') + el2 = self.driver.find_element_by_accessibility_id('Animation') + self.driver.scroll(el1, el2) + + el = self.driver.find_element_by_accessibility_id('Views') + action = TouchAction(self.driver) + action.tap(el).perform() + + # simulate a swipe/scroll + el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') + action.press(el).move_to(x=100, y=-1000).release().perform() + el = self.driver.find_element_by_accessibility_id('Layouts') + action.press(el).move_to(x=100, y=-1000).release().perform() + + el = self.driver.find_element_by_accessibility_id('Splitting Touches across Views') + action.tap(el).perform() + + wait_for_element(self.driver, MobileBy.ID, 'io.appium.android.apis:id/list1') + def test_driver_multi_tap(self): + if is_ci(): + self.skipTest('Skip since the test must be watched to check if it works') el = self.driver.find_element_by_accessibility_id('Graphics') action = TouchAction(self.driver) action.tap(el).perform() diff --git a/test/functional/android/network_connection_tests.py b/test/functional/android/network_connection_tests.py index 5727e1d6..34267345 100644 --- a/test/functional/android/network_connection_tests.py +++ b/test/functional/android/network_connection_tests.py @@ -17,7 +17,6 @@ from appium.webdriver.connectiontype import ConnectionType -from ..test_helper import is_ci from .helper.test_helper import BaseTestCase @@ -27,8 +26,6 @@ def test_get_network_connection(self): self.assertIsInstance(nc, int) def test_set_network_connection(self): - if is_ci(): - self.skipTest('Need to fix flaky test during running on CI') nc = self.driver.set_network_connection(ConnectionType.DATA_ONLY) self.assertIsInstance(nc, int) self.assertEqual(nc, ConnectionType.DATA_ONLY) 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 index 508edc21..b5e8a878 100644 --- a/test/functional/android/search_context/find_by_accessibility_id_tests.py +++ b/test/functional/android/search_context/find_by_accessibility_id_tests.py @@ -19,7 +19,6 @@ BaseTestCase, wait_for_element ) -from test.functional.test_helper import is_ci class FindByAccessibilityIDTests(BaseTestCase): @@ -35,12 +34,10 @@ def test_find_multiple_elements(self): self.assertIsInstance(els, list) def test_element_find_single_element(self): - if is_ci(): - self.skipTest('Need to fix flaky test during running on CI') wait_for_element(self.driver, MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Accessibility")').click() wait_for_element(self.driver, MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Accessibility Node Querying")').click() - el = wait_for_element(self.driver, MobileBy.CLASS_NAME, 'android.widget.ListView') + el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Task Take out Trash') sub_el = el.find_element_by_accessibility_id('Task Take out Trash') self.assertIsNotNone(sub_el) diff --git a/test/functional/android/search_context/find_by_uiautomator_tests.py b/test/functional/android/search_context/find_by_uiautomator_tests.py index 47430356..b602a591 100644 --- a/test/functional/android/search_context/find_by_uiautomator_tests.py +++ b/test/functional/android/search_context/find_by_uiautomator_tests.py @@ -19,7 +19,6 @@ from test.functional.android.helper.test_helper import BaseTestCase -@pytest.mark.skip(reason="Need to fix flaky test") class FindByUIAutomatorTests(BaseTestCase): def test_find_single_element(self): el = self.driver.find_element_by_android_uiautomator('new UiSelector().text("Animation")') diff --git a/test/functional/android/touch_action_tests.py b/test/functional/android/touch_action_tests.py index 7bf7e30f..5fb5dca5 100644 --- a/test/functional/android/touch_action_tests.py +++ b/test/functional/android/touch_action_tests.py @@ -22,6 +22,7 @@ from .helper.test_helper import ( APIDEMO_PKG_NAME, BaseTestCase, + is_ci, wait_for_element ) @@ -73,20 +74,8 @@ def test_press_and_immediately_release_x_y(self): self.assertIsNotNone(el) def test_press_and_wait(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - + self._move_to_custom_adapter() action = TouchAction(self.driver) - action.press(el1).move_to(el2).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Views') - action.tap(el).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') - action.tap(el).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, '1. Custom Adapter') - action.tap(el).perform() el = wait_for_element(self.driver, MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("People Names")') @@ -118,20 +107,8 @@ def test_press_and_moveto_x_y(self): self.assertIsNotNone(el) def test_long_press(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - + self._move_to_custom_adapter() action = TouchAction(self.driver) - action.press(el1).move_to(el2).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Views') - action.tap(el).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') - action.tap(el).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, '1. Custom Adapter') - action.tap(el).perform() el = wait_for_element(self.driver, MobileBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("People Names")') @@ -143,20 +120,10 @@ def test_long_press(self): self.assertIsNotNone(el) def test_long_press_x_y(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - + if is_ci(): + self.skipTest("Skip since this check is low robust due to hard-coded position.") + self._move_to_custom_adapter() action = TouchAction(self.driver) - action.press(el1).move_to(el2).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Views') - action.tap(el).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') - action.tap(el).perform() - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, '1. Custom Adapter') - action.tap(el).perform() # the element "People Names" is located at 430:310 (top left corner) # location can be changed by phone resolusion, OS version @@ -168,13 +135,8 @@ def test_long_press_x_y(self): self.assertIsNotNone(el) def test_drag_and_drop(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - self.driver.scroll(el1, el2) - - el = self.driver.find_element_by_accessibility_id('Views') + self._move_to_views() action = TouchAction(self.driver) - action.tap(el).perform() el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Drag and Drop') action.tap(el).perform() @@ -185,17 +147,12 @@ def test_drag_and_drop(self): # dnd is stimulated by longpress-move_to-release action.long_press(dd3).move_to(dd2).release().perform() - el = wait_for_element(self.driver, MobileBy.ID, '{}:id/drag_text'.format(APIDEMO_PKG_NAME)) - self.assertTrue('drag_dot_3' in el.text) + el = wait_for_element(self.driver, MobileBy.ID, '{}:id/drag_result_text'.format(APIDEMO_PKG_NAME)) + self.assertTrue('Dropped!' in el.text) def test_driver_drag_and_drop(self): - el1 = self.driver.find_element_by_accessibility_id('Content') - el2 = self.driver.find_element_by_accessibility_id('Animation') - self.driver.scroll(el1, el2) - - el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Views') + self._move_to_views() action = TouchAction(self.driver) - action.tap(el).perform() el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Drag and Drop') action.tap(el).perform() @@ -205,20 +162,40 @@ def test_driver_drag_and_drop(self): self.driver.drag_and_drop(dd3, dd2) - el = wait_for_element(self.driver, MobileBy.ID, '{}:id/drag_text'.format(APIDEMO_PKG_NAME)) - self.assertTrue('drag_dot_3' in el.text) + el = wait_for_element(self.driver, MobileBy.ID, '{}:id/drag_result_text'.format(APIDEMO_PKG_NAME)) + self.assertTrue('Dropped!' in el.text) def test_driver_swipe(self): el = self.driver.find_element_by_accessibility_id('Views') action = TouchAction(self.driver) action.tap(el).perform() + wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Animation') self.assertRaises(NoSuchElementException, self.driver.find_element_by_accessibility_id, 'ImageView') self.driver.swipe(100, 1000, 100, 100, 800) - el = self.driver.find_element_by_accessibility_id('ImageView') + el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'ImageView') self.assertIsNotNone(el) + def _move_to_views(self): + el1 = self.driver.find_element_by_accessibility_id('Content') + el2 = self.driver.find_element_by_accessibility_id('Animation') + self.driver.scroll(el1, el2) + + el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Views') + action = TouchAction(self.driver) + action.tap(el).perform() + + def _move_to_custom_adapter(self): + self._move_to_views() + action = TouchAction(self.driver) + + el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Expandable Lists') + action.tap(el).perform() + + el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, '1. Custom Adapter') + action.tap(el).perform() + if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(TouchActionTests) From 4641b45bebd8719c9d3ebd1e2e191b35fd2de5d3 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Sat, 11 Apr 2020 19:33:40 +0200 Subject: [PATCH 10/25] feat: Add idempotency key header to create session requests (#514) --- appium/webdriver/appium_connection.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py index 719923fd..9b7218dd 100644 --- a/appium/webdriver/appium_connection.py +++ b/appium/webdriver/appium_connection.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import uuid + from selenium.webdriver.remote.remote_connection import RemoteConnection from appium.common.helper import library_version @@ -24,5 +26,8 @@ def get_remote_connection_headers(cls, parsed_url, keep_alive=True): """Override get_remote_connection_headers in RemoteConnection""" headers = RemoteConnection.get_remote_connection_headers(parsed_url, keep_alive=keep_alive) headers['User-Agent'] = 'appium/python {} ({})'.format(library_version(), headers['User-Agent']) + if parsed_url.path.endswith('/session'): + # https://github.com/appium/appium-base-driver/pull/400 + headers['X-Idempotency-Key'] = str(uuid.uuid4()) return headers From 34427a15c1ec8426068c5b04801d601b8a0db5d2 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Sun, 12 Apr 2020 11:22:20 +0900 Subject: [PATCH 11/25] feat: Override send_keys without file upload function (#515) * add send_keys_direct * override send_keys * tune * add unittest instead of functional test * tweak syntax --- appium/webdriver/webelement.py | 16 +++++++ test/unit/webdriver/webelement_test.py | 62 ++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 test/unit/webdriver/webelement_test.py diff --git a/appium/webdriver/webelement.py b/appium/webdriver/webelement.py index bf8efc5b..9c28a6b3 100644 --- a/appium/webdriver/webelement.py +++ b/appium/webdriver/webelement.py @@ -13,6 +13,7 @@ # limitations under the License. from selenium.webdriver.common.by import By +from selenium.webdriver.common.utils import keys_to_typing from selenium.webdriver.remote.command import Command as RemoteCommand from .extensions.search_context import AppiumWebElementSearchContext @@ -204,3 +205,18 @@ def set_value(self, value): } self._execute(Command.SET_IMMEDIATE_VALUE, data) return self + + # Override + def send_keys(self, *value): + """Simulates typing into the element. + + Args: + value (str): A string for typing. + + Returns: + `appium.webdriver.webelement.WebElement` + """ + keys = keys_to_typing(value) + self._execute(RemoteCommand.SEND_KEYS_TO_ELEMENT, + {'text': ''.join(keys), 'value': keys}) + return self diff --git a/test/unit/webdriver/webelement_test.py b/test/unit/webdriver/webelement_test.py new file mode 100644 index 00000000..06b62036 --- /dev/null +++ b/test/unit/webdriver/webelement_test.py @@ -0,0 +1,62 @@ +#!/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 os +import tempfile + +import httpretty + +from appium.webdriver.webelement import WebElement as MobileWebElement +from test.unit.helper.test_helper import ( + android_w3c_driver, + appium_command, + get_httpretty_request_body +) + + +class TestWebElement(object): + + @httpretty.activate + def test_send_key(self): + driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element/element_id/value') + ) + + element = MobileWebElement(driver, 'element_id', w3c=True) + element.send_keys('happy testing') + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['text'] == ''.join(d['value']) + + @httpretty.activate + def test_send_key_with_file(self): + driver = android_w3c_driver() + # Should not send this file + tmp_f = tempfile.NamedTemporaryFile() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/element/element_id/value') + ) + + try: + element = MobileWebElement(driver, 'element_id', w3c=True) + element.send_keys(tmp_f.name) + finally: + tmp_f.close() + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['text'] == ''.join(d['value']) From 5a545d7df7c0e839f9ca651cbf0f32055c6c763b Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Sun, 12 Apr 2020 12:12:52 +0900 Subject: [PATCH 12/25] Bump 0.51 --- appium/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appium/version.py b/appium/version.py index 70897073..772242eb 100644 --- a/appium/version.py +++ b/appium/version.py @@ -1 +1 @@ -version = '0.50' +version = '0.51' From 2141dbded55e6f888fc498b518634f70e9871866 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Sun, 12 Apr 2020 12:12:58 +0900 Subject: [PATCH 13/25] Update changelog for 0.51 --- CHANGELOG.rst | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a7b9fbaf..65eb09b4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,56 @@ Changelog ========= +v0.51 (2020-04-12) +------------------ +- Bump 0.51. [Kazuaki Matsuo] +- Feat: Override send_keys without file upload function (#515) [Kazuaki + Matsuo] + + * add send_keys_direct + + * override send_keys + + * tune + + * add unittest instead of functional test + + * tweak syntax +- Feat: Add idempotency key header to create session requests (#514) + [Mykola Mokhnach] +- Fix flaky functional tests (#473) [Mori Atsushi] + + * Run all tests + + * Fix apk file path + + * Skip find_element_by_image test cases + + * Skip context switching test + + * Skip multi tap test on CI + + * Change strategy for waiting element + + * Add functions for same steps + + * Restore unexpected changes + + * Fix touch_action_tests + + * Fix + + * Fix + Fix test_driver_swipe + + * fix + + * Create _move_to_[target_view] + + * [test_driver_swipe] Add wait +- Update changelog for 0.50. [Kazuaki Matsuo] + + v0.50 (2020-02-10) ------------------ - Bump 0.50. [Kazuaki Matsuo] From 9bdf1f7556649dc10cf0c926559ef4c3716b9e87 Mon Sep 17 00:00:00 2001 From: Nrupesh Patel Date: Tue, 14 Apr 2020 21:57:52 -0700 Subject: [PATCH 14/25] test: Fix test_clear flaky functional test (#519) --- test/functional/ios/webdriver_tests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/functional/ios/webdriver_tests.py b/test/functional/ios/webdriver_tests.py index 51c7bc58..e3672161 100644 --- a/test/functional/ios/webdriver_tests.py +++ b/test/functional/ios/webdriver_tests.py @@ -80,6 +80,8 @@ def test_clear(self): 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_class_name('XCUIElementTypeTextField')[0] text = el.get_attribute('value') From ba64adf0928a3b039e5aae6c686c1def6a880c20 Mon Sep 17 00:00:00 2001 From: Nrupesh Patel Date: Wed, 15 Apr 2020 03:07:38 -0700 Subject: [PATCH 15/25] test: Add unit test for set_value (setImmediateValue) (#518) --- .gitignore | 4 +++- test/unit/webdriver/webelement_test.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c5b30b79..710b3114 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ __pycache__ venv* .tox -Pipfile.lock \ No newline at end of file +Pipfile.lock + +.coverage diff --git a/test/unit/webdriver/webelement_test.py b/test/unit/webdriver/webelement_test.py index 06b62036..746e4b24 100644 --- a/test/unit/webdriver/webelement_test.py +++ b/test/unit/webdriver/webelement_test.py @@ -28,6 +28,21 @@ class TestWebElement(object): + @httpretty.activate + def test_set_value(self): + driver = android_w3c_driver() + httpretty.register_uri( + httpretty.POST, + appium_command('/session/1234567890/appium/element/element_id/value') + ) + + element = MobileWebElement(driver, 'element_id', w3c=True) + value = 'happy testing' + element.set_value(value) + + d = get_httpretty_request_body(httpretty.last_request()) + assert d['value'] == [value] + @httpretty.activate def test_send_key(self): driver = android_w3c_driver() From 9e1959c691bf4d1e9906071f6075915a7ec090cd Mon Sep 17 00:00:00 2001 From: Venkatesh Date: Sun, 19 Apr 2020 19:37:31 +0530 Subject: [PATCH 16/25] chore: Fix int - str comparison error in ios desired capabilities (#517) if number >= PytestXdistWorker.COUNT: TypeError: '>=' not supported between instances of 'int' and 'str' 2. Updated test case path and iPhone model in Readme file --- README.md | 6 +++--- test/functional/ios/helper/desired_capabilities.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9cb50efc..0a86a2ac 100644 --- a/README.md +++ b/README.md @@ -92,18 +92,18 @@ $ pytest -n 2 test/unit ### Functional ``` -$ pytest test/functional/ios/find_by_ios_class_chain_tests.py +$ pytest test/functional/ios/search_context/find_by_ios_class_chain_tests.py ``` ### In parallel for iOS -1. Create simulators named 'iPhone 6s - 8100' and 'iPhone 6s - 8101' +1. Create simulators named 'iPhone 8 - 8100' and 'iPhone 8 - 8101' 2. Install test libraries via pip ``` $ pip install pytest pytest-xdist ``` 3. Run tests ``` - $ pytest -n 2 test/functional/ios/find_by_ios_class_chain_tests.py + $ pytest -n 2 test/functional/ios/search_context/find_by_ios_class_chain_tests.py ``` # Release diff --git a/test/functional/ios/helper/desired_capabilities.py b/test/functional/ios/helper/desired_capabilities.py index 7a98f61a..2fc9b25c 100644 --- a/test/functional/ios/helper/desired_capabilities.py +++ b/test/functional/ios/helper/desired_capabilities.py @@ -50,7 +50,7 @@ def gw(number): if PytestXdistWorker.COUNT is None: return '0' - if number >= PytestXdistWorker.COUNT: + if number >= int(PytestXdistWorker.COUNT): return 'gw0' return 'gw{}'.format(number) @@ -65,7 +65,7 @@ def wda_port(): return 8100 -# Before running tests, you must have iOS simulators named 'iPhone 6s - 8100' and 'iPhone 6s - 8101' +# Before running tests, you must have iOS simulators named 'iPhone 8 - 8100' and 'iPhone 8 - 8101' def iphone_device_name(port=None): From a2e21d1249ebe36fbd450cc15b8003c136b2b640 Mon Sep 17 00:00:00 2001 From: Hannes Hauer Date: Thu, 23 Apr 2020 09:25:57 +0200 Subject: [PATCH 17/25] fix: Handling of dictionary-values in WebElement.get_attribute() (#521) --- appium/webdriver/webelement.py | 8 +++++++- test/unit/webdriver/webelement_test.py | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/appium/webdriver/webelement.py b/appium/webdriver/webelement.py index 9c28a6b3..a90080c4 100644 --- a/appium/webdriver/webelement.py +++ b/appium/webdriver/webelement.py @@ -59,8 +59,14 @@ def get_attribute(self, name): if attributeValue is None: return None + if isinstance(attributeValue, dict): + return attributeValue + if not isinstance(attributeValue, str): - attributeValue = unicode(attributeValue) + try: + attributeValue = unicode(attributeValue) + except NameError: + attributeValue = str(attributeValue) if name != 'value' and attributeValue.lower() in ('true', 'false'): return attributeValue.lower() diff --git a/test/unit/webdriver/webelement_test.py b/test/unit/webdriver/webelement_test.py index 746e4b24..88347b57 100644 --- a/test/unit/webdriver/webelement_test.py +++ b/test/unit/webdriver/webelement_test.py @@ -75,3 +75,26 @@ 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_get_attribute_with_dict(self): + driver = android_w3c_driver() + rect_dict = { + 'y': 200, + 'x': 100, + 'width': 300, + 'height': 56 + } + httpretty.register_uri( + httpretty.GET, + appium_command('/session/1234567890/element/element_id/attribute/rect'), + body=json.dumps({"value": rect_dict}) + ) + + element = MobileWebElement(driver, 'element_id', w3c=True) + ef = element.get_attribute('rect') + + d = httpretty.last_request() + + assert isinstance(ef, dict) + assert ef == rect_dict From c702cefc6acde7e3e65ce7522e717e9a7c928e3a Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Thu, 23 Apr 2020 18:18:40 +0900 Subject: [PATCH 18/25] Bump 0.52 --- appium/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appium/version.py b/appium/version.py index 772242eb..04e6d68c 100644 --- a/appium/version.py +++ b/appium/version.py @@ -1 +1 @@ -version = '0.51' +version = '0.52' From cfd99cfef68a24bd0c310b76bb28791fb3b3c872 Mon Sep 17 00:00:00 2001 From: Kazuaki Matsuo Date: Thu, 23 Apr 2020 18:18:47 +0900 Subject: [PATCH 19/25] Update changelog for 0.52 --- CHANGELOG.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 65eb09b4..94cbed85 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,27 @@ Changelog ========= +v0.52 (2020-04-23) +------------------ + +Fix +~~~ +- Handling of dictionary-values in WebElement.get_attribute() (#521) + [Hannes Hauer] + +Other +~~~~~ +- Bump 0.52. [Kazuaki Matsuo] +- Chore: Fix int - str comparison error in ios desired capabilities + (#517) [Venkatesh] + + if number >= PytestXdistWorker.COUNT: +- Test: Add unit test for set_value (setImmediateValue) (#518) [Nrupesh + Patel] +- Test: Fix test_clear flaky functional test (#519) [Nrupesh Patel] +- Update changelog for 0.51. [Kazuaki Matsuo] + + v0.51 (2020-04-12) ------------------ - Bump 0.51. [Kazuaki Matsuo] From c8d1af3316a06403f7770d5227096049e74b79bf Mon Sep 17 00:00:00 2001 From: Atsushi Mori Date: Sun, 26 Apr 2020 13:11:09 +0900 Subject: [PATCH 20/25] Fix mypy error --- appium/webdriver/appium_connection.py | 7 +++-- appium/webdriver/appium_service.py | 2 +- .../extensions/search_context/android.py | 11 ++++---- appium/webdriver/webelement.py | 4 +-- test/functional/android/multi_action_tests.py | 2 +- .../find_by_view_matcher_tests.py | 26 +++++++------------ test/functional/android/touch_action_tests.py | 8 +++--- 7 files changed, 28 insertions(+), 32 deletions(-) diff --git a/appium/webdriver/appium_connection.py b/appium/webdriver/appium_connection.py index ca6cc68f..f643a168 100644 --- a/appium/webdriver/appium_connection.py +++ b/appium/webdriver/appium_connection.py @@ -13,17 +13,20 @@ # limitations under the License. import uuid -from typing import Any, Dict +from typing import TYPE_CHECKING, Any, Dict from selenium.webdriver.remote.remote_connection import RemoteConnection from appium.common.helper import library_version +if TYPE_CHECKING: + from urllib.parse import ParseResult + class AppiumConnection(RemoteConnection): @classmethod - def get_remote_connection_headers(cls, parsed_url: str, keep_alive: bool = True) -> Dict[str, Any]: + def get_remote_connection_headers(cls, parsed_url: 'ParseResult', keep_alive: bool = True) -> Dict[str, Any]: """Override get_remote_connection_headers in RemoteConnection""" headers = RemoteConnection.get_remote_connection_headers(parsed_url, keep_alive=keep_alive) headers['User-Agent'] = 'appium/python {} ({})'.format(library_version(), headers['User-Agent']) diff --git a/appium/webdriver/appium_service.py b/appium/webdriver/appium_service.py index 8aecc145..ca190d88 100644 --- a/appium/webdriver/appium_service.py +++ b/appium/webdriver/appium_service.py @@ -170,7 +170,7 @@ def start(self, **kwargs: Any) -> sp.Popen: if not self.is_running or (timeout_ms > 0 and not poll_url(host, port, STATUS_URL, timeout_ms)): error_msg = f'Appium has failed to start on {host}:{port} within {timeout_ms}ms timeout' if error_msg is not None: - if stderr == sp.PIPE: + if stderr == sp.PIPE and self._process.stderr is not None: err_output = self._process.stderr.read() if err_output: error_msg += f'\nOriginal error: {str(err_output)}' diff --git a/appium/webdriver/extensions/search_context/android.py b/appium/webdriver/extensions/search_context/android.py index 868aa682..bdfdb027 100644 --- a/appium/webdriver/extensions/search_context/android.py +++ b/appium/webdriver/extensions/search_context/android.py @@ -15,7 +15,7 @@ # pylint: disable=abstract-method import json -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Any, List, Optional from appium.webdriver.common.mobileby import MobileBy @@ -28,7 +28,8 @@ class AndroidSearchContext(BaseSearchContext): """Define search context for Android""" - def find_element_by_android_view_matcher(self, name=None, args=None, className=None): + def find_element_by_android_view_matcher( + self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> 'WebElement': """Finds element by [onView](https://developer.android.com/training/testing/espresso/basics) in Android It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). @@ -62,7 +63,7 @@ def find_element_by_android_view_matcher(self, name=None, args=None, className=N ) def find_element_by_android_data_matcher( - self, name: Optional[str] = None, args: Optional[str] = None, className: Optional[str] = None) -> 'WebElement': + self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> 'WebElement': """Finds element by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). @@ -95,7 +96,7 @@ def find_element_by_android_data_matcher( ) def find_elements_by_android_data_matcher( - self, name: Optional[str] = None, args: Optional[str] = None, className: Optional[str] = None) -> List['WebElement']: + self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> List['WebElement']: """Finds elements by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). @@ -122,7 +123,7 @@ def find_elements_by_android_data_matcher( value=self._build_data_matcher(name=name, args=args, className=className) ) - def _build_data_matcher(self, name: Optional[str] = None, args: Optional[str] + def _build_data_matcher(self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> str: result = {} diff --git a/appium/webdriver/webelement.py b/appium/webdriver/webelement.py index 56629029..42af922b 100644 --- a/appium/webdriver/webelement.py +++ b/appium/webdriver/webelement.py @@ -25,7 +25,7 @@ class WebElement(AppiumWebElementSearchContext): - def get_attribute(self, name: str) -> Optional[str]: + def get_attribute(self, name: str) -> Optional[Union[str, Dict]]: """Gets the given attribute or property of the element. Override for Appium @@ -211,7 +211,7 @@ def set_value(self, value: str) -> T: return self # Override - def send_keys(self, *value): + def send_keys(self, *value: str) -> T: """Simulates typing into the element. Args: diff --git a/test/functional/android/multi_action_tests.py b/test/functional/android/multi_action_tests.py index 8234b858..6739bf59 100644 --- a/test/functional/android/multi_action_tests.py +++ b/test/functional/android/multi_action_tests.py @@ -64,7 +64,7 @@ def test_actions_with_waits(self) -> None: ma.add(a1, a2) ma.perform() - def _move_to_splitting_touches_accros_views(self): + def _move_to_splitting_touches_accros_views(self) -> None: el1 = self.driver.find_element_by_accessibility_id('Content') el2 = self.driver.find_element_by_accessibility_id('Animation') self.driver.scroll(el1, el2) 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 index 0f8ed0d0..5bb0a47d 100644 --- a/test/functional/android/search_context/find_by_view_matcher_tests.py +++ b/test/functional/android/search_context/find_by_view_matcher_tests.py @@ -24,36 +24,33 @@ AndroidSearchContext ) from test.functional.android.helper.test_helper import ( + BaseTestCase, desired_capabilities, is_ci ) -class FindByViewMatcherTests(unittest.TestCase): +class TestFindByViewMatcher(BaseTestCase): - def setUp(self): + # Override + def setup_method(self, method) -> None: # type: ignore desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip') desired_caps['automationName'] = 'Espresso' self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) - - def tearDown(self): if is_ci(): - # Take the screenshot to investigate when tests failed only on CI - img_path = os.path.join(os.getcwd(), self._testMethodName + '.png') - self.driver.get_screenshot_as_file(img_path) - self.driver.quit() + self.driver.start_recording_screen() - def test_find_single_element(self): + def test_find_single_element(self) -> None: el = self.driver.find_element_by_android_view_matcher( name='withText', args=['Accessibility'], className='ViewMatchers') assert el.text == 'Accessibility' - def test_find_single_element_ful_class_name(self): + def test_find_single_element_ful_class_name(self) -> None: el = self.driver.find_element_by_android_view_matcher( name='withText', args=['Accessibility'], className='androidx.test.espresso.matcher.ViewMatchers') assert el.text == 'Accessibility' - def test_find_single_element_using_hamcrest_matcher(self): + def test_find_single_element_using_hamcrest_matcher(self) -> None: el = self.driver.find_element_by_android_view_matcher( name='withText', args={ @@ -65,13 +62,8 @@ def test_find_single_element_using_hamcrest_matcher(self): # androidx.test.espresso.AmbiguousViewMatcherException: # 'with text: a string containing "Access"' matches multiple views in the hierarchy. - def test_find_multiple_elements(self): + def test_find_multiple_elements(self) -> None: value = AndroidSearchContext()._build_data_matcher( name='withSubstring', args=['Access'], className='ViewMatchers') with pytest.raises(WebDriverException): self.driver.find_elements(by=MobileBy.ANDROID_VIEW_MATCHER, value=value) - - -if __name__ == '__main__': - suite = unittest.TestLoader().loadTestsFromTestCase(FindByViewMatcherTests) - unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/test/functional/android/touch_action_tests.py b/test/functional/android/touch_action_tests.py index afd1341c..00dad1aa 100644 --- a/test/functional/android/touch_action_tests.py +++ b/test/functional/android/touch_action_tests.py @@ -146,7 +146,7 @@ def test_drag_and_drop(self) -> None: action.long_press(dd3).move_to(dd2).release().perform() el = wait_for_element(self.driver, MobileBy.ID, '{}:id/drag_result_text'.format(APIDEMO_PKG_NAME)) - self.assertTrue('Dropped!' in el.text) + assert 'Dropped!' in el.text def test_driver_drag_and_drop(self) -> None: self._move_to_views() @@ -161,7 +161,7 @@ def test_driver_drag_and_drop(self) -> None: self.driver.drag_and_drop(dd3, dd2) el = wait_for_element(self.driver, MobileBy.ID, '{}:id/drag_result_text'.format(APIDEMO_PKG_NAME)) - self.assertTrue('Dropped!' in el.text) + assert 'Dropped!' in el.text def test_driver_swipe(self) -> None: el = self.driver.find_element_by_accessibility_id('Views') @@ -175,7 +175,7 @@ def test_driver_swipe(self) -> None: el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'ImageView') assert el is not None - def _move_to_views(self): + def _move_to_views(self) -> None: el1 = self.driver.find_element_by_accessibility_id('Content') el2 = self.driver.find_element_by_accessibility_id('Animation') self.driver.scroll(el1, el2) @@ -184,7 +184,7 @@ def _move_to_views(self): action = TouchAction(self.driver) action.tap(el).perform() - def _move_to_custom_adapter(self): + def _move_to_custom_adapter(self) -> None: self._move_to_views() action = TouchAction(self.driver) From 91a26e887f51054ff30e09422ce31983f83ce253 Mon Sep 17 00:00:00 2001 From: Atsushi Mori Date: Sun, 26 Apr 2020 13:36:42 +0900 Subject: [PATCH 21/25] tweak --- .gitignore | 2 ++ appium/webdriver/extensions/search_context/android.py | 9 ++++----- .../android/search_context/find_by_image_tests.py | 5 ++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index de40cecd..911a3616 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,12 @@ MANIFEST build dist +# Cache .cache __pycache__ .idea .pytest_cache +.mypy_cache # Virtual Environments venv* diff --git a/appium/webdriver/extensions/search_context/android.py b/appium/webdriver/extensions/search_context/android.py index bdfdb027..0e710fd6 100644 --- a/appium/webdriver/extensions/search_context/android.py +++ b/appium/webdriver/extensions/search_context/android.py @@ -38,7 +38,7 @@ def find_element_by_android_view_matcher( name (:obj:`str`, optional): The name of a method to invoke. The method must return a Hamcrest [Matcher](http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html) - args (:obj:`str`, optional): The args provided to the method + args (:obj:`Any`, optional): The args provided to the method className (:obj:`str`, optional): The class name that the method is part of (defaults to `org.hamcrest.Matchers`). Can be fully qualified by having the androidx.test.espresso.matcher. prefix. If the prefix is not provided then it is going to be added implicitly. @@ -72,7 +72,7 @@ def find_element_by_android_data_matcher( name (:obj:`str`, optional): The name of a method to invoke. The method must return a Hamcrest [Matcher](http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html) - args (:obj:`str`, optional): The args provided to the method + args (:obj:`Any`, optional): The args provided to the method className (:obj:`str`, optional): The class name that the method is part of (defaults to `org.hamcrest.Matchers`). Can be fully qualified, or simple, and simple defaults to `androidx.test.espresso.matcher` package (e.g.: `class=CursorMatchers` fully qualified is `class=androidx.test.espresso.matcher.CursorMatchers` @@ -104,7 +104,7 @@ def find_elements_by_android_data_matcher( name (:obj:`str`, optional): The name of a method to invoke. The method must return a Hamcrest [Matcher](http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matcher.html) - args (:obj:`str`, optional): The args provided to the method + args (:obj:`Any`, optional): The args provided to the method className (:obj:`str`, optional): The class name that the method is part of (defaults to `org.hamcrest.Matchers`). Can be fully qualified, or simple, and simple defaults to `androidx.test.espresso.matcher` package (e.g.: `class=CursorMatchers` fully qualified is `class=androidx.test.espresso.matcher.CursorMatchers` @@ -123,8 +123,7 @@ def find_elements_by_android_data_matcher( value=self._build_data_matcher(name=name, args=args, className=className) ) - def _build_data_matcher(self, name: Optional[str] = None, args: Any - = None, className: Optional[str] = None) -> str: + def _build_data_matcher(self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> str: result = {} for key, value in {'name': name, 'args': args, 'class': className}.items(): diff --git a/test/functional/android/search_context/find_by_image_tests.py b/test/functional/android/search_context/find_by_image_tests.py index f4ff322c..24dbde03 100644 --- a/test/functional/android/search_context/find_by_image_tests.py +++ b/test/functional/android/search_context/find_by_image_tests.py @@ -76,8 +76,7 @@ def test_find_throws_no_such_element(self) -> None: b64_data = base64.b64encode(png_file.read()).decode('UTF-8') with pytest.raises(TimeoutException): - WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((MobileBy.IMAGE, b64_data)) - ) + wait_for_element(self.driver, MobileBy.IMAGE, b64_data, timeout=3) + with pytest.raises(NoSuchElementException): self.driver.find_element_by_image(image_path) From e800ce7b6784defe5785f3965b7b3ae86c200bda Mon Sep 17 00:00:00 2001 From: Atsushi Mori Date: Sun, 26 Apr 2020 14:04:47 +0900 Subject: [PATCH 22/25] Add wait to test --- test/functional/android/touch_action_tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/functional/android/touch_action_tests.py b/test/functional/android/touch_action_tests.py index 00dad1aa..b1832e68 100644 --- a/test/functional/android/touch_action_tests.py +++ b/test/functional/android/touch_action_tests.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import time + import pytest from selenium.common.exceptions import NoSuchElementException @@ -53,6 +55,8 @@ def test_tap_twice(self) -> None: el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Add') action.tap(el, count=2).perform() + time.sleep(3) # [For CI since behavior is slow] To wait action completed + els = self.driver.find_elements_by_class_name('android.widget.TextView') assert 'This is a test\nThis is a test\n' == els[1].get_attribute("text") From efd065546cef2a5240e233e7451587a94e3248d5 Mon Sep 17 00:00:00 2001 From: Atsushi Mori Date: Sun, 26 Apr 2020 15:39:23 +0900 Subject: [PATCH 23/25] Skip tap_twice test --- test/functional/android/touch_action_tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/functional/android/touch_action_tests.py b/test/functional/android/touch_action_tests.py index b1832e68..a2da2e0c 100644 --- a/test/functional/android/touch_action_tests.py +++ b/test/functional/android/touch_action_tests.py @@ -44,6 +44,7 @@ def test_tap_x_y(self) -> None: el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Bouncing Balls') assert el is not None + @pytest.mark.skipif(condition=is_ci(), reason='Need to fix flaky test during running on CI.') def test_tap_twice(self) -> None: el = self.driver.find_element_by_accessibility_id('Text') action = TouchAction(self.driver) @@ -55,8 +56,6 @@ def test_tap_twice(self) -> None: el = wait_for_element(self.driver, MobileBy.ACCESSIBILITY_ID, 'Add') action.tap(el, count=2).perform() - time.sleep(3) # [For CI since behavior is slow] To wait action completed - els = self.driver.find_elements_by_class_name('android.widget.TextView') assert 'This is a test\nThis is a test\n' == els[1].get_attribute("text") From c378859a5bb100cbe752a12efff5f21dedd2d7e3 Mon Sep 17 00:00:00 2001 From: Atsushi Mori Date: Sun, 26 Apr 2020 19:15:28 +0900 Subject: [PATCH 24/25] review comments --- appium/webdriver/extensions/search_context/android.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/appium/webdriver/extensions/search_context/android.py b/appium/webdriver/extensions/search_context/android.py index 0e710fd6..a4f3a958 100644 --- a/appium/webdriver/extensions/search_context/android.py +++ b/appium/webdriver/extensions/search_context/android.py @@ -29,7 +29,7 @@ class AndroidSearchContext(BaseSearchContext): """Define search context for Android""" def find_element_by_android_view_matcher( - self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> 'WebElement': + self, name: Optional[str] = None, args: Optional[Any] = None, className: Optional[str] = None) -> 'WebElement': """Finds element by [onView](https://developer.android.com/training/testing/espresso/basics) in Android It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). @@ -63,7 +63,7 @@ def find_element_by_android_view_matcher( ) def find_element_by_android_data_matcher( - self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> 'WebElement': + self, name: Optional[str] = None, args: Optional[Any] = None, className: Optional[str] = None) -> 'WebElement': """Finds element by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). @@ -96,7 +96,7 @@ def find_element_by_android_data_matcher( ) def find_elements_by_android_data_matcher( - self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> List['WebElement']: + self, name: Optional[str] = None, args: Optional[Any] = None, className: Optional[str] = None) -> List['WebElement']: """Finds elements by [onData](https://medium.com/androiddevelopers/adapterviews-and-espresso-f4172aa853cf) in Android It works with [Espresso Driver](https://github.com/appium/appium-espresso-driver). @@ -123,7 +123,8 @@ def find_elements_by_android_data_matcher( value=self._build_data_matcher(name=name, args=args, className=className) ) - def _build_data_matcher(self, name: Optional[str] = None, args: Any = None, className: Optional[str] = None) -> str: + def _build_data_matcher(self, name: Optional[str] = None, args: Optional[Any] + = None, className: Optional[str] = None) -> str: result = {} for key, value in {'name': name, 'args': args, 'class': className}.items(): From 56e8e0c243bd856a6117041deadc853f801f456c Mon Sep 17 00:00:00 2001 From: Atsushi Mori Date: Sun, 26 Apr 2020 19:18:03 +0900 Subject: [PATCH 25/25] Remove unnecessary import --- test/functional/android/touch_action_tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/functional/android/touch_action_tests.py b/test/functional/android/touch_action_tests.py index a2da2e0c..42f22fb6 100644 --- a/test/functional/android/touch_action_tests.py +++ b/test/functional/android/touch_action_tests.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import time - import pytest from selenium.common.exceptions import NoSuchElementException