Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/configuration/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ file. This is noted per option below.
| [`attachments-variable`](#attachments-variable) | — | ✅ | G | variable name (`ITB_ATTACHMENTS_DIR`) |
| [`keep-extracted-report`](#keep-extracted-report) | — | ✅ | G · F | bool (`false`) |
| [`merge-protocol`](#merge-protocol) | `--no-merge-protocol` | ✅ | F | bool (`true`) |
| [`set-tester-from-report-creator`](#set-tester-from-report-creator) | `--set-tester-from-report-creator` | ✅ | F | bool (`false`) |
| [`keyword-comment-style`](#keyword-comment-style) | — | ✅ | F | `STRUCTURED` \| `FLAT` (`STRUCTURED`) |
| [`keyword-comment-max-depth`](#keyword-comment-max-depth) | — | ✅ | F | int (`5`) |
| [`keyword-comment-max-rows`](#keyword-comment-max-rows) | — | ✅ | F | int, `0` = no limit (`300`) |
Expand Down Expand Up @@ -535,6 +536,29 @@ testbench2robotframework fetch-results --no-merge-protocol output.xml my_report.
merge-protocol = true
```

### `set-tester-from-report-creator`

Sets the user who created the TestBench report as **tester** of the test cases the
current Robot run executed. The user is read from `reportCreation.creator` in the
report's `manifest.json`: its `userKey` becomes the `testerKey` of the test case in
`protocol.json`, and its `userKey` and `userName` become `exec.tester` in the test
case's JSON file. An existing tester is overwritten.

Only test cases with the verdict `Pass` or `Fail` get a tester. Skipped and not run
tests, and executions kept from the input protocol when merging, stay unchanged. If
`manifest.json` is missing or names no creator, a warning is logged and no tester is
set.

- **Values:** `true` / `false`. Default: `false`.
- **CLI:** `--set-tester-from-report-creator` (turns it on).

```bash
testbench2robotframework fetch-results --set-tester-from-report-creator output.xml my_report.zip
```
```toml
set-tester-from-report-creator = true
```

### `keyword-comment-style`

How the execution comment of a keyword is rendered. `STRUCTURED` shows the whole
Expand Down
7 changes: 7 additions & 0 deletions docs/usage/fetch_results.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ second run starts over. To combine several Robot runs, merge the output XMLs fir
(`rebot --merge output1.xml output2.xml`) and call `fetch-results` once.
:::

## Tester

The results do not name a tester by default. With
[`set-tester-from-report-creator` / `--set-tester-from-report-creator`](../configuration/overview.md#set-tester-from-report-creator)
the user who created the TestBench report becomes the tester of every test case the
run executed with the verdict `Pass` or `Fail`.

---

## Execution comments
Expand Down
2 changes: 1 addition & 1 deletion testbench2robotframework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@

from .testbench2robotframework import testbench2robotframework # noqa: F401

__version__ = "2.0.1rc1"
__version__ = "2.1.0rc1"
9 changes: 9 additions & 0 deletions testbench2robotframework/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ def generate_tests( # noqa: PLR0913
help="""Overwrite the main protocol instead of merging the Robot Framework results
into the protocol.json that is already part of the TestBench report.""",
)
@click.option(
"--set-tester-from-report-creator",
is_flag=True,
help="""Set the user who created the TestBench report (manifest.json) as tester of
every test case that this run executed with the verdict Pass or Fail.""",
)
@click.argument("robot-result", type=click.Path(path_type=Path))
@click.argument("testbench-report", type=click.Path(path_type=Path))
def fetch_results(
Expand All @@ -275,6 +281,7 @@ def fetch_results(
output_directory: Path,
testbench_report: Path,
no_merge_protocol: bool,
set_tester_from_report_creator: bool,
):
"""
Fetch Robot Framework execution results from <output XML> and save to a <TestBench Report>.
Expand All @@ -284,6 +291,8 @@ def fetch_results(
configuration["merge-protocol"] = False
else:
configuration["merge-protocol"] = configuration.get("merge-protocol", True)
if set_tester_from_report_creator:
configuration["set-tester-from-report-creator"] = True
robot2testbench(testbench_report, robot_result, output_directory, configuration)


Expand Down
4 changes: 4 additions & 0 deletions testbench2robotframework/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class Configuration:
_: KW_ONLY
include_blocked: bool = False
merge_protocol: bool = True
set_tester_from_report_creator: bool = False
keep_extracted_report: bool = False
attachments_directory: str = ""
attachments_variable: str = "ITB_ATTACHMENTS_DIR"
Expand Down Expand Up @@ -274,6 +275,9 @@ def from_dict(cls, dictionary) -> Configuration:
}
),
merge_protocol=dictionary.get("merge-protocol", True),
set_tester_from_report_creator=dictionary.get(
"set-tester-from-report-creator", False
),
keep_extracted_report=dictionary.get("keep-extracted-report", False),
attachments_directory=dictionary.get("attachments-directory", "").replace("\\", "/"),
attachments_variable=dictionary.get("attachments-variable", "ITB_ATTACHMENTS_DIR"),
Expand Down
45 changes: 44 additions & 1 deletion testbench2robotframework/result_writer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import html
import json
import os
import re
import shutil
Expand Down Expand Up @@ -40,6 +41,7 @@
TestCaseNode,
TestCaseSetExecutionForImport,
TestCaseSetNode,
UserInfo,
UserReference,
VerdictStatus,
)
Expand All @@ -52,7 +54,7 @@
index_by_test_case_set_key,
merge_test_case_executions,
)
from .utils import directory_to_zip, open_report
from .utils import directory_to_zip, open_report, read_manifest_json_from_testbench_report

try:
from robot.result import Group
Expand Down Expand Up @@ -200,6 +202,32 @@ def unexecuted_table_row(unique_id: str, verdict) -> TestCaseRow:
)


def read_report_creator(json_dir: str | Path) -> UserInfo | None:
"""The user who created the report, as named in its 'manifest.json'.

Returns None, and warns, when the manifest is missing, unreadable or names no creator.
"""
try:
manifest = read_manifest_json_from_testbench_report(Path(json_dir))
except (FileNotFoundError, json.JSONDecodeError) as e:
logger.warning(f"Could not read the report's manifest.json, no tester is set: {e}")
return None
report_creation = manifest.get("reportCreation") if isinstance(manifest, dict) else None
creator = (report_creation or {}).get("creator") or {}
user_key = creator.get("userKey")
Comment on lines +215 to +217
if user_key in (None, ""):
logger.warning(
"The report's manifest.json names no creator (reportCreation.creator.userKey), "
"no tester is set."
)
return None
return UserInfo(
userKey=str(user_key),
userLogin=creator.get("userLogin", ""),
userName=creator.get("userName", ""),
)


class ResultWriter(ResultVisitor):
def __init__(
self,
Expand Down Expand Up @@ -243,6 +271,9 @@ def __init__(
)
self.base_protocol_by_key = index_by_test_case_set_key(self.base_protocol)
self.merged_verdicts: dict[str, Verdict] = {}
self.report_creator: UserInfo | None = (
read_report_creator(self.json_dir) if config.set_tester_from_report_creator else None
)
self.keyword_comment_style = config.keyword_comment_style
self.keyword_comment_renderer = KeywordCommentRenderer(
max_depth=config.keyword_comment_max_depth,
Expand Down Expand Up @@ -319,6 +350,7 @@ def end_test(self, test: TestCase):
step.exec = _empty_keyword_call_execution()
step.exec.verdict = KeywordVerdict.Skipped
self._set_itb_testcase_execution_result(itb_test_case, self.test_chain)
self._set_itb_testcase_tester(itb_test_case)
self._set_itb_testcase_execution_comment(itb_test_case, self.test_chain)
self._set_itb_testcase_references(itb_test_case, self.test_chain)
except TypeError as e:
Expand Down Expand Up @@ -445,6 +477,17 @@ def _set_itb_testcase_execution_result(self, itb_test_case: TestCaseDetails, tes
protocol_result = self._set_itb_test_case_status(itb_test_case, "undef")
self.protocol_test_case.result = protocol_result

def _set_itb_testcase_tester(self, itb_test_case: TestCaseDetails):
"""Makes the report's creator the tester of a test case this run performed."""
if self.report_creator is None or not itb_test_case.exec:
return
if self.protocol_test_case.result.status != ActivityStatus.Performed:
return
self.protocol_test_case.testerKey = self.report_creator.userKey
itb_test_case.exec.tester = UserReference(
key=self.report_creator.userKey, name=self.report_creator.userName
)

def _get_test_phase_body(self, test_phase: TestCase) -> list[Keyword]:
return self._get_keywords_from_rf_body(test_phase)

Expand Down
6 changes: 4 additions & 2 deletions testbench2robotframework/robotframework2testbench.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ def robot2testbench(
json_input_report: str | Path,
robot_result_xml: str | Path,
json_output_result: str | Path | None = None,
config: dict | None = None,
config: dict | Configuration | None = None,
):
if not Path(json_input_report).exists():
sys.exit("No TestBench report found at the given path: expected a directory or a ZIP file.")
if not Path(robot_result_xml).exists():
sys.exit("The Robot Framework result XML does not exist at the given path.")
perform_version_check(Path(json_input_report))

configuration = Configuration.from_dict(config)
configuration = (
config if isinstance(config, Configuration) else Configuration.from_dict(config or {})
)
setup_logger(configuration)
logger.debug("Configuration loaded.")
result = ExecutionResult(robot_result_xml)
Expand Down
4 changes: 3 additions & 1 deletion testbench2robotframework/testbench2robotframework.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

def testbench2robotframework(testbench_report: str | Path, config: dict | Configuration):
perform_version_check(Path(testbench_report))
configuration = Configuration.from_dict(config) if isinstance(config, dict) else config
configuration = (
config if isinstance(config, Configuration) else Configuration.from_dict(config or {})
)
setup_logger(configuration)
logger.debug("Configuration loaded.")
with open_report(Path(testbench_report), configuration.keep_extracted_report) as report:
Expand Down
Loading
Loading