From 16f71a1ac767abb4727649d872d65df1bfb497b5 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Tue, 8 Oct 2024 15:00:30 +0300 Subject: [PATCH] Add screenshot logging for Selenium and Browser libraries --- examples/library/Log.py | 20 +----- .../Screenshot_test_FAILURE_SCREENSHOT_1.png | Bin 0 -> 25977 bytes examples/res/selenium-screenshot-1.png | Bin 0 -> 30784 bytes examples/screenshot.robot | 8 +++ robotframework_reportportal/listener.py | 63 +++++++++++++----- tests/integration/test_screenshot.py | 44 ++++++++++++ 6 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 examples/res/Screenshot_test_FAILURE_SCREENSHOT_1.png create mode 100644 examples/res/selenium-screenshot-1.png create mode 100644 examples/screenshot.robot create mode 100644 tests/integration/test_screenshot.py diff --git a/examples/library/Log.py b/examples/library/Log.py index a120a9b..00d1e1c 100644 --- a/examples/library/Log.py +++ b/examples/library/Log.py @@ -19,30 +19,16 @@ from robotframework_reportportal import logger -def screenshot_log(level, message, screenshot_file): - """ - Attach a screenshot file into a log entry on ReportPortal. - - :param level: log entry level - :param message: screenshot description - :param screenshot_file: path to image file - """ - with open(screenshot_file, "rb") as image_file: - file_data = image_file.read() - item_log(level, message, {"name": screenshot_file.split(os.path.sep)[-1], - "data": file_data, - "mime": "image/png"}) - - -def item_log(level, message, attachment=None): +def item_log(level, message, attachment=None, html=False): """ Post a log entry on which will be attached to the current processing item. :param level: log entry level :param message: message to post :param attachment: path to attachment file + :param html: format or not format the message as html """ - logger.write(message, level, attachment=attachment) + logger.write(message, level, attachment=attachment, html=html) def launch_log(level, message, attachment=None): diff --git a/examples/res/Screenshot_test_FAILURE_SCREENSHOT_1.png b/examples/res/Screenshot_test_FAILURE_SCREENSHOT_1.png new file mode 100644 index 0000000000000000000000000000000000000000..d4acbf3ae626d99bc5a0cd5d206fbf8a3595a9f9 GIT binary patch literal 25977 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 XHyLnnVUPxP804qp=wr+ugRlJ$rWN<6 literal 0 HcmV?d00001 diff --git a/examples/res/selenium-screenshot-1.png b/examples/res/selenium-screenshot-1.png new file mode 100644 index 0000000000000000000000000000000000000000..62539c7953ea173ea435e0bb12801f90dcdb000e GIT binary patch literal 30784 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 I{=)5l0(SCp+5i9m literal 0 HcmV?d00001 diff --git a/examples/screenshot.robot b/examples/screenshot.robot new file mode 100644 index 0000000..c77a4c1 --- /dev/null +++ b/examples/screenshot.robot @@ -0,0 +1,8 @@ +*** Settings *** +Library library/Log.py + +*** Test Cases *** +Selenium Screenshot test + Item Log INFO None True +Playwright Screenshot test + Item Log INFO None True diff --git a/robotframework_reportportal/listener.py b/robotframework_reportportal/listener.py index 3cdc8ea..c65f9db 100644 --- a/robotframework_reportportal/listener.py +++ b/robotframework_reportportal/listener.py @@ -31,7 +31,12 @@ from robotframework_reportportal.variables import Variables logger = logging.getLogger(__name__) -VARIABLE_PATTERN = r'^\s*\${[^}]*}\s*=\s*' +VARIABLE_PATTERN = re.compile(r'^\s*\${[^}]*}\s*=\s*') +IMAGE_PATTERN = re.compile( + r'' + r'') + +DEFAULT_BINARY_FILE_TYPE = 'application/octet-stream' TRUNCATION_SIGN = "...'" @@ -98,7 +103,7 @@ def __init__(self) -> None: self._service = None self._variables = None - def _build_msg_struct(self, message: Dict) -> LogMessage: + def _build_msg_struct(self, message: Dict[str, Any]) -> LogMessage: """Check if the given message comes from our custom logger or not. :param message: Message passed by the Robot Framework @@ -110,6 +115,44 @@ def _build_msg_struct(self, message: Dict) -> LogMessage: msg.level = message['level'] if not msg.launch_log: msg.item_id = getattr(self.current_item, 'rp_item_id', None) + + message_str = msg.message + if is_binary(message_str): + variable_match = VARIABLE_PATTERN.search(message_str) + if variable_match: + # Treat as partial binary data + msg_content = message_str[variable_match.end():] + # remove trailing `'"...`, add `...'` + msg.message = (message_str[variable_match.start():variable_match.end()] + + str(msg_content.encode('utf-8'))[:-5] + TRUNCATION_SIGN) + else: + # Do not log full binary data, since it's usually corrupted + content_type = guess_content_type_from_bytes(_unescape(message_str, 128)) + msg.message = (f'Binary data of type "{content_type}" logging skipped, as it was processed as text and' + ' hence corrupted.') + msg.level = 'WARN' + elif message.get('html', 'no') == 'yes': + image_match = IMAGE_PATTERN.match(message_str) + if image_match: + image_path = image_match.group(1) + msg.message = f'Image attached: {image_path}' + if os.path.exists(image_path): + image_type_by_name = guess_type(image_path)[0] + with open(image_path, 'rb') as fh: + image_data = fh.read() + image_type_by_data = guess_content_type_from_bytes(image_data) + if image_type_by_name and image_type_by_data and image_type_by_name != image_type_by_data: + logger.warning( + f'Image type mismatch: type by file name "{image_type_by_name}" ' + f'!= type by file content "{image_type_by_data}"') + mime_type = DEFAULT_BINARY_FILE_TYPE + else: + mime_type = image_type_by_name or image_type_by_data or DEFAULT_BINARY_FILE_TYPE + msg.attachment = { + 'name': os.path.basename(image_path), + 'data': image_data, + 'mime': mime_type + } return msg def _add_current_item(self, item: Union[Keyword, Launch, Suite, Test]) -> None: @@ -132,20 +175,6 @@ def log_message(self, message: Dict) -> None: :param message: Message passed by the Robot Framework """ msg = self._build_msg_struct(message) - if is_binary(msg.message): - variable_match = re.search(VARIABLE_PATTERN, msg.message) - if variable_match: - # Treat as partial binary data - msg_content = msg.message[variable_match.end():] - # remove trailing `'"...`, add `...'` - msg.message = (msg.message[variable_match.start():variable_match.end()] - + str(msg_content.encode('utf-8'))[:-5] + TRUNCATION_SIGN) - else: - # Do not log full binary data, since it's usually corrupted - content_type = guess_content_type_from_bytes(_unescape(msg.message, 128)) - msg.message = (f'Binary data of type "{content_type}" logging skipped, as it was processed as text and' - ' hence corrupted.') - msg.level = 'WARN' logger.debug(f'ReportPortal - Log Message: {message}') self.service.log(message=msg) @@ -161,7 +190,7 @@ def log_message_with_image(self, msg: Dict, image: str): mes.attachment = { 'name': os.path.basename(image), 'data': fh.read(), - 'mime': guess_type(image)[0] or 'application/octet-stream' + 'mime': guess_type(image)[0] or DEFAULT_BINARY_FILE_TYPE } logger.debug(f'ReportPortal - Log Message with Image: {mes} {image}') self.service.log(message=mes) diff --git a/tests/integration/test_screenshot.py b/tests/integration/test_screenshot.py new file mode 100644 index 0000000..1b2b5b4 --- /dev/null +++ b/tests/integration/test_screenshot.py @@ -0,0 +1,44 @@ +# Copyright 2023 EPAM Systems +# +# 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 +# +# https://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 tests.helpers import utils +from unittest import mock + +from tests import REPORT_PORTAL_SERVICE + +EXAMPLE_TEST = 'examples/screenshot.robot' +SELENIUM_SCREENSHOT = 'examples/res/selenium-screenshot-1.png' +PLAYWRIGHT_SCREENSHOT = 'examples/res/Screenshot_test_FAILURE_SCREENSHOT_1.png' +SCREENSHOTS = [SELENIUM_SCREENSHOT, PLAYWRIGHT_SCREENSHOT] + + +@mock.patch(REPORT_PORTAL_SERVICE) +def test_screenshot_log(mock_client_init): + result = utils.run_robot_tests([EXAMPLE_TEST]) + assert result == 0 # the test successfully passed + + mock_client = mock_client_init.return_value + calls = utils.get_log_calls(mock_client) + assert len(calls) == 2 + + for i, call in enumerate(calls): + message = call[1]['message'] + assert message == f'Image attached: {SCREENSHOTS[i]}' + + attachment = call[1]['attachment'] + + assert attachment['name'] == SCREENSHOTS[i].split('/')[-1] + assert attachment['mime'] == 'image/png' + with open(SCREENSHOTS[i], 'rb') as file: + assert attachment['data'] == file.read()