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
2 changes: 1 addition & 1 deletion docs/APILibraryDocumentation.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/DesktopLibraryDocumentation.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/GUILibraryDocumentation.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/MobileLibraryDocumentation.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/SOAPLibraryDocumentation.html
Original file line number Diff line number Diff line change
Expand Up @@ -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<e.childNodes.length;a++){a+=jQuery.highlight(e.childNodes[a],t,n,r)}}return 0}});jQuery.fn.unhighlight=function(e){var t={className:"highlight",element:"span"};jQuery.extend(t,e);return this.find(t.element+"."+t.className).each(function(){var e=this.parentNode;e.replaceChild(this.firstChild,this);e.normalize()}).end()};jQuery.fn.highlight=function(e,t){var n={className:"highlight",element:"span",caseSensitive:false,wordsOnly:false};jQuery.extend(n,t);if(e.constructor===String){e=[e]}e=jQuery.grep(e,function(e,t){return e!=""});e=jQuery.map(e,function(e,t){return e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")});if(e.length==0){return this}var r=n.caseSensitive?"":"i";var i="("+e.join("|")+")";if(n.wordsOnly){i="\\b"+i+"\\b"}var s=new RegExp(i,r);return this.each(function(){jQuery.highlight(this,s,n.element,n.className)})}
</script>
<script type="text/javascript">
libdoc = {"all_tags":[],"contains_tags":false,"doc":"<p>Zoomba SOAP Library\x3c/p>\n<p>This class is the base Library used to generate automated SOAP Tests in the Zoomba Automation Framework.\x3c/p>","generated":"2020-11-25 15:10:57","inits":[],"keywords":[{"args":["action=None","soap_object=None"],"doc":"<p>Call Soap Method. Calls soap method with list object\x3c/p>\n<p>action: (string) SOAP Action to be called.\x3c/p>\n<p>soap_object: (list) Soap Object in list format, list must be ordered wrt schema\x3c/p>","matched":true,"name":"Call Soap Method With List Object","shortdoc":"Call Soap Method. Calls soap method with list object ","tags":[]},{"args":["action=None","**soap_object"],"doc":"<p>Call Soap Method with dictionary object. Calls soap method\x3c/p>\n<p>action: (string) SOAP Action to be called.\x3c/p>\n<p>soap_object: (dict) Soap Object in dict format, dict must contain all required parts of schema object.\x3c/p>","matched":true,"name":"Call Soap Method With Object","shortdoc":"Call Soap Method with dictionary object. Calls soap method ","tags":[]},{"args":["soap_response=None"],"doc":"<p>Convert Soap Response To Dictionary: This keyword builds a dictionary from the sudsLibrary response\x3c/p>\n<p>json_actual_response: (request response object) The response from an API.\x3c/p>\n<p>return: There is no actual returned output, other than error messages when comparisons fail.\x3c/p>","matched":true,"name":"Convert Soap Response To Json","shortdoc":"Convert Soap Response To Dictionary: This keyword builds a dictionary from the sudsLibrary response","tags":[]},{"args":["host=None","endpoint=None","alias=None","**kwargs"],"doc":"<p>Create Soap Session. This Keyword utilizes the WSDL to create a soap client.\x3c/p>\n<p>host: (string) The host url.\x3c/p>\n<p>endpoint: (string) SOAP API endpoint containing the actions to be referenced.\x3c/p>\n<p>**kwargs: (optional) Parameters that could be included to add options to client creation. Current supported parameters are:\x3c/p>\n<p>set_location: http address\x3c/p>","matched":true,"name":"Create Soap Session","shortdoc":"Create Soap Session. This Keyword utilizes the WSDL to create a soap client.","tags":[]},{"args":["host=None","endpoint=None","alias=None","**kwargs"],"doc":"<p>Create Soap Session. This Keyword utilizes the WSDL and directly accesses calls from sudsLibrary.\x3c/p>\n<p>host: (string) The host url.\x3c/p>\n<p>endpoint: (string) SOAP API endpoint containing the actions to be referenced.\x3c/p>\n<p>alias: (string} Sets the alias for the SudsLibrary Framework **kwargs: (optional) Parameters that could be included to add options to client creation. Current supported parameters are:\x3c/p>\n<p>set_location: http address\x3c/p>","matched":true,"name":"Create Soap Session And Fix Wsdl","shortdoc":"Create Soap Session. This Keyword utilizes the WSDL and directly accesses calls from sudsLibrary.","tags":[]},{"args":["host=None","endpoint=None","alias=None","set_location=None","fix=False"],"doc":"<p>Create Soap Session and Set Location. In addition to the client creation, this keyword sets the location as specified.\x3c/p>\n<p>host: (string) The host url.\x3c/p>\n<p>endpoint: (string) SOAP API endpoint containing the actions to be referenced.\x3c/p>\n<p>set_location: (string) If set will overwrite the WSDL location with specified address.\x3c/p>\n<p>If set to None will replace location with host and endpoint specified\x3c/p>","matched":true,"name":"Create Soap Session And Set Location","shortdoc":"Create Soap Session and Set Location. In addition to the client creation, this keyword sets the location as specified.","tags":[]},{"args":["wsdl_type=None","object_dict=None"],"doc":"<p>Create Wsdl Objects. This Keyword utilizes the WSDL to create a WSDL object based on the information provided.\x3c/p>\n<p>wsdl_type: (string) Wsdl object to be created.\x3c/p>\n<p>object_dict: (dict) Python Dictionary containing values and nested dictionaries with construction similar to wsdl defined objects.\x3c/p>\n<p>return: (response object) Returns the SOAP client object.\x3c/p>","matched":true,"name":"Create Wsdl Objects","shortdoc":"Create Wsdl Objects. This Keyword utilizes the WSDL to create a WSDL object based on the information provided.","tags":[]}],"name":"SOAP_Library","named_args":true,"scope":"TEST","version":"2.7.0"};
libdoc = {"all_tags":[],"contains_tags":false,"doc":"<p>Zoomba SOAP Library\x3c/p>\n<p>This class is the base Library used to generate automated SOAP Tests in the Zoomba Automation Framework.\x3c/p>","generated":"2020-12-07 14:45:01","inits":[],"keywords":[{"args":["action=None","soap_object=None"],"doc":"<p>Call Soap Method. Calls soap method with list object\x3c/p>\n<p>action: (string) SOAP Action to be called.\x3c/p>\n<p>soap_object: (list) Soap Object in list format, list must be ordered wrt schema\x3c/p>","matched":true,"name":"Call Soap Method With List Object","shortdoc":"Call Soap Method. Calls soap method with list object ","tags":[]},{"args":["action=None","**soap_object"],"doc":"<p>Call Soap Method with dictionary object. Calls soap method\x3c/p>\n<p>action: (string) SOAP Action to be called.\x3c/p>\n<p>soap_object: (dict) Soap Object in dict format, dict must contain all required parts of schema object.\x3c/p>","matched":true,"name":"Call Soap Method With Object","shortdoc":"Call Soap Method with dictionary object. Calls soap method ","tags":[]},{"args":["soap_response=None"],"doc":"<p>Convert Soap Response To Dictionary: This keyword builds a dictionary from the sudsLibrary response\x3c/p>\n<p>json_actual_response: (request response object) The response from an API.\x3c/p>\n<p>return: There is no actual returned output, other than error messages when comparisons fail.\x3c/p>","matched":true,"name":"Convert Soap Response To Json","shortdoc":"Convert Soap Response To Dictionary: This keyword builds a dictionary from the sudsLibrary response","tags":[]},{"args":["host=None","endpoint=None","alias=None","**kwargs"],"doc":"<p>Create Soap Session. This Keyword utilizes the WSDL to create a soap client.\x3c/p>\n<p>host: (string) The host url.\x3c/p>\n<p>endpoint: (string) SOAP API endpoint containing the actions to be referenced.\x3c/p>\n<p>**kwargs: (optional) Parameters that could be included to add options to client creation. Current supported parameters are:\x3c/p>\n<p>set_location: http address\x3c/p>","matched":true,"name":"Create Soap Session","shortdoc":"Create Soap Session. This Keyword utilizes the WSDL to create a soap client.","tags":[]},{"args":["host=None","endpoint=None","alias=None","**kwargs"],"doc":"<p>Create Soap Session. This Keyword utilizes the WSDL and directly accesses calls from sudsLibrary.\x3c/p>\n<p>host: (string) The host url.\x3c/p>\n<p>endpoint: (string) SOAP API endpoint containing the actions to be referenced.\x3c/p>\n<p>alias: (string} Sets the alias for the SudsLibrary Framework **kwargs: (optional) Parameters that could be included to add options to client creation. Current supported parameters are:\x3c/p>\n<p>set_location: http address\x3c/p>","matched":true,"name":"Create Soap Session And Fix Wsdl","shortdoc":"Create Soap Session. This Keyword utilizes the WSDL and directly accesses calls from sudsLibrary.","tags":[]},{"args":["host=None","endpoint=None","alias=None","set_location=None","fix=False"],"doc":"<p>Create Soap Session and Set Location. In addition to the client creation, this keyword sets the location as specified.\x3c/p>\n<p>host: (string) The host url.\x3c/p>\n<p>endpoint: (string) SOAP API endpoint containing the actions to be referenced.\x3c/p>\n<p>set_location: (string) If set will overwrite the WSDL location with specified address.\x3c/p>\n<p>If set to None will replace location with host and endpoint specified\x3c/p>","matched":true,"name":"Create Soap Session And Set Location","shortdoc":"Create Soap Session and Set Location. In addition to the client creation, this keyword sets the location as specified.","tags":[]},{"args":["wsdl_type=None","object_dict=None"],"doc":"<p>Create Wsdl Objects. This Keyword utilizes the WSDL to create a WSDL object based on the information provided.\x3c/p>\n<p>wsdl_type: (string) Wsdl object to be created.\x3c/p>\n<p>object_dict: (dict) Python Dictionary containing values and nested dictionaries with construction similar to wsdl defined objects.\x3c/p>\n<p>return: (response object) Returns the SOAP client object.\x3c/p>","matched":true,"name":"Create Wsdl Objects","shortdoc":"Create Wsdl Objects. This Keyword utilizes the WSDL to create a WSDL object based on the information provided.","tags":[]}],"name":"SOAP_Library","named_args":true,"scope":"TEST","version":"2.7.0"};
</script>
<title></title>
</head>
Expand Down
9 changes: 9 additions & 0 deletions samples/Appium-DesktopTests.robot
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
76 changes: 75 additions & 1 deletion src/Zoomba/DesktopLibrary.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -132,6 +132,7 @@ def get_keyword_names(self):
'drag_and_drop_by_touch_offset', 'wait_for_and_tap', 'wait_for_and_double_tap',
'double_tap', 'flick', 'flick_from_element', 'scroll', 'scroll_from_element',
'wait_for_and_flick_from_element', 'wait_for_and_scroll_from_element',
'start_screen_recording', 'stop_screen_recording',
# External Libraries
'clear_text', 'click_button', 'click_element', 'close_all_applications',
'close_application', 'element_attribute_should_match', 'element_should_be_disabled',
Expand All @@ -149,6 +150,79 @@ def get_keyword_names(self):
'xpath_should_match_x_times', 'tap'
]

# 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('</td></tr><tr><td colspan="3"><a href="{vid}">'
'<video width="800px" controls>'
'<source src="{vid}" type="video/mp4">'
'</video></a>'.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.
Expand Down
23 changes: 23 additions & 0 deletions test/Desktop/test_desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,3 +698,26 @@ 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)
self.assertTrue(mock_desk._recording == unittest.mock.ANY)

def test_stop_screen_recording(self):
mock_desk = MagicMock()
mock_desk._recording = None
DesktopLibrary.start_screen_recording(mock_desk)
file = DesktopLibrary.stop_screen_recording(mock_desk)
self.assertTrue(file == unittest.mock.ANY)

def test_save_recording(self):
mock_desk = MagicMock()
mock_desk._recording = b'some data'
mock_desk._is_remotepath_set = MagicMock(return_value=False)
options = ["username=test"]
mock_desk._get_screenrecord_paths = MagicMock(return_value=("path", "link"))
file = DesktopLibrary._save_recording(mock_desk, "filename", options)
self.assertTrue(file == "path")
os.remove("path")