Skip to content

Commit 5a9000a

Browse files
authored
Merge pull request #1939 from emanlove/embed-screenshot-with-base64
Return screenshot as base64 string and embed into log
2 parents 1458cbe + 29237fd commit 5a9000a

File tree

3 files changed

+81
-36
lines changed

3 files changed

+81
-36
lines changed

src/SeleniumLibrary/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
WebDriverCache,
5151
WindowKeywords,
5252
)
53-
from SeleniumLibrary.keywords.screenshot import EMBED
53+
from SeleniumLibrary.keywords.screenshot import EMBED, BASE64
5454
from SeleniumLibrary.locators import ElementFinder
5555
from SeleniumLibrary.utils import LibraryListener, is_truthy, _convert_timeout, _convert_delay
5656

@@ -614,8 +614,8 @@ def __init__(
614614
- ``run_on_failure``:
615615
Default action for the `run-on-failure functionality`.
616616
- ``screenshot_root_directory``:
617-
Path to folder where possible screenshots are created or EMBED.
618-
See `Set Screenshot Directory` keyword for further details about EMBED.
617+
Path to folder where possible screenshots are created or EMBED or BASE64.
618+
See `Set Screenshot Directory` keyword for further details about EMBED and BASE64.
619619
If not given, the directory where the log file is written is used.
620620
- ``plugins``:
621621
Allows extending the SeleniumLibrary with external Python classes.
@@ -846,6 +846,8 @@ def _resolve_screenshot_root_directory(self):
846846
if is_string(screenshot_root_directory):
847847
if screenshot_root_directory.upper() == EMBED:
848848
self.screenshot_root_directory = EMBED
849+
if screenshot_root_directory.upper() == BASE64:
850+
self.screenshot_root_directory = BASE64
849851

850852
@staticmethod
851853
def _get_translation(language: Union[str, None]) -> Union[Path, None]:

src/SeleniumLibrary/keywords/screenshot.py

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
DEFAULT_FILENAME_PAGE = "selenium-screenshot-{index}.png"
2828
DEFAULT_FILENAME_ELEMENT = "selenium-element-screenshot-{index}.png"
2929
EMBED = "EMBED"
30+
BASE64 = "BASE64"
31+
EMBEDDED_OPTIONS = [EMBED, BASE64]
3032
DEFAULT_FILENAME_PDF = "selenium-page-{index}.pdf"
3133

3234

@@ -59,6 +61,8 @@ def set_screenshot_directory(self, path: Union[None, str]) -> str:
5961
path = None
6062
elif path.upper() == EMBED:
6163
path = EMBED
64+
elif path.upper() == BASE64:
65+
path = BASE64
6266
else:
6367
path = os.path.abspath(path)
6468
self._create_directory(path)
@@ -79,7 +83,14 @@ def capture_page_screenshot(self, filename: str = DEFAULT_FILENAME_PAGE) -> str:
7983
8084
If ``filename`` equals to EMBED (case insensitive), then screenshot
8185
is embedded as Base64 image to the log.html. In this case file is not
82-
created in the filesystem.
86+
created in the filesystem. If ``filename`` equals to BASE64 (case
87+
insensitive), then the base64 string is returned and the screenshot
88+
is embedded to the log. This allows one to reuse the image elsewhere
89+
in the report.
90+
91+
Example:
92+
| ${ss}= | `Capture Page Screenshot` | BASE64 |
93+
| Set Test Message | *HTML*Test Success<p><img src="data:image/png;base64,${ss}" width="256px"> |
8394
8495
Starting from SeleniumLibrary 1.8, if ``filename`` contains marker
8596
``{index}``, it will be automatically replaced with an unique running
@@ -89,9 +100,10 @@ def capture_page_screenshot(self, filename: str = DEFAULT_FILENAME_PAGE) -> str:
89100
format string syntax].
90101
91102
An absolute path to the created screenshot file is returned or if
92-
``filename`` equals to EMBED, word `EMBED` is returned.
103+
``filename`` equals to EMBED, word `EMBED` is returned. If ``filename``
104+
equals to BASE64, the base64 string containing the screenshot is returned.
93105
94-
Support for EMBED is new in SeleniumLibrary 4.2
106+
Support for BASE64 is new in SeleniumLibrary 6.8
95107
96108
Examples:
97109
| `Capture Page Screenshot` | |
@@ -111,8 +123,9 @@ def capture_page_screenshot(self, filename: str = DEFAULT_FILENAME_PAGE) -> str:
111123
if not self.drivers.current:
112124
self.info("Cannot capture screenshot because no browser is open.")
113125
return
114-
if self._decide_embedded(filename):
115-
return self._capture_page_screen_to_log()
126+
is_embedded, method = self._decide_embedded(filename)
127+
if is_embedded:
128+
return self._capture_page_screen_to_log(method)
116129
return self._capture_page_screenshot_to_file(filename)
117130

