From 238a0f3e2f0ae659660a7cb083187f2def6959a2 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 22 Jan 2018 11:22:15 +0100 Subject: [PATCH 1/6] Add methods for start/stop screen record API endpoints --- .../webdriver/common/screenrecord/__init__.py | 13 ++++ .../android_start_screen_recording_options.py | 58 +++++++++++++++ .../android_stop_screen_recording_options.py | 22 ++++++ .../base_screen_recording_options.py | 41 +++++++++++ .../ios_start_screen_recording_options.py | 71 ++++++++++++++++++ .../ios_stop_screen_recording_options.py | 22 ++++++ .../screen_recording_upload_options.py | 61 ++++++++++++++++ .../start_screen_recording_options.py | 72 +++++++++++++++++++ .../stop_screen_recording_options.py | 34 +++++++++ appium/webdriver/common/screenrecord/utils.py | 22 ++++++ appium/webdriver/mobilecommand.py | 2 + appium/webdriver/webdriver.py | 27 ++++++- test/functional/ios/appium_tests.py | 8 ++- 13 files changed, 450 insertions(+), 3 deletions(-) create mode 100644 appium/webdriver/common/screenrecord/__init__.py create mode 100644 appium/webdriver/common/screenrecord/android_start_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/base_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/screen_recording_upload_options.py create mode 100644 appium/webdriver/common/screenrecord/start_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/stop_screen_recording_options.py create mode 100644 appium/webdriver/common/screenrecord/utils.py diff --git a/appium/webdriver/common/screenrecord/__init__.py b/appium/webdriver/common/screenrecord/__init__.py new file mode 100644 index 00000000..cc173e9d --- /dev/null +++ b/appium/webdriver/common/screenrecord/__init__.py @@ -0,0 +1,13 @@ +#!/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. diff --git a/appium/webdriver/common/screenrecord/android_start_screen_recording_options.py b/appium/webdriver/common/screenrecord/android_start_screen_recording_options.py new file mode 100644 index 00000000..e32ff31e --- /dev/null +++ b/appium/webdriver/common/screenrecord/android_start_screen_recording_options.py @@ -0,0 +1,58 @@ +#!/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 copy + +from webdriver.common.screenrecord.start_screen_recording_options import StartScreenRecordingOptions +from webdriver.common.screenrecord.utils import attributes_to_dict + + +class AndroidStartScreenRecordingOptions(StartScreenRecordingOptions): + + def __init__(self): + super(AndroidStartScreenRecordingOptions, self).__init__() + self._bitRate = None + + @staticmethod + def start_screen_recording_options(): + return AndroidStartScreenRecordingOptions() + + def with_bit_rate(self, bit_rate): + """ + 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. + + :param bit_rate: The actual bit rate (Mb/s) + :return: self instance for chaining. + """ + self._bitRate = bit_rate + return self + + def with_time_limit(self, time_limit): + """ + The maximum recording time. The default and maximum value is 180 seconds (3 minutes). + Setting values greater than this or less than zero will cause an exception. The minimum + time resolution unit is one second. + + :param time_limit: The actual time limit of the recorded video. Can be a number of seconds + or timedelta instance. + :return: self instance for chaining. + """ + return super(AndroidStartScreenRecordingOptions, self).with_time_limit(time_limit) + + def build(self): + result = super(AndroidStartScreenRecordingOptions, self).build() + result.update(attributes_to_dict(self, ('_bitRate', ))) + return copy.copy(result) diff --git a/appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py b/appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py new file mode 100644 index 00000000..1d08a2e0 --- /dev/null +++ b/appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py @@ -0,0 +1,22 @@ +#!/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 webdriver.common.screenrecord.stop_screen_recording_options import StopScreenRecordingOptions + + +class AndroidStopScreenRecordingOptions(StopScreenRecordingOptions): + + @staticmethod + def stop_screen_recording_options(): + return AndroidStopScreenRecordingOptions() diff --git a/appium/webdriver/common/screenrecord/base_screen_recording_options.py b/appium/webdriver/common/screenrecord/base_screen_recording_options.py new file mode 100644 index 00000000..3e990780 --- /dev/null +++ b/appium/webdriver/common/screenrecord/base_screen_recording_options.py @@ -0,0 +1,41 @@ +#!/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. + + +class BaseScreenRecordingOptions(object): + + def __init__(self): + self._upload_options = None + + def with_upload_options(self, upload_options): + """ + Upload options for the recorded screen capture. + + :param upload_options: see the documentation on {@link ScreenRecordingUploadOptions} + for more details. + :return: self instance for chaining. + """ + self._upload_options = upload_options + return self + + def build(self): + """ + Builds a map, which is ready to be passed to the subordinated + Appium API. + + :return: arguments mapping. + """ + if self._upload_options is None: + return {} + return self._upload_options.build() diff --git a/appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py b/appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py new file mode 100644 index 00000000..563cc777 --- /dev/null +++ b/appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py @@ -0,0 +1,71 @@ +#!/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 copy + +from webdriver.common.screenrecord.start_screen_recording_options import StartScreenRecordingOptions +from webdriver.common.screenrecord.utils import attributes_to_dict + + +class IOSStartScreenRecordingOptions(StartScreenRecordingOptions): + + def __init__(self): + super(IOSStartScreenRecordingOptions, self).__init__() + self._videoType = None + self._videoQuality = None + + @staticmethod + def start_screen_recording_options(): + return IOSStartScreenRecordingOptions() + + def with_video_type(self, video_type): + """ + The format of the screen capture to be recorded. + Available formats: "h264", "mp4" or "fmp4". Default is "mp4". + Only works for Simulator. + + :param video_type: one of available format names. + :return: self instance for chaining. + """ + self._videoType = video_type + return self + + def with_wideo_quality(self, video_quality): + """ + The video encoding quality ('low', 'medium', 'high', 'photo' - defaults to 'medium'). + Only works for real devices. + + :param video_quality: one of possible quality preset names. + :return: self instance for chaining. + """ + self._videoQuality = video_quality + return self + + def with_time_limit(self, time_limit): + """ + The maximum recording time. The default value is 180 seconds (3 minutes). + The maximum value is 10 minutes. + Setting values greater than this or less than zero will cause an exception. The minimum + time resolution unit is one second. + + :param time_limit: The actual time limit of the recorded video. Can be a number of seconds + or timedelta instance. + :return: self instance for chaining. + """ + return super(IOSStartScreenRecordingOptions, self).with_time_limit(time_limit) + + def build(self): + result = super(IOSStartScreenRecordingOptions, self).build() + result.update(attributes_to_dict(self, ('_videoType', '_videoQuality'))) + return copy.copy(result) diff --git a/appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py b/appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py new file mode 100644 index 00000000..4645f4b6 --- /dev/null +++ b/appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py @@ -0,0 +1,22 @@ +#!/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 webdriver.common.screenrecord.stop_screen_recording_options import StopScreenRecordingOptions + + +class IOSStopScreenRecordingOptions(StopScreenRecordingOptions): + + @staticmethod + def stop_screen_recording_options(): + return IOSStopScreenRecordingOptions() diff --git a/appium/webdriver/common/screenrecord/screen_recording_upload_options.py b/appium/webdriver/common/screenrecord/screen_recording_upload_options.py new file mode 100644 index 00000000..c9cfaa14 --- /dev/null +++ b/appium/webdriver/common/screenrecord/screen_recording_upload_options.py @@ -0,0 +1,61 @@ +#!/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 webdriver.common.screenrecord.utils import attributes_to_dict + + +class ScreenRecordingUploadOptions(object): + + def __init__(self): + self._remotePath = None + self._user = None + self._pass = None + self._method = None + + def with_remote_path(self, remote_path): + """ + The path to a remote location, where the resulting video should be uploaded. + + :param remote_path: The path to a writable remote location. + :return: self instance for chaining. + """ + self._remotePath = remote_path + return self + + def with_auth_credentials(self, user, password): + """ + Sets the credentials for remote ftp/http authentication (if needed). + This option only has an effect if remotePath is provided. + + :param user: The name of the user for the remote authentication. + :param password: The password for the remote authentication. + :return: self instance for chaining. + """ + self._user = user + self._pass = password + return self + + def with_http_method(self, method): + """ + Sets the method name for http(s) upload. PUT is used by default. + This option only has an effect if remotePath is provided. + + :param method:The HTTP method name ('PUT'/'POST'). + :return: self instance for chaining. + """ + self._method = method + return self + + def build(self): + return attributes_to_dict(self, ('_remotePath', '_user', '_pass', '_method')) diff --git a/appium/webdriver/common/screenrecord/start_screen_recording_options.py b/appium/webdriver/common/screenrecord/start_screen_recording_options.py new file mode 100644 index 00000000..920d1f4b --- /dev/null +++ b/appium/webdriver/common/screenrecord/start_screen_recording_options.py @@ -0,0 +1,72 @@ +#!/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 copy +from datetime import timedelta + +from webdriver.common.screenrecord.base_screen_recording_options import BaseScreenRecordingOptions +from webdriver.common.screenrecord.utils import attributes_to_dict + + +class StartScreenRecordingOptions(BaseScreenRecordingOptions): + + def __init__(self): + super(StartScreenRecordingOptions, self).__init__() + self._forceRestart = None + self._timeLimit = None + + def with_upload_options(self, upload_options): + """ + The remotePath upload option is the path to the remote location, + where the resulting video should be uploaded. + The following protocols are supported: http/https (multipart), ftp. + Missing value (the default setting) means the content of the resulting + file should be encoded as Base64 and passed as the endpoint response value, but + an exception will be thrown if the generated media file is too big to + fit into the available process memory. + This option only has an effect if there is a screen recording session in progress + and forced restart is not enabled (the default setting). + + :param upload_options: see the documentation on ScreenRecordingUploadOptions + for more details. + :return: self instance for chaining. + """ + return super(StartScreenRecordingOptions, self).with_upload_options(upload_options) + + def with_time_limit(self, time_limit): + """ + The maximum recording time. + + :param time_limit: The actual time limit of the recorded video. Can be either number of seconds + or timedelta instance. + :return: self instance for chaining. + """ + self._timeLimit = time_limit.total_seconds() if isinstance(time_limit, timedelta) else time_limit + return self + + def enable_forced_restart(self): + """ + Whether to ignore the result of previous capture and start a new recording + immediately. By default the endpoint will try to catch and return the result of + the previous capture if it's still available. + + :return: self instance for chaining. + """ + self._forceRestart = True + return self + + def build(self): + result = super(StartScreenRecordingOptions, self).build() + result.update(attributes_to_dict(self, ('_forceRestart', '_timeLimit'))) + return copy.copy(result) diff --git a/appium/webdriver/common/screenrecord/stop_screen_recording_options.py b/appium/webdriver/common/screenrecord/stop_screen_recording_options.py new file mode 100644 index 00000000..3d794514 --- /dev/null +++ b/appium/webdriver/common/screenrecord/stop_screen_recording_options.py @@ -0,0 +1,34 @@ +#!/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 webdriver.common.screenrecord.base_screen_recording_options import BaseScreenRecordingOptions + + +class StopScreenRecordingOptions(BaseScreenRecordingOptions): + + def with_upload_options(self, upload_options): + """ + The remotePath upload option is the path to the remote location, + where the resulting video should be uploaded. + The following protocols are supported: http/https (multipart), ftp. + Missing value (the default setting) means the content of resulting + file should be encoded as Base64 and passed as the endpoint response value, but + an exception will be thrown if the generated media file is too big to + fit into the available process memory. + + :param upload_options: see the documentation on ScreenRecordingUploadOptions + for more details. + :return: self instance for chaining. + """ + return super(StopScreenRecordingOptions, self).with_upload_options(upload_options) diff --git a/appium/webdriver/common/screenrecord/utils.py b/appium/webdriver/common/screenrecord/utils.py new file mode 100644 index 00000000..97a2eddf --- /dev/null +++ b/appium/webdriver/common/screenrecord/utils.py @@ -0,0 +1,22 @@ +#!/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. + + +def attributes_to_dict(obj, attribute_names): + result = {} + for attr_name in attribute_names: + attr_value = getattr(obj, attr_name) + if attr_value is not None: + result[attr_name[1:]] = attr_value + return result diff --git a/appium/webdriver/mobilecommand.py b/appium/webdriver/mobilecommand.py index b4065be0..824fe1cb 100644 --- a/appium/webdriver/mobilecommand.py +++ b/appium/webdriver/mobilecommand.py @@ -63,3 +63,5 @@ class MobileCommand(object): SET_LOCATION = 'setLocation' GET_DEVICE_TIME = 'getDeviceTime' CLEAR = 'clear' + START_RECORDING_SCREEN = 'startRecordingScreen' + STOP_RECORDING_SCREEN = 'stopRecordingScreen' diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index 41af0c3f..f15165ba 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -24,7 +24,6 @@ from appium.webdriver.common.multi_action import MultiAction from selenium.webdriver.common.by import By -from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.ui import WebDriverWait from selenium.common.exceptions import TimeoutException @@ -809,6 +808,28 @@ def set_location(self, latitude, longitude, altitude): self.execute(Command.SET_LOCATION, data) return self + def start_recording_screen(self, options=None): + """ + Start asynchronous screen recording process. + + :param options: see the documentation on the StartScreenRecordingOptions + descendant for the particular platform. + :return: Base-64 encoded content of the recorded media file or an empty string + if the file has been successfully uploaded to a remote location (depends on the actual options). + """ + return self.execute(Command.START_RECORDING_SCREEN, options.build() if options else {})['value'] + + def stop_recording_screen(self, options=None): + """ + Gather the output from the previously started screen recording to a media file. + + :param options: see the documentation on the StopScreenRecordingOptions + descendant for the particular platform. + :return: Base-64 encoded content of the recorded media file or an empty string + if the file has been successfully uploaded to a remote location (depends on the actual options). + """ + return self.execute(Command.STOP_RECORDING_SCREEN, options.build() if options else {})['value'] + @property def device_time(self): """Returns the date and time from the device @@ -911,3 +932,7 @@ def _addCommands(self): ('GET', '/session/$sessionId/appium/device/system_time') self.command_executor._commands[Command.CLEAR] = \ ('POST', '/session/$sessionId/element/$id/clear') + self.command_executor._commands[Command.START_RECORDING_SCREEN] = \ + ('POST', '/session/$sessionId/appium/start_recording_screen') + self.command_executor._commands[Command.STOP_RECORDING_SCREEN] = \ + ('POST', '/session/$sessionId/appium/stop_recording_screen') diff --git a/test/functional/ios/appium_tests.py b/test/functional/ios/appium_tests.py index 73db5108..bc821256 100644 --- a/test/functional/ios/appium_tests.py +++ b/test/functional/ios/appium_tests.py @@ -15,8 +15,6 @@ import unittest from time import sleep -from selenium.common.exceptions import NoSuchElementException - from appium import webdriver import desired_capabilities @@ -37,6 +35,12 @@ def test_lock(self): self.driver.unlock() self.assertFalse(self.driver.is_locked()) + def test_screnn_record(self): + self.driver.start_recording_screen() + sleep(5) + result = self.driver.stop_recording_screen() + self.assertTrue(len(result) > 0) + def test_shake(self): # what can we assert about this? self.driver.shake() From 5406bf4571b59fd583d7ed0887429cc1dd4ea174 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 22 Jan 2018 11:24:47 +0100 Subject: [PATCH 2/6] Fix typo --- test/functional/ios/appium_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/ios/appium_tests.py b/test/functional/ios/appium_tests.py index bc821256..ebc6dcf0 100644 --- a/test/functional/ios/appium_tests.py +++ b/test/functional/ios/appium_tests.py @@ -35,7 +35,7 @@ def test_lock(self): self.driver.unlock() self.assertFalse(self.driver.is_locked()) - def test_screnn_record(self): + def test_screen_record(self): self.driver.start_recording_screen() sleep(5) result = self.driver.stop_recording_screen() From a0cb4f5cc255754dd5e41020b43a3450d07e87fa Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 22 Jan 2018 14:05:05 +0100 Subject: [PATCH 3/6] Add a separate test for Android and get rid of redundant stuff --- .../webdriver/common/screenrecord/__init__.py | 13 ---- .../android_start_screen_recording_options.py | 58 --------------- .../android_stop_screen_recording_options.py | 22 ------ .../base_screen_recording_options.py | 41 ---------- .../ios_start_screen_recording_options.py | 71 ------------------ .../ios_stop_screen_recording_options.py | 22 ------ .../screen_recording_upload_options.py | 61 --------------- .../start_screen_recording_options.py | 72 ------------------ .../stop_screen_recording_options.py | 34 --------- appium/webdriver/common/screenrecord/utils.py | 22 ------ appium/webdriver/webdriver.py | 74 ++++++++++++++++--- test/functional/android/appium_tests.py | 6 ++ 12 files changed, 70 insertions(+), 426 deletions(-) delete mode 100644 appium/webdriver/common/screenrecord/__init__.py delete mode 100644 appium/webdriver/common/screenrecord/android_start_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/base_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/screen_recording_upload_options.py delete mode 100644 appium/webdriver/common/screenrecord/start_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/stop_screen_recording_options.py delete mode 100644 appium/webdriver/common/screenrecord/utils.py diff --git a/appium/webdriver/common/screenrecord/__init__.py b/appium/webdriver/common/screenrecord/__init__.py deleted file mode 100644 index cc173e9d..00000000 --- a/appium/webdriver/common/screenrecord/__init__.py +++ /dev/null @@ -1,13 +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. diff --git a/appium/webdriver/common/screenrecord/android_start_screen_recording_options.py b/appium/webdriver/common/screenrecord/android_start_screen_recording_options.py deleted file mode 100644 index e32ff31e..00000000 --- a/appium/webdriver/common/screenrecord/android_start_screen_recording_options.py +++ /dev/null @@ -1,58 +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 copy - -from webdriver.common.screenrecord.start_screen_recording_options import StartScreenRecordingOptions -from webdriver.common.screenrecord.utils import attributes_to_dict - - -class AndroidStartScreenRecordingOptions(StartScreenRecordingOptions): - - def __init__(self): - super(AndroidStartScreenRecordingOptions, self).__init__() - self._bitRate = None - - @staticmethod - def start_screen_recording_options(): - return AndroidStartScreenRecordingOptions() - - def with_bit_rate(self, bit_rate): - """ - 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. - - :param bit_rate: The actual bit rate (Mb/s) - :return: self instance for chaining. - """ - self._bitRate = bit_rate - return self - - def with_time_limit(self, time_limit): - """ - The maximum recording time. The default and maximum value is 180 seconds (3 minutes). - Setting values greater than this or less than zero will cause an exception. The minimum - time resolution unit is one second. - - :param time_limit: The actual time limit of the recorded video. Can be a number of seconds - or timedelta instance. - :return: self instance for chaining. - """ - return super(AndroidStartScreenRecordingOptions, self).with_time_limit(time_limit) - - def build(self): - result = super(AndroidStartScreenRecordingOptions, self).build() - result.update(attributes_to_dict(self, ('_bitRate', ))) - return copy.copy(result) diff --git a/appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py b/appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py deleted file mode 100644 index 1d08a2e0..00000000 --- a/appium/webdriver/common/screenrecord/android_stop_screen_recording_options.py +++ /dev/null @@ -1,22 +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 webdriver.common.screenrecord.stop_screen_recording_options import StopScreenRecordingOptions - - -class AndroidStopScreenRecordingOptions(StopScreenRecordingOptions): - - @staticmethod - def stop_screen_recording_options(): - return AndroidStopScreenRecordingOptions() diff --git a/appium/webdriver/common/screenrecord/base_screen_recording_options.py b/appium/webdriver/common/screenrecord/base_screen_recording_options.py deleted file mode 100644 index 3e990780..00000000 --- a/appium/webdriver/common/screenrecord/base_screen_recording_options.py +++ /dev/null @@ -1,41 +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. - - -class BaseScreenRecordingOptions(object): - - def __init__(self): - self._upload_options = None - - def with_upload_options(self, upload_options): - """ - Upload options for the recorded screen capture. - - :param upload_options: see the documentation on {@link ScreenRecordingUploadOptions} - for more details. - :return: self instance for chaining. - """ - self._upload_options = upload_options - return self - - def build(self): - """ - Builds a map, which is ready to be passed to the subordinated - Appium API. - - :return: arguments mapping. - """ - if self._upload_options is None: - return {} - return self._upload_options.build() diff --git a/appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py b/appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py deleted file mode 100644 index 563cc777..00000000 --- a/appium/webdriver/common/screenrecord/ios_start_screen_recording_options.py +++ /dev/null @@ -1,71 +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 copy - -from webdriver.common.screenrecord.start_screen_recording_options import StartScreenRecordingOptions -from webdriver.common.screenrecord.utils import attributes_to_dict - - -class IOSStartScreenRecordingOptions(StartScreenRecordingOptions): - - def __init__(self): - super(IOSStartScreenRecordingOptions, self).__init__() - self._videoType = None - self._videoQuality = None - - @staticmethod - def start_screen_recording_options(): - return IOSStartScreenRecordingOptions() - - def with_video_type(self, video_type): - """ - The format of the screen capture to be recorded. - Available formats: "h264", "mp4" or "fmp4". Default is "mp4". - Only works for Simulator. - - :param video_type: one of available format names. - :return: self instance for chaining. - """ - self._videoType = video_type - return self - - def with_wideo_quality(self, video_quality): - """ - The video encoding quality ('low', 'medium', 'high', 'photo' - defaults to 'medium'). - Only works for real devices. - - :param video_quality: one of possible quality preset names. - :return: self instance for chaining. - """ - self._videoQuality = video_quality - return self - - def with_time_limit(self, time_limit): - """ - The maximum recording time. The default value is 180 seconds (3 minutes). - The maximum value is 10 minutes. - Setting values greater than this or less than zero will cause an exception. The minimum - time resolution unit is one second. - - :param time_limit: The actual time limit of the recorded video. Can be a number of seconds - or timedelta instance. - :return: self instance for chaining. - """ - return super(IOSStartScreenRecordingOptions, self).with_time_limit(time_limit) - - def build(self): - result = super(IOSStartScreenRecordingOptions, self).build() - result.update(attributes_to_dict(self, ('_videoType', '_videoQuality'))) - return copy.copy(result) diff --git a/appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py b/appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py deleted file mode 100644 index 4645f4b6..00000000 --- a/appium/webdriver/common/screenrecord/ios_stop_screen_recording_options.py +++ /dev/null @@ -1,22 +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 webdriver.common.screenrecord.stop_screen_recording_options import StopScreenRecordingOptions - - -class IOSStopScreenRecordingOptions(StopScreenRecordingOptions): - - @staticmethod - def stop_screen_recording_options(): - return IOSStopScreenRecordingOptions() diff --git a/appium/webdriver/common/screenrecord/screen_recording_upload_options.py b/appium/webdriver/common/screenrecord/screen_recording_upload_options.py deleted file mode 100644 index c9cfaa14..00000000 --- a/appium/webdriver/common/screenrecord/screen_recording_upload_options.py +++ /dev/null @@ -1,61 +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 webdriver.common.screenrecord.utils import attributes_to_dict - - -class ScreenRecordingUploadOptions(object): - - def __init__(self): - self._remotePath = None - self._user = None - self._pass = None - self._method = None - - def with_remote_path(self, remote_path): - """ - The path to a remote location, where the resulting video should be uploaded. - - :param remote_path: The path to a writable remote location. - :return: self instance for chaining. - """ - self._remotePath = remote_path - return self - - def with_auth_credentials(self, user, password): - """ - Sets the credentials for remote ftp/http authentication (if needed). - This option only has an effect if remotePath is provided. - - :param user: The name of the user for the remote authentication. - :param password: The password for the remote authentication. - :return: self instance for chaining. - """ - self._user = user - self._pass = password - return self - - def with_http_method(self, method): - """ - Sets the method name for http(s) upload. PUT is used by default. - This option only has an effect if remotePath is provided. - - :param method:The HTTP method name ('PUT'/'POST'). - :return: self instance for chaining. - """ - self._method = method - return self - - def build(self): - return attributes_to_dict(self, ('_remotePath', '_user', '_pass', '_method')) diff --git a/appium/webdriver/common/screenrecord/start_screen_recording_options.py b/appium/webdriver/common/screenrecord/start_screen_recording_options.py deleted file mode 100644 index 920d1f4b..00000000 --- a/appium/webdriver/common/screenrecord/start_screen_recording_options.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import copy -from datetime import timedelta - -from webdriver.common.screenrecord.base_screen_recording_options import BaseScreenRecordingOptions -from webdriver.common.screenrecord.utils import attributes_to_dict - - -class StartScreenRecordingOptions(BaseScreenRecordingOptions): - - def __init__(self): - super(StartScreenRecordingOptions, self).__init__() - self._forceRestart = None - self._timeLimit = None - - def with_upload_options(self, upload_options): - """ - The remotePath upload option is the path to the remote location, - where the resulting video should be uploaded. - The following protocols are supported: http/https (multipart), ftp. - Missing value (the default setting) means the content of the resulting - file should be encoded as Base64 and passed as the endpoint response value, but - an exception will be thrown if the generated media file is too big to - fit into the available process memory. - This option only has an effect if there is a screen recording session in progress - and forced restart is not enabled (the default setting). - - :param upload_options: see the documentation on ScreenRecordingUploadOptions - for more details. - :return: self instance for chaining. - """ - return super(StartScreenRecordingOptions, self).with_upload_options(upload_options) - - def with_time_limit(self, time_limit): - """ - The maximum recording time. - - :param time_limit: The actual time limit of the recorded video. Can be either number of seconds - or timedelta instance. - :return: self instance for chaining. - """ - self._timeLimit = time_limit.total_seconds() if isinstance(time_limit, timedelta) else time_limit - return self - - def enable_forced_restart(self): - """ - Whether to ignore the result of previous capture and start a new recording - immediately. By default the endpoint will try to catch and return the result of - the previous capture if it's still available. - - :return: self instance for chaining. - """ - self._forceRestart = True - return self - - def build(self): - result = super(StartScreenRecordingOptions, self).build() - result.update(attributes_to_dict(self, ('_forceRestart', '_timeLimit'))) - return copy.copy(result) diff --git a/appium/webdriver/common/screenrecord/stop_screen_recording_options.py b/appium/webdriver/common/screenrecord/stop_screen_recording_options.py deleted file mode 100644 index 3d794514..00000000 --- a/appium/webdriver/common/screenrecord/stop_screen_recording_options.py +++ /dev/null @@ -1,34 +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 webdriver.common.screenrecord.base_screen_recording_options import BaseScreenRecordingOptions - - -class StopScreenRecordingOptions(BaseScreenRecordingOptions): - - def with_upload_options(self, upload_options): - """ - The remotePath upload option is the path to the remote location, - where the resulting video should be uploaded. - The following protocols are supported: http/https (multipart), ftp. - Missing value (the default setting) means the content of resulting - file should be encoded as Base64 and passed as the endpoint response value, but - an exception will be thrown if the generated media file is too big to - fit into the available process memory. - - :param upload_options: see the documentation on ScreenRecordingUploadOptions - for more details. - :return: self instance for chaining. - """ - return super(StopScreenRecordingOptions, self).with_upload_options(upload_options) diff --git a/appium/webdriver/common/screenrecord/utils.py b/appium/webdriver/common/screenrecord/utils.py deleted file mode 100644 index 97a2eddf..00000000 --- a/appium/webdriver/common/screenrecord/utils.py +++ /dev/null @@ -1,22 +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. - - -def attributes_to_dict(obj, attribute_names): - result = {} - for attr_name in attribute_names: - attr_value = getattr(obj, attr_name) - if attr_value is not None: - result[attr_name[1:]] = attr_value - return result diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index f15165ba..e32d6897 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -808,27 +808,81 @@ def set_location(self, latitude, longitude, altitude): self.execute(Command.SET_LOCATION, data) return self - def start_recording_screen(self, options=None): + def start_recording_screen(self, **options): """ Start asynchronous screen recording process. - :param options: see the documentation on the StartScreenRecordingOptions - descendant for the particular platform. + :param options: The following options are supported: + - remotePath: The remotePath upload option is the path to the remote location, + where the resulting video from the previous screen recording should be uploaded. + The following protocols are supported: http/https (multipart), ftp. + Missing value (the default setting) means the content of the resulting + file should be encoded as Base64 and passed as the endpoint response value, but + an exception will be thrown if the generated media file is too big to + fit into the available process memory. + This option only has an effect if there is/was an active screen recording session + and forced restart is not enabled (the default setting). + - user: The name of the user for the remote authentication. + Only has an effect if both `remotePath` and `password` are set. + - password: The password for the remote authentication. + Only has an effect if both `remotePath` and `user` are set. + - method: The HTTP method name ('PUT'/'POST'). PUT method is used by default. + Only has an effect if `remotePath` is set. + - timeLimit: The actual time limit of the recorded video in seconds. + The default value for both iOS and Android is 180 seconds (3 minutes). + The maximum value for Android is 3 minutes. + The maximum value for iOS is 10 minutes. + - forcedRestart: 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. + + iOS Specific: + - videoQuality: The video encoding quality ('low', 'medium', 'high', 'photo' - defaults to 'medium'). + Only works for iOS real devices. + - videoType: The video encoding quality ('low', 'medium', 'high', 'photo' - defaults to 'medium'). + Only works for real devices. + + Android Specific: + - bitRate: 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. + :return: Base-64 encoded content of the recorded media file or an empty string - if the file has been successfully uploaded to a remote location (depends on the actual options). + if the file has been successfully uploaded to a remote location + (depends on the actual `remotePath` value). """ - return self.execute(Command.START_RECORDING_SCREEN, options.build() if options else {})['value'] + if 'password' in options: + options['pass'] = options['password'] + del options['password'] + return self.execute(Command.START_RECORDING_SCREEN, options)['value'] - def stop_recording_screen(self, options=None): + def stop_recording_screen(self, **options): """ Gather the output from the previously started screen recording to a media file. - :param options: see the documentation on the StopScreenRecordingOptions - descendant for the particular platform. + :param options: The following options are supported: + - remotePath: The remotePath upload option is the path to the remote location, + where the resulting video should be uploaded. + The following protocols are supported: http/https (multipart), ftp. + Missing value (the default setting) means the content of the resulting + file should be encoded as Base64 and passed as the endpoint response value, but + an exception will be thrown if the generated media file is too big to + fit into the available process memory. + - user: The name of the user for the remote authentication. + Only has an effect if both `remotePath` and `password` are set. + - password: The password for the remote authentication. + Only has an effect if both `remotePath` and `user` are set. + - method: The HTTP method name ('PUT'/'POST'). PUT method is used by default. + Only has an effect if `remotePath` is set. + :return: Base-64 encoded content of the recorded media file or an empty string - if the file has been successfully uploaded to a remote location (depends on the actual options). + if the file has been successfully uploaded to a remote location + (depends on the actual `remotePath` value). """ - return self.execute(Command.STOP_RECORDING_SCREEN, options.build() if options else {})['value'] + if 'password' in options: + options['pass'] = options['password'] + del options['password'] + return self.execute(Command.STOP_RECORDING_SCREEN, options)['value'] @property def device_time(self): diff --git a/test/functional/android/appium_tests.py b/test/functional/android/appium_tests.py index e7227873..acc4e9e6 100644 --- a/test/functional/android/appium_tests.py +++ b/test/functional/android/appium_tests.py @@ -43,6 +43,12 @@ def tearDown(self): if hasattr(self, 'zipfilename') and os.path.isfile(self.zipfilename): os.remove(self.zipfilename) + def test_screen_record(self): + self.driver.start_recording_screen(time_limit=10, forcedRestart=True) + sleep(5) + result = self.driver.stop_recording_screen() + self.assertTrue(len(result) > 0) + def test_lock(self): self.driver.lock(-1) try: From 613c34ba5e4420e355bd0e0ea37ee8e5d02cc110 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Mon, 22 Jan 2018 14:07:30 +0100 Subject: [PATCH 4/6] Tune documentation --- appium/webdriver/webdriver.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index e32d6897..948d27b0 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -837,10 +837,11 @@ def start_recording_screen(self, **options): the previous capture if it's still available. iOS Specific: - - videoQuality: The video encoding quality ('low', 'medium', 'high', 'photo' - defaults to 'medium'). - Only works for iOS real devices. - - videoType: The video encoding quality ('low', 'medium', 'high', 'photo' - defaults to 'medium'). + - videoQuality: The video encoding quality: 'low', 'medium', 'high', 'photo'. Defaults to 'medium'. Only works for real devices. + - videoType: The format of the screen capture to be recorded. + Available formats: 'h264', 'mp4' or 'fmp4'. Default is 'mp4'. + Only works for Simulator. Android Specific: - bitRate: The video bit rate for the video, in megabits per second. From 6f5ba88f533ab80a056146ea3abf5e9f3592ce2b Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Wed, 24 Jan 2018 14:09:05 +0100 Subject: [PATCH 5/6] Add videoSize arg description --- appium/webdriver/webdriver.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/appium/webdriver/webdriver.py b/appium/webdriver/webdriver.py index 948d27b0..fbd5432c 100644 --- a/appium/webdriver/webdriver.py +++ b/appium/webdriver/webdriver.py @@ -844,6 +844,10 @@ def start_recording_screen(self, **options): Only works for Simulator. Android Specific: + - videoSize: The video size of the generated media file. The format is WIDTHxHEIGHT. + 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: 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. From 0c1766b6d47c83f7981315515ee5fed693180f6c Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Thu, 25 Jan 2018 07:53:29 +0100 Subject: [PATCH 6/6] Fix arg name --- test/functional/android/appium_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/android/appium_tests.py b/test/functional/android/appium_tests.py index acc4e9e6..d358c43b 100644 --- a/test/functional/android/appium_tests.py +++ b/test/functional/android/appium_tests.py @@ -44,7 +44,7 @@ def tearDown(self): os.remove(self.zipfilename) def test_screen_record(self): - self.driver.start_recording_screen(time_limit=10, forcedRestart=True) + self.driver.start_recording_screen(timeLimit=10, forcedRestart=True) sleep(5) result = self.driver.stop_recording_screen() self.assertTrue(len(result) > 0)