diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e83ea..4eabec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] ### Added +- `Test Case ID` reporting on Item Finish, by @HardNorth +### Changed +- Client version updated on [5.5.8](https://github.com/reportportal/client-Python/releases/tag/5.5.8), by @HardNorth + +## [5.5.6] +### Added - `timezone` command line argument for `post_report.py` script, by @HardNorth ## [5.5.5] diff --git a/examples/dynamic_test_case_id.robot b/examples/dynamic_test_case_id.robot new file mode 100644 index 0000000..a046a79 --- /dev/null +++ b/examples/dynamic_test_case_id.robot @@ -0,0 +1,7 @@ +*** Settings *** +Documentation Example of setting Test Case ID in runtime +Library library/TestCaseId.py + +*** Test Cases *** +Test set dynamic Test Case ID + Case Id dynamic_tags.robot[{scope_var}] diff --git a/examples/library/Log.py b/examples/library/Log.py index 2412199..a120a9b 100644 --- a/examples/library/Log.py +++ b/examples/library/Log.py @@ -1,18 +1,18 @@ -"""Logging library for Robot Framework. - -Copyright (c) 2021 http://reportportal.io . -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 - -http://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 -""" +"""Logging library for Robot Framework.""" + +# Copyright 2024 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. import os diff --git a/examples/library/TestCaseId.py b/examples/library/TestCaseId.py new file mode 100644 index 0000000..fffaf2d --- /dev/null +++ b/examples/library/TestCaseId.py @@ -0,0 +1,38 @@ +"""Test Case ID library for Robot Framework.""" +# Copyright 2024 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 typing import Optional + +from robot.libraries.BuiltIn import BuiltIn + + +def case_id(test_case_id_pattern: Optional[str]) -> None: + """ + Set test case ID based on suite metadata. + + :param test_case_id_pattern: Test Case ID pattern + """ + built_in = BuiltIn() + if not test_case_id_pattern: + return + suite_metadata = built_in.get_variable_value('${suitemetadata}') + scope = None + for key in suite_metadata: + if key.lower() == 'scope': + scope = suite_metadata[key] + break + if not scope: + return + built_in.set_tags('test_case_id:' + test_case_id_pattern.format(scope_var=scope)) diff --git a/requirements.txt b/requirements.txt index e6052b8..bca7838 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ # Basic dependencies python-dateutil~=2.8.1 -reportportal-client~=5.5.7 +reportportal-client~=5.5.8 robotframework diff --git a/robotframework_reportportal/listener.py b/robotframework_reportportal/listener.py index 85e7f2c..3cdc8ea 100644 --- a/robotframework_reportportal/listener.py +++ b/robotframework_reportportal/listener.py @@ -195,7 +195,7 @@ def start_launch(self, attributes: Dict[str, Any], ts: Optional[Any] = None) -> """ launch = Launch(self.variables.launch_name, attributes, self.variables.launch_attributes) launch.doc = self.variables.launch_doc or launch.doc - if self.variables.pabot_used: + if self.variables.pabot_used and not self._variables.launch_id: warn(PABOT_WITHOUT_LAUNCH_ID_MSG, stacklevel=2) logger.debug(f'ReportPortal - Start Launch: {launch.robot_attributes}') self.service.start_launch( diff --git a/robotframework_reportportal/model.py b/robotframework_reportportal/model.py index 51bdbd7..9fa7cf3 100644 --- a/robotframework_reportportal/model.py +++ b/robotframework_reportportal/model.py @@ -20,6 +20,8 @@ from robotframework_reportportal.helpers import robot_markup_to_markdown from reportportal_client.helpers import gen_attributes +TEST_CASE_ID_SIGN = 'test_case_id:' + class Suite: """Class represents Robot Framework test suite.""" @@ -167,7 +169,7 @@ def critical(self) -> bool: @property def tags(self) -> List[str]: """Get list of test tags excluding test_case_id.""" - return [tag for tag in self._tags if not tag.startswith('test_case_id')] + return [tag for tag in self._tags if not tag.startswith(TEST_CASE_ID_SIGN)] @property def attributes(self) -> Optional[List[Dict[str, str]]]: @@ -196,7 +198,7 @@ def test_case_id(self) -> Optional[str]: """Get test case ID through the tags.""" # use test case id from tags if specified for tag in self._tags: - if tag.startswith('test_case_id:'): + if tag.startswith(TEST_CASE_ID_SIGN): return tag.split(':')[1] # generate it if not return '{0}:{1}'.format(self.source, self.name) diff --git a/robotframework_reportportal/service.py b/robotframework_reportportal/service.py index 7f34ce8..e0e2ffc 100644 --- a/robotframework_reportportal/service.py +++ b/robotframework_reportportal/service.py @@ -50,7 +50,7 @@ def to_epoch(date: Optional[str]) -> Optional[str]: return str(int(epoch_time * 1000)) -class RobotService(object): +class RobotService: """Class represents service that sends Robot items to ReportPortal.""" agent_name: str @@ -212,7 +212,8 @@ def finish_test(self, test: Test, issue: Optional[str] = None, ts: Optional[str] 'end_time': ts or to_epoch(test.end_time) or timestamp(), 'issue': issue, 'item_id': test.rp_item_id, - 'status': STATUS_MAPPING[test.status] + 'status': STATUS_MAPPING[test.status], + 'test_case_id': test.test_case_id } logger.debug('ReportPortal - Finish test: request_body={0}'.format(fta_rq)) self.rp.finish_test_item(**fta_rq) diff --git a/setup.py b/setup.py index 1e60cbe..af0ff72 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ from setuptools import setup -__version__ = '5.5.6' +__version__ = '5.5.7' def read_file(fname): diff --git a/tests/integration/test_case_id.py b/tests/integration/test_case_id.py index abae269..160fe98 100644 --- a/tests/integration/test_case_id.py +++ b/tests/integration/test_case_id.py @@ -12,11 +12,10 @@ # 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 - +from tests.helpers import utils SIMPLE_TEST = 'examples/simple.robot' diff --git a/tests/integration/test_dynamic_test_case_id.py b/tests/integration/test_dynamic_test_case_id.py new file mode 100644 index 0000000..9fb8196 --- /dev/null +++ b/tests/integration/test_dynamic_test_case_id.py @@ -0,0 +1,41 @@ +# Copyright 2024 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 unittest import mock + +from tests import REPORT_PORTAL_SERVICE +from tests.helpers import utils + +EXAMPLE_TEST = 'examples/dynamic_test_case_id.robot' + + +@mock.patch(REPORT_PORTAL_SERVICE) +def test_case_id_simple(mock_client_init): + result = utils.run_robot_tests([EXAMPLE_TEST], arguments={'--metadata': 'Scope:Smoke'}) + assert result == 0 # the test successfully passed + + mock_client = mock_client_init.return_value + launch_start = mock_client.start_launch.call_args_list + launch_finish = mock_client.finish_launch.call_args_list + assert len(launch_start) == len(launch_finish) == 1 + + item_start_calls = mock_client.start_test_item.call_args_list + item_finish_calls = mock_client.finish_test_item.call_args_list + assert len(item_start_calls) == len(item_finish_calls) == 3 + + test_item_start = item_start_calls[-2] + assert test_item_start[1]['test_case_id'] == f'{EXAMPLE_TEST}:Test set dynamic Test Case ID' + + test_item_finish = item_finish_calls[-2] + assert test_item_finish[1]['test_case_id'] == 'dynamic_tags.robot[Smoke]'