118131
def _capture_page_screenshot_to_file(self, filename):
@@ -123,9 +136,11 @@ def _capture_page_screenshot_to_file(self, filename):
123136
self._embed_to_log_as_file(path, 800)
124137
return path
125138

126-
def _capture_page_screen_to_log(self):
139+
def _capture_page_screen_to_log(self, return_val):
127140
screenshot_as_base64 = self.driver.get_screenshot_as_base64()
128-
self._embed_to_log_as_base64(screenshot_as_base64, 800)
141+
base64_str = self._embed_to_log_as_base64(screenshot_as_base64, 800)
142+
if return_val == BASE64:
143+
return base64_str
129144
return EMBED
130145

131146
@keyword
@@ -140,27 +155,34 @@ def capture_element_screenshot(
140155
See the `Locating elements` section for details about the locator
141156
syntax.
142157
143-
An absolute path to the created element screenshot is returned.
158+
An absolute path to the created element screenshot is returned. If the ``filename``
159+
equals to BASE64 (case insensitive), then the base64 string is returned in addition
160+
to the screenshot embedded to the log. See ``Capture Page Screenshot`` for more
161+
information.
144162
145163
Support for capturing the screenshot from an element has limited support
146164
among browser vendors. Please check the browser vendor driver documentation
147165
does the browser support capturing a screenshot from an element.
148166
149167
New in SeleniumLibrary 3.3. Support for EMBED is new in SeleniumLibrary 4.2.
168+
Support for BASE64 is new in SeleniumLibrary 6.8.
150169
151170
Examples:
152171
| `Capture Element Screenshot` | id:image_id | |
153172
| `Capture Element Screenshot` | id:image_id | ${OUTPUTDIR}/id_image_id-1.png |
154173
| `Capture Element Screenshot` | id:image_id | EMBED |
174+
| ${ess}= | `Capture Element Screenshot` | id:image_id | BASE64 |
175+
155176
"""
156177
if not self.drivers.current:
157178
self.info(
158179
"Cannot capture screenshot from element because no browser is open."
159180
)
160181
return
161182
element = self.find_element(locator, required=True)
162-
if self._decide_embedded(filename):
163-
return self._capture_element_screen_to_log(element)
183+
is_embedded, method = self._decide_embedded(filename)
184+
if is_embedded:
185+
return self._capture_element_screen_to_log(element, method)
164186
return self._capture_element_screenshot_to_file(element, filename)
165187

166188
def _capture_element_screenshot_to_file(self, element, filename):
@@ -171,8 +193,10 @@ def _capture_element_screenshot_to_file(self, element, filename):
171193
self._embed_to_log_as_file(path, 400)
172194
return path
173195

174-
def _capture_element_screen_to_log(self, element):
175-
self._embed_to_log_as_base64(element.screenshot_as_base64, 400)
196+
def _capture_element_screen_to_log(self, element, return_val):
197+
base64_str = self._embed_to_log_as_base64(element.screenshot_as_base64, 400)
198+
if return_val == BASE64:
199+
return base64_str
176200
return EMBED
177201

178202
@property
@@ -184,20 +208,20 @@ def _screenshot_root_directory(self, value):
184208
self.ctx.screenshot_root_directory = value
185209

186210
def _decide_embedded(self, filename):
187-
filename = filename.lower()
211+
filename = filename.upper()
188212
if (
189-
filename == DEFAULT_FILENAME_PAGE
190-
and self._screenshot_root_directory == EMBED
213+
filename == DEFAULT_FILENAME_PAGE.upper()
214+
and self._screenshot_root_directory in EMBEDDED_OPTIONS
191215
):
192-
return True
216+
return True, self._screenshot_root_directory
193217
if (
194-
filename == DEFAULT_FILENAME_ELEMENT
195-
and self._screenshot_root_directory == EMBED
218+
filename == DEFAULT_FILENAME_ELEMENT.upper()
219+
and self._screenshot_root_directory in EMBEDDED_OPTIONS
196220
):
197-
return True
198-
if filename == EMBED.lower():
199-
return True
200-
return False
221+
return True, self._screenshot_root_directory
222+
if filename in EMBEDDED_OPTIONS:
223+
return True, self._screenshot_root_directory
224+
return False, None
201225

202226
def _get_screenshot_path(self, filename):
203227
if self._screenshot_root_directory != EMBED:

utest/test/keywords/test_screen_shot.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
SCREENSHOT_FILE_NAME = "selenium-screenshot-{index}.png"
99
ELEMENT_FILE_NAME = "selenium-element-screenshot-{index}.png"
1010
EMBED = "EMBED"
11-
11+
BASE64 = "BASE64"
1212

1313
@pytest.fixture(scope="module")
1414
def screen_shot():
@@ -22,24 +22,34 @@ def teardown_function():
2222

2323

2424
def test_defaults(screen_shot):
25-
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME) is False
26-
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME) is False
25+
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME) == (False, None)
26+
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME) == (False, None)
2727

2828

2929
def test_screen_shotdir_embeded(screen_shot):
3030
screen_shot.ctx.screenshot_root_directory = EMBED
31-
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME) is True
32-
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME.upper()) is True
33-
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME) is True
34-
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME.upper()) is True
35-
assert screen_shot._decide_embedded("other.psn") is False
31+
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME) == (True, EMBED)
32+
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME.upper()) == (True, EMBED)
33+
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME) == (True, EMBED)
34+
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME.upper()) == (True, EMBED)
35+
assert screen_shot._decide_embedded("other.psn") == (False, None)
36+
37+
38+
def test_screen_shotdir_return_base64(screen_shot):
39+
screen_shot.ctx.screenshot_root_directory = BASE64
40+
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME) == (True, BASE64)
41+
assert screen_shot._decide_embedded(SCREENSHOT_FILE_NAME.upper()) == (True, BASE64)
42+
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME) == (True, BASE64)
43+
assert screen_shot._decide_embedded(ELEMENT_FILE_NAME.upper()) == (True, BASE64)
44+
assert screen_shot._decide_embedded("other.psn") == (False, None)
3645

3746

3847
def test_file_name_embeded(screen_shot):
39-
assert screen_shot._decide_embedded(EMBED) is True
40-
assert screen_shot._decide_embedded("other.psn") is False
48+
assert screen_shot._decide_embedded("other.psn") == (False, None)
4149
screen_shot.ctx.screenshot_root_directory = EMBED
42-
assert screen_shot._decide_embedded(EMBED) is True
50+
assert screen_shot._decide_embedded(EMBED) == (True, EMBED)
51+
screen_shot.ctx.screenshot_root_directory = BASE64
52+
assert screen_shot._decide_embedded(BASE64) == (True, BASE64)
4353

4454

4555
def test_screenshot_path_embedded(screen_shot):
@@ -56,6 +66,12 @@ def test_sl_init_embed():
5666
sl = SeleniumLibrary(screenshot_root_directory=EMBED)
5767
assert sl.screenshot_root_directory == EMBED
5868

69+
sl = SeleniumLibrary(screenshot_root_directory="bAsE64")
70+
assert sl.screenshot_root_directory == BASE64
71+
72+
sl = SeleniumLibrary(screenshot_root_directory=BASE64)
73+
assert sl.screenshot_root_directory == BASE64
74+
5975

6076
def test_sl_init_not_embed():
6177
sl = SeleniumLibrary(screenshot_root_directory=None)
@@ -76,6 +92,9 @@ def test_sl_set_screenshot_directory():
7692
sl.set_screenshot_directory(EMBED)
7793
assert sl.screenshot_root_directory == EMBED
7894

95+
sl.set_screenshot_directory(BASE64)
96+
assert sl.screenshot_root_directory == BASE64
97+
7998
sl.set_screenshot_directory("EEmBedD")
8099
assert "EEmBedD" in sl.screenshot_root_directory
81100
assert len("EEmBedD") < len(sl.screenshot_root_directory)

0 commit comments

Comments
 (0)