Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion appium/webdriver/extensions/screen_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ def start_recording_screen(self: T, **options: Any) -> Union[bytes, str]:
Only has an effect if `remotePath` is set.
timeLimit (int): The actual time limit of the recorded video in seconds.
The default value for both iOS and Android is 180 seconds (3 minutes).
The default value for macOS is 600 seconds (10 minutes).
The maximum value for Android is 3 minutes.
The maximum value for iOS is 10 minutes.
The maximum value for macOS is 10000 seconds (166 minutes).
forcedRestart (bool): Whether to ignore the result of previous capture and start a new recording
immediately (`True` value). By default (`False`) the endpoint will try to catch and
return the result of the previous capture if it's still available.
Expand All @@ -68,7 +70,7 @@ def start_recording_screen(self: T, **options: Any) -> Union[bytes, str]:
'mjpeg' by default. (Since Appium 1.10.0)
videoFps (int): [iOS only] The Frames Per Second rate of the recorded video. Change this value if the
resulting video is too slow or too fast. Defaults to 10. This can decrease the resulting file size.
videoFilters (str): [iOS only] The FFMPEG video filters to apply. These filters allow to scale,
videoFilters (str): [iOS, macOS only] The FFMPEG video filters to apply. These filters allow to scale,
flip, rotate and do many other useful transformations on the source video stream. The format of the
property must comply with https://ffmpeg.org/ffmpeg-filters.html. (Since Appium 1.15)
videoScale (str): [iOS only] The scaling value to apply. Read https://trac.ffmpeg.org/wiki/Scaling for
Expand All @@ -80,10 +82,29 @@ def start_recording_screen(self: T, **options: Any) -> Union[bytes, str]:
The default value is the device's native display resolution (if supported),
1280x720 if not. For best results, use a size supported by your device's
Advanced Video Coding (AVC) encoder.

bitRate (int): [Android only] The video bit rate for the video, in megabits per second.
The default value is 4. You can increase the bit rate to improve video quality,
but doing so results in larger movie files.

fps (int): [macOS only] The count of frames per second in the resulting video.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, Python gives arguments as kewrgas in a method.

Increasing fps value also increases the size of the resulting video file and the CPU usage.
captureCursor (bool): [macOS only] Whether to capture the mouse cursor while recording the screen.
Disabled by default.
captureClick (bool): [macOS only] Whether to capture the click gestures while recording the screen.
Disabled by default.
deviceId (int): [macOS only] Screen device index to use for the recording.
The list of available devices could be retrieved using
`ffmpeg -f avfoundation -list_devices true -i` command.
This option is mandatory and must be always provided.
preset (str): [macOS only] A preset is a collection of options that will provide a certain encoding
speed to compression ratio. A slower preset will provide better compression
(compression is quality per filesize). This means that, for example, if you target a certain file size
or constant bit rate, you will achieve better quality with a slower preset.
Read https://trac.ffmpeg.org/wiki/Encode/H.264 for more details.
Possible values are 'ultrafast', 'superfast', 'veryfast'(default), 'faster', 'fast', 'medium', 'slow',
'slower', 'veryslow'

Returns:
bytes: Base-64 encoded content of the recorded media
if `stop_recording_screen` isn't called after previous `start_recording_screen`.
Expand Down
3 changes: 2 additions & 1 deletion test/functional/android/appium_service_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@

from appium.webdriver.appium_service import AppiumService
from appium.webdriver.common.mobileby import MobileBy
from test.functional.android.helper.test_helper import BaseTestCase, wait_for_element
from test.functional.android.helper.test_helper import BaseTestCase
from test.functional.test_helper import wait_for_element

DEFAULT_PORT = 4723

Expand Down
3 changes: 2 additions & 1 deletion test/functional/android/common_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
from selenium.common.exceptions import NoSuchElementException

from appium.webdriver.common.mobileby import MobileBy
from test.functional.test_helper import wait_for_element

from ..test_helper import is_ci
from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase, wait_for_element
from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase


class TestCommon(BaseTestCase):
Expand Down
29 changes: 0 additions & 29 deletions test/functional/android/helper/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,15 @@
import os
from typing import TYPE_CHECKING

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from appium import webdriver
from test.functional.test_helper import is_ci

from . import desired_capabilities

if TYPE_CHECKING:
from appium.webdriver.webdriver import WebDriver
from appium.webdriver.webelement import WebElement

# the emulator is sometimes slow and needs time to think
SLEEPY_TIME = 10

# The package name of ApiDemos-debug.apk.zip
APIDEMO_PKG_NAME = 'io.appium.android.apis'


def wait_for_element(driver: 'WebDriver', locator: str, value: str, timeout: int = SLEEPY_TIME) -> 'WebElement':
"""Wait until the element located

Args:
driver: WebDriver instance
locator: Locator like WebDriver, Mobile JSON Wire Protocol
(e.g. `appium.webdriver.common.mobileby.MobileBy.ACCESSIBILITY_ID`)
value: Query value to locator
timeout: Maximum time to wait the element. If time is over, `TimeoutException` is thrown

Raises:
`selenium.common.exceptions.TimeoutException`

Returns:
The found WebElement
"""
return WebDriverWait(driver, timeout).until(EC.presence_of_element_located((locator, value)))


class BaseTestCase:
def setup_method(self, method) -> None: # type: ignore
desired_caps = desired_capabilities.get_desired_capabilities('ApiDemos-debug.apk.zip')
Expand Down
3 changes: 2 additions & 1 deletion test/functional/android/multi_action_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.common.multi_action import MultiAction
from appium.webdriver.common.touch_action import TouchAction
from test.functional.test_helper import wait_for_element

