Skip to content

Commit 992baa9

Browse files
authored
Merge pull request #197 from reportportal/develop
Release
2 parents 18de07c + 42daccb commit 992baa9

File tree

13 files changed

+184
-74
lines changed

13 files changed

+184
-74
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changelog
22

33
## [Unreleased]
4+
### Added
5+
- Issue [#192](https://github.com/reportportal/agent-Python-RobotFramework/issues/192): Robot link markup to Markdown conversion, by @HardNorth
6+
7+
## [5.5.4]
48
### Fixed
59
- Issue [#187](https://github.com/reportportal/agent-Python-RobotFramework/issues/187): Distutils in the agent, by @HardNorth
610
### Added

examples/rkie_keyword.robot

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
*** Settings ***
2+
Documentation Example of 'Run Keyword And Ignore Error' keyword reporting
3+
4+
*** Variables ***
5+
${countval} 0
6+
7+
*** Test Cases ***
8+
Rkie test
9+
Run Keyword And Ignore Error Fail on first try
10+
Fail on first try
11+
12+
*** Keywords ***
13+
Fail on first try
14+
${counter} Evaluate ${countval} + 1
15+
Set Suite Variable ${countval} ${counter}
16+
IF ${countval} < 2
17+
Fail To less executions
18+
END

examples/suite_doc_with_urls.robot

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*** Settings ***
2+
Documentation This is a test suite with URLs: [https://www.google.com | Google] and [https://www.google.com]
3+
4+
*** Test Cases ***
5+
Simple test
6+
Log Hello, world!
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2024 EPAM Systems
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""This module contains functions to ease reporting to ReportPortal."""
16+
17+
import re
18+
from typing import Iterable, Tuple
19+
20+
21+
def replace_patterns(text: str, patterns: Iterable[Tuple[re.Pattern, str]]) -> str:
22+
"""Replace given patterns in the text."""
23+
result = text
24+
for p, repl in patterns:
25+
result = p.sub(repl, result)
26+
return result
27+
28+
29+
BARE_LINK_PATTERN = re.compile(r'\[\s*([^]|]+)]')
30+
NAMED_LINK_PATTERN = re.compile(r'\[\s*([^]|]+)\|\s*([^]]+)]')
31+
32+
ROBOT_MARKUP_REPLACEMENT_PATTERS = [
33+
(BARE_LINK_PATTERN, r'<\1>'),
34+
(NAMED_LINK_PATTERN, r'[\2](\1)'),
35+
]
36+
37+
38+
def robot_markup_to_markdown(text: str) -> str:
39+
"""Convert Robot Framework's text markup to Markdown format."""
40+
return replace_patterns(text, ROBOT_MARKUP_REPLACEMENT_PATTERS)

robotframework_reportportal/listener.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525

2626
from reportportal_client.helpers import LifoQueue, is_binary, guess_content_type_from_bytes
2727

28-
from .model import Keyword, Launch, Test, LogMessage, Suite
29-
from .service import RobotService
30-
from .static import MAIN_SUITE_ID, PABOT_WITHOUT_LAUNCH_ID_MSG
31-
from .variables import Variables
28+
from robotframework_reportportal.model import Keyword, Launch, Test, LogMessage, Suite
29+
from robotframework_reportportal.service import RobotService
30+
from robotframework_reportportal.static import MAIN_SUITE_ID, PABOT_WITHOUT_LAUNCH_ID_MSG
31+
from robotframework_reportportal.variables import Variables
3232

3333
logger = logging.getLogger(__name__)
3434
VARIABLE_PATTERN = r'^\s*\${[^}]*}\s*=\s*'

robotframework_reportportal/logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def log_free_memory(self):
3939

4040
from robot.api import logger
4141

42-
from .model import LogMessage
42+
from robotframework_reportportal.model import LogMessage
4343

4444

4545
def write(msg: str, level: str = 'INFO', html: bool = False, attachment: Optional[Dict[str, str]] = None,

robotframework_reportportal/model.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import os
1818
from typing import Any, Dict, List, Optional, Union
1919

20+
from robotframework_reportportal.helpers import robot_markup_to_markdown
2021
from reportportal_client.helpers import gen_attributes
2122

2223

@@ -48,7 +49,7 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any]):
4849
:param robot_attributes: Suite attributes passed through the listener
4950
"""
5051
self.robot_attributes = robot_attributes
51-
self.doc = robot_attributes['doc']
52+
self.doc = robot_markup_to_markdown(robot_attributes['doc'])
5253
self.end_time = robot_attributes.get('endtime', '')
5354
self.longname = robot_attributes['longname']
5455
self.message = robot_attributes.get('message')
@@ -145,7 +146,7 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any], test_attributes:
145146
self._tags = robot_attributes['tags']
146147
self.test_attributes = gen_attributes(test_attributes)
147148
self.robot_attributes = robot_attributes
148-
self.doc = robot_attributes['doc']
149+
self.doc = robot_markup_to_markdown(robot_attributes['doc'])
149150
self.end_time = robot_attributes.get('endtime', '')
150151
self.longname = robot_attributes['longname']
151152
self.message = robot_attributes.get('message')
@@ -242,7 +243,7 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any], parent_type: Opt
242243
self.robot_attributes = robot_attributes
243244
self.args = robot_attributes['args']
244245
self.assign = robot_attributes['assign']
245-
self.doc = robot_attributes['doc']
246+
self.doc = robot_markup_to_markdown(robot_attributes['doc'])
246247
self.end_time = robot_attributes.get('endtime')
247248
self.keyword_name = robot_attributes['kwname']
248249
self.keyword_type = robot_attributes['type']

robotframework_reportportal/result_visitor.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,37 @@
1515
import re
1616
import string
1717
from datetime import datetime
18-
19-
from robot.api import ResultVisitor
18+
from typing import List, Pattern, Optional
2019
from urllib.parse import unquote
2120

22-
from . import listener
23-
from .time_visitor import corrections
24-
# noinspection PyUnresolvedReferences
25-
from .variables import _variables
21+
from robot.result import ResultVisitor, Result, TestSuite, TestCase, Keyword, Message
2622

23+
from robotframework_reportportal import listener
24+
from robotframework_reportportal.time_visitor import corrections
25+
# noinspection PyUnresolvedReferences
26+
from robotframework_reportportal.variables import _variables
2727

2828
listener = listener.listener()
2929

3030

31-
def to_timestamp(time_str):
31+
def to_timestamp(time_str: str) -> Optional[str]:
3232
if time_str:
3333
dt = datetime.strptime(time_str, '%Y%m%d %H:%M:%S.%f')
3434
return str(int(dt.timestamp() * 1000))
3535
return None
3636

3737

3838
class RobotResultsVisitor(ResultVisitor):
39-
_link_pattern = re.compile("src=[\"\']([^\"\']+)[\"\']")
39+
_link_pattern: Pattern = re.compile("src=[\"\']([^\"\']+)[\"\']")
4040

41-
def start_result(self, result):
41+
def start_result(self, result: Result) -> bool:
4242
if "RP_LAUNCH" not in _variables:
4343
_variables["RP_LAUNCH"] = result.suite.name
4444
if "RP_LAUNCH_DOC" not in _variables:
4545
_variables["RP_LAUNCH_DOC"] = result.suite.doc
46+
return True
4647

47-
def start_suite(self, suite):
48+
def start_suite(self, suite: TestSuite) -> bool:
4849
ts = to_timestamp(suite.starttime if suite.id not in corrections else corrections[suite.id][0])
4950
attrs = {
5051
'id': suite.id,
@@ -58,8 +59,9 @@ def start_suite(self, suite):
5859
'starttime': ts
5960
}
6061
listener.start_suite(suite.name, attrs, ts)
62+
return True
6163

62-
def end_suite(self, suite):
64+
def end_suite(self, suite: TestSuite) -> None:
6365
ts = to_timestamp(suite.endtime if suite.id not in corrections else corrections[suite.id][1])
6466
attrs = {
6567
'id': suite.id,
@@ -78,7 +80,7 @@ def end_suite(self, suite):
7880
}
7981
listener.end_suite(None, attrs, ts)
8082

81-
def start_test(self, test):
83+
def start_test(self, test: TestCase) -> bool:
8284
ts = to_timestamp(test.starttime if test.id not in corrections else corrections[test.id][0])
8385
attrs = {
8486
'id': test.id,
@@ -95,8 +97,9 @@ def start_test(self, test):
9597
'starttime': ts,
9698
}
9799
listener.start_test(test.name, attrs, ts)
100+
return True
98101

99-
def end_test(self, test):
102+
def end_test(self, test: TestCase) -> None:
100103
ts = to_timestamp(test.endtime if test.id not in corrections else corrections[test.id][1])
101104
attrs = {
102105
'id': test.id,
@@ -117,7 +120,7 @@ def end_test(self, test):
117120
}
118121
listener.end_test(test.name, attrs, ts)
119122

120-
def start_keyword(self, kw):
123+
def start_keyword(self, kw: Keyword) -> bool:
121124
ts = to_timestamp(kw.starttime if kw.id not in corrections else corrections[kw.id][0])
122125
attrs = {
123126
'type': string.capwords(kw.type),
@@ -130,8 +133,9 @@ def start_keyword(self, kw):
130133
'starttime': ts,
131134
}
132135
listener.start_keyword(kw.name, attrs, ts)
136+
return True
133137

134-
def end_keyword(self, kw):
138+
def end_keyword(self, kw: Keyword) -> None:
135139
ts = to_timestamp(kw.endtime if kw.id not in corrections else corrections[kw.id][1])
136140
attrs = {
137141
'type': string.capwords(kw.type),
@@ -147,7 +151,7 @@ def end_keyword(self, kw):
147151
}
148152
listener.end_keyword(kw.name, attrs, ts)
149153

150-
def start_message(self, msg):
154+
def start_message(self, msg: Message) -> bool:
151155
if msg.message:
152156
message = {
153157
'message': msg.message,
@@ -162,8 +166,9 @@ def start_message(self, msg):
162166
try:
163167
listener.log_message(message)
164168
except Exception:
165-
pass
169+
return False
170+
return True
166171

167-
def parse_message(self, msg):
172+
def parse_message(self, msg: str) -> List[str]:
168173
m = self._link_pattern.search(msg)
169174
return [m.group(), unquote(m.group(1))]

robotframework_reportportal/result_visitor.pyi

Lines changed: 0 additions & 41 deletions
This file was deleted.

robotframework_reportportal/service.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,21 @@
1414

1515
"""This module is a Robot service for reporting results to ReportPortal."""
1616

17+
import logging
1718
from typing import Optional
1819

1920
from dateutil.parser import parse
20-
import logging
21-
21+
from reportportal_client import RP, create_client
2222
from reportportal_client.helpers import (
2323
dict_to_payload,
2424
get_launch_sys_attrs,
2525
get_package_version,
2626
timestamp
2727
)
28-
from reportportal_client import RP, create_client
2928

30-
from .model import Launch, Suite, Test, Keyword, LogMessage
31-
from .variables import Variables
32-
from .static import LOG_LEVEL_MAPPING, STATUS_MAPPING
29+
from robotframework_reportportal.model import Launch, Suite, Test, Keyword, LogMessage
30+
from robotframework_reportportal.static import LOG_LEVEL_MAPPING, STATUS_MAPPING
31+
from robotframework_reportportal.variables import Variables
3332

3433
logger = logging.getLogger(__name__)
3534

0 commit comments

Comments
 (0)