robotframework-appiumwindows 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- AppiumLibrary/__init__.py +106 -106
- AppiumLibrary/appium_path.py +10 -9
- AppiumLibrary/keywords/__init__.py +21 -21
- AppiumLibrary/keywords/_applicationmanagement.py +595 -515
- AppiumLibrary/keywords/_element.py +1308 -1282
- AppiumLibrary/keywords/_logging.py +63 -63
- AppiumLibrary/keywords/_powershell.py +551 -553
- AppiumLibrary/keywords/_runonfailure.py +74 -74
- AppiumLibrary/keywords/_screenrecord.py +138 -138
- AppiumLibrary/keywords/_screenshot.py +105 -109
- AppiumLibrary/keywords/_waiting.py +163 -163
- AppiumLibrary/keywords/_windows.py +271 -215
- AppiumLibrary/keywords/keywordgroup.py +71 -70
- AppiumLibrary/locators/__init__.py +7 -7
- AppiumLibrary/locators/elementfinder.py +264 -264
- AppiumLibrary/utils/__init__.py +50 -50
- AppiumLibrary/utils/applicationcache.py +48 -48
- AppiumLibrary/version.py +2 -2
- robotframework_appiumwindows-0.1.2.dist-info/METADATA +214 -0
- robotframework_appiumwindows-0.1.2.dist-info/RECORD +23 -0
- {robotframework_appiumwindows-0.1.0.dist-info → robotframework_appiumwindows-0.1.2.dist-info}/WHEEL +1 -1
- {robotframework_appiumwindows-0.1.0.dist-info → robotframework_appiumwindows-0.1.2.dist-info}/licenses/LICENSE +20 -20
- robotframework_appiumwindows-0.1.0.dist-info/METADATA +0 -148
- robotframework_appiumwindows-0.1.0.dist-info/RECORD +0 -23
- {robotframework_appiumwindows-0.1.0.dist-info → robotframework_appiumwindows-0.1.2.dist-info}/top_level.txt +0 -0
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from robot.libraries import BuiltIn
|
|
4
|
-
|
|
5
|
-
from .keywordgroup import KeywordGroup
|
|
6
|
-
|
|
7
|
-
BUILTIN = BuiltIn.BuiltIn()
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class _RunOnFailureKeywords(KeywordGroup):
|
|
11
|
-
|
|
12
|
-
def __init__(self):
|
|
13
|
-
self._run_on_failure_keyword = None
|
|
14
|
-
self._running_on_failure_routine = False
|
|
15
|
-
|
|
16
|
-
# Public
|
|
17
|
-
|
|
18
|
-
def register_keyword_to_run_on_failure(self, keyword):
|
|
19
|
-
"""Sets the keyword to execute when a AppiumLibrary keyword fails.
|
|
20
|
-
|
|
21
|
-
`keyword_name` is the name of a keyword (from any available
|
|
22
|
-
libraries) that will be executed if a AppiumLibrary keyword fails.
|
|
23
|
-
It is not possible to use a keyword that requires arguments.
|
|
24
|
-
Using the value "Nothing" will disable this feature altogether.
|
|
25
|
-
|
|
26
|
-
The initial keyword to use is set in `importing`, and the
|
|
27
|
-
keyword that is used by default is `Capture Page Screenshot`.
|
|
28
|
-
Taking a screenshot when something failed is a very useful
|
|
29
|
-
feature, but notice that it can slow down the execution.
|
|
30
|
-
|
|
31
|
-
This keyword returns the name of the previously registered
|
|
32
|
-
failure keyword. It can be used to restore the original
|
|
33
|
-
value later.
|
|
34
|
-
|
|
35
|
-
Example:
|
|
36
|
-
| Register Keyword To Run On Failure | Log Source | # Run `Log Source` on failure. |
|
|
37
|
-
| ${previous kw}= | Register Keyword To Run On Failure | Nothing | # Disables run-on-failure functionality and stores the previous kw name in a variable. |
|
|
38
|
-
| Register Keyword To Run On Failure | ${previous kw} | # Restore to the previous keyword. |
|
|
39
|
-
|
|
40
|
-
This run-on-failure functionality only works when running tests on Python/Jython 2.4
|
|
41
|
-
or newer and it does not work on IronPython at all.
|
|
42
|
-
"""
|
|
43
|
-
old_keyword = self._run_on_failure_keyword
|
|
44
|
-
old_keyword_text = old_keyword if old_keyword is not None else "Nothing"
|
|
45
|
-
|
|
46
|
-
new_keyword = keyword if keyword.strip().lower() != "nothing" else None
|
|
47
|
-
new_keyword_text = new_keyword if new_keyword is not None else "Nothing"
|
|
48
|
-
|
|
49
|
-
self._run_on_failure_keyword = new_keyword
|
|
50
|
-
self._info('%s will be run on failure.' % new_keyword_text)
|
|
51
|
-
|
|
52
|
-
return old_keyword_text
|
|
53
|
-
|
|
54
|
-
# Private
|
|
55
|
-
|
|
56
|
-
def _run_on_failure(self):
|
|
57
|
-
if self._run_on_failure_keyword is None:
|
|
58
|
-
return
|
|
59
|
-
if self._running_on_failure_routine:
|
|
60
|
-
return
|
|
61
|
-
self._running_on_failure_routine = True
|
|
62
|
-
try:
|
|
63
|
-
BUILTIN.run_keyword(self._run_on_failure_keyword)
|
|
64
|
-
except Exception as err:
|
|
65
|
-
self._run_on_failure_error(err)
|
|
66
|
-
finally:
|
|
67
|
-
self._running_on_failure_routine = False
|
|
68
|
-
|
|
69
|
-
def _run_on_failure_error(self, err):
|
|
70
|
-
err = "Keyword '%s' could not be run on failure: %s" % (self._run_on_failure_keyword, err)
|
|
71
|
-
if hasattr(self, '_warn'):
|
|
72
|
-
self._warn(err)
|
|
73
|
-
return
|
|
74
|
-
raise Exception(err)
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from robot.libraries import BuiltIn
|
|
4
|
+
|
|
5
|
+
from .keywordgroup import KeywordGroup
|
|
6
|
+
|
|
7
|
+
BUILTIN = BuiltIn.BuiltIn()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _RunOnFailureKeywords(KeywordGroup):
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self._run_on_failure_keyword = None
|
|
14
|
+
self._running_on_failure_routine = False
|
|
15
|
+
|
|
16
|
+
# Public
|
|
17
|
+
|
|
18
|
+
def register_keyword_to_run_on_failure(self, keyword):
|
|
19
|
+
"""Sets the keyword to execute when a AppiumLibrary keyword fails.
|
|
20
|
+
|
|
21
|
+
`keyword_name` is the name of a keyword (from any available
|
|
22
|
+
libraries) that will be executed if a AppiumLibrary keyword fails.
|
|
23
|
+
It is not possible to use a keyword that requires arguments.
|
|
24
|
+
Using the value "Nothing" will disable this feature altogether.
|
|
25
|
+
|
|
26
|
+
The initial keyword to use is set in `importing`, and the
|
|
27
|
+
keyword that is used by default is `Capture Page Screenshot`.
|
|
28
|
+
Taking a screenshot when something failed is a very useful
|
|
29
|
+
feature, but notice that it can slow down the execution.
|
|
30
|
+
|
|
31
|
+
This keyword returns the name of the previously registered
|
|
32
|
+
failure keyword. It can be used to restore the original
|
|
33
|
+
value later.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
| Register Keyword To Run On Failure | Log Source | # Run `Log Source` on failure. |
|
|
37
|
+
| ${previous kw}= | Register Keyword To Run On Failure | Nothing | # Disables run-on-failure functionality and stores the previous kw name in a variable. |
|
|
38
|
+
| Register Keyword To Run On Failure | ${previous kw} | # Restore to the previous keyword. |
|
|
39
|
+
|
|
40
|
+
This run-on-failure functionality only works when running tests on Python/Jython 2.4
|
|
41
|
+
or newer and it does not work on IronPython at all.
|
|
42
|
+
"""
|
|
43
|
+
old_keyword = self._run_on_failure_keyword
|
|
44
|
+
old_keyword_text = old_keyword if old_keyword is not None else "Nothing"
|
|
45
|
+
|
|
46
|
+
new_keyword = keyword if keyword.strip().lower() != "nothing" else None
|
|
47
|
+
new_keyword_text = new_keyword if new_keyword is not None else "Nothing"
|
|
48
|
+
|
|
49
|
+
self._run_on_failure_keyword = new_keyword
|
|
50
|
+
self._info('%s will be run on failure.' % new_keyword_text)
|
|
51
|
+
|
|
52
|
+
return old_keyword_text
|
|
53
|
+
|
|
54
|
+
# Private
|
|
55
|
+
|
|
56
|
+
def _run_on_failure(self):
|
|
57
|
+
if self._run_on_failure_keyword is None:
|
|
58
|
+
return
|
|
59
|
+
if self._running_on_failure_routine:
|
|
60
|
+
return
|
|
61
|
+
self._running_on_failure_routine = True
|
|
62
|
+
try:
|
|
63
|
+
BUILTIN.run_keyword(self._run_on_failure_keyword)
|
|
64
|
+
except Exception as err:
|
|
65
|
+
self._run_on_failure_error(err)
|
|
66
|
+
finally:
|
|
67
|
+
self._running_on_failure_routine = False
|
|
68
|
+
|
|
69
|
+
def _run_on_failure_error(self, err):
|
|
70
|
+
err = "Keyword '%s' could not be run on failure: %s" % (self._run_on_failure_keyword, err)
|
|
71
|
+
if hasattr(self, '_warn'):
|
|
72
|
+
self._warn(err)
|
|
73
|
+
return
|
|
74
|
+
raise Exception(err)
|
|
@@ -1,138 +1,138 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
import base64
|
|
4
|
-
import os
|
|
5
|
-
|
|
6
|
-
import robot
|
|
7
|
-
|
|
8
|
-
from .keywordgroup import KeywordGroup
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class _ScreenrecordKeywords(KeywordGroup):
|
|
12
|
-
|
|
13
|
-
def __init__(self):
|
|
14
|
-
self._screenrecord_index = 0
|
|
15
|
-
self._recording = None
|
|
16
|
-
self._output_format = None
|
|
17
|
-
|
|
18
|
-
def start_screen_recording(self,
|
|
19
|
-
timeLimit='180s',
|
|
20
|
-
**options):
|
|
21
|
-
"""Starts an asynchronous Screen Recording for the current open application.
|
|
22
|
-
|
|
23
|
-
``timeLimit`` sets the actual time limit of the recorded video.
|
|
24
|
-
- The default value for both iOS and Android is 180 seconds (3 minutes).
|
|
25
|
-
- The maximum value for Android is 3 minutes.
|
|
26
|
-
- The maximum value for iOS is 10 minutes.
|
|
27
|
-
|
|
28
|
-
=== Optional Args ===
|
|
29
|
-
|
|
30
|
-
- ``bitRate`` (Android Only) The video bit rate for the video, in megabits per second.
|
|
31
|
-
4 Mbp/s(4000000) is by default for Android API level below 27. \
|
|
32
|
-
20 Mb/s(20000000) for API level 27 and above.
|
|
33
|
-
|
|
34
|
-
- ``videoSize`` (Android Only) The format is widthxheight. The default value is the \
|
|
35
|
-
device's native display resolution (if supported), 1280x720 if not. For best \
|
|
36
|
-
results, use a size supported by your device's Advanced Video Coding (AVC) \
|
|
37
|
-
encoder. For example, "1280x720"
|
|
38
|
-
|
|
39
|
-
- ``bugReport`` (Android Only) Set it to true in order to display additional \
|
|
40
|
-
information on the video overlay, such as a timestamp, that is helpful in \
|
|
41
|
-
videos captured to illustrate bugs. This option is only supported since \
|
|
42
|
-
API level 27 (Android O).
|
|
43
|
-
|
|
44
|
-
- ``videoQuality`` (iOS Only) The video encoding quality (low, medium, high, \
|
|
45
|
-
photo - defaults to medium).
|
|
46
|
-
|
|
47
|
-
- ``videoFps`` (iOS Only) The Frames Per Second rate of the recorded video. \
|
|
48
|
-
Change this value if the resulting video is too slow or too fast. Defaults to 10. \
|
|
49
|
-
This can decrease the resulting file size.
|
|
50
|
-
|
|
51
|
-
- ``videoScale`` (iOS Only) The scaling value to apply. Read \
|
|
52
|
-
https://trac.ffmpeg.org/wiki/Scaling for possible values. Example value of 720p \
|
|
53
|
-
scaling is '1280:720'. This can decrease/increase the resulting file size. \
|
|
54
|
-
No scale is applied by default.
|
|
55
|
-
|
|
56
|
-
`Start Screen Recording` is used hand in hand with `Stop Screen Recording`.
|
|
57
|
-
See `Stop Screen Recording` for more details.
|
|
58
|
-
Example:
|
|
59
|
-
| `Start Screen Recording` | | # starts a screen record session |
|
|
60
|
-
| .... keyword actions | | |
|
|
61
|
-
| `Stop Screen Recording` | filename=output | # saves the recorded session |
|
|
62
|
-
"""
|
|
63
|
-
timeLimit = robot.utils.timestr_to_secs(timeLimit)
|
|
64
|
-
options['timeLimit'] = timeLimit
|
|
65
|
-
self._output_format = self._set_output_format() \
|
|
66
|
-
if self._output_format is None else self._output_format
|
|
67
|
-
if self._recording is None:
|
|
68
|
-
self._recording = self._current_application().start_recording_screen(**options)
|
|
69
|
-
|
|
70
|
-
def stop_screen_recording(self, filename=None, **options):
|
|
71
|
-
"""Gathers the output from the previously started screen recording \
|
|
72
|
-
to a media file, then embeds it to the log.html(Android Only).
|
|
73
|
-
|
|
74
|
-
Requires an active or exhausted Screen Recording Session.
|
|
75
|
-
See `Start Screen Recording` for more details.
|
|
76
|
-
|
|
77
|
-
=== Optional Args ===
|
|
78
|
-
|
|
79
|
-
- ``remotePath`` The path to the remote location, where the resulting video should be \
|
|
80
|
-
uploaded. The following protocols are supported _http/https_, ftp. Null or empty \
|
|
81
|
-
string value (the default setting) means the content of resulting file should \
|
|
82
|
-
be encoded as Base64 and passed as the endpoint response value. An \
|
|
83
|
-
exception will be thrown if the generated media file is too big to fit \
|
|
84
|
-
into the available process memory.
|
|
85
|
-
|
|
86
|
-
- ``username`` The name of the user for the remote authentication.
|
|
87
|
-
|
|
88
|
-
- ``password`` The password for the remote authentication.
|
|
89
|
-
|
|
90
|
-
- ``method`` The http multipart upload method name. The _PUT_ one is used by default.
|
|
91
|
-
|
|
92
|
-
Example:
|
|
93
|
-
| `Start Screen Recording` | | # starts a screen record session |
|
|
94
|
-
| .... keyword actions | | |
|
|
95
|
-
| `Stop Screen Recording` | filename=output | # saves the recorded session |
|
|
96
|
-
"""
|
|
97
|
-
if self._recording is not None:
|
|
98
|
-
self._recording = self._current_application().stop_recording_screen(**options)
|
|
99
|
-
return self._save_recording(filename, options)
|
|
100
|
-
else:
|
|
101
|
-
raise RuntimeError("There is no Active Screen Record Session.")
|
|
102
|
-
|
|
103
|
-
def _save_recording(self, filename, options):
|
|
104
|
-
path, link = self._get_screenrecord_paths(options, filename)
|
|
105
|
-
decoded = base64.b64decode(self._recording)
|
|
106
|
-
with open(path, 'wb') as screenrecording:
|
|
107
|
-
screenrecording.write(decoded)
|
|
108
|
-
# Embed the Screen Recording to the log file
|
|
109
|
-
# if the current platform is Android and no remotePath is set.
|
|
110
|
-
if self._is_android() and not self._is_remotepath_set(options):
|
|
111
|
-
self._html('</td></tr><tr><td colspan="3"><a href="{vid}">'
|
|
112
|
-
'<video width="800px" controls>'
|
|
113
|
-
'<source src="{vid}" type="video/mp4">'
|
|
114
|
-
'</video></a>'.format(vid=link)
|
|
115
|
-
)
|
|
116
|
-
# Empty Screen Record Variable
|
|
117
|
-
self._recording = None
|
|
118
|
-
return path
|
|
119
|
-
|
|
120
|
-
def _set_output_format(self):
|
|
121
|
-
return '.ffmpeg' if self._is_ios() else '.mp4'
|
|
122
|
-
|
|
123
|
-
def _get_screenrecord_paths(self, options, filename=None):
|
|
124
|
-
if filename is None:
|
|
125
|
-
self._screenrecord_index += 1
|
|
126
|
-
filename = 'appium-screenrecord-{index}{ext}'.format(index=self._screenrecord_index,
|
|
127
|
-
ext=self._output_format
|
|
128
|
-
)
|
|
129
|
-
else:
|
|
130
|
-
filename = (filename.replace('/', os.sep)) + self._output_format
|
|
131
|
-
logdir = options['remotePath'] if self._is_remotepath_set(options) \
|
|
132
|
-
else self._get_log_dir()
|
|
133
|
-
path = os.path.join(logdir, filename)
|
|
134
|
-
link = robot.utils.get_link_path(path, logdir)
|
|
135
|
-
return path, link
|
|
136
|
-
|
|
137
|
-
def _is_remotepath_set(self, options):
|
|
138
|
-
return True if 'remotePath' in options else False
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
import robot
|
|
7
|
+
|
|
8
|
+
from .keywordgroup import KeywordGroup
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _ScreenrecordKeywords(KeywordGroup):
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self._screenrecord_index = 0
|
|
15
|
+
self._recording = None
|
|
16
|
+
self._output_format = None
|
|
17
|
+
|
|
18
|
+
def start_screen_recording(self,
|
|
19
|
+
timeLimit='180s',
|
|
20
|
+
**options):
|
|
21
|
+
"""Starts an asynchronous Screen Recording for the current open application.
|
|
22
|
+
|
|
23
|
+
``timeLimit`` sets the actual time limit of the recorded video.
|
|
24
|
+
- The default value for both iOS and Android is 180 seconds (3 minutes).
|
|
25
|
+
- The maximum value for Android is 3 minutes.
|
|
26
|
+
- The maximum value for iOS is 10 minutes.
|
|
27
|
+
|
|
28
|
+
=== Optional Args ===
|
|
29
|
+
|
|
30
|
+
- ``bitRate`` (Android Only) The video bit rate for the video, in megabits per second.
|
|
31
|
+
4 Mbp/s(4000000) is by default for Android API level below 27. \
|
|
32
|
+
20 Mb/s(20000000) for API level 27 and above.
|
|
33
|
+
|
|
34
|
+
- ``videoSize`` (Android Only) The format is widthxheight. The default value is the \
|
|
35
|
+
device's native display resolution (if supported), 1280x720 if not. For best \
|
|
36
|
+
results, use a size supported by your device's Advanced Video Coding (AVC) \
|
|
37
|
+
encoder. For example, "1280x720"
|
|
38
|
+
|
|
39
|
+
- ``bugReport`` (Android Only) Set it to true in order to display additional \
|
|
40
|
+
information on the video overlay, such as a timestamp, that is helpful in \
|
|
41
|
+
videos captured to illustrate bugs. This option is only supported since \
|
|
42
|
+
API level 27 (Android O).
|
|
43
|
+
|
|
44
|
+
- ``videoQuality`` (iOS Only) The video encoding quality (low, medium, high, \
|
|
45
|
+
photo - defaults to medium).
|
|
46
|
+
|
|
47
|
+
- ``videoFps`` (iOS Only) The Frames Per Second rate of the recorded video. \
|
|
48
|
+
Change this value if the resulting video is too slow or too fast. Defaults to 10. \
|
|
49
|
+
This can decrease the resulting file size.
|
|
50
|
+
|
|
51
|
+
- ``videoScale`` (iOS Only) The scaling value to apply. Read \
|
|
52
|
+
https://trac.ffmpeg.org/wiki/Scaling for possible values. Example value of 720p \
|
|
53
|
+
scaling is '1280:720'. This can decrease/increase the resulting file size. \
|
|
54
|
+
No scale is applied by default.
|
|
55
|
+
|
|
56
|
+
`Start Screen Recording` is used hand in hand with `Stop Screen Recording`.
|
|
57
|
+
See `Stop Screen Recording` for more details.
|
|
58
|
+
Example:
|
|
59
|
+
| `Start Screen Recording` | | # starts a screen record session |
|
|
60
|
+
| .... keyword actions | | |
|
|
61
|
+
| `Stop Screen Recording` | filename=output | # saves the recorded session |
|
|
62
|
+
"""
|
|
63
|
+
timeLimit = robot.utils.timestr_to_secs(timeLimit)
|
|
64
|
+
options['timeLimit'] = timeLimit
|
|
65
|
+
self._output_format = self._set_output_format() \
|
|
66
|
+
if self._output_format is None else self._output_format
|
|
67
|
+
if self._recording is None:
|
|
68
|
+
self._recording = self._current_application().start_recording_screen(**options)
|
|
69
|
+
|
|
70
|
+
def stop_screen_recording(self, filename=None, **options):
|
|
71
|
+
"""Gathers the output from the previously started screen recording \
|
|
72
|
+
to a media file, then embeds it to the log.html(Android Only).
|
|
73
|
+
|
|
74
|
+
Requires an active or exhausted Screen Recording Session.
|
|
75
|
+
See `Start Screen Recording` for more details.
|
|
76
|
+
|
|
77
|
+
=== Optional Args ===
|
|
78
|
+
|
|
79
|
+
- ``remotePath`` The path to the remote location, where the resulting video should be \
|
|
80
|
+
uploaded. The following protocols are supported _http/https_, ftp. Null or empty \
|
|
81
|
+
string value (the default setting) means the content of resulting file should \
|
|
82
|
+
be encoded as Base64 and passed as the endpoint response value. An \
|
|
83
|
+
exception will be thrown if the generated media file is too big to fit \
|
|
84
|
+
into the available process memory.
|
|
85
|
+
|
|
86
|
+
- ``username`` The name of the user for the remote authentication.
|
|
87
|
+
|
|
88
|
+
- ``password`` The password for the remote authentication.
|
|
89
|
+
|
|
90
|
+
- ``method`` The http multipart upload method name. The _PUT_ one is used by default.
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
| `Start Screen Recording` | | # starts a screen record session |
|
|
94
|
+
| .... keyword actions | | |
|
|
95
|
+
| `Stop Screen Recording` | filename=output | # saves the recorded session |
|
|
96
|
+
"""
|
|
97
|
+
if self._recording is not None:
|
|
98
|
+
self._recording = self._current_application().stop_recording_screen(**options)
|
|
99
|
+
return self._save_recording(filename, options)
|
|
100
|
+
else:
|
|
101
|
+
raise RuntimeError("There is no Active Screen Record Session.")
|
|
102
|
+
|
|
103
|
+
def _save_recording(self, filename, options):
|
|
104
|
+
path, link = self._get_screenrecord_paths(options, filename)
|
|
105
|
+
decoded = base64.b64decode(self._recording)
|
|
106
|
+
with open(path, 'wb') as screenrecording:
|
|
107
|
+
screenrecording.write(decoded)
|
|
108
|
+
# Embed the Screen Recording to the log file
|
|
109
|
+
# if the current platform is Android and no remotePath is set.
|
|
110
|
+
if self._is_android() and not self._is_remotepath_set(options):
|
|
111
|
+
self._html('</td></tr><tr><td colspan="3"><a href="{vid}">'
|
|
112
|
+
'<video width="800px" controls>'
|
|
113
|
+
'<source src="{vid}" type="video/mp4">'
|
|
114
|
+
'</video></a>'.format(vid=link)
|
|
115
|
+
)
|
|
116
|
+
# Empty Screen Record Variable
|
|
117
|
+
self._recording = None
|
|
118
|
+
return path
|
|
119
|
+
|
|
120
|
+
def _set_output_format(self):
|
|
121
|
+
return '.ffmpeg' if self._is_ios() else '.mp4'
|
|
122
|
+
|
|
123
|
+
def _get_screenrecord_paths(self, options, filename=None):
|
|
124
|
+
if filename is None:
|
|
125
|
+
self._screenrecord_index += 1
|
|
126
|
+
filename = 'appium-screenrecord-{index}{ext}'.format(index=self._screenrecord_index,
|
|
127
|
+
ext=self._output_format
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
filename = (filename.replace('/', os.sep)) + self._output_format
|
|
131
|
+
logdir = options['remotePath'] if self._is_remotepath_set(options) \
|
|
132
|
+
else self._get_log_dir()
|
|
133
|
+
path = os.path.join(logdir, filename)
|
|
134
|
+
link = robot.utils.get_link_path(path, logdir)
|
|
135
|
+
return path, link
|
|
136
|
+
|
|
137
|
+
def _is_remotepath_set(self, options):
|
|
138
|
+
return True if 'remotePath' in options else False
|
|
@@ -1,109 +1,105 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from base64 import b64decode
|
|
5
|
-
|
|
6
|
-
import robot
|
|
7
|
-
|
|
8
|
-
from .keywordgroup import KeywordGroup, ignore_on_fail
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class _ScreenshotKeywords(KeywordGroup):
|
|
12
|
-
|
|
13
|
-
# Public
|
|
14
|
-
|
|
15
|
-
def appium_get_element_screenshot(self, locator, timeout=20, filename=None):
|
|
16
|
-
"""
|
|
17
|
-
Get a screenshot of element to base64
|
|
18
|
-
If provide filename, saves a screenshot to a PNG image file.
|
|
19
|
-
|
|
20
|
-
Parameters:
|
|
21
|
-
-----------
|
|
22
|
-
locator: Element locator
|
|
23
|
-
timeout: timeout in second to find element
|
|
24
|
-
filename : str
|
|
25
|
-
- The full path you wish to save your screenshot to. This
|
|
26
|
-
- should end with a `.png` extension.
|
|
27
|
-
|
|
28
|
-
Example:
|
|
29
|
-
--------
|
|
30
|
-
>>> driver.get_screenshot_as_file("/Screenshots/foo.png")
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
element = self.
|
|
34
|
-
|
|
35
|
-
if not element:
|
|
36
|
-
self._info(f'Not found {locator}, return None')
|
|
37
|
-
return None
|
|
38
|
-
|
|
39
|
-
base64data = element.screenshot_as_base64
|
|
40
|
-
|
|
41
|
-
if filename:
|
|
42
|
-
if not str(filename).lower().endswith(".png"):
|
|
43
|
-
self._info(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`filename`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
logdir = self._get_log_dir()
|
|
107
|
-
path = os.path.join(logdir, filename)
|
|
108
|
-
link = robot.utils.get_link_path(path, logdir)
|
|
109
|
-
return path, link
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from base64 import b64decode
|
|
5
|
+
|
|
6
|
+
import robot
|
|
7
|
+
|
|
8
|
+
from .keywordgroup import KeywordGroup, ignore_on_fail
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _ScreenshotKeywords(KeywordGroup):
|
|
12
|
+
|
|
13
|
+
# Public
|
|
14
|
+
|
|
15
|
+
def appium_get_element_screenshot(self, locator, timeout=20, filename=None):
|
|
16
|
+
"""
|
|
17
|
+
Get a screenshot of element to base64
|
|
18
|
+
If provide filename, saves a screenshot to a PNG image file.
|
|
19
|
+
|
|
20
|
+
Parameters:
|
|
21
|
+
-----------
|
|
22
|
+
locator: Element locator
|
|
23
|
+
timeout: timeout in second to find element
|
|
24
|
+
filename : str
|
|
25
|
+
- The full path you wish to save your screenshot to. This
|
|
26
|
+
- should end with a `.png` extension.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
--------
|
|
30
|
+
>>> driver.get_screenshot_as_file("/Screenshots/foo.png")
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
element = self.appium_get_element(locator, timeout, False)
|
|
34
|
+
|
|
35
|
+
if not element:
|
|
36
|
+
self._info(f'Not found {locator}, return None')
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
base64data = element.screenshot_as_base64
|
|
40
|
+
|
|
41
|
+
if filename:
|
|
42
|
+
if not str(filename).lower().endswith(".png"):
|
|
43
|
+
self._info("name used for saved screenshot does not match file type. It should end with a `.png` extension")
|
|
44
|
+
|
|
45
|
+
png = b64decode(base64data.encode("ascii"))
|
|
46
|
+
try:
|
|
47
|
+
with open(filename, "wb") as f:
|
|
48
|
+
f.write(png)
|
|
49
|
+
except OSError:
|
|
50
|
+
self._info(f'Fail to write screenshot file {filename}, return False')
|
|
51
|
+
return False
|
|
52
|
+
finally:
|
|
53
|
+
del png
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
return base64data
|
|
57
|
+
|
|
58
|
+
@ignore_on_fail
|
|
59
|
+
def appium_get_screenshot(self):
|
|
60
|
+
return self.appium_capture_page_screenshot(None, False)
|
|
61
|
+
|
|
62
|
+
def appium_capture_page_screenshot(self, filename=None, embed=True):
|
|
63
|
+
try:
|
|
64
|
+
return self.capture_page_screenshot(filename, embed)
|
|
65
|
+
except Exception as err:
|
|
66
|
+
self._info(err)
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
def capture_page_screenshot(self, filename=None, embed=True):
|
|
70
|
+
"""Takes a screenshot of the current page and embeds it into the log.
|
|
71
|
+
|
|
72
|
+
`filename` argument specifies the name of the file to write the
|
|
73
|
+
screenshot into. If no `filename` is given, the screenshot will be
|
|
74
|
+
embedded as Base64 image to the log.html. In this case no file is created in the filesystem.
|
|
75
|
+
|
|
76
|
+
`embed` is True: the screenshot will be embedded to the log.html
|
|
77
|
+
|
|
78
|
+
Warning: this behavior is new in 1.7. Previously if no filename was given
|
|
79
|
+
the screenshots where stored as separate files named `appium-screenshot-<counter>.png`
|
|
80
|
+
"""
|
|
81
|
+
if filename:
|
|
82
|
+
path, link = self._get_screenshot_paths(filename)
|
|
83
|
+
|
|
84
|
+
if hasattr(self._current_application(), 'get_screenshot_as_file'):
|
|
85
|
+
self._current_application().get_screenshot_as_file(path)
|
|
86
|
+
else:
|
|
87
|
+
self._current_application().save_screenshot(path)
|
|
88
|
+
|
|
89
|
+
# Image is shown on its own row and thus prev row is closed on purpose
|
|
90
|
+
if embed:
|
|
91
|
+
self._html('</td></tr><tr><td colspan="3"><a href="%s"><img src="%s" width="800px"></a>' % (link, link))
|
|
92
|
+
return path
|
|
93
|
+
else:
|
|
94
|
+
base64_screenshot = self._current_application().get_screenshot_as_base64()
|
|
95
|
+
if embed:
|
|
96
|
+
self._html('</td></tr><tr><td colspan="3"><img src="data:image/png;base64, %s" width="800px">' % base64_screenshot)
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
# Private
|
|
100
|
+
def _get_screenshot_paths(self, filename):
|
|
101
|
+
filename = filename.replace('/', os.sep)
|
|
102
|
+
logdir = self._get_log_dir()
|
|
103
|
+
path = os.path.join(logdir, filename)
|
|
104
|
+
link = robot.utils.get_link_path(path, logdir)
|
|
105
|
+
return path, link
|