from .helper.test_helper import BaseTestCase, is_ci, wait_for_element
from .helper.test_helper import BaseTestCase, is_ci


class TestMultiAction(BaseTestCase):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
import pytest

from appium.webdriver.common.mobileby import MobileBy
from test.functional.android.helper.test_helper import BaseTestCase, is_ci, wait_for_element
from test.functional.android.helper.test_helper import BaseTestCase, is_ci
from test.functional.test_helper import wait_for_element


class TestFindByAccessibilityID(BaseTestCase):
Expand Down
4 changes: 2 additions & 2 deletions test/functional/android/search_context/find_by_image_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from test.functional.android.helper import desired_capabilities
from test.functional.android.helper.test_helper import wait_for_element
from test.functional.test_helper import wait_for_element


class TestFindByImage(object):
Expand Down Expand Up @@ -70,7 +70,7 @@ def test_find_throws_no_such_element(self) -> None:
b64_data = base64.b64encode(png_file.read()).decode('UTF-8')

with pytest.raises(TimeoutException):
wait_for_element(self.driver, MobileBy.IMAGE, b64_data, timeout=3)
wait_for_element(self.driver, MobileBy.IMAGE, b64_data, timeout_sec=3)

with pytest.raises(NoSuchElementException):
self.driver.find_element_by_image(image_path)
3 changes: 2 additions & 1 deletion test/functional/android/touch_action_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@

from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.common.touch_action import TouchAction
from test.functional.test_helper import wait_for_element

from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase, is_ci, wait_for_element
from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase, is_ci


class TestTouchAction(BaseTestCase):
Expand Down
3 changes: 2 additions & 1 deletion test/functional/android/webelement_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
# limitations under the License.

from appium.webdriver.common.mobileby import MobileBy
from test.functional.test_helper import wait_for_element

from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase, wait_for_element
from .helper.test_helper import APIDEMO_PKG_NAME, BaseTestCase


class TestWebelement(BaseTestCase):
Expand Down
Empty file added test/functional/mac/__init__.py
Empty file.
39 changes: 39 additions & 0 deletions test/functional/mac/execute_script_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/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.mobileby import MobileBy
from test.functional.mac.helper.test_helper import BaseTestCase
from test.functional.test_helper import wait_for_element


class TestExecuteScript(BaseTestCase):
def test_sending_custom_keys(self) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is good to have these tests, but they are not going to work in CI env, because the necessary accessibility permissions are missing there

edit_field = wait_for_element(self.driver, MobileBy.CLASS_NAME, 'XCUIElementTypeTextView')
flagsShift = 1 << 1
self.driver.execute_script(
'macos: keys',
{
'keys': [
{
'key': 'h',
'modifierFlags': flagsShift,
},
{
'key': 'i',
'modifierFlags': flagsShift,
},
]
},
)
assert edit_field.text == 'HI'
Empty file.
21 changes: 21 additions & 0 deletions test/functional/mac/helper/desired_capabilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Any, Dict


def get_desired_capabilities() -> Dict[str, Any]:
desired_caps: Dict[str, Any] = {'platformName': 'mac', 'automationName': 'Mac2', 'bundleId': 'com.apple.TextEdit'}

return desired_caps
26 changes: 26 additions & 0 deletions test/functional/mac/helper/test_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/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 import webdriver

from .desired_capabilities import get_desired_capabilities


class BaseTestCase(object):
def setup_method(self) -> None:
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', get_desired_capabilities())

def teardown_method(self, method) -> None: # type: ignore
self.driver.quit()
26 changes: 26 additions & 0 deletions test/functional/mac/webelement_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/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.mobileby import MobileBy
from test.functional.mac.helper.test_helper import BaseTestCase
from test.functional.test_helper import wait_for_element


class TestWebElement(BaseTestCase):
def test_clear_text_field(self) -> None:
edit_field = wait_for_element(self.driver, MobileBy.CLASS_NAME, 'XCUIElementTypeTextView')
edit_field.send_keys('helloworld')
assert edit_field.text == 'helloworld'
edit_field.clear()
assert edit_field.text == ''
28 changes: 27 additions & 1 deletion test/functional/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
import socket
import time
from time import sleep
from typing import Any, Callable
from typing import TYPE_CHECKING, Any, Callable

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

if TYPE_CHECKING:
from appium.webdriver.webdriver import WebDriver
from appium.webdriver.webelement import WebElement


class NoAvailablePortError(Exception):
Expand Down Expand Up @@ -69,3 +76,22 @@ def wait_for_condition(method: Callable, timeout_sec: float = 5, interval_sec: f
break
sleep(interval_sec)
return result


def wait_for_element(driver: 'WebDriver', locator: str, value: str, timeout_sec: float = 10) -> 'WebElement':
"""Wait until the element located

Args:
driver: WebDriver instance
locator: Locator like WebDriver, Mobile JSON Wire Protocol
(e.g. `appium.webdriver.common.mobileby.MobileBy.ACCESSIBILITY_ID`)
value: Query value to locator
timeout_sec: Maximum time to wait the element. If time is over, `TimeoutException` is thrown

Raises:
`selenium.common.exceptions.TimeoutException`

Returns:
The found WebElement
"""
return WebDriverWait(driver, timeout_sec).until(EC.presence_of_element_located((locator, value)))