diff --git a/docs/APILibraryDocumentation.html b/docs/APILibraryDocumentation.html index 138646ff..1726833e 100644 --- a/docs/APILibraryDocumentation.html +++ b/docs/APILibraryDocumentation.html @@ -614,7 +614,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a diff --git a/docs/DesktopLibraryDocumentation.html b/docs/DesktopLibraryDocumentation.html index 86de05dd..bc18229e 100644 --- a/docs/DesktopLibraryDocumentation.html +++ b/docs/DesktopLibraryDocumentation.html @@ -614,7 +614,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a diff --git a/docs/GUILibraryDocumentation.html b/docs/GUILibraryDocumentation.html index 97c63c1d..f34304f9 100644 --- a/docs/GUILibraryDocumentation.html +++ b/docs/GUILibraryDocumentation.html @@ -614,7 +614,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a diff --git a/docs/MobileLibraryDocumentation.html b/docs/MobileLibraryDocumentation.html index 8f8fc4dd..e0a189fc 100644 --- a/docs/MobileLibraryDocumentation.html +++ b/docs/MobileLibraryDocumentation.html @@ -614,7 +614,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a diff --git a/docs/SOAPLibraryDocumentation.html b/docs/SOAPLibraryDocumentation.html index 29d6cab7..4c7f5c9a 100644 --- a/docs/SOAPLibraryDocumentation.html +++ b/docs/SOAPLibraryDocumentation.html @@ -614,7 +614,7 @@ jQuery.extend({highlight:function(e,t,n,r){if(e.nodeType===3){var i=e.data.match(t);if(i){var s=document.createElement(n||"span");s.className=r||"highlight";var o=e.splitText(i.index);o.splitText(i[0].length);var u=o.cloneNode(true);s.appendChild(u);o.parentNode.replaceChild(s,o);return 1}}else if(e.nodeType===1&&e.childNodes&&!/(script|style)/i.test(e.tagName)&&!(e.tagName===n.toUpperCase()&&e.className===r)){for(var a=0;a diff --git a/samples/Appium-DesktopTests.robot b/samples/Appium-DesktopTests.robot index 0f0fb69f..79af6cd2 100644 --- a/samples/Appium-DesktopTests.robot +++ b/samples/Appium-DesktopTests.robot @@ -147,6 +147,15 @@ Flick Tests Wait Until Page Contains Element accessibility_id=Standard Wait For And Click Element accessibility_id=Standard +Screen Recording Keyword Test - See video with logs or in log.html + Start Screen Recording + Wait For And Click Element accessibility_id=num2Button + Wait For And Click Element accessibility_id=num3Button + Wait For And Click Element accessibility_id=num4Button + Wait For And Click Element accessibility_id=num5Button + Wait Until Element Contains accessibility_id=CalculatorResults 2,345 + Stop Screen Recording + Switch To Desktop Test Close Application Switch Application Desktop diff --git a/src/Zoomba/DesktopLibrary.py b/src/Zoomba/DesktopLibrary.py index a8a0039c..57049a77 100644 --- a/src/Zoomba/DesktopLibrary.py +++ b/src/Zoomba/DesktopLibrary.py @@ -9,9 +9,9 @@ from selenium.common.exceptions import NoSuchElementException, InvalidSelectorException from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.touch_actions import TouchActions - from time import sleep, time from robot import utils +from base64 import b64decode try: AppiumCommon = importlib.import_module('Helpers.AppiumCommon', package='Helpers') @@ -109,6 +109,10 @@ def __init__(self, timeout=5, run_on_failure='Save Appium Screenshot', | Library | DesktopLibrary | timeout=10 | driver_path="C:/WinAppDriver.exe" | # Sets a new path for the WinAppDriver | """ self.winappdriver = WinAppDriver(driver_path) + # self.recorder = _ScreenrecordKeywords() + self._screenrecord_index = 0 + self._recording = None + self._output_format = None super().__init__(timeout, run_on_failure) def get_keyword_names(self): @@ -146,9 +150,82 @@ def get_keyword_names(self): 'text_should_be_visible', 'wait_until_element_is_visible', 'wait_until_page_contains', 'wait_until_page_contains_element', 'wait_until_page_does_not_contain', 'wait_until_page_does_not_contain_element', 'get_matching_xpath_count', - 'xpath_should_match_x_times', 'tap' + 'xpath_should_match_x_times', 'tap', 'start_screen_recording', 'stop_screen_recording' ] + # Screen Recorder - adapted from AppiumLibrary + @keyword("Start Screen Recording") + def start_screen_recording(self, time_limit='180s', **options): + """Starts an asynchronous Screen Recording for the current open application. + + ``timeLimit`` sets the actual time limit of the recorded video (defaulting to 180 seconds). + + `Start Screen Recording` is used hand in hand with `Stop Screen Recording`. + See `Stop Screen Recording` for more details. + + Keyword requires Appium to be used. + + Example: + | `Start Screen Recording` | | # starts a screen record session | + | .... keyword actions | | | + | `Stop Screen Recording` | filename=output | # saves the recorded session | + """ + options['time_limit'] = utils.timestr_to_secs(time_limit) + self._output_format = '.mp4' + if self._recording is None: + self._recording = self._current_application().start_recording_screen(**options) + + @keyword("Stop Screen Recording") + def stop_screen_recording(self, filename=None, **options): + """Gathers the output from the previously started screen recording \ + to a media file, then embeds it to the log.html(Android Only). + + Requires an active or exhausted Screen Recording Session. + See `Start Screen Recording` for more details. + + === Optional Args === + + - ``remotePath`` The path to the remote location, where the resulting video should be \ + uploaded. The following protocols are supported _http/https_, ftp. Null or empty \ + string value (the default setting) means the content of resulting file should \ + be encoded as Base64 and passed as the endpoint response value. An \ + exception will be thrown if the generated media file is too big to fit \ + into the available process memory. + + - ``username`` The name of the user for the remote authentication. + + - ``password`` The password for the remote authentication. + + - ``method`` The http multipart upload method name. The _PUT_ one is used by default. + + Keyword requires Appium to be used. + + Example: + | `Start Screen Recording` | | # starts a screen record session | + | .... keyword actions | | | + | `Stop Screen Recording` | filename=output | # saves the recorded session | + """ + self._recording = self._current_application().stop_recording_screen(**options) + return self._save_recording(filename, options) + + def _save_recording(self, filename, options): + path, link = self._get_screenrecord_paths(options, filename) + decoded = b64decode(self._recording) + with open(path, 'wb') as screenrecording: + screenrecording.write(decoded) + # Embed the Screen Recording to the log file + if not self._is_remotepath_set(options): + self._html('' + ''.format(vid=link) + ) + # Empty Screen Record Variable + self._recording = None + return path + + # End Screen Recorder Keywords + @keyword("Driver Setup") def driver_setup(self, path=None): """Starts the WinAppDriver. diff --git a/test/Desktop/test_desktop.py b/test/Desktop/test_desktop.py index c5974ebb..69ab7917 100644 --- a/test/Desktop/test_desktop.py +++ b/test/Desktop/test_desktop.py @@ -698,3 +698,14 @@ def test_drag_and_drop_by_touch_offset(self, tap_and_hold): mock_desk = MagicMock() DesktopLibrary.drag_and_drop_by_touch_offset(mock_desk, "some_locator", 50, 100) tap_and_hold.assert_called_with(unittest.mock.ANY, unittest.mock.ANY) + + def test_start_screen_recording(self): + mock_desk = MagicMock() + mock_desk._recording = None + DesktopLibrary.start_screen_recording(mock_desk) + + def test_stop_screen_recording(self): + mock_desk = MagicMock() + mock_desk._recording = None + DesktopLibrary.start_screen_recording(mock_desk) + DesktopLibrary.stop_screen_recording(mock_desk)