From 40420f315769caa6d17e1707916636f0b03dc199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81?= Date: Mon, 20 Apr 2026 12:11:46 +0200 Subject: [PATCH 01/15] there were two issues. Please review and fix --- testbench2robotframework/config.py | 2 ++ testbench2robotframework/testbench2rf.py | 34 ++++++++++++------- .../testbench2robotframework.py | 4 +-- testbench2robotframework/testsuite_write.py | 2 ++ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/testbench2robotframework/config.py b/testbench2robotframework/config.py index 7361fcd..63a8843 100644 --- a/testbench2robotframework/config.py +++ b/testbench2robotframework/config.py @@ -189,6 +189,7 @@ class Configuration: attachmentConflictBehaviour: AttachmentConflictBehaviour clean: bool compound_keyword_logging: CompoundKeywordLogging + create_output_zip: bool forced_import: ForcedImport fully_qualified: bool library_regex: list[str] @@ -210,6 +211,7 @@ class Configuration: def from_dict(cls, dictionary) -> Configuration: return cls( clean=dictionary.get("clean", True), + create_output_zip=dictionary.get("create-output-zip", False), library_regex=dictionary.get( "library-regex", [DEFAULT_LIBRARY_REGEX] ), diff --git a/testbench2robotframework/testbench2rf.py b/testbench2robotframework/testbench2rf.py index aa5343f..02836b1 100644 --- a/testbench2robotframework/testbench2rf.py +++ b/testbench2robotframework/testbench2rf.py @@ -197,7 +197,7 @@ def _get_keyword_import( for pattern in self.lib_pattern_list: match = pattern.search(keyword_path) if match: - return LIBRARY_IMPORT_TYPE, match.group("resourceName").strip() + return LIBRARY_IMPORT_TYPE, match.group("libraryName").strip() for pattern in self.res_pattern_list: match = pattern.search(keyword_path) if match: @@ -205,7 +205,7 @@ def _get_keyword_import( splitted_keyword_path = keyword_path.split(".") minimum_length_subdivision_path_length = 2 if ( - len(splitted_keyword_path) == minimum_length_subdivision_path_length + len(splitted_keyword_path) >= minimum_length_subdivision_path_length and splitted_keyword_path[0] in self.config.library_root ): return LIBRARY_IMPORT_TYPE, splitted_keyword_path[1] @@ -460,17 +460,25 @@ def _create_cbr_parameters( return cbr_parameters def _get_keyword_import_prefix(self, keyword: RFKeywordCallInformation) -> str: - for resource_regex in self.config.resource_regex: - if not keyword.import_prefix: - continue - resource_name_match = re.search( - resource_regex, keyword.import_prefix, flags=re.IGNORECASE - ) - if resource_name_match: - return ( - self.config.fully_qualified or False - ) * f"{resource_name_match.group('resourceName').strip()}." - return "" + # FIXME: hier dieser code scheint zu verhindern, dass fully qualified paths verwendet werden, wenn man nicht über Regex arbeitet. + # FIXME: Ich erkenne keinen Grund für diese komplexität. Bitte fixen. René + + # # for resource_regex in self.config.resource_regex: # TODO: hier haben wir schon eine liste von compilierten pattern. + # for resource_pattern in self.res_pattern_list: + # if not keyword.import_prefix: + # continue + # # resource_name_match = re.search( # TODO: Siehe oben + # # resource_regex, keyword.import_prefix, flags=re.IGNORECASE + # # ) + # resource_name_match = resource_pattern.search( + # keyword.import_prefix, flags=re.IGNORECASE + # ) + # if resource_name_match: + # return ( + # self.config.fully_qualified or False + # ) * f"{resource_name_match.group('resourceName').strip()}." + # return "" + return (self.config.fully_qualified or False) * f"{keyword.import_prefix}." def _get_keyword_indent(self, keyword: RFKeywordCallInformation) -> str: return ( diff --git a/testbench2robotframework/testbench2robotframework.py b/testbench2robotframework/testbench2robotframework.py index 09bde21..6e41945 100644 --- a/testbench2robotframework/testbench2robotframework.py +++ b/testbench2robotframework/testbench2robotframework.py @@ -10,8 +10,8 @@ from .utils import PathResolver, extract_to_working_directory, is_zip_file -def testbench2robotframework(testbench_report: str, config: dict): - configuration = Configuration.from_dict(config) +def testbench2robotframework(testbench_report: str, config: dict | Configuration): + configuration = Configuration.from_dict(config) if isinstance(config, dict) else config setup_logger(configuration) logger.debug("Configuration loaded.") testbench_report = Path(testbench_report) diff --git a/testbench2robotframework/testsuite_write.py b/testbench2robotframework/testsuite_write.py index 5193340..bcd5bb3 100644 --- a/testbench2robotframework/testsuite_write.py +++ b/testbench2robotframework/testsuite_write.py @@ -17,6 +17,8 @@ def write_test_suites(test_suites: dict[str, File], config: Configuration) -> No clear_generation_directory(generation_directory) if generation_directory.suffix.lower() != ".zip": write_test_suite_files(test_suites, generation_directory) + if config.create_output_zip: + directory_to_zip(generation_directory) else: with tempfile.TemporaryDirectory(dir=Path.cwd()) as temp_dir: write_test_suite_files(test_suites, Path(temp_dir)) From 881f8758d72acc290ff13651704c281876b02579 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Tue, 19 May 2026 13:35:53 +0200 Subject: [PATCH 02/15] fix for subdivision 2 resource conversion --- testbench2robotframework/testbench2rf.py | 36 +++++++----------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/testbench2robotframework/testbench2rf.py b/testbench2robotframework/testbench2rf.py index 02836b1..f1a855f 100644 --- a/testbench2robotframework/testbench2rf.py +++ b/testbench2robotframework/testbench2rf.py @@ -191,13 +191,11 @@ def _append_atomic_ia( ) ) - def _get_keyword_import( - self, test_step: TBKeywordCall, keyword_path: str - ) -> tuple[str, str]: + def _get_keyword_import(self, test_step: TBKeywordCall, keyword_path: str) -> tuple[str, str]: for pattern in self.lib_pattern_list: match = pattern.search(keyword_path) if match: - return LIBRARY_IMPORT_TYPE, match.group("libraryName").strip() + return LIBRARY_IMPORT_TYPE, match.group("resourceName").strip() for pattern in self.res_pattern_list: match = pattern.search(keyword_path) if match: @@ -209,6 +207,11 @@ def _get_keyword_import( and splitted_keyword_path[0] in self.config.library_root ): return LIBRARY_IMPORT_TYPE, splitted_keyword_path[1] + if ( + len(splitted_keyword_path) >= minimum_length_subdivision_path_length + and splitted_keyword_path[0] in self.config.resource_root + ): + return RESOURCE_IMPORT_TYPE, splitted_keyword_path[1] return UNKNOWN_IMPORT_TYPE, keyword_path def _append_compound_ia( @@ -265,10 +268,7 @@ def _create_rf_keyword_calls( group_stack[-1][0].body.append(compound_keyword_call) else: keyword_lists[tc_index].append(compound_keyword_call) - if ( - Group - and self.config.compound_keyword_logging == CompoundKeywordLogging.GROUP - ): + if Group and self.config.compound_keyword_logging == CompoundKeywordLogging.GROUP: group_stack.append((compound_keyword_call, keyword_call.indent)) return keyword_lists @@ -460,24 +460,6 @@ def _create_cbr_parameters( return cbr_parameters def _get_keyword_import_prefix(self, keyword: RFKeywordCallInformation) -> str: - # FIXME: hier dieser code scheint zu verhindern, dass fully qualified paths verwendet werden, wenn man nicht über Regex arbeitet. - # FIXME: Ich erkenne keinen Grund für diese komplexität. Bitte fixen. René - - # # for resource_regex in self.config.resource_regex: # TODO: hier haben wir schon eine liste von compilierten pattern. - # for resource_pattern in self.res_pattern_list: - # if not keyword.import_prefix: - # continue - # # resource_name_match = re.search( # TODO: Siehe oben - # # resource_regex, keyword.import_prefix, flags=re.IGNORECASE - # # ) - # resource_name_match = resource_pattern.search( - # keyword.import_prefix, flags=re.IGNORECASE - # ) - # if resource_name_match: - # return ( - # self.config.fully_qualified or False - # ) * f"{resource_name_match.group('resourceName').strip()}." - # return "" return (self.config.fully_qualified or False) * f"{keyword.import_prefix}." def _get_keyword_indent(self, keyword: RFKeywordCallInformation) -> str: @@ -713,6 +695,8 @@ def _get_resource_name(self, resource: str) -> str | None: resource_name_match = re.search(resource_regex, resource_path_part, flags=re.IGNORECASE) if resource_name_match: return resource_name_match.group("resourceName").strip() + if resource_path_part: + return resource_path_part.strip() return None def _get_resource_directory_path_index(self, resource: str) -> int | None: From 7bcaf5fc00d616d9e0ff9b3b7a5b6f295c31baf0 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Wed, 20 May 2026 09:11:16 +0200 Subject: [PATCH 03/15] make regex more robust --- testbench2robotframework/testbench2rf.py | 58 ++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/testbench2robotframework/testbench2rf.py b/testbench2robotframework/testbench2rf.py index f1a855f..2eaea7b 100644 --- a/testbench2robotframework/testbench2rf.py +++ b/testbench2robotframework/testbench2rf.py @@ -97,8 +97,20 @@ def __init__(self, test_case_details: TestCaseDetails, config: Configuration) -> self.rf_keyword_call_information: list[RFKeywordCallInformation] = [] self.used_imports: dict[str, set[str]] = {} self.config = config - self.lib_pattern_list = [re.compile(pattern) for pattern in config.library_regex] - self.res_pattern_list = [re.compile(pattern) for pattern in config.resource_regex] + + # Validate and compile regex patterns + for pattern in config.library_regex: + self._validate_regex_pattern(pattern, "library") + for pattern in config.resource_regex: + self._validate_regex_pattern(pattern, "resource") + + self.lib_pattern_list = [ + re.compile(pattern, re.IGNORECASE) for pattern in config.library_regex + ] + self.res_pattern_list = [ + re.compile(pattern, re.IGNORECASE) for pattern in config.resource_regex + ] + for keyword in test_case_details.testSequence: self._get_keyword_call(keyword) self.rf_tags = self._get_tags(test_case_details) @@ -106,6 +118,36 @@ def __init__(self, test_case_details: TestCaseDetails, config: Configuration) -> self.teardown_keyword: Keyword | None = None # TODO description + @staticmethod + def _validate_regex_pattern(pattern: str, pattern_type: str) -> None: + """Validate that regex pattern has correct capture groups. + + Args: + pattern: The regex pattern to validate + pattern_type: Type description for error messages (e.g., "library" or "resource") + + Raises: + ValueError: If pattern doesn't meet requirements + """ + try: + compiled = re.compile(pattern, re.IGNORECASE) + except re.error as e: + raise ValueError(f"Invalid {pattern_type} regex pattern '{pattern}': {e}") + + num_groups = compiled.groups + + if num_groups == 0: + raise ValueError( + f"{pattern_type.capitalize()} regex pattern must contain at least one capture group: '{pattern}'" + ) + + if num_groups > 1: + if "resourceName" not in compiled.groupindex: + raise ValueError( + f"{pattern_type.capitalize()} regex pattern with multiple capture groups must have " + f"one named 'resourceName': '{pattern}'" + ) + @staticmethod def _get_tags(test_case_details: TestCaseDetails) -> list[str]: tags = [tag.name for tag in test_case_details.spec.tags] @@ -195,7 +237,7 @@ def _get_keyword_import(self, test_step: TBKeywordCall, keyword_path: str) -> tu for pattern in self.lib_pattern_list: match = pattern.search(keyword_path) if match: - return LIBRARY_IMPORT_TYPE, match.group("resourceName").strip() + return LIBRARY_IMPORT_TYPE, match.group(1).strip() for pattern in self.res_pattern_list: match = pattern.search(keyword_path) if match: @@ -460,6 +502,14 @@ def _create_cbr_parameters( return cbr_parameters def _get_keyword_import_prefix(self, keyword: RFKeywordCallInformation) -> str: + for resource_pattern in self.res_pattern_list: + if not keyword.import_prefix: + continue + resource_name_match = resource_pattern.search(keyword.import_prefix) + if resource_name_match: + return ( + self.config.fully_qualified or False + ) * f"{resource_name_match.group(1).strip()}." return (self.config.fully_qualified or False) * f"{keyword.import_prefix}." def _get_keyword_indent(self, keyword: RFKeywordCallInformation) -> str: @@ -694,7 +744,7 @@ def _get_resource_name(self, resource: str) -> str | None: for resource_regex in self.config.resource_regex: resource_name_match = re.search(resource_regex, resource_path_part, flags=re.IGNORECASE) if resource_name_match: - return resource_name_match.group("resourceName").strip() + return resource_name_match.group(1).strip() if resource_path_part: return resource_path_part.strip() return None From 455526e29da0d11ff5a1c7e660c2052e84cff2f1 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Tue, 7 Apr 2026 15:51:11 +0200 Subject: [PATCH 04/15] updated documentation for testbench2robotframework --- README.md | 131 +-------------- docs/configuration/_category_.json | 4 + docs/configuration/cli_options.md | 113 +++++++++++++ docs/configuration/overview.md | 76 +++++++++ docs/configuration/pyproject_config.md | 156 ++++++++++++++++++ docs/getting_started/_category_.json | 4 + docs/getting_started/installation.md | 31 ++++ docs/getting_started/quick_start.md | 54 ++++++ docs/intro.md | 36 ++++ docs/usage/_category_.json | 4 + docs/usage/fetch_results.md | 77 +++++++++ docs/usage/generate_tests.md | 77 +++++++++ .../configurations/invalid_config.json | 46 ------ .../configurations/valid_config.json | 45 ----- tests/test_missing_files.py | 9 - ...s_should_not_contain_invalid_characters.py | 11 -- tests/test_zip_file_generation.py | 29 ---- 17 files changed, 641 insertions(+), 262 deletions(-) create mode 100644 docs/configuration/_category_.json create mode 100644 docs/configuration/cli_options.md create mode 100644 docs/configuration/overview.md create mode 100644 docs/configuration/pyproject_config.md create mode 100644 docs/getting_started/_category_.json create mode 100644 docs/getting_started/installation.md create mode 100644 docs/getting_started/quick_start.md create mode 100644 docs/intro.md create mode 100644 docs/usage/_category_.json create mode 100644 docs/usage/fetch_results.md create mode 100644 docs/usage/generate_tests.md delete mode 100644 tests/test_data/configurations/invalid_config.json delete mode 100644 tests/test_data/configurations/valid_config.json delete mode 100644 tests/test_missing_files.py delete mode 100644 tests/test_robot_files_should_not_contain_invalid_characters.py delete mode 100644 tests/test_zip_file_generation.py diff --git a/README.md b/README.md index 26c53d1..81396b9 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,18 @@ # TestBench2RobotFramework -**TestBench2RobotFramework** is a CLI tool used to convert a TestBench JSON report into Robot Framework test suites and to write the execution results provided by Robot Framework to the TestBench report. +testbench2robotframework is a CLI tool to convert a TestBench JSON report into Robot Framework test suites and to write the execution results provided by Robot Framework back to the TestBench report. -## Installation +This can be used for automated test execution with Robot Framework, triggered by a CI/CD pipeline. -You can install TestBench2RobotFramework via pip using the following command: +## Documentation -```powershell -pip install testbench2robotframework -``` +The documentation is available in the [docs](./docs) folder of this repository. -Python 3.10 or higher is required to run this tool. +## Releases -## Remark -TestBench2RobotFramework requires TestBench version >= 4. If you're running an older version please contact the TestBench support for information on how to connect your Version of TestBench to Robot Framework. The TestBench Report can be either be exported via the TestBench Rest API with tools like the testbench-cli-reporter or directly from the client. +Releases are published on GitHub and available via [PyPI](https://pypi.org/project/testbench2robotframework/). -## Usage +## Requirements -TestBench2RobotFramework supports two main use cases, which are described in more detail in the following sections: - -1. Generating Robot Framework test suites from a TestBench report. -2. Fetching results from a Robot Framework output XML file and saving them back to a TestBench report. - -### Generating Robot Framework Test Suites -To generate Robot Framework test suites, use the `generate-tests` subcommand: - -```powershell -testbench2robotframework generate-tests TESTBENCH_REPORT -``` - -This command generates a Robot Framework test suite for each test case set specified in the `TESTBENCH_REPORT`. - -![](./images/testthemen.PNG) -![](./images/generated.PNG) - -The example above demonstrates how Robot Framework test suites are generated based on the *Test Theme Tree* defined in TestBench. - - - -#### Configuration - -There are multiple configuration options available for **TestBench2RobotFramework** that can be used to customize the generated test suites. Options can be specified either via the command line, in a `pyproject.toml` file or in a `robot.toml` file. - -To use options via the command line, the following syntax is used: - -```powershell -testbench2robotframework generate-tests [OPTIONS] TESTBENCH_REPORT -``` - -| Option | Description | -|--------|-------------| -| `-c`, `--config PATH` | Path to a configuration file for TestBench2RobotFramework. | -| `--clean` | Deletes all files present in the output-directory before new test suites are created. | -| `-d`, `--output-directory PATH` | Directory or ZIP archive containing the generated test suites. | -| `--compound-keyword-logging` | Mode for logging compound keywords. Options: `GROUP`, `COMMENT`, or `NONE`. | -| `--fully-qualified` | Calls Robot Framework keywords by their fully qualified names in the generated test suites. | -| `--log-suite-numbering` | Enables logging of the test suite numbering. | -| `--metadata` | Add extra metadata to the settings of the generated Robot Framework test suite. Provide entries as key:value pairs, where *key* is the metadata name and *value* is the corresponding value. Values may also be Python expressions. The special variable '$tcs' gives access to the TestBench Python model of the test case set. | -| `--resource-directory PATH` | Directory containing the Robot Framework resource files. | -| `--resource-directory-regex TEXT` | Regex that can be used to identify the TestBench Subdivision that corresponds to the . Resources will be imported relative to this subdivision based on the test elements structure in TestBench. | -| `--library-regex TEXT` | Regular expression used to identify TestBench subdivisions corresponding to Robot Framework libraries. | -| `--library-root TEXT` | TestBench root subdivision whose direct children correspond to Robot Framework libraries. | -| `--resource-regex TEXT` | Regular expression used to identify TestBench subdivisions corresponding to Robot Framework resources. | -| `--resource-root TEXT` | TestBench root subdivision whose direct children correspond to Robot Framework resources. | -| `--library-mapping TEXT` | Library import statement to use when a keyword from the specified TestBench subdivision is encountered. | -| `--resource-mapping TEXT` | Resource import statement to use when a keyword from the specified TestBench subdivision is encountered. | -| `--help` | Displays the help message and exits. | -| `--version` | Writes the TestBench2RobotFramework, Robot Framework and Python version to console. | - - -### Saving Robot Framework Results - -Saving the results requires a Robot Framework output XML file, along with the original TestBench report from which the test suites were generated. - -Use the following command: - -```powershell -testbench2robotframework fetch-results [OPTIONS] ROBOT_RESULT TESTBENCH_REPORT -``` - -| Option | Description | -|--------|-------------| -| `-c`, `--config PATH` | Path to a configuration file for TestBench2RobotFramework. | -| `-d`, `--output-directory PATH` | Path to the directory or ZIP file where the updated TestBench JSON report (with results) should be saved. | -| `--help` | Displays the help message and exits. | - - - -### Using pyproject.toml -All CLI options available for ``testbench2robotframework`` can also be defined in your ``pyproject.toml`` file, ``robot.toml``, or a workspace-local ``.robot.toml``. This offers a convenient way to store and reuse configuration settings, particularly in larger projects or automated environments. - -#### Example -```toml -[tool.testbench2robotframework] -library-regex = ['(?:.*\.)?(?P[^.]+?)\s*\[Robot-Library\].*'] -resource-regex = ['(?:.*\.)?(?P[^.]+?)\s*\[Robot-Resource\].*'] -library-root = ["RF", "RF-Library"] -resource-root = ["RF-Resource"] -fully-qualified = false -output-directory = "{root}/Generated" -log-suite-numbering = false -clean = true -compound-keyword-logging = GROUP -resource-directory = "{root}/Resources" -resource-directory-regex = ".*\\[Robot-Resources\\].*" -reference-behaviour = "ATTACHMENT" -attachment-conflict-behaviour = "USE_EXISTING" - -[tool.testbench2robotframework.library-mapping] -SeleniumLibrary = "SeleniumLibrary timeout=10 implicit_wait=1 run_on_failure=Capture Page Screenshot" -SuperRemoteLibrary = "Remote http://127.0.0.1:8270 WITH NAME SuperRemoteLibrary" - -[tool.testbench2robotframework.resource-mapping] -MyKeywords = "{root}/../MyKeywords.resource" -MyOtherKeywords = "{resourceDirectory}/subdir/MyOtherKeywords.resource" - -[tool.testbench2robotframework.forced-import] -libraries = ["test.py"] -resources = [] -variables = [] - -[tool.testbench2robotframework.console-logging] -logLevel = "INFO" -logFormat = "%(levelname)s: %(message)s" - -[tool.testbench2robotframework.file-logging] -logLevel = "DEBUG" -logFormat = "%(asctime)s - %(filename)s:%(lineno)d - %(levelname)8s - %(message)s" -fileName = "testbench2robotframework.log" -``` +- Python 3.10 or higher +- TestBench version >= 4 diff --git a/docs/configuration/_category_.json b/docs/configuration/_category_.json new file mode 100644 index 0000000..6959ebd --- /dev/null +++ b/docs/configuration/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Configuration", + "position": 4 +} \ No newline at end of file diff --git a/docs/configuration/cli_options.md b/docs/configuration/cli_options.md new file mode 100644 index 0000000..db2ed30 --- /dev/null +++ b/docs/configuration/cli_options.md @@ -0,0 +1,113 @@ +--- +sidebar_position: 2 +--- + +# CLI Options Reference + +Complete reference for all command-line options available in TestBench2RobotFramework. + +## Global Options + +These options are available for all commands: + +| Option | Description | +|--------|-------------| +| `--help` | Displays the help message and exits. | +| `--version` | Writes the TestBench2RobotFramework, Robot Framework and Python version to console. | +| `-c`, `--config PATH` | Path to a configuration file for TestBench2RobotFramework. | + +## generate-tests Options + +Options specific to the `generate-tests` subcommand: + +### Output Options + +| Option | Type | Description | +|--------|------|-------------| +| `-d`, `--output-directory PATH` | Path | Directory or ZIP archive containing the generated test suites. | +| `--clean` | Flag | Deletes all files present in the output-directory before new test suites are created. | + +### Keyword & Logging Options + +| Option | Type | Description | +|--------|------|-------------| +| `--compound-keyword-logging` | Choice | Mode for logging compound keywords. Options: `GROUP`, `COMMENT`, or `NONE`. | +| `--fully-qualified` | Flag | Calls Robot Framework keywords by their fully qualified names in the generated test suites. | +| `--log-suite-numbering` | Flag | Enables logging of the test suite numbering. | + + + +### Resource & Library Options + +| Option | Type | Description | +|--------|------|-------------| +| `--resource-directory PATH` | Path | Directory containing the Robot Framework resource files. | +| `--resource-directory-regex TEXT` | Regex | Regex that can be used to identify the TestBench Subdivision that corresponds to the resource-directory. Resources will be imported relative to this subdivision based on the test elements structure in TestBench. | +| `--library-regex TEXT` | Regex | Regular expression used to identify TestBench subdivisions corresponding to Robot Framework libraries. | +| `--library-root TEXT` | Text | TestBench root subdivision whose direct children correspond to Robot Framework libraries. | +| `--resource-regex TEXT` | Regex | Regular expression used to identify TestBench subdivisions corresponding to Robot Framework resources. | +| `--resource-root TEXT` | Text | TestBench root subdivision whose direct children correspond to Robot Framework resources. | +| `--library-mapping TEXT` | Text | Library import statement to use when a keyword from the specified TestBench subdivision is encountered. | +| `--resource-mapping TEXT` | Text | Resource import statement to use when a keyword from the specified TestBench subdivision is encountered. | + +## fetch-results Options + +Options specific to the `fetch-results` subcommand: + +| Option | Type | Description | +|--------|------|-------------| +| `-d`, `--output-directory PATH` | Path | Path to the directory or ZIP file where the updated TestBench JSON report (with results) should be saved. | + +## Usage Examples + +### Display Help + +```powershell +testbench2robotframework --help +testbench2robotframework generate-tests --help +testbench2robotframework fetch-results --help +``` + +### Check Version + +```powershell +testbench2robotframework --version +``` + +### Generate Tests with Multiple Options + +```powershell +testbench2robotframework generate-tests \ + --clean \ + -d ./Generated \ + --compound-keyword-logging GROUP \ + --fully-qualified \ + --log-suite-numbering \ + my_report.json +``` + +### Fetch Results with Custom Output + +```powershell +testbench2robotframework fetch-results \ + -d ./updated_reports \ + output.xml \ + testbench_report.json +``` + +### Using Configuration File + +```powershell +testbench2robotframework generate-tests -c config.toml my_report.json +``` + +## Option Priority + +When the same option is specified in multiple places, the priority order is: + +1. **Command-line options** (highest priority) +2. **Workspace-local `.robot.toml`** +3. **Project `robot.toml` or `pyproject.toml`** +4. **Default values** (lowest priority) + + diff --git a/docs/configuration/overview.md b/docs/configuration/overview.md new file mode 100644 index 0000000..1f1c95b --- /dev/null +++ b/docs/configuration/overview.md @@ -0,0 +1,76 @@ +--- +sidebar_position: 1 +--- + +# Configuration Overview + +TestBench2RobotFramework offers flexible configuration options to customize the behavior of test generation and result fetching. + +## Configuration Methods + +You can configure TestBench2RobotFramework using three different methods: + +### 1. Command-Line Options + +Pass options directly when running commands: + +```powershell +testbench2robotframework generate-tests --clean -d ./Generated my_report.json +``` + +✅ **Best for:** Quick, one-time runs and overriding specific settings + +### 2. Configuration Files + +Store settings in configuration files for reusable configurations: + +- `pyproject.toml` - Python project configuration +- `robot.toml` - Robot Framework specific configuration +- `.robot.toml` - Workspace-local configuration + +✅ **Best for:** Team projects, consistent settings, complex configurations + +TestBench2RobotFramework will automatically detect and apply settings from these files when present. + +### 3. Mixed Approach + +Combine both methods - command-line options override configuration file settings: + +```powershell +testbench2robotframework generate-tests -c config.toml --clean my_report.json +``` + +## Configuration Hierarchy + +When multiple configuration methods are used, settings are applied in the following order (later overrides earlier): + +1. Default values +2. `pyproject.toml` or `robot.toml` +3. `.robot.toml` (workspace-local) +4. Command-line options + +## Common Configuration Options + +### Output Settings + +- `output-directory` - Where to save generated files or results +- `clean` - Delete existing files before generating new ones +- `fully-qualified` - Enable/disable fully qualified keyword names in generated test suites +- `compound-keyword-logging` - Control compound TestBench keyword logging (`GROUP`, `COMMENT`, `NONE`) +- `log-suite-numbering` - Enable/disable suite numbering in generated file names + +### Library & Resource Mapping + +- `library-regex` - Pattern to identify TestBench subdivisions as libraries +- `resource-regex` - Pattern to identify TestBench subdivisions as resources +- `library-root` - Root subdivision for libraries +- `resource-root` - Root subdivision for resources +- `library-mapping` - Custom library import statements +- `resource-mapping` - Custom resource import statements + +### Attachment & Reference Handling + +- `reference-behaviour` - How to handle references +- `attachment-conflict-behaviour` - How to handle attachment conflicts + + diff --git a/docs/configuration/pyproject_config.md b/docs/configuration/pyproject_config.md new file mode 100644 index 0000000..92f8364 --- /dev/null +++ b/docs/configuration/pyproject_config.md @@ -0,0 +1,156 @@ +--- +sidebar_position: 3 +--- + +# pyproject.toml Configuration + +All CLI options available for `testbench2robotframework` can be defined in your `pyproject.toml` file, `robot.toml`, or a workspace-local `.robot.toml`. This offers a convenient way to store and reuse configuration settings, particularly in larger projects or automated environments. + +## Basic Structure + +Add a `[tool.testbench2robotframework]` section to your `pyproject.toml`: + +```toml +[tool.testbench2robotframework] +# Your configuration options here +``` + +## Complete Configuration Example + +Here's a comprehensive example with all available options: + +```toml +[tool.testbench2robotframework] +library-regex = ['(?:.*\.)?(?P[^.]+?)\s*\[Robot-Library\].*'] +resource-regex = ['(?:.*\.)?(?P[^.]+?)\s*\[Robot-Resource\].*'] +library-root = ["RF", "RF-Library"] +resource-root = ["RF-Resource"] +fully-qualified = false +output-directory = "{root}/Generated" +log-suite-numbering = false +clean = true +compound-keyword-logging = "GROUP" +resource-directory = "{root}/Resources" +resource-directory-regex = ".*\\[Robot-Resources\\].*" +reference-behaviour = "ATTACHMENT" +attachment-conflict-behaviour = "USE_EXISTING" + +[tool.testbench2robotframework.library-mapping] +SeleniumLibrary = "SeleniumLibrary timeout=10 implicit_wait=1 run_on_failure=Capture Page Screenshot" +SuperRemoteLibrary = "Remote http://127.0.0.1:8270 WITH NAME SuperRemoteLibrary" + +[tool.testbench2robotframework.resource-mapping] +MyKeywords = "{root}/../MyKeywords.resource" +MyOtherKeywords = "{resourceDirectory}/subdir/MyOtherKeywords.resource" + +[tool.testbench2robotframework.forced-import] +libraries = ["test.py"] +resources = [] +variables = [] + +[tool.testbench2robotframework.console-logging] +logLevel = "INFO" +logFormat = "%(levelname)s: %(message)s" + +[tool.testbench2robotframework.file-logging] +logLevel = "DEBUG" +logFormat = "%(asctime)s - %(filename)s:%(lineno)d - %(levelname)8s - %(message)s" +fileName = "testbench2robotframework.log" +``` + +## Configuration Sections + +### Main Configuration + +The main `[tool.testbench2robotframework]` section contains general settings: + +```toml +[tool.testbench2robotframework] +output-directory = "{root}/Generated" +clean = true +fully-qualified = false +log-suite-numbering = false +compound-keyword-logging = "GROUP" +``` + +### Library Mapping + +Define custom import statements for libraries: + +```toml +[tool.testbench2robotframework.library-mapping] +SeleniumLibrary = "SeleniumLibrary timeout=10 implicit_wait=1" +MyLibrary = "MyLibrary arg1=value1 arg2=value2" +``` + +### Resource Mapping + +Define custom import statements for resources: + +```toml +[tool.testbench2robotframework.resource-mapping] +MyKeywords = "{root}/../MyKeywords.resource" +CommonKeywords = "{resourceDirectory}/common/keywords.resource" +``` + +### Forced Imports + +Force specific libraries, resources, or variables to be imported: + +```toml +[tool.testbench2robotframework.forced-import] +libraries = ["BuiltIn", "Collections"] +resources = ["common.resource"] +variables = ["variables.py"] +``` + +### Console Logging + +Configure console output: + +```toml +[tool.testbench2robotframework.console-logging] +logLevel = "INFO" +logFormat = "%(levelname)s: %(message)s" +``` + +### File Logging + +Configure file-based logging: + +```toml +[tool.testbench2robotframework.file-logging] +logLevel = "DEBUG" +logFormat = "%(asctime)s - %(filename)s:%(lineno)d - %(levelname)8s - %(message)s" +fileName = "testbench2robotframework.log" +``` + +## Variable Placeholders + +You can use the following placeholders in configuration values: + +- `{root}` - Project root directory +- `{resourceDirectory}` - Configured resource directory path + +**Example:** +```toml +output-directory = "{root}/Generated" +resource-directory = "{root}/Resources" +``` + +## Using robot.toml + +Instead of `pyproject.toml`, you can use `robot.toml` with the same structure: + +```toml +[testbench2robotframework] +# Same options as in pyproject.toml, but without the "tool." prefix +output-directory = "./Generated" +clean = true +``` + +## Workspace-Local Configuration + +Create a `.robot.toml` file in your workspace for project-specific settings that override the global configuration. + + diff --git a/docs/getting_started/_category_.json b/docs/getting_started/_category_.json new file mode 100644 index 0000000..49462ce --- /dev/null +++ b/docs/getting_started/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Getting started", + "position": 2 +} \ No newline at end of file diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md new file mode 100644 index 0000000..938ed3d --- /dev/null +++ b/docs/getting_started/installation.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 1 +--- + +# Installation + +## Prerequisites + +Before installing TestBench2RobotFramework, ensure you have: + +- **Python 3.10 or higher** installed on your system +- **pip** package manager (usually comes with Python) + +## Install via pip + +You can install TestBench2RobotFramework via pip using the following command: + +```powershell +pip install testbench2robotframework +``` + +## Verify Installation + +After installation, verify that the tool is correctly installed by checking the version: + +```powershell +testbench2robotframework --version +``` + +This command will display the TestBench2RobotFramework, Robot Framework, and Python versions. + diff --git a/docs/getting_started/quick_start.md b/docs/getting_started/quick_start.md new file mode 100644 index 0000000..09fcebb --- /dev/null +++ b/docs/getting_started/quick_start.md @@ -0,0 +1,54 @@ +--- +sidebar_position: 2 +--- + +# Quick Start + +This guide will help you get started with TestBench2RobotFramework quickly. + +## Basic Workflow + +TestBench2RobotFramework supports two main operations: + +### 1. Generate Robot Framework Test Suites + +Convert a TestBench report into Robot Framework test suites: + +```powershell +testbench2robotframework generate-tests TESTBENCH_REPORT +``` + +This command generates a Robot Framework test suite for each test case set specified in the `TESTBENCH_REPORT`. + +**Example:** +```powershell +testbench2robotframework generate-tests my_testbench_report.zip +``` + +### 2. Fetch and Save Results + +After executing your Robot Framework tests, save the results back to the TestBench report: + +```powershell +testbench2robotframework fetch-results ROBOT_RESULT TESTBENCH_REPORT +``` + +**Example:** +```powershell +testbench2robotframework fetch-results output.xml my_testbench_report.zip +``` + + +## Common Options + +Here are some frequently used options: + +- `-d, --output-directory PATH` - Specify where to save generated files +- `--clean` - Delete existing files before generating new ones +- `-c, --config PATH` - Use a configuration file + +**Example with options:** +```powershell +testbench2robotframework generate-tests -d ./Generated --clean testbench_report.zip +``` + diff --git a/docs/intro.md b/docs/intro.md new file mode 100644 index 0000000..f16d903 --- /dev/null +++ b/docs/intro.md @@ -0,0 +1,36 @@ +--- +sidebar_position: 1 +--- + +# TestBench2RobotFramework + +**TestBench2RobotFramework** is a CLI tool used to convert a TestBench JSON report into Robot Framework test suites and to write the execution results provided by Robot Framework to the TestBench report. + +## Overview + +This tool supports two main use cases: + +1. **Generating Robot Framework test suites** from a TestBench report +2. **Fetching results** from a Robot Framework output XML file and saving them back to a TestBench report + +## Key Features + +- ✅ Convert TestBench reports to Robot Framework test suites +- ✅ Synchronization of Robot Framework test results +- ✅ Flexible configuration via CLI, `pyproject.toml`, or `robot.toml` +- ✅ Support for custom libraries and resources + +## Requirements + +- **Python 3.10 or higher** +- **TestBench version >= 4** + +:::info +If you're running an older version of TestBench, please contact the TestBench support for information on how to connect your version of TestBench to Robot Framework. +::: + +## TestBench Report + +The TestBench Report can be either exported via the TestBench Rest API with tools like the testbench-cli-reporter or directly from the client. + + diff --git a/docs/usage/_category_.json b/docs/usage/_category_.json new file mode 100644 index 0000000..09dee0a --- /dev/null +++ b/docs/usage/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Usage", + "position": 3 +} \ No newline at end of file diff --git a/docs/usage/fetch_results.md b/docs/usage/fetch_results.md new file mode 100644 index 0000000..d339dff --- /dev/null +++ b/docs/usage/fetch_results.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 2 +--- + +# Fetching Results + +Learn how to save Robot Framework execution results back to TestBench reports. + +## Overview + +After executing your Robot Framework tests, you can write the results back to the TestBench report. This requires: + +1. A Robot Framework output XML file (typically `output.xml`) +2. The original TestBench report from which the test suites were generated + +## Basic Command + +Use the `fetch-results` subcommand: + +```powershell +testbench2robotframework fetch-results ROBOT_RESULT TESTBENCH_REPORT +``` + +## Command-Line Options + +The `fetch-results` command supports the following options: + +| Option | Description | +|--------|-------------| +| `-c`, `--config PATH` | Path to a configuration file for TestBench2RobotFramework. | +| `-d`, `--output-directory PATH` | Path to the directory or ZIP file where the updated TestBench JSON report (with results) should be saved. | +| `--help` | Displays the help message and exits. | + +## Examples + +### Basic Result Fetch + +```powershell +testbench2robotframework fetch-results output.xml my_testbench_report.zip +``` + +### With Custom Output Directory + +```powershell +testbench2robotframework fetch-results -d ./results output.xml my_testbench_report.zip +``` + +### With Configuration File + +```powershell +testbench2robotframework fetch-results -c config.toml output.xml my_testbench_report.zip +``` + +## Workflow Example + +Here's a complete workflow from test generation to result fetching: + +```powershell +# 1. Generate test suites from TestBench report +testbench2robotframework generate-tests -d ./Generated testbench_report.zip + +# 2. Execute the generated Robot Framework tests +robot --outputdir ./results ./Generated + +# 3. Save the results back to TestBench +testbench2robotframework fetch-results -d ./updated_report ./results/output.xml testbench_report.zip +``` + +## Result Synchronization + +The tool automatically maps the Robot Framework test results to the corresponding TestBench test cases based on the test structure. This includes: + +- ✅ Test execution status (PASS/FAIL) +- ✅ Execution timestamps +- ✅ Error messages and logs + + diff --git a/docs/usage/generate_tests.md b/docs/usage/generate_tests.md new file mode 100644 index 0000000..62ed8d1 --- /dev/null +++ b/docs/usage/generate_tests.md @@ -0,0 +1,77 @@ +--- +sidebar_position: 1 +--- + +# Generating Tests + +Learn how to generate Robot Framework test suites from TestBench reports. + +## Basic Command + +To generate Robot Framework test suites, use the `generate-tests` subcommand: + +```powershell +testbench2robotframework generate-tests TESTBENCH_REPORT +``` + +This command generates a Robot Framework test suite for each test case set specified in the `TESTBENCH_REPORT`. + +## Command-Line Options + +The `generate-tests` command supports the following options: + +| Option | Description | +|--------|-------------| +| `-c`, `--config PATH` | Path to a configuration file for TestBench2RobotFramework. | +| `--clean` | Deletes all files present in the output-directory before new test suites are created. | +| `-d`, `--output-directory PATH` | Directory or ZIP archive containing the generated test suites. | +| `--compound-keyword-logging` | Mode for logging compound keywords. Options: `GROUP`, `COMMENT`, or `NONE`. | +| `--fully-qualified` | Calls Robot Framework keywords by their fully qualified names in the generated test suites. | +| `--log-suite-numbering` | Enables logging of the test suite numbering. | +| `--resource-directory PATH` | Directory containing the Robot Framework resource files. | +| `--resource-directory-regex TEXT` | Regex that can be used to identify the TestBench Subdivision that corresponds to the resource-directory. | +| `--library-regex TEXT` | Regular expression used to identify TestBench subdivisions corresponding to Robot Framework libraries. | +| `--library-root TEXT` | TestBench root subdivision whose direct children correspond to Robot Framework libraries. | +| `--resource-regex TEXT` | Regular expression used to identify TestBench subdivisions corresponding to Robot Framework resources. | +| `--resource-root TEXT` | TestBench root subdivision whose direct children correspond to Robot Framework resources. | +| `--library-mapping TEXT` | Library import statement to use when a keyword from the specified TestBench subdivision is encountered. | +| `--resource-mapping TEXT` | Resource import statement to use when a keyword from the specified TestBench subdivision is encountered. | +| `--help` | Displays the help message and exits. | +| `--version` | Writes the TestBench2RobotFramework, Robot Framework and Python version to console. | + +## Examples + +### Basic Test Generation + +```powershell +testbench2robotframework generate-tests my_report.zip +``` + +### With Custom Output Directory + +```powershell +testbench2robotframework generate-tests -d ./Generated my_report.zip +``` + +### Clean Build + +```powershell +testbench2robotframework generate-tests --clean -d ./Generated my_report.zip +``` + +### With Configuration File + +```powershell +testbench2robotframework generate-tests -c config.toml my_report.zip +``` + + +## Resource and Library Mapping + +TestBench2RobotFramework provides flexible options for mapping TestBench subdivisions to Robot Framework libraries and resources: + +- Use `--library-regex` and `--resource-regex` to identify subdivisions with regular expressions +- Use `--library-root` and `--resource-root` to specify root subdivisions +- Use `--library-mapping` and `--resource-mapping` to define custom import statements + +For detailed configuration examples, see the [Configuration Guide](../configuration/pyproject_config.md). diff --git a/tests/test_data/configurations/invalid_config.json b/tests/test_data/configurations/invalid_config.json deleted file mode 100644 index ab6343d..0000000 --- a/tests/test_data/configurations/invalid_config.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "rfLibraryRoots": [ - "Interactions", - "RF-Library" - ], - "rfResourceRoots": [ - "RF-Resource" - ], - "testbalala": true, - "fullyQualified": true, - "generationDirectory": "{root}/Generated", - "createOutputZip": true, - "resourceDirectory": "{root}/Resources", - "clearGenerationDirectory": true, - "logSuiteNumbering": true, - "logCompoundInteractions": true, - "subdivisionsMapping": { - "libraries": { - "SeleniumLibrary": "SeleniumLibrary timeout=10 implicit_wait=1 run_on_failure=Capture Page Screenshot", - "SuperRemoteLibrary": "Remote http://127.0.0.1:8270 WITH NAME SuperRemoteLibrary" - }, - "resources": { - "MyKeywords": "{root}/../MyKeywords.resource", - "MyOtherKeywords": "{resourceDirectory}/subdir/MyOtherKeywords.resource" - } - }, - "forcedImport": { - "libraries": [ - "SuperRemoteLibrary", - "SeleniumLibrary" - ], - "resources": - [ - "technical_keywords.resource" - ], - "variables": [ - "myVars.py" - ] - }, - "testCaseSplitPathRegEx": "^splitting\\..*", - "loggingConfiguration": { - "console": { - "logLevel": "info" - } - } -} diff --git a/tests/test_data/configurations/valid_config.json b/tests/test_data/configurations/valid_config.json deleted file mode 100644 index 589e5c2..0000000 --- a/tests/test_data/configurations/valid_config.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "rfLibraryRoots": [ - "Interactions", - "RF-Library" - ], - "rfResourceRoots": [ - "RF-Resource" - ], - "fullyQualified": true, - "generationDirectory": "{root}/Generated", - "createOutputZip": true, - "resourceDirectory": "{root}/Resources", - "clearGenerationDirectory": true, - "logSuiteNumbering": true, - "logCompoundInteractions": true, - "subdivisionsMapping": { - "libraries": { - "SeleniumLibrary": "SeleniumLibrary timeout=10 implicit_wait=1 run_on_failure=Capture Page Screenshot", - "SuperRemoteLibrary": "Remote http://127.0.0.1:8270 WITH NAME SuperRemoteLibrary" - }, - "resources": { - "MyKeywords": "{root}/../MyKeywords.resource", - "MyOtherKeywords": "{resourceDirectory}/subdir/MyOtherKeywords.resource" - } - }, - "forcedImport": { - "libraries": [ - "SuperRemoteLibrary", - "SeleniumLibrary" - ], - "resources": - [ - "technical_keywords.resource" - ], - "variables": [ - "myVars.py" - ] - }, - "testCaseSplitPathRegEx": "^splitting\\..*", - "loggingConfiguration": { - "console": { - "logLevel": "info" - } - } -} diff --git a/tests/test_missing_files.py b/tests/test_missing_files.py deleted file mode 100644 index 12ae83f..0000000 --- a/tests/test_missing_files.py +++ /dev/null @@ -1,9 +0,0 @@ -import pytest - -from testbench2robotframework.utils import get_directory - - -def test_json_dir_does_not_exist(): - json_files_path = "invalid/file/path" - with pytest.raises(SystemExit): - get_directory(json_files_path) diff --git a/tests/test_robot_files_should_not_contain_invalid_characters.py b/tests/test_robot_files_should_not_contain_invalid_characters.py deleted file mode 100644 index a15b687..0000000 --- a/tests/test_robot_files_should_not_contain_invalid_characters.py +++ /dev/null @@ -1,11 +0,0 @@ -import pytest - -from testbench2robotframework.utils import replace_invalid_characters - -invalid_chars = ['<', '>', ':', '"', '/', '\\', '|', '?', '*'] - - -@pytest.mark.parametrize("invalid_char", invalid_chars) -def test_filename_does_not_contain_invalid_char(invalid_char): - filename = f"1.2.1 Testsuite with {invalid_char} in the name.robot" - assert replace_invalid_characters(filename).find(invalid_char) == -1 diff --git a/tests/test_zip_file_generation.py b/tests/test_zip_file_generation.py deleted file mode 100644 index be62808..0000000 --- a/tests/test_zip_file_generation.py +++ /dev/null @@ -1,29 +0,0 @@ -import os -import shutil -from pathlib import Path - -import pytest -from robot.parsing.model.blocks import File - -from testbench2robotframework.testsuite_write import ( - clear_generation_directory, - directory_to_zip, - write_test_suite_files, -) - - -def test_previous_zip_gets_deleted(): - generation_dir = Path("./tests/test_data/Generated") - if not os.path.isdir("./tests/test_data/Generated"): - os.mkdir("./tests/test_data/Generated") - shutil.make_archive(str(generation_dir), 'zip', str(generation_dir)) - clear_generation_directory(generation_dir) - assert not os.path.exists(generation_dir) - - -def test_zip_gets_created(): - test_suites = {"iTB-TC-322-PC-12121212212": File()} - generation_dir = Path("./tests/test_data/Generated/zip_file") - write_test_suite_files(test_suites, generation_dir) - directory_to_zip(generation_dir) - assert os.path.exists("".join([str(generation_dir), ".zip"])) From 54b3516d1363ba8277abc68f06e99aa5ac245349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81?= Date: Mon, 18 May 2026 18:29:25 +0200 Subject: [PATCH 05/15] new model according to TB 4.0 --- testbench2robotframework/model.py | 3831 +++++++++++++++-------------- 1 file changed, 1922 insertions(+), 1909 deletions(-) diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index 68667cf..b71996c 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -1,1909 +1,1922 @@ -# generated by datamodel-codegen: -# filename: openapi.yml -# timestamp: 2026-03-31T06:28:07+00:00 - -from __future__ import annotations - -from dataclasses import dataclass -from enum import Enum -from typing import List, Optional, Union - - -class TestElementStatus(Enum): - InProgress = 'InProgress' - Released = 'Released' - - -class ContentType(Enum): - txt = 'txt' - xml = 'xml' - - -@dataclass -class AdvancedContent: - contentType: Optional[ContentType] = None - contentExtID: Optional[str] = None - content: Optional[str] = None - - -@dataclass -class OptionalAdvancedContent: - optional: Optional[AdvancedContent] = None - - -@dataclass -class OptionalUser: - optional: Optional[str] = None - - -class ProjectStatus(Enum): - Planned = 'Planned' - Active = 'Active' - Finished = 'Finished' - Closed = 'Closed' - - -class DefectMetricType(Enum): - Status = 'Status' - Priority = 'Priority' - Classification = 'Classification' - - -class ProjectTreeNodeType(Enum): - Project = 'Project' - Version = 'Version' - Cycle = 'Cycle' - - -class Severity(Enum): - Information = 'Information' - Warning = 'Warning' - Error = 'Error' - - -class UDFType(Enum): - String = 'String' - Enumeration = 'Enumeration' - Boolean = 'Boolean' - - -class TestLabelVisibilityType(Enum): - OnlyPrivate = 'OnlyPrivate' - OnlyPublic = 'OnlyPublic' - All = 'All' - - -class AutStatus(Enum): - NotPlanned = 'NotPlanned' - Planned = 'Planned' - InProgress = 'InProgress' - InReview = 'InReview' - Released = 'Released' - - -class SpecStatus(Enum): - NotPlanned = 'NotPlanned' - Planned = 'Planned' - InProgress = 'InProgress' - InReview = 'InReview' - Released = 'Released' - - -class Priority(Enum): - Undefined = 'Undefined' - Low = 'Low' - Middle = 'Middle' - High = 'High' - - -class ActivityStatus(Enum): - NotPlanned = 'NotPlanned' - Planned = 'Planned' - Assigned = 'Assigned' - Running = 'Running' - Skipped = 'Skipped' - Canceled = 'Canceled' - Performed = 'Performed' - - -class ExecStatus(Enum): - NotBlocked = 'NotBlocked' - Blocked = 'Blocked' - - -class VerdictStatus(Enum): - Undefined = 'Undefined' - ToVerify = 'ToVerify' - Fail = 'Fail' - Pass = 'Pass' - - -class KeywordVerdict(Enum): - Pass = 'Pass' - Fail = 'Fail' - Skipped = 'Skipped' - ToVerify = 'ToVerify' - Warn = 'Warn' - Undefined = 'Undefined' - Blocked = 'Blocked' - - -class SequencePhase(Enum): - Setup = 'Setup' - TestStep = 'TestStep' - Teardown = 'Teardown' - - -class KeywordCallType(Enum): - Flow = 'Flow' - Check = 'Check' - - -class KeywordType(Enum): - Atomic = 'Atomic' - Compound = 'Compound' - Textual = 'Textual' - - -class OperationalState(Enum): - Enabled = 'Enabled' - Disabled = 'Disabled' - - -class GlobalHumanRole(Enum): - Administrator = 'Administrator' - ProjectAdministrator = 'ProjectAdministrator' - ProjectUser = 'ProjectUser' - - -class ProjectRole(Enum): - TestManager = 'TestManager' - TestDesigner = 'TestDesigner' - TestProgrammer = 'TestProgrammer' - Tester = 'Tester' - ReadOnlyDesigner = 'ReadOnlyDesigner' - ReadOnlyImplementer = 'ReadOnlyImplementer' - ReadOnlyTester = 'ReadOnlyTester' - - -class TOVExchangeFormat(Enum): - xml = 'xml' - json = 'json' - inherited = 'inherited' - - -class ProjectExchangeFormat(Enum): - default_xml = 'default_xml' - default_json = 'default_json' - - -class ImportResult(Enum): - Imported = 'Imported' - PartiallyImported = 'PartiallyImported' - NotImported = 'NotImported' - - -class ExecutionMode(Enum): - execute = 'execute' - continue_ = 'continue' - view = 'view' - simulate = 'simulate' - - -@dataclass -class UserGlobalRoles: - roles: List[GlobalHumanRole] - - -@dataclass -class UserProjectRoles: - userKey: str - roles: List[ProjectRole] - - -@dataclass -class ProjectUser: - key: str - name: str - login: str - projectRoles: List[ProjectRole] - - -@dataclass -class LicenseWarning: - expiresSoon: bool - baseCountExceeded: bool - gracedForeignLicense: bool - - -@dataclass -class LoginData: - login: str - password: str - force: Optional[bool] = None - context: Optional[str] = None - - -@dataclass -class ChangePasswordData: - login: str - password: str - newPassword: str - - -@dataclass -class LoginSession: - userKey: str - login: str - sessionToken: str - globalRoles: List[str] - internalUserManagement: bool - serverVersion: str - licenseWarning: Optional[LicenseWarning] = None - - -@dataclass -class ActiveUser: - login: str - contexts: List[str] - - -@dataclass -class TestLabel: - key: str - name: str - ownerKey: str - visibility: bool - libraryKey: Optional[str] = None - - -@dataclass -class TestLabelDataForUpdate: - name: Optional[str] = None - visibility: Optional[bool] = None - - -@dataclass -class TestLabelDataForInsert: - name: str - adaptName: Optional[bool] = True - - -@dataclass -class ProjectMember: - userKey: str - userLogin: str - userName: str - projectKey: str - projectName: str - roles: List[ProjectRole] - - -@dataclass -class UserInfo: - userKey: str - userLogin: str - userName: str - - -@dataclass -class ProjectSummary: - key: str - creationTime: str - name: str - status: ProjectStatus - visibility: bool - tovsCount: int - cyclesCount: int - description: str - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - - -@dataclass -class ProjectContext: - tovName: str - cycleName: Optional[str] = None - executionMode: Optional[ExecutionMode] = None - - -@dataclass -class ProjectDetails: - key: str - creationTime: str - name: str - status: ProjectStatus - visibility: bool - tovsCount: int - cyclesCount: int - description: str - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - projectContext: Optional[ProjectContext] = None - - -@dataclass -class ProjectInspection: - inspectionKey: str - severity: Severity - - -@dataclass -class ProjectCustomer: - customerName: str - customerAddress: str - testLab: str - placeOfInspection: str - contactPerson: str - - -@dataclass -class ProjectCreation: - pass - - -@dataclass -class UnknownProject(ProjectCreation): - pass - - -@dataclass -class NewProject(ProjectCreation): - pass - - -@dataclass -class ProjectImported(ProjectCreation): - project: str - - -@dataclass -class TOVCreation: - pass - - -@dataclass -class UnknownTOV(TOVCreation): - pass - - -@dataclass -class NewTOV(TOVCreation): - pass - - -@dataclass -class TOVCloned(TOVCreation): - tov: str - tovKey: Optional[str] = None - - -@dataclass -class TOVClonedFromSameProject(TOVCreation): - tov: str - tovKey: Optional[str] = None - - -@dataclass -class TOVClonedFromDifferentProject(TOVCreation): - project: str - tov: str - tovKey: Optional[str] = None - - -@dataclass -class TOVImportedAsNew(TOVCreation): - project: str - tov: str - - -@dataclass -class TOVImportedAsClone(TOVCreation): - project: str - tov: str - tovCloned: str - - -@dataclass -class TOVImportedAsNewFromPlugin(TOVCreation): - plugin: str - tov: str - - -@dataclass -class TOVImportedAsCloneFromPlugin(TOVCreation): - plugin: str - tov: str - tovCloned: str - - -@dataclass -class TOVImported(TOVCreation): - project: str - tov: str - - -@dataclass -class TOVDerivedFromSameProject(TOVCreation): - baseTOV: str - variantsDefinition: str - baseTOVKey: Optional[str] = None - variantsDefinitionKey: Optional[str] = None - - -@dataclass -class TOVDerivedFromDifferentProject(TOVCreation): - project: str - baseTOV: str - variantsDefinition: str - baseTOVKey: Optional[str] = None - variantsDefinitionKey: Optional[str] = None - - -@dataclass -class ProjectDetailsResponse: - projectKey: str - name: str - inspection: List[ProjectInspection] - testObjectName: str - id: str - customer: ProjectCustomer - status: ProjectStatus - visible: bool - testIntelligence: bool - description: str - creation: ProjectCreation - instantOfCreation: str - onlyAdminsMayManageUDFs: bool - variantsManagementEnabled: str - startDate: Optional[str] = None - endDate: Optional[str] = None - - -@dataclass -class TOVSummary: - key: str - creationTime: str - name: str - status: ProjectStatus - visibility: bool - cyclesCount: int - description: str - exchangeFormat: TOVExchangeFormat - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - - -@dataclass -class TOVDetails: - key: str - creationTime: str - name: str - status: ProjectStatus - visibility: bool - cyclesCount: int - description: str - exchangeFormat: TOVExchangeFormat - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - - -@dataclass -class TOVResponse: - name: str - visible: bool - projectKey: str - description: str - isBaseTov: bool - creation: TOVCreation - key: str - status: ProjectStatus - testingIntelligence: bool - instantOfCreation: str - cloningVisibility: bool - endDate: Optional[str] = None - startDate: Optional[str] = None - - -@dataclass -class OptionalLocalDate: - optional: Optional[str] = None - - -@dataclass -class CycleSummary: - key: str - creationTime: str - name: str - status: ProjectStatus - visibility: bool - description: str - startDate: Optional[str] = None - endDate: Optional[str] = None - - -@dataclass -class CycleDetails: - key: str - creationTime: str - name: str - status: ProjectStatus - visibility: bool - description: str - startDate: Optional[str] = None - endDate: Optional[str] = None - - -@dataclass -class CycleNode: - nodeType: ProjectTreeNodeType - key: str - name: str - creationTime: str - status: ProjectStatus - visibility: bool - - -@dataclass -class UserDetails: - key: str - login: str - name: str - email: str - passwordExpired: bool - active: bool - - -@dataclass -class UserDataForInsert: - login: str - password: str - name: str - email: str - - -@dataclass -class UserDataForUpdate: - login: Optional[str] = None - name: Optional[str] = None - email: Optional[str] = None - passwordExpired: Optional[bool] = None - active: Optional[bool] = None - - -@dataclass -class UserSummary: - key: str - login: str - name: str - active: bool - - -@dataclass -class TestBenchVersions: - version: str - databaseVersion: str - revision: str - - -@dataclass -class ServerLocations: - jBossHost: str - jBossJNDIPort: int - legacyPlayHost: str - legacyPlayPort: int - - -@dataclass -class ActionFailure: - code: int - message: str - description: Optional[str] = None - - -@dataclass -class UserDefinedField: - key: str - name: str - value: str - udfType: UDFType - - -@dataclass -class UDFEnumerationValue: - valueKey: str - valueName: str - - -@dataclass -class Tag: - key: str - name: str - isVariantsMarker: bool - - -@dataclass -class UserReference: - key: str - name: str - - -@dataclass -class RequirementAssignment: - key: str - name: str - id: str - extendedId: str - version: str - owner: str - status: str - priority: str - repositoryId: str - - -@dataclass -class ReportItemsSummary: - testThemesCount: int - testCaseSetsCount: int - testCasesCount: int - - -@dataclass -class ServerLocation: - host: str - port: int - - -@dataclass -class ReportExportOptions: - pass - - -@dataclass -class ReportScope: - projectKey: str - tovKey: str - cycleKey: Optional[str] = None - - -@dataclass -class ReportCreation: - creator: UserInfo - startDate: str - endDate: str - scope: ReportScope - exportOptions: ReportExportOptions - summary: ReportItemsSummary - - -@dataclass -class ReportMetaInformation: - formatVersion: str - serverLocation: ServerLocation - reportCreation: ReportCreation - serverVersions: TestBenchVersions - - -@dataclass -class RequirementReference: - key: str - edited: bool - - -@dataclass -class MetricsDistribution: - name: str - count: int - percentage: int - - -@dataclass -class DefectsDistribution: - statusDistribution: Optional[List[MetricsDistribution]] = None - priorityDistribution: Optional[List[MetricsDistribution]] = None - classDistribution: Optional[List[MetricsDistribution]] = None - - -@dataclass -class ConditionSummary: - key: str - uniqueID: str - name: str - description: str - version: Optional[str] = None - - -@dataclass -class SpecificationSummary: - key: str - description: str - reviewComment: str - status: SpecStatus - priority: Priority - locker: Optional[UserReference] = None - responsible: Optional[UserReference] = None - dueDate: Optional[str] = None - reviewer: Optional[UserReference] = None - - -@dataclass -class TestCaseSetSpecificationSummary: - key: str - description: str - reviewComment: str - status: SpecStatus - priority: Priority - preConditions: List[ConditionSummary] - postConditions: List[ConditionSummary] - udfs: List[UserDefinedField] - tags: List[Tag] - references: List[str] - requirements: List[RequirementReference] - responsible: Optional[UserReference] = None - dueDate: Optional[str] = None - reviewer: Optional[UserReference] = None - - -@dataclass -class TestCaseSpecificationDetails: - key: str - comments: str - udfs: List[UserDefinedField] - tags: List[Tag] - requirements: List[RequirementReference] - version: Optional[str] = None - - -@dataclass -class TestThemeSpecification: - key: str - description: str - reviewComment: str - status: SpecStatus - priority: Priority - udfs: List[UserDefinedField] - tags: List[Tag] - requirements: List[RequirementReference] - references: List[str] - dueDate: Optional[str] = None - reviewer: Optional[UserReference] = None - responsible: Optional[UserReference] = None - - -@dataclass -class TestCaseSetExecutionSummary: - key: str - comments: str - udfs: List[UserDefinedField] - tags: List[Tag] - - -@dataclass -class TestCaseSpecificationSummary: - key: str - comments: str - requirements: List[RequirementReference] - - -@dataclass -class TestCaseExecutionSummary: - key: str - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - defects: List[str] - comments: str - tester: Optional[UserReference] = None - - -@dataclass -class TestCaseSummary: - uniqueID: str - index: int - spec: TestCaseSpecificationSummary - exec: Optional[TestCaseExecutionSummary] = None - - -@dataclass -class TestCaseExecutionDetails: - key: str - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - plannedDuration: int - actualDuration: int - currentUser: UserReference - comments: str - defects: List[str] - udfs: List[UserDefinedField] - tags: List[Tag] - references: List[str] - version: Optional[str] = None - tester: Optional[UserReference] = None - - -class KindOfDataType(Enum): - Regular = 'Regular' - Reference = 'Reference' - Global = 'Global' - AcceptingGlobal = 'AcceptingGlobal' - - -@dataclass -class DataTypeSummary: - key: str - kind: KindOfDataType - name: str - path: str - uniqueID: str - version: Optional[str] = None - - -class ParameterDefinitionType(Enum): - DetailedInstance = 'DetailedInstance' - InstanceTable = 'InstanceTable' - AtomicInstance = 'AtomicInstance' - - -@dataclass -class ParameterValue: - name: str - key: str - dtSequenceKeys: List[str] - - -@dataclass -class InstanceArrayValue: - name: str - isDefaultValue: bool - - -class ParameterEvaluationType(Enum): - CallByValue = 'CallByValue' - CallByReference = 'CallByReference' - CallByReferenceMandatory = 'CallByReferenceMandatory' - - -class RepresentativeType(Enum): - Text = 'Text' - Placeholder = 'Placeholder' - Attachment = 'Attachment' - Hyperlink = 'Hyperlink' - Reference = 'Reference' - - -class ArgumentValueType(Enum): - EquivalenceClass = 'EquivalenceClass' - Representative = 'Representative' - InstancesArray = 'InstancesArray' - CBRRepresentative = 'CBRRepresentative' - - -class TestCaseDetailsOrigin(Enum): - Fallback = 'Fallback' - Generated = 'Generated' - Rejected = 'Rejected' - Restored = 'Restored' - Upgraded = 'Upgraded' - - -class ReferenceKind(Enum): - Reference = 'Reference' - Link = 'Link' - Attachment = 'Attachment' - Hyperlink = 'Hyperlink' - - -class TestFilterType(Enum): - TestTheme = 'TestTheme' - TestCaseSet = 'TestCaseSet' - TestCase = 'TestCase' - - -@dataclass -class FilterInfo: - name: str - filterType: TestFilterType - testThemeUID: Optional[str] = None - - -@dataclass -class TovStructureOptions(ReportExportOptions): - treeRootUID: Optional[str] = None - suppressFilteredData: Optional[bool] = None - suppressEmptyTestThemes: Optional[bool] = None - filters: Optional[List[FilterInfo]] = None - - -@dataclass -class CycleStructureOptions: - treeRootUID: Optional[str] = None - basedOnExecution: Optional[bool] = None - suppressFilteredData: Optional[bool] = None - suppressNotExecutable: Optional[bool] = None - suppressEmptyTestThemes: Optional[bool] = None - filters: Optional[List[FilterInfo]] = None - - -@dataclass -class CycleReportOptions(ReportExportOptions): - treeRootUID: Optional[str] = None - executionMode: Optional[ExecutionMode] = None - suppressFilteredData: Optional[bool] = None - suppressNotExecutable: Optional[bool] = None - suppressEmptyTestThemes: Optional[bool] = None - filters: Optional[List[FilterInfo]] = None - - -@dataclass -class DefectAttribute: - name: str - value: str - - -@dataclass -class ProjectDefectField: - values: List[str] - defaultValue: Optional[str] = None - - -@dataclass -class DefectUDF: - name: str - udfType: UDFType - isMandatory: bool - values: Optional[List[str]] = None - - -@dataclass -class TestStructureExecution: - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - - -@dataclass -class TestStructureItemExecution(TestStructureExecution): - key: str - locker: Optional[UserReference] = None - - -@dataclass -class TestCaseExecution(TestStructureExecution): - key: str - - -@dataclass -class TestStructureSpecification: - pass - - -@dataclass -class TestStructureItemSpecification(TestStructureSpecification): - key: str - status: SpecStatus - locker: Optional[UserReference] = None - - -@dataclass -class TestCaseSpecification(TestStructureSpecification): - key: str - - -@dataclass -class TestStructureItemBaseInformation: - key: str - numbering: str - path: str - parentKey: str - name: str - uniqueID: str - matchesFilter: bool - - -@dataclass -class TestCaseBaseInformation: - numbering: str - parentKey: str - name: str - uniqueID: str - matchesFilter: bool - - -@dataclass -class TestStructureAutomation: - key: str - status: AutStatus - locker: Optional[UserReference] = None - - -class TestStructureElementType(Enum): - RootNode = 'RootNode' - TestThemeNode = 'TestThemeNode' - TestCaseSetNode = 'TestCaseSetNode' - TestCaseNode = 'TestCaseNode' - - -@dataclass -class AttachedFilter: - key: str - name: str - filterType: TestFilterType - content: str - - -@dataclass -class CreatedJob: - jobID: str - - -@dataclass -class JobProgress: - totalItemsCount: int - handledItemsCount: int - - -@dataclass -class ReportingResult: - pass - - -@dataclass -class ReportingFailure(ReportingResult): - error: ActionFailure - - -@dataclass -class ReportingSuccess(ReportingResult): - reportName: str - - -@dataclass -class ExecutionImportingResult: - pass - - -@dataclass -class ExecutionImportingFailure(ExecutionImportingResult): - error: ActionFailure - - -@dataclass -class CheckedInElement: - elementName: str - newlyCheckedIn: bool - versionName: str - - -@dataclass -class CreatedDefect: - foreignKey: str - createdKey: str - - -@dataclass -class CreatedReference: - foreignKey: str - createdKey: str - - -@dataclass -class TestCaseExecutionImportResult: - key: str - executionKey: str - uid: str - importResult: ImportResult - warnings: List[ActionFailure] - error: Optional[ActionFailure] = None - - -@dataclass -class CheckInData: - comment: str - label: Optional[str] = None - - -@dataclass -class ExecutionImportSimulationOptions: - fileName: str - treeRootUID: Optional[str] = None - filters: Optional[List[FilterInfo]] = None - - -@dataclass -class UploadedFile: - fileName: str - - -@dataclass -class TestStructureElement: - key: str - uniqueID: str - name: str - executionKey: str - - -@dataclass -class TestThemeExecution: - key: str - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - udfs: List[UserDefinedField] - tags: List[Tag] - references: List[str] - comments: Optional[str] = None - responsible: Optional[UserReference] = None - - -@dataclass -class TestThemeDetails: - key: str - name: str - uniqueID: str - numbering: str - path: str - spec: TestThemeSpecification - exec: Optional[TestThemeExecution] = None - - -@dataclass -class TestObjectVersionCSVReportOptions: - reportRootUID: Optional[str] = None - fields: Optional[List[str]] = None - characterEncoding: Optional[str] = None - - -@dataclass -class JWTResponse: - accessToken: str - expiresAt: str - - -@dataclass -class TestCycleCSVReportOptions: - reportRootUID: Optional[str] = None - fields: Optional[List[str]] = None - characterEncoding: Optional[str] = None - basedOn: Optional[str] = None - - -class UDFLocation(Enum): - TestThemesInSpecification = 'TestThemesInSpecification' - TestCaseSetsInSpecification = 'TestCaseSetsInSpecification' - TestCasesInSpecification = 'TestCasesInSpecification' - TestThemesInExecution = 'TestThemesInExecution' - TestCaseSetsInExecution = 'TestCaseSetsInExecution' - TestCasesInExecution = 'TestCasesInExecution' - - -class UDFAbsolutePosition(Enum): - First = 'First' - Last = 'Last' - - -@dataclass -class AfterUDF: - udfKey: str - - -@dataclass -class BeforeUDF: - udfKey: str - - -@dataclass -class UDFForUpdate: - name: Optional[str] = None - isMandatory: Optional[bool] = None - definedFor: Optional[List[UDFLocation]] = None - udfType: Optional[UDFType] = None - enumerationValues: Optional[List[UDFEnumerationValue]] = None - - -@dataclass -class UDFsMoveResponse: - targetKeys: List[str] - - -@dataclass -class TestFilter: - key: str - name: str - owner: str - pluginName: Optional[str] = None - - -@dataclass -class UDFUpdatedResponse: - pass - - -@dataclass -class UDFForRestriction: - onlyAdminsMayManageUDFs: bool - - -@dataclass -class UDFRestrictionResponse: - projectKey: str - onlyAdminsMayManageUDFs: bool - - -@dataclass -class CycleCreation: - pass - - -@dataclass -class CycleCloned(CycleCreation): - cycle: str - - -@dataclass -class CycleImported(CycleCreation): - project: str - tov: str - cycle: str - - -@dataclass -class CycleImportedAsNew(CycleCreation): - tov: str - cycle: str - - -@dataclass -class CycleImportedAsClone(CycleCreation): - tov: str - cycle: str - clonedCycle: str - - -@dataclass -class CycleImportedAsNewFromPlugin(CycleCreation): - plugin: str - tov: str - cycle: str - - -@dataclass -class CycleClonedFromPlugin(CycleCreation): - plugin: str - cycle: str - - -@dataclass -class CycleImportedAsCloneFromPlugin(CycleCreation): - plugin: str - tov: str - cycle: str - clonedCycle: str - - -@dataclass -class UnknownCycle(CycleCreation): - pass - - -@dataclass -class NewCycle(CycleCreation): - pass - - -@dataclass -class NewPassword: - newPassword: str - - -@dataclass -class ImageDetails: - key: str - suffix: str - imageData: str - - -@dataclass -class UdfValueForImport: - udfKey: str - value: str - - -@dataclass -class RichTextForImport: - html: Optional[str] = None - plain: Optional[str] = None - - -@dataclass -class ExecutionResultForImport: - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - timestamp: Optional[str] = None - - -@dataclass -class TestCaseExecutionForImport: - uniqueID: str - testCaseExecutionKey: str - result: ExecutionResultForImport - durationMillis: int - testerKey: Optional[str] = None - comments: Optional[RichTextForImport] = None - defects: Optional[List[str]] = None - udfs: Optional[List[UdfValueForImport]] = None - references: Optional[List[str]] = None - - -@dataclass -class TestCaseSetExecutionForImport: - testCaseSetKey: str - executionKey: str - durationMillis: int - testCases: List[TestCaseExecutionForImport] - testerKey: Optional[str] = None - comments: Optional[RichTextForImport] = None - udfs: Optional[List[UdfValueForImport]] = None - - -@dataclass -class KeywordCallExecution: - verdict: KeywordVerdict - duration: int - currentUser: UserReference - comments: str - references: List[str] - defects: List[str] - time: Optional[str] = None - tester: Optional[UserReference] = None - - -class Permission(Enum): - AccessSecuredData = 'AccessSecuredData' - DeleteUserAccount = 'DeleteUserAccount' - DeleteUserSession = 'DeleteUserSession' - DownloadReportFile = 'DownloadReportFile' - ImportExecutionResults = 'ImportExecutionResults' - ModifyGlobalTestLabels = 'ModifyGlobalTestLabels' - ModifyProjectDetails = 'ModifyProjectDetails' - ModifyProjectUDFs = 'ModifyProjectUDFs' - ModifySpecifications = 'ModifySpecifications' - ModifySpecManagementInfo = 'ModifySpecManagementInfo' - ModifySpecPriorityAndDueDate = 'ModifySpecPriorityAndDueDate' - ModifyTestElements = 'ModifyTestElements' - ModifyTestLabels = 'ModifyTestLabels' - ModifyUserData = 'ModifyUserData' - ModifyUserRolesInProject = 'ModifyUserRolesInProject' - PrivatizeGlobalTestLabels = 'PrivatizeGlobalTestLabels' - ReadActiveUsersList = 'ReadActiveUsersList' - ReadCompleteProjectsList = 'ReadCompleteProjectsList' - ReadCompleteUsersList = 'ReadCompleteUsersList' - ReadCycleReport = 'ReadCycleReport' - ReadCycleReportOverRMI = 'ReadCycleReportOverRMI' - ReadCycleRequirements = 'ReadCycleRequirements' - ReadDefectsMetricDistribution = 'ReadDefectsMetricDistribution' - ReadExecutionImportingJobDetails = 'ReadExecutionImportingJobDetails' - ReadInvisibleProjectContent = 'ReadInvisibleProjectContent' - ReadOwnProjectsList = 'ReadOwnProjectsList' - ReadOwnUserDetails = 'ReadOwnUserDetails' - ReadProjectDefectsAndTheirAssignments = 'ReadProjectDefectsAndTheirAssignments' - ReadProjectDetails = 'ReadProjectDetails' - ReadProjectExportOverRMI = 'ReadProjectExportOverRMI' - ReadProjectHierarchy = 'ReadProjectHierarchy' - ReadProjectMembers = 'ReadProjectMembers' - ReadProjectUDFs = 'ReadProjectUDFs' - ReadReportingJobDetails = 'ReadReportingJobDetails' - ReadTestCaseDetails = 'ReadTestCaseDetails' - ReadTestCaseSetDetails = 'ReadTestCaseSetDetails' - ReadTestElements = 'ReadTestElements' - ReadTestLabels = 'ReadTestLabels' - ReadTestThemeDetails = 'ReadTestThemeDetails' - ReadTestThemeStatusDistribution = 'ReadTestThemeStatusDistribution' - ReadTestThemeTree = 'ReadTestThemeTree' - ReadTovReport = 'ReadTovReport' - ReadTovReportOverRMI = 'ReadTovReportOverRMI' - ReadTovRequirements = 'ReadTovRequirements' - ReadUserDetails = 'ReadUserDetails' - ReadUserMemberships = 'ReadUserMemberships' - ReadUserSessions = 'ReadUserSessions' - RestrictProjectUDFs = 'RestrictProjectUDFs' - SynchronizeUsers = 'SynchronizeUsers' - UnlockForeignSpecs = 'UnlockForeignSpecs' - UnlockForeignTestElements = 'UnlockForeignTestElements' - - -@dataclass -class DefaultValue: - name: str - valueType: ArgumentValueType - - -@dataclass -class SubdivisionDetails: - key: str - name: str - uniqueID: str - description: str - path: str - references: List[str] - locker: Optional[UserReference] = None - parentUniqueID: Optional[str] = None - libraryKey: Optional[str] = None - - -@dataclass -class KeywordParameterForInsert: - name: str - dataTypeKey: Optional[str] = None - evaluationType: Optional[ParameterEvaluationType] = None - - -@dataclass -class ParameterDetails: - key: str - name: str - definitionType: ParameterDefinitionType - evaluationType: ParameterEvaluationType - dataTypeKey: Optional[str] = None - defaultValue: Optional[DefaultValue] = None - signatureID: Optional[str] = None - - -@dataclass -class TOVNode: - nodeType: ProjectTreeNodeType - key: str - name: str - creationTime: str - status: ProjectStatus - visibility: bool - exchangeFormat: TOVExchangeFormat - children: List[CycleNode] - - -@dataclass -class UDF: - udfKey: str - name: str - projectKey: str - isMandatory: bool - definedFor: List[UDFLocation] - - -@dataclass -class BooleanUDF(UDF): - pass - - -@dataclass -class EnumerationUDF(UDF): - enumerationValues: List[UDFEnumerationValue] - - -@dataclass -class StringUDF(UDF): - pass - - -@dataclass -class ReferenceAssignment: - key: str - value: str - referenceType: ReferenceKind - versionName: Optional[str] = None - - -@dataclass -class RepresentativeValue: - name: str - valueType: RepresentativeType - isDefaultValue: bool - - -@dataclass -class AssignedDefect: - key: str - title: str - id: str - description: str - identicalVersionKey: str - status: str - priority: str - classification: str - creationTime: str - references: List[str] - udfs: List[DefectAttribute] - version: Optional[str] = None - tester: Optional[str] = None - lastEditTime: Optional[str] = None - lastEditorKey: Optional[str] = None - defectManagementSystem: Optional[str] = None - defectManagementProject: Optional[str] = None - - -@dataclass -class ExternalDefectManagement: - system: str - project: str - udfs: List[DefectUDF] - - -@dataclass -class DefectConfig: - status: ProjectDefectField - classification: ProjectDefectField - priority: ProjectDefectField - external: Optional[ExternalDefectManagement] = None - - -@dataclass -class TestStructureTreeNode: - elementType: TestStructureElementType - - -@dataclass -class RootNode(TestStructureTreeNode): - base: TestStructureItemBaseInformation - filters: List[AttachedFilter] - - -@dataclass -class TestThemeNode(TestStructureTreeNode): - base: TestStructureItemBaseInformation - filters: List[AttachedFilter] - spec: Optional[TestStructureItemSpecification] = None - aut: Optional[TestStructureAutomation] = None - exec: Optional[TestStructureItemExecution] = None - - -@dataclass -class TestCaseSetNode(TestStructureTreeNode): - base: TestStructureItemBaseInformation - spec: Optional[TestStructureItemSpecification] = None - aut: Optional[TestStructureAutomation] = None - exec: Optional[TestStructureItemExecution] = None - - -@dataclass -class TestCaseNode(TestStructureTreeNode): - base: TestCaseBaseInformation - spec: Optional[TestCaseSpecification] = None - exec: Optional[TestCaseExecution] = None - - -@dataclass -class TestStructureTree: - nodes: List[Union[TestThemeNode, TestCaseSetNode, TestCaseNode]] - root: Optional[Union[RootNode, TestThemeNode, TestCaseSetNode, TestCaseNode]] = None - - -@dataclass -class ReportingCompletion: - time: str - result: ReportingResult - - -@dataclass -class ExecutionImportingCompletion: - time: str - result: ExecutionImportingResult - - -@dataclass -class TestCaseSetExecutionImportResult: - key: str - executionKey: str - name: str - uid: str - finished: bool - testCases: List[TestCaseExecutionImportResult] - error: Optional[ActionFailure] = None - - -@dataclass -class ExecutionImportOptions: - fileName: str - treeRootUID: Optional[str] = None - useExistingDefect: Optional[bool] = None - discardTesterInformation: Optional[bool] = None - defaultTester: Optional[str] = None - filters: Optional[List[FilterInfo]] = None - checkInData: Optional[CheckInData] = None - - -@dataclass -class JWTDataOptions: - permissions: List[Permission] - projectKey: Optional[str] = None - tovKey: Optional[str] = None - cycleKey: Optional[str] = None - subject: Optional[str] = None - expiresAfterSeconds: Optional[int] = None - - -@dataclass -class UDFPosition: - after: Optional[AfterUDF] = None - before: Optional[BeforeUDF] = None - absolute: Optional[UDFAbsolutePosition] = None - - -@dataclass -class UDFForMove: - udfKeys: List[str] - newPositions: UDFPosition - - -@dataclass -class StringUDFUpdatedResponse(UDFUpdatedResponse): - stringUDF: StringUDF - affectedFilters: List[TestFilter] - - -@dataclass -class BooleanUDFUpdatedResponse(UDFUpdatedResponse): - booleanUdf: BooleanUDF - affectedFilters: List[TestFilter] - - -@dataclass -class EnumerationUDFUpdatedResponse(UDFUpdatedResponse): - enumerationUDF: EnumerationUDF - affectedFilters: List[TestFilter] - - -@dataclass -class CycleResponse: - name: str - visible: bool - projectKey: str - description: str - creation: CycleCreation - key: str - tovKey: str - status: ProjectStatus - testingIntelligence: bool - instantOfCreation: str - endDate: Optional[str] = None - startDate: Optional[str] = None - - -@dataclass -class ImageInfo: - key: str - value: ImageDetails - - -@dataclass -class KeywordDetails: - key: str - name: str - uniqueID: str - status: TestElementStatus - defaultCallType: KeywordCallType - description: str - path: str - parameters: List[ParameterDetails] - preConditions: List[ConditionSummary] - postConditions: List[ConditionSummary] - references: List[str] - locker: Optional[UserReference] = None - version: Optional[str] = None - parentUniqueID: Optional[str] = None - libraryKey: Optional[str] = None - advancedContent: Optional[AdvancedContent] = None - - -@dataclass -class ProjectNode: - nodeType: ProjectTreeNodeType - key: str - name: str - creationTime: str - status: ProjectStatus - visibility: bool - exchangeFormat: ProjectExchangeFormat - children: List[TOVNode] - - -@dataclass -class ParameterSummary: - definitionType: ParameterDefinitionType - key: str - name: str - evaluationType: ParameterEvaluationType - dataType: Optional[DataTypeSummary] = None - value: Optional[str] = None - valueType: Optional[RepresentativeType] = None - representativeValue: Optional[RepresentativeValue] = None - parameterValue: Optional[ParameterValue] = None - instanceArrayValue: Optional[InstanceArrayValue] = None - alias: Optional[str] = None - - -@dataclass -class ReportingJob: - id: str - projectKey: str - owner: str - start: str - progress: Optional[JobProgress] = None - completion: Optional[ReportingCompletion] = None - - -@dataclass -class ExecutionImportingJob: - id: str - projectKey: str - owner: str - start: str - progress: Optional[JobProgress] = None - completion: Optional[ExecutionImportingCompletion] = None - - -@dataclass -class ExecutionImportingSuccess(ExecutionImportingResult): - testCaseSets: List[TestCaseSetExecutionImportResult] - checkedInTestStructureElements: List[TestStructureElement] - checkedInTestElements: List[CheckedInElement] - createdDefects: List[CreatedDefect] - createdReferences: List[CreatedReference] - - -@dataclass -class UDFForInsert: - name: str - isMandatory: bool - definedFor: List[UDFLocation] - udfType: UDFType - enumerationName: Optional[List[str]] = None - position: Optional[UDFPosition] = None - - -@dataclass -class RichTextInfo: - html: str - images: List[ImageInfo] - - -@dataclass -class KeywordCallSpecification: - key: str - name: str - sequencePhase: SequencePhase - callType: KeywordCallType - comments: str - callParameters: List[ParameterSummary] - keywordType: Optional[KeywordType] = None - description: Optional[str] = None - keywordKey: Optional[str] = None - callingKeywordKey: Optional[str] = None - - -@dataclass -class KeywordCall: - sequenceID: str - numbering: str - spec: KeywordCallSpecification - parentID: Optional[str] = None - exec: Optional[KeywordCallExecution] = None - - -@dataclass -class KeywordForInsert: - parentKey: str - name: str - parameters: List[KeywordParameterForInsert] - uid: Optional[str] = None - description: Optional[RichTextInfo] = None - advancedContent: Optional[AdvancedContent] = None - callType: Optional[KeywordCallType] = None - - -@dataclass -class SubdivisionForInsert: - name: str - parentKey: Optional[str] = None - uid: Optional[str] = None - description: Optional[RichTextInfo] = None - - -@dataclass -class SubdivisionForUpdate: - name: Optional[str] = None - description: Optional[RichTextInfo] = None - - -@dataclass -class KeywordDetailsForUpdate: - name: Optional[str] = None - description: Optional[RichTextInfo] = None - callType: Optional[KeywordCallType] = None - locker: Optional[OptionalUser] = None - advancedContent: Optional[OptionalAdvancedContent] = None - - -@dataclass -class SpecificationDetailsForUpdate: - responsible: Optional[OptionalUser] = None - reviewer: Optional[OptionalUser] = None - locker: Optional[OptionalUser] = None - priority: Optional[Priority] = None - dueDate: Optional[OptionalLocalDate] = None - description: Optional[RichTextInfo] = None - reviewComment: Optional[RichTextInfo] = None - - -@dataclass -class ProjectDetailsForUpdate: - name: Optional[str] = None - testObjectName: Optional[str] = None - id: Optional[str] = None - customerName: Optional[str] = None - customerAddress: Optional[str] = None - contactPerson: Optional[str] = None - testLab: Optional[str] = None - placeOfInspection: Optional[str] = None - status: Optional[ProjectStatus] = None - isVisibleToTester: Optional[bool] = None - isTestingIntelligenceActive: Optional[bool] = None - description: Optional[RichTextInfo] = None - startDate: Optional[OptionalLocalDate] = None - endDate: Optional[OptionalLocalDate] = None - inspections: Optional[List[str]] = None - variantsManagementEnabled: Optional[OperationalState] = None - - -@dataclass -class TOVForUpdate: - name: Optional[str] = None - endDate: Optional[OptionalLocalDate] = None - visible: Optional[bool] = None - description: Optional[RichTextInfo] = None - isBaseTov: Optional[bool] = None - status: Optional[ProjectStatus] = None - testingIntelligence: Optional[bool] = None - startDate: Optional[OptionalLocalDate] = None - cloningVisibility: Optional[bool] = None - - -@dataclass -class TestCaseSetDetails: - key: str - numbering: str - path: str - uniqueID: str - name: str - spec: TestCaseSetSpecificationSummary - testCases: List[TestCaseSummary] - testSequence: List[KeywordCall] - parameters: List[ParameterDetails] - keywords: List[KeywordDetails] - exec: Optional[TestCaseSetExecutionSummary] = None - - -@dataclass -class TestCaseDetails: - uniqueID: str - spec: TestCaseSpecificationDetails - testSequence: List[KeywordCall] - parameters: List[ParameterSummary] - keywords: List[KeywordDetails] - exec: Optional[TestCaseExecutionDetails] = None - origin: Optional[TestCaseDetailsOrigin] = None - - -@dataclass -class CycleForUpdate: - name: Optional[str] = None - endDate: Optional[OptionalLocalDate] = None - visible: Optional[bool] = None - description: Optional[RichTextInfo] = None - status: Optional[ProjectStatus] = None - testingIntelligence: Optional[bool] = None - startDate: Optional[OptionalLocalDate] = None +# generated by datamodel-codegen: +# filename: openapi.yml +# timestamp: 2026-05-18T16:27:28+00:00 + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import List, Optional + + +class TestElementStatus(Enum): + InProgress = 'InProgress' + Released = 'Released' + + +class ContentType(Enum): + txt = 'txt' + xml = 'xml' + + +class ReferenceUpdateMode(Enum): + Replace = 'Replace' + Extend = 'Extend' + + +@dataclass +class AdvancedContent: + contentType: Optional[ContentType] = None + contentExtID: Optional[str] = None + content: Optional[str] = None + + +@dataclass +class OptionalAdvancedContent: + optional: Optional[AdvancedContent] = None + + +@dataclass +class OptionalUser: + optional: Optional[str] = None + + +class ProjectStatus(Enum): + Planned = 'Planned' + Active = 'Active' + Finished = 'Finished' + Closed = 'Closed' + + +class DefectMetricType(Enum): + Status = 'Status' + Priority = 'Priority' + Classification = 'Classification' + + +class ProjectTreeNodeType(Enum): + Project = 'Project' + Version = 'Version' + Cycle = 'Cycle' + + +class Severity(Enum): + Information = 'Information' + Warning = 'Warning' + Error = 'Error' + + +class UDFType(Enum): + String = 'String' + Enumeration = 'Enumeration' + Boolean = 'Boolean' + + +class TestLabelVisibilityType(Enum): + OnlyPrivate = 'OnlyPrivate' + OnlyPublic = 'OnlyPublic' + All = 'All' + + +class AutStatus(Enum): + NotPlanned = 'NotPlanned' + Planned = 'Planned' + InProgress = 'InProgress' + InReview = 'InReview' + Released = 'Released' + + +class SpecStatus(Enum): + NotPlanned = 'NotPlanned' + Planned = 'Planned' + InProgress = 'InProgress' + InReview = 'InReview' + Released = 'Released' + + +class Priority(Enum): + Undefined = 'Undefined' + Low = 'Low' + Middle = 'Middle' + High = 'High' + + +class ActivityStatus(Enum): + NotPlanned = 'NotPlanned' + Planned = 'Planned' + Assigned = 'Assigned' + Running = 'Running' + Skipped = 'Skipped' + Canceled = 'Canceled' + Performed = 'Performed' + + +class ExecStatus(Enum): + NotBlocked = 'NotBlocked' + Blocked = 'Blocked' + + +class VerdictStatus(Enum): + Undefined = 'Undefined' + ToVerify = 'ToVerify' + Fail = 'Fail' + Pass = 'Pass' + + +class KeywordVerdict(Enum): + Pass = 'Pass' + Fail = 'Fail' + Skipped = 'Skipped' + ToVerify = 'ToVerify' + Warn = 'Warn' + Undefined = 'Undefined' + Blocked = 'Blocked' + + +class SequencePhase(Enum): + Setup = 'Setup' + TestStep = 'TestStep' + Teardown = 'Teardown' + + +class KeywordCallType(Enum): + Flow = 'Flow' + Check = 'Check' + + +class KeywordType(Enum): + Atomic = 'Atomic' + Compound = 'Compound' + Textual = 'Textual' + + +class OperationalState(Enum): + Enabled = 'Enabled' + Disabled = 'Disabled' + + +class GlobalHumanRole(Enum): + Administrator = 'Administrator' + ProjectAdministrator = 'ProjectAdministrator' + ProjectUser = 'ProjectUser' + + +class ProjectRole(Enum): + TestManager = 'TestManager' + TestDesigner = 'TestDesigner' + TestProgrammer = 'TestProgrammer' + Tester = 'Tester' + ReadOnlyDesigner = 'ReadOnlyDesigner' + ReadOnlyImplementer = 'ReadOnlyImplementer' + ReadOnlyTester = 'ReadOnlyTester' + + +class TOVExchangeFormat(Enum): + xml = 'xml' + json = 'json' + inherited = 'inherited' + + +class ProjectExchangeFormat(Enum): + default_xml = 'default_xml' + default_json = 'default_json' + + +class ImportResult(Enum): + Imported = 'Imported' + PartiallyImported = 'PartiallyImported' + NotImported = 'NotImported' + + +class ExecutionMode(Enum): + execute = 'execute' + continue_ = 'continue' + view = 'view' + simulate = 'simulate' + + +@dataclass +class UserGlobalRoles: + roles: List[GlobalHumanRole] + + +@dataclass +class UserProjectRoles: + userKey: str + roles: List[ProjectRole] + + +@dataclass +class ProjectUser: + key: str + name: str + login: str + projectRoles: List[ProjectRole] + + +@dataclass +class LicenseWarning: + expiresSoon: bool + baseCountExceeded: bool + gracedForeignLicense: bool + + +@dataclass +class LoginData: + login: str + password: str + force: Optional[bool] = None + context: Optional[str] = None + + +@dataclass +class ChangePasswordData: + login: str + password: str + newPassword: str + + +@dataclass +class LoginSession: + userKey: str + login: str + sessionToken: str + globalRoles: List[str] + internalUserManagement: bool + serverVersion: str + licenseWarning: Optional[LicenseWarning] = None + + +@dataclass +class ActiveUser: + login: str + contexts: List[str] + + +@dataclass +class TestLabel: + key: str + name: str + ownerKey: str + visibility: bool + libraryKey: Optional[str] = None + + +@dataclass +class TestLabelDataForUpdate: + name: Optional[str] = None + visibility: Optional[bool] = None + + +@dataclass +class TestLabelDataForInsert: + name: str + adaptName: Optional[bool] = True + + +@dataclass +class ProjectMember: + userKey: str + userLogin: str + userName: str + projectKey: str + projectName: str + roles: List[ProjectRole] + + +@dataclass +class UserInfo: + userKey: str + userLogin: str + userName: str + + +@dataclass +class ProjectSummary: + key: str + creationTime: str + name: str + status: ProjectStatus + visibility: bool + tovsCount: int + cyclesCount: int + description: str + lockerKey: Optional[str] = None + startDate: Optional[str] = None + endDate: Optional[str] = None + + +@dataclass +class ProjectContext: + tovName: str + cycleName: Optional[str] = None + executionMode: Optional[ExecutionMode] = None + + +@dataclass +class ProjectDetails: + key: str + creationTime: str + name: str + status: ProjectStatus + visibility: bool + tovsCount: int + cyclesCount: int + description: str + exchangeFormat: ProjectExchangeFormat + lockerKey: Optional[str] = None + startDate: Optional[str] = None + endDate: Optional[str] = None + projectContext: Optional[ProjectContext] = None + + +@dataclass +class ProjectInspection: + inspectionKey: str + severity: Severity + + +@dataclass +class ProjectCustomer: + customerName: str + customerAddress: str + testLab: str + placeOfInspection: str + contactPerson: str + + +@dataclass +class ProjectCreation: + pass + + +@dataclass +class UnknownProject(ProjectCreation): + pass + + +@dataclass +class NewProject(ProjectCreation): + pass + + +@dataclass +class ProjectImported(ProjectCreation): + project: str + + +@dataclass +class TOVCreation: + pass + + +@dataclass +class UnknownTOV(TOVCreation): + pass + + +@dataclass +class NewTOV(TOVCreation): + pass + + +@dataclass +class TOVCloned(TOVCreation): + tov: str + tovKey: Optional[str] = None + + +@dataclass +class TOVClonedFromSameProject(TOVCreation): + tov: str + tovKey: Optional[str] = None + + +@dataclass +class TOVClonedFromDifferentProject(TOVCreation): + project: str + tov: str + tovKey: Optional[str] = None + + +@dataclass +class TOVImportedAsNew(TOVCreation): + project: str + tov: str + + +@dataclass +class TOVImportedAsClone(TOVCreation): + project: str + tov: str + tovCloned: str + + +@dataclass +class TOVImportedAsNewFromPlugin(TOVCreation): + plugin: str + tov: str + + +@dataclass +class TOVImportedAsCloneFromPlugin(TOVCreation): + plugin: str + tov: str + tovCloned: str + + +@dataclass +class TOVImported(TOVCreation): + project: str + tov: str + + +@dataclass +class TOVDerivedFromSameProject(TOVCreation): + baseTOV: str + variantsDefinition: str + baseTOVKey: Optional[str] = None + variantsDefinitionKey: Optional[str] = None + + +@dataclass +class TOVDerivedFromDifferentProject(TOVCreation): + project: str + baseTOV: str + variantsDefinition: str + baseTOVKey: Optional[str] = None + variantsDefinitionKey: Optional[str] = None + + +@dataclass +class ProjectDetailsResponse: + projectKey: str + name: str + inspection: List[ProjectInspection] + testObjectName: str + id: str + customer: ProjectCustomer + status: ProjectStatus + visible: bool + testIntelligence: bool + description: str + creation: ProjectCreation + instantOfCreation: str + onlyAdminsMayManageUDFs: bool + variantsManagementEnabled: str + exchangeFormat: ProjectExchangeFormat + startDate: Optional[str] = None + endDate: Optional[str] = None + + +@dataclass +class TOVSummary: + key: str + creationTime: str + name: str + status: ProjectStatus + visibility: bool + cyclesCount: int + description: str + exchangeFormat: TOVExchangeFormat + lockerKey: Optional[str] = None + startDate: Optional[str] = None + endDate: Optional[str] = None + + +@dataclass +class TOVDetails: + key: str + creationTime: str + name: str + status: ProjectStatus + visibility: bool + cyclesCount: int + description: str + exchangeFormat: TOVExchangeFormat + lockerKey: Optional[str] = None + startDate: Optional[str] = None + endDate: Optional[str] = None + + +@dataclass +class TOVResponse: + name: str + visible: bool + projectKey: str + description: str + isBaseTov: bool + creation: TOVCreation + key: str + status: ProjectStatus + testingIntelligence: bool + instantOfCreation: str + cloningVisibility: bool + exchangeFormat: TOVExchangeFormat + endDate: Optional[str] = None + startDate: Optional[str] = None + + +@dataclass +class OptionalLocalDate: + optional: Optional[str] = None + + +@dataclass +class CycleSummary: + key: str + creationTime: str + name: str + status: ProjectStatus + visibility: bool + description: str + startDate: Optional[str] = None + endDate: Optional[str] = None + + +@dataclass +class CycleDetails: + key: str + creationTime: str + name: str + status: ProjectStatus + visibility: bool + description: str + startDate: Optional[str] = None + endDate: Optional[str] = None + + +@dataclass +class CycleNode: + nodeType: ProjectTreeNodeType + key: str + name: str + creationTime: str + status: ProjectStatus + visibility: bool + + +@dataclass +class UserDetails: + key: str + login: str + name: str + email: str + passwordExpired: bool + active: bool + + +@dataclass +class UserDataForInsert: + login: str + password: str + name: str + email: str + + +@dataclass +class UserDataForUpdate: + login: Optional[str] = None + name: Optional[str] = None + email: Optional[str] = None + passwordExpired: Optional[bool] = None + active: Optional[bool] = None + + +@dataclass +class UserSummary: + key: str + login: str + name: str + active: bool + + +@dataclass +class TestBenchVersions: + version: str + databaseVersion: str + revision: str + + +@dataclass +class ServerLocations: + jBossHost: str + jBossJNDIPort: int + legacyPlayHost: str + legacyPlayPort: int + + +@dataclass +class ActionFailure: + code: int + message: str + description: Optional[str] = None + + +@dataclass +class UserDefinedField: + key: str + name: str + value: str + udfType: UDFType + + +@dataclass +class UDFEnumerationValue: + valueKey: str + valueName: str + + +@dataclass +class Tag: + key: str + name: str + isVariantsMarker: bool + + +@dataclass +class UserReference: + key: str + name: str + + +@dataclass +class RequirementAssignment: + key: str + name: str + id: str + extendedId: str + version: str + owner: str + status: str + priority: str + repositoryId: str + + +@dataclass +class ReportItemsSummary: + testThemesCount: int + testCaseSetsCount: int + testCasesCount: int + + +@dataclass +class ServerLocation: + host: str + port: int + + +@dataclass +class ReportExportOptions: + pass + + +@dataclass +class ReportScope: + projectKey: str + tovKey: str + cycleKey: Optional[str] = None + + +@dataclass +class ReportCreation: + creator: UserInfo + startDate: str + endDate: str + scope: ReportScope + summary: ReportItemsSummary + exportOptions: Optional[ReportExportOptions] = None + + +@dataclass +class ReportMetaInformation: + formatVersion: str + serverLocation: ServerLocation + reportCreation: ReportCreation + serverVersions: TestBenchVersions + + +@dataclass +class RequirementReference: + key: str + edited: bool + + +@dataclass +class MetricsDistribution: + name: str + count: int + percentage: int + + +@dataclass +class DefectsDistribution: + statusDistribution: Optional[List[MetricsDistribution]] = None + priorityDistribution: Optional[List[MetricsDistribution]] = None + classDistribution: Optional[List[MetricsDistribution]] = None + + +@dataclass +class ConditionSummary: + key: str + uniqueID: str + name: str + description: str + version: Optional[str] = None + + +@dataclass +class SpecificationSummary: + key: str + description: str + reviewComment: str + status: SpecStatus + priority: Priority + locker: Optional[UserReference] = None + responsible: Optional[UserReference] = None + dueDate: Optional[str] = None + reviewer: Optional[UserReference] = None + + +@dataclass +class TestCaseSetSpecificationSummary: + key: str + description: str + reviewComment: str + status: SpecStatus + priority: Priority + preConditions: List[ConditionSummary] + postConditions: List[ConditionSummary] + udfs: List[UserDefinedField] + tags: List[Tag] + references: List[str] + requirements: List[RequirementReference] + responsible: Optional[UserReference] = None + dueDate: Optional[str] = None + reviewer: Optional[UserReference] = None + + +@dataclass +class TestCaseSpecificationDetails: + key: str + comments: str + udfs: List[UserDefinedField] + tags: List[Tag] + requirements: List[RequirementReference] + version: Optional[str] = None + + +@dataclass +class TestThemeSpecification: + key: str + description: str + reviewComment: str + status: SpecStatus + priority: Priority + udfs: List[UserDefinedField] + tags: List[Tag] + requirements: List[RequirementReference] + references: List[str] + dueDate: Optional[str] = None + reviewer: Optional[UserReference] = None + responsible: Optional[UserReference] = None + + +@dataclass +class TestCaseSetExecutionSummary: + key: str + comments: str + udfs: List[UserDefinedField] + tags: List[Tag] + + +@dataclass +class TestCaseSpecificationSummary: + key: str + comments: str + requirements: List[RequirementReference] + + +@dataclass +class TestCaseExecutionSummary: + key: str + status: ActivityStatus + execStatus: ExecStatus + verdict: VerdictStatus + defects: List[str] + comments: str + tester: Optional[UserReference] = None + + +@dataclass +class TestCaseSummary: + uniqueID: str + index: int + spec: TestCaseSpecificationSummary + exec: Optional[TestCaseExecutionSummary] = None + + +@dataclass +class TestCaseExecutionDetails: + key: str + status: ActivityStatus + execStatus: ExecStatus + verdict: VerdictStatus + plannedDuration: int + actualDuration: int + currentUser: UserReference + comments: str + defects: List[str] + udfs: List[UserDefinedField] + tags: List[Tag] + references: List[str] + version: Optional[str] = None + tester: Optional[UserReference] = None + + +class KindOfDataType(Enum): + Regular = 'Regular' + Reference = 'Reference' + Global = 'Global' + AcceptingGlobal = 'AcceptingGlobal' + + +@dataclass +class DataTypeSummary: + key: str + kind: KindOfDataType + name: str + path: str + uniqueID: str + version: Optional[str] = None + + +class ParameterDefinitionType(Enum): + DetailedInstance = 'DetailedInstance' + InstanceTable = 'InstanceTable' + AtomicInstance = 'AtomicInstance' + + +@dataclass +class ParameterValue: + name: str + key: str + dtSequenceKeys: List[str] + + +@dataclass +class InstanceArrayValue: + name: str + isDefaultValue: bool + + +class ParameterEvaluationType(Enum): + CallByValue = 'CallByValue' + CallByReference = 'CallByReference' + CallByReferenceMandatory = 'CallByReferenceMandatory' + + +class RepresentativeType(Enum): + Text = 'Text' + Placeholder = 'Placeholder' + Attachment = 'Attachment' + Hyperlink = 'Hyperlink' + Reference = 'Reference' + + +class ArgumentValueType(Enum): + EquivalenceClass = 'EquivalenceClass' + Representative = 'Representative' + InstancesArray = 'InstancesArray' + CBRRepresentative = 'CBRRepresentative' + + +class TestCaseDetailsOrigin(Enum): + Fallback = 'Fallback' + Generated = 'Generated' + Rejected = 'Rejected' + Restored = 'Restored' + Upgraded = 'Upgraded' + + +class ReferenceKind(Enum): + Reference = 'Reference' + Link = 'Link' + Attachment = 'Attachment' + + +class TestFilterType(Enum): + TestTheme = 'TestTheme' + TestCaseSet = 'TestCaseSet' + TestCase = 'TestCase' + + +@dataclass +class FilterInfo: + name: str + filterType: TestFilterType + testThemeUID: Optional[str] = None + + +@dataclass +class TovStructureOptions(ReportExportOptions): + treeRootUID: Optional[str] = None + suppressFilteredData: Optional[bool] = None + suppressEmptyTestThemes: Optional[bool] = None + filters: Optional[List[FilterInfo]] = None + + +@dataclass +class CycleStructureOptions: + treeRootUID: Optional[str] = None + basedOnExecution: Optional[bool] = None + suppressFilteredData: Optional[bool] = None + suppressNotExecutable: Optional[bool] = None + suppressEmptyTestThemes: Optional[bool] = None + filters: Optional[List[FilterInfo]] = None + + +@dataclass +class CycleReportOptions(ReportExportOptions): + treeRootUID: Optional[str] = None + executionMode: Optional[ExecutionMode] = None + suppressFilteredData: Optional[bool] = None + suppressNotExecutable: Optional[bool] = None + suppressEmptyTestThemes: Optional[bool] = None + filters: Optional[List[FilterInfo]] = None + + +@dataclass +class DefectAttribute: + name: str + value: str + + +@dataclass +class ProjectDefectField: + values: List[str] + defaultValue: Optional[str] = None + + +@dataclass +class DefectUDF: + name: str + udfType: UDFType + isMandatory: bool + values: Optional[List[str]] = None + + +@dataclass +class TestStructureTreeNode: + pass + + +@dataclass +class TestStructureExecution: + status: ActivityStatus + execStatus: ExecStatus + verdict: VerdictStatus + + +@dataclass +class TestStructureItemExecution(TestStructureExecution): + key: str + locker: Optional[UserReference] = None + + +@dataclass +class TestCaseExecution(TestStructureExecution): + key: str + + +@dataclass +class TestStructureSpecification: + pass + + +@dataclass +class TestStructureItemSpecification(TestStructureSpecification): + key: str + status: SpecStatus + locker: Optional[UserReference] = None + + +@dataclass +class TestCaseSpecification(TestStructureSpecification): + key: str + + +@dataclass +class TestStructureItemBaseInformation: + key: str + numbering: str + path: str + parentKey: str + name: str + uniqueID: str + matchesFilter: bool + + +@dataclass +class TestCaseBaseInformation: + numbering: str + parentKey: str + name: str + uniqueID: str + matchesFilter: bool + + +@dataclass +class TestStructureAutomation: + key: str + status: AutStatus + locker: Optional[UserReference] = None + + +class TestStructureElementType(Enum): + RootNode = 'RootNode' + TestThemeNode = 'TestThemeNode' + TestCaseSetNode = 'TestCaseSetNode' + TestCaseNode = 'TestCaseNode' + + +@dataclass +class AttachedFilter: + key: str + name: str + filterType: TestFilterType + content: str + + +@dataclass +class TestStructureTree: + nodes: List[TestStructureTreeNode] + root: Optional[TestStructureTreeNode] = None + + +@dataclass +class CreatedJob: + jobID: str + + +@dataclass +class JobProgress: + totalItemsCount: int + handledItemsCount: int + + +@dataclass +class ReportingResult: + pass + + +@dataclass +class ReportingFailure(ReportingResult): + error: ActionFailure + + +@dataclass +class ReportingSuccess(ReportingResult): + reportName: str + + +@dataclass +class ExecutionImportingResult: + pass + + +@dataclass +class ExecutionImportingFailure(ExecutionImportingResult): + error: ActionFailure + + +@dataclass +class CheckedInElement: + elementName: str + newlyCheckedIn: bool + versionName: str + + +@dataclass +class CreatedDefect: + foreignKey: str + createdKey: str + + +@dataclass +class CreatedReference: + foreignKey: str + createdKey: str + newFileName: Optional[str] = None + + +@dataclass +class TestCaseExecutionImportResult: + key: str + executionKey: str + uid: str + importResult: ImportResult + warnings: List[ActionFailure] + error: Optional[ActionFailure] = None + + +@dataclass +class CheckInData: + comment: str + label: Optional[str] = None + + +@dataclass +class ExecutionImportSimulationOptions: + fileName: str + treeRootUID: Optional[str] = None + filters: Optional[List[FilterInfo]] = None + + +@dataclass +class UploadedFile: + fileName: str + + +@dataclass +class TestStructureElement: + key: str + uniqueID: str + name: str + executionKey: str + + +@dataclass +class TestThemeExecution: + key: str + status: ActivityStatus + execStatus: ExecStatus + verdict: VerdictStatus + udfs: List[UserDefinedField] + tags: List[Tag] + references: List[str] + comments: Optional[str] = None + responsible: Optional[UserReference] = None + + +@dataclass +class TestThemeDetails: + key: str + name: str + uniqueID: str + numbering: str + path: str + spec: TestThemeSpecification + exec: Optional[TestThemeExecution] = None + + +@dataclass +class TestObjectVersionCSVReportOptions: + reportRootUID: Optional[str] = None + fields: Optional[List[str]] = None + characterEncoding: Optional[str] = None + + +@dataclass +class JWTResponse: + accessToken: str + expiresAt: str + + +@dataclass +class TestCycleCSVReportOptions: + reportRootUID: Optional[str] = None + fields: Optional[List[str]] = None + characterEncoding: Optional[str] = None + basedOn: Optional[str] = None + + +class UDFLocation(Enum): + TestThemesInSpecification = 'TestThemesInSpecification' + TestCaseSetsInSpecification = 'TestCaseSetsInSpecification' + TestCasesInSpecification = 'TestCasesInSpecification' + TestThemesInExecution = 'TestThemesInExecution' + TestCaseSetsInExecution = 'TestCaseSetsInExecution' + TestCasesInExecution = 'TestCasesInExecution' + + +class UDFAbsolutePosition(Enum): + First = 'First' + Last = 'Last' + + +@dataclass +class AfterUDF: + udfKey: str + + +@dataclass +class BeforeUDF: + udfKey: str + + +@dataclass +class UDFForUpdate: + name: Optional[str] = None + isMandatory: Optional[bool] = None + definedFor: Optional[List[UDFLocation]] = None + udfType: Optional[UDFType] = None + enumerationValues: Optional[List[UDFEnumerationValue]] = None + + +@dataclass +class UDFsMoveResponse: + targetKeys: List[str] + + +@dataclass +class TestFilter: + key: str + name: str + owner: str + pluginName: Optional[str] = None + + +@dataclass +class UDFUpdatedResponse: + pass + + +@dataclass +class UDFForRestriction: + onlyAdminsMayManageUDFs: bool + + +@dataclass +class UDFRestrictionResponse: + projectKey: str + onlyAdminsMayManageUDFs: bool + + +@dataclass +class CycleCreation: + pass + + +@dataclass +class CycleCloned(CycleCreation): + cycle: str + + +@dataclass +class CycleImported(CycleCreation): + project: str + tov: str + cycle: str + + +@dataclass +class CycleImportedAsNew(CycleCreation): + tov: str + cycle: str + + +@dataclass +class CycleImportedAsClone(CycleCreation): + tov: str + cycle: str + clonedCycle: str + + +@dataclass +class CycleImportedAsNewFromPlugin(CycleCreation): + plugin: str + tov: str + cycle: str + + +@dataclass +class CycleClonedFromPlugin(CycleCreation): + plugin: str + cycle: str + + +@dataclass +class CycleImportedAsCloneFromPlugin(CycleCreation): + plugin: str + tov: str + cycle: str + clonedCycle: str + + +@dataclass +class UnknownCycle(CycleCreation): + pass + + +@dataclass +class NewCycle(CycleCreation): + pass + + +@dataclass +class NewPassword: + newPassword: str + + +@dataclass +class ImageDetails: + key: str + suffix: str + imageData: str + + +@dataclass +class UdfValueForImport: + udfKey: str + value: str + + +@dataclass +class RichTextForImport: + html: Optional[str] = None + plain: Optional[str] = None + + +@dataclass +class ExecutionResultForImport: + status: ActivityStatus + execStatus: ExecStatus + verdict: VerdictStatus + timestamp: Optional[str] = None + + +@dataclass +class TestCaseExecutionForImport: + uniqueID: str + testCaseExecutionKey: str + result: ExecutionResultForImport + durationMillis: int + testerKey: Optional[str] = None + comments: Optional[RichTextForImport] = None + defects: Optional[List[str]] = None + udfs: Optional[List[UdfValueForImport]] = None + references: Optional[List[str]] = None + + +@dataclass +class TestCaseSetExecutionForImport: + testCaseSetKey: str + executionKey: str + durationMillis: int + testCases: List[TestCaseExecutionForImport] + testerKey: Optional[str] = None + comments: Optional[RichTextForImport] = None + udfs: Optional[List[UdfValueForImport]] = None + + +@dataclass +class KeywordCallExecution: + verdict: KeywordVerdict + duration: int + currentUser: UserReference + comments: str + references: List[str] + defects: List[str] + time: Optional[str] = None + tester: Optional[UserReference] = None + + +class Permission(Enum): + AccessSecuredData = 'AccessSecuredData' + AdministerServer = 'AdministerServer' + DeleteUserAccount = 'DeleteUserAccount' + DeleteUserSession = 'DeleteUserSession' + DownloadReportFile = 'DownloadReportFile' + ExecuteOthersTestCaseSets = 'ExecuteOthersTestCaseSets' + ImportExecutionResults = 'ImportExecutionResults' + ModifyGlobalTestLabels = 'ModifyGlobalTestLabels' + ModifyProjectDetails = 'ModifyProjectDetails' + ModifyProjectUDFs = 'ModifyProjectUDFs' + ModifySpecifications = 'ModifySpecifications' + ModifySpecManagementInfo = 'ModifySpecManagementInfo' + ModifySpecPriorityAndDueDate = 'ModifySpecPriorityAndDueDate' + ModifyTestElements = 'ModifyTestElements' + ModifyTestLabels = 'ModifyTestLabels' + ModifyUserData = 'ModifyUserData' + ModifyUserRolesInProject = 'ModifyUserRolesInProject' + PrivatizeGlobalTestLabels = 'PrivatizeGlobalTestLabels' + ReadActiveUsersList = 'ReadActiveUsersList' + ReadCompleteProjectsList = 'ReadCompleteProjectsList' + ReadCompleteUsersList = 'ReadCompleteUsersList' + ReadCycleReport = 'ReadCycleReport' + ReadCycleReportOverRMI = 'ReadCycleReportOverRMI' + ReadCycleRequirements = 'ReadCycleRequirements' + ReadDefectsMetricDistribution = 'ReadDefectsMetricDistribution' + ReadExecutionImportingJobDetails = 'ReadExecutionImportingJobDetails' + ReadInvisibleProjectContent = 'ReadInvisibleProjectContent' + ReadOwnProjectsList = 'ReadOwnProjectsList' + ReadOwnUserDetails = 'ReadOwnUserDetails' + ReadProjectDefectsAndTheirAssignments = 'ReadProjectDefectsAndTheirAssignments' + ReadProjectDetails = 'ReadProjectDetails' + ReadProjectExportOverRMI = 'ReadProjectExportOverRMI' + ReadProjectHierarchy = 'ReadProjectHierarchy' + ReadProjectMembers = 'ReadProjectMembers' + ReadProjectUDFs = 'ReadProjectUDFs' + ReadReportingJobDetails = 'ReadReportingJobDetails' + ReadTestCaseDetails = 'ReadTestCaseDetails' + ReadTestCaseSetDetails = 'ReadTestCaseSetDetails' + ReadTestElements = 'ReadTestElements' + ReadTestLabels = 'ReadTestLabels' + ReadTestThemeDetails = 'ReadTestThemeDetails' + ReadTestThemeStatusDistribution = 'ReadTestThemeStatusDistribution' + ReadTestThemeTree = 'ReadTestThemeTree' + ReadTovReport = 'ReadTovReport' + ReadTovReportOverRMI = 'ReadTovReportOverRMI' + ReadTovRequirements = 'ReadTovRequirements' + ReadUserDetails = 'ReadUserDetails' + ReadUserMemberships = 'ReadUserMemberships' + ReadUserSessions = 'ReadUserSessions' + RestrictProjectUDFs = 'RestrictProjectUDFs' + SynchronizeUsers = 'SynchronizeUsers' + UnlockForeignSpecs = 'UnlockForeignSpecs' + UnlockForeignTestElements = 'UnlockForeignTestElements' + + +@dataclass +class DefaultValue: + name: str + valueType: ArgumentValueType + + +@dataclass +class SubdivisionDetails: + key: str + name: str + uniqueID: str + description: str + path: str + references: List[str] + locker: Optional[UserReference] = None + parentUniqueID: Optional[str] = None + libraryKey: Optional[str] = None + + +@dataclass +class KeywordParameterForInsert: + name: str + dataTypeKey: Optional[str] = None + evaluationType: Optional[ParameterEvaluationType] = None + + +@dataclass +class ParameterDetails: + key: str + name: str + definitionType: ParameterDefinitionType + evaluationType: ParameterEvaluationType + dataTypeKey: Optional[str] = None + defaultValue: Optional[DefaultValue] = None + signatureID: Optional[str] = None + + +@dataclass +class TOVNode: + nodeType: ProjectTreeNodeType + key: str + name: str + creationTime: str + status: ProjectStatus + visibility: bool + exchangeFormat: TOVExchangeFormat + children: List[CycleNode] + + +@dataclass +class UDF: + udfKey: str + name: str + projectKey: str + isMandatory: bool + definedFor: List[UDFLocation] + + +@dataclass +class BooleanUDF(UDF): + pass + + +@dataclass +class EnumerationUDF(UDF): + enumerationValues: List[UDFEnumerationValue] + + +@dataclass +class StringUDF(UDF): + pass + + +@dataclass +class ReferenceAssignment: + key: str + value: str + referenceType: ReferenceKind + versionName: Optional[str] = None + + +@dataclass +class RepresentativeValue: + name: str + valueType: RepresentativeType + isDefaultValue: bool + + +@dataclass +class AssignedDefect: + key: str + title: str + id: str + description: str + identicalVersionKey: str + status: str + priority: str + classification: str + creationTime: str + references: List[str] + udfs: List[DefectAttribute] + version: Optional[str] = None + tester: Optional[str] = None + lastEditTime: Optional[str] = None + lastEditorKey: Optional[str] = None + defectManagementSystem: Optional[str] = None + defectManagementProject: Optional[str] = None + + +@dataclass +class ExternalDefectManagement: + system: str + project: str + udfs: List[DefectUDF] + + +@dataclass +class DefectConfig: + status: ProjectDefectField + classification: ProjectDefectField + priority: ProjectDefectField + external: Optional[ExternalDefectManagement] = None + + +@dataclass +class RootNode(TestStructureTreeNode): + base: TestStructureItemBaseInformation + filters: List[AttachedFilter] + + +@dataclass +class TestThemeNode(TestStructureTreeNode): + base: TestStructureItemBaseInformation + filters: List[AttachedFilter] + spec: Optional[TestStructureItemSpecification] = None + aut: Optional[TestStructureAutomation] = None + exec: Optional[TestStructureItemExecution] = None + + +@dataclass +class TestCaseSetNode(TestStructureTreeNode): + base: TestStructureItemBaseInformation + spec: Optional[TestStructureItemSpecification] = None + aut: Optional[TestStructureAutomation] = None + exec: Optional[TestStructureItemExecution] = None + + +@dataclass +class TestCaseNode(TestStructureTreeNode): + base: TestCaseBaseInformation + spec: Optional[TestCaseSpecification] = None + exec: Optional[TestCaseExecution] = None + + +@dataclass +class ReportingCompletion: + time: str + result: ReportingResult + + +@dataclass +class ExecutionImportingCompletion: + time: str + result: ExecutionImportingResult + + +@dataclass +class TestCaseSetExecutionImportResult: + key: str + executionKey: str + name: str + uid: str + finished: bool + testCases: List[TestCaseExecutionImportResult] + error: Optional[ActionFailure] = None + + +@dataclass +class ExecutionImportOptions: + fileName: str + treeRootUID: Optional[str] = None + useExistingDefect: Optional[bool] = None + discardTesterInformation: Optional[bool] = None + defaultTester: Optional[str] = None + updateReferencesList: Optional[ReferenceUpdateMode] = None + filters: Optional[List[FilterInfo]] = None + checkInData: Optional[CheckInData] = None + + +@dataclass +class JWTDataOptions: + permissions: List[Permission] + projectKey: Optional[str] = None + tovKey: Optional[str] = None + cycleKey: Optional[str] = None + subject: Optional[str] = None + expiresAfterSeconds: Optional[int] = None + + +@dataclass +class UDFPosition: + after: Optional[AfterUDF] = None + before: Optional[BeforeUDF] = None + absolute: Optional[UDFAbsolutePosition] = None + + +@dataclass +class UDFForMove: + udfKeys: List[str] + newPositions: UDFPosition + + +@dataclass +class StringUDFUpdatedResponse(UDFUpdatedResponse): + stringUDF: StringUDF + affectedFilters: List[TestFilter] + + +@dataclass +class BooleanUDFUpdatedResponse(UDFUpdatedResponse): + booleanUdf: BooleanUDF + affectedFilters: List[TestFilter] + + +@dataclass +class EnumerationUDFUpdatedResponse(UDFUpdatedResponse): + enumerationUDF: EnumerationUDF + affectedFilters: List[TestFilter] + + +@dataclass +class CycleResponse: + name: str + visible: bool + projectKey: str + description: str + creation: CycleCreation + key: str + tovKey: str + status: ProjectStatus + testingIntelligence: bool + instantOfCreation: str + endDate: Optional[str] = None + startDate: Optional[str] = None + + +@dataclass +class ImageInfo: + key: str + value: ImageDetails + + +@dataclass +class KeywordDetails: + key: str + name: str + uniqueID: str + status: TestElementStatus + defaultCallType: KeywordCallType + description: str + path: str + parameters: List[ParameterDetails] + preConditions: List[ConditionSummary] + postConditions: List[ConditionSummary] + references: List[str] + locker: Optional[UserReference] = None + version: Optional[str] = None + parentUniqueID: Optional[str] = None + libraryKey: Optional[str] = None + advancedContent: Optional[AdvancedContent] = None + + +@dataclass +class ProjectNode: + nodeType: ProjectTreeNodeType + key: str + name: str + creationTime: str + status: ProjectStatus + visibility: bool + exchangeFormat: ProjectExchangeFormat + children: List[TOVNode] + + +@dataclass +class ParameterSummary: + definitionType: ParameterDefinitionType + key: str + name: str + evaluationType: ParameterEvaluationType + dataType: Optional[DataTypeSummary] = None + value: Optional[str] = None + valueType: Optional[RepresentativeType] = None + representativeValue: Optional[RepresentativeValue] = None + parameterValue: Optional[ParameterValue] = None + instanceArrayValue: Optional[InstanceArrayValue] = None + parentAlias: Optional[str] = None + rootAlias: Optional[str] = None + usesCount: Optional[int] = None + + +@dataclass +class ReportingJob: + id: str + projectKey: str + owner: str + start: str + progress: Optional[JobProgress] = None + completion: Optional[ReportingCompletion] = None + + +@dataclass +class ExecutionImportingJob: + id: str + projectKey: str + owner: str + start: str + progress: Optional[JobProgress] = None + completion: Optional[ExecutionImportingCompletion] = None + + +@dataclass +class ExecutionImportingSuccess(ExecutionImportingResult): + testCaseSets: List[TestCaseSetExecutionImportResult] + checkedInTestStructureElements: List[TestStructureElement] + checkedInTestElements: List[CheckedInElement] + createdDefects: List[CreatedDefect] + createdReferences: List[CreatedReference] + + +@dataclass +class UDFForInsert: + name: str + isMandatory: bool + definedFor: List[UDFLocation] + udfType: UDFType + enumerationName: Optional[List[str]] = None + position: Optional[UDFPosition] = None + + +@dataclass +class RichTextInfo: + html: str + images: List[ImageInfo] + + +@dataclass +class KeywordCallSpecification: + key: str + name: str + sequencePhase: SequencePhase + callType: KeywordCallType + comments: str + callParameters: List[ParameterSummary] + keywordType: Optional[KeywordType] = None + description: Optional[str] = None + keywordKey: Optional[str] = None + callingKeywordKey: Optional[str] = None + + +@dataclass +class KeywordCall: + sequenceID: str + numbering: str + spec: KeywordCallSpecification + parentID: Optional[str] = None + exec: Optional[KeywordCallExecution] = None + + +@dataclass +class KeywordForInsert: + parentKey: str + name: str + parameters: List[KeywordParameterForInsert] + uid: Optional[str] = None + description: Optional[RichTextInfo] = None + advancedContent: Optional[AdvancedContent] = None + callType: Optional[KeywordCallType] = None + + +@dataclass +class SubdivisionForInsert: + name: str + parentKey: Optional[str] = None + uid: Optional[str] = None + description: Optional[RichTextInfo] = None + + +@dataclass +class SubdivisionForUpdate: + name: Optional[str] = None + description: Optional[RichTextInfo] = None + + +@dataclass +class KeywordDetailsForUpdate: + name: Optional[str] = None + description: Optional[RichTextInfo] = None + callType: Optional[KeywordCallType] = None + locker: Optional[OptionalUser] = None + advancedContent: Optional[OptionalAdvancedContent] = None + + +@dataclass +class SpecificationDetailsForUpdate: + responsible: Optional[OptionalUser] = None + reviewer: Optional[OptionalUser] = None + locker: Optional[OptionalUser] = None + priority: Optional[Priority] = None + dueDate: Optional[OptionalLocalDate] = None + description: Optional[RichTextInfo] = None + reviewComment: Optional[RichTextInfo] = None + + +@dataclass +class ProjectDetailsForUpdate: + name: Optional[str] = None + testObjectName: Optional[str] = None + id: Optional[str] = None + customerName: Optional[str] = None + customerAddress: Optional[str] = None + contactPerson: Optional[str] = None + testLab: Optional[str] = None + placeOfInspection: Optional[str] = None + status: Optional[ProjectStatus] = None + isVisibleToTester: Optional[bool] = None + isTestingIntelligenceActive: Optional[bool] = None + description: Optional[RichTextInfo] = None + startDate: Optional[OptionalLocalDate] = None + endDate: Optional[OptionalLocalDate] = None + inspections: Optional[List[str]] = None + variantsManagementEnabled: Optional[OperationalState] = None + + +@dataclass +class TOVForUpdate: + name: Optional[str] = None + endDate: Optional[OptionalLocalDate] = None + visible: Optional[bool] = None + description: Optional[RichTextInfo] = None + isBaseTov: Optional[bool] = None + status: Optional[ProjectStatus] = None + testingIntelligence: Optional[bool] = None + startDate: Optional[OptionalLocalDate] = None + cloningVisibility: Optional[bool] = None + + +@dataclass +class TestCaseSetDetails: + key: str + numbering: str + path: str + uniqueID: str + name: str + spec: TestCaseSetSpecificationSummary + testCases: List[TestCaseSummary] + testSequence: List[KeywordCall] + parameters: List[ParameterDetails] + keywords: List[KeywordDetails] + exec: Optional[TestCaseSetExecutionSummary] = None + + +@dataclass +class TestCaseDetails: + uniqueID: str + spec: TestCaseSpecificationDetails + testSequence: List[KeywordCall] + parameters: List[ParameterSummary] + keywords: List[KeywordDetails] + exec: Optional[TestCaseExecutionDetails] = None + origin: Optional[TestCaseDetailsOrigin] = None + + +@dataclass +class CycleForUpdate: + name: Optional[str] = None + endDate: Optional[OptionalLocalDate] = None + visible: Optional[bool] = None + description: Optional[RichTextInfo] = None + status: Optional[ProjectStatus] = None + testingIntelligence: Optional[bool] = None + startDate: Optional[OptionalLocalDate] = None From 48de07668364b37f7e77ad3e92b0059608d00cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81?= Date: Mon, 18 May 2026 18:46:21 +0200 Subject: [PATCH 06/15] ruff format py39 --- atest/robot/libs/json_config.py | 1 - atest/robot/libs/pyproject_config.py | 11 +- create_json_schema.py | 7 +- testbench2robotframework/cli.py | 4 +- testbench2robotframework/config.py | 4 +- testbench2robotframework/model.py | 1170 +++++++++++---------- testbench2robotframework/result_writer.py | 43 +- testbench2robotframework/testbench2rf.py | 2 +- 8 files changed, 645 insertions(+), 597 deletions(-) diff --git a/atest/robot/libs/json_config.py b/atest/robot/libs/json_config.py index 1a72a45..5411379 100644 --- a/atest/robot/libs/json_config.py +++ b/atest/robot/libs/json_config.py @@ -43,4 +43,3 @@ def create_json_configuration_file(path: Path): with path.open("w") as json_file: json.dump(data, json_file, indent=2) - diff --git a/atest/robot/libs/pyproject_config.py b/atest/robot/libs/pyproject_config.py index 7437d47..f40ba38 100644 --- a/atest/robot/libs/pyproject_config.py +++ b/atest/robot/libs/pyproject_config.py @@ -1,13 +1,8 @@ import tomli_w -data = { - "tool": { - "testbench2robotframework": { - "generationDirectory": "{root}/toml_config_tests" - } - } -} +data = {"tool": {"testbench2robotframework": {"generationDirectory": "{root}/toml_config_tests"}}} + def create_toml_configuration_file(): with open("pyproject.toml", "w") as toml_file: - toml_file.write(tomli_w.dumps(data)) \ No newline at end of file + toml_file.write(tomli_w.dumps(data)) diff --git a/create_json_schema.py b/create_json_schema.py index 0a1bd91..ce8cc3f 100644 --- a/create_json_schema.py +++ b/create_json_schema.py @@ -7,7 +7,12 @@ pydantic_model = re.sub(r"(@dataclass\n)(class .*?)(:)", r"\2(BaseModel)\3", model_str) pydantic_model = re.sub(r"( {4}@classmethod.*?)(\nclass|$)", r"\2", pydantic_model, flags=re.DOTALL) -pydantic_model = re.sub(r"from dataclasses import dataclass", r"from pydantic import BaseModel", pydantic_model, flags=re.DOTALL) +pydantic_model = re.sub( + r"from dataclasses import dataclass", + r"from pydantic import BaseModel", + pydantic_model, + flags=re.DOTALL, +) with open("pydantic_model.py", "w", encoding="utf8") as pydantic_model_py: diff --git a/testbench2robotframework/cli.py b/testbench2robotframework/cli.py index a144073..696e298 100644 --- a/testbench2robotframework/cli.py +++ b/testbench2robotframework/cli.py @@ -198,8 +198,8 @@ def generate_tests( # noqa: PLR0913 else: configuration["log-suite-numbering"] = configuration.get("log-suite-numbering", False) configuration["metadata"] = metadata or configuration.get("metadata", {}) - configuration["compound-keyword-logging"] = ( - compound_keyword_logging or configuration.get("compound-keyword-logging", "GROUP") + configuration["compound-keyword-logging"] = compound_keyword_logging or configuration.get( + "compound-keyword-logging", "GROUP" ) configuration["resource-directory"] = ( resource_directory.as_posix() diff --git a/testbench2robotframework/config.py b/testbench2robotframework/config.py index 63a8843..e645f77 100644 --- a/testbench2robotframework/config.py +++ b/testbench2robotframework/config.py @@ -198,7 +198,7 @@ class Configuration: loggingConfiguration: LoggingConfig metadata: dict[str, str] output_directory: str - phasePattern: str + phase_pattern: str referenceBehaviour: ReferenceBehaviour resource_directory: str resource_directory_regex: str @@ -238,7 +238,7 @@ def from_dict(cls, dictionary) -> Configuration: "\\", "/" ), testCaseSplitPathRegEx=dictionary.get("testcase-splitting-regex", ".*StopWithRestart.*"), - phasePattern=dictionary.get("phasePattern", "{testcase} : Phase {index}/{length}"), + phase_pattern=dictionary.get("phase-pattern", "{testcase} : Phase {index}/{length}"), referenceBehaviour=ReferenceBehaviour( dictionary.get("reference-behaviour", "ATTACHMENT").upper() ), diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index b71996c..b5dcec0 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -1,209 +1,211 @@ # generated by datamodel-codegen: # filename: openapi.yml -# timestamp: 2026-05-18T16:27:28+00:00 +# timestamp: 2026-05-18T16:35:43+00:00 from __future__ import annotations +__VERSION__ = "4.0.45" + from dataclasses import dataclass from enum import Enum -from typing import List, Optional +from typing import Any, Literal class TestElementStatus(Enum): - InProgress = 'InProgress' - Released = 'Released' + InProgress = "InProgress" + Released = "Released" class ContentType(Enum): - txt = 'txt' - xml = 'xml' + txt = "txt" + xml = "xml" class ReferenceUpdateMode(Enum): - Replace = 'Replace' - Extend = 'Extend' + Replace = "Replace" + Extend = "Extend" @dataclass class AdvancedContent: - contentType: Optional[ContentType] = None - contentExtID: Optional[str] = None - content: Optional[str] = None + contentType: ContentType | None = None + contentExtID: str | None = None + content: str | None = None @dataclass class OptionalAdvancedContent: - optional: Optional[AdvancedContent] = None + optional: AdvancedContent | None = None @dataclass class OptionalUser: - optional: Optional[str] = None + optional: str | None = None class ProjectStatus(Enum): - Planned = 'Planned' - Active = 'Active' - Finished = 'Finished' - Closed = 'Closed' + Planned = "Planned" + Active = "Active" + Finished = "Finished" + Closed = "Closed" class DefectMetricType(Enum): - Status = 'Status' - Priority = 'Priority' - Classification = 'Classification' + Status = "Status" + Priority = "Priority" + Classification = "Classification" class ProjectTreeNodeType(Enum): - Project = 'Project' - Version = 'Version' - Cycle = 'Cycle' + Project = "Project" + Version = "Version" + Cycle = "Cycle" class Severity(Enum): - Information = 'Information' - Warning = 'Warning' - Error = 'Error' + Information = "Information" + Warning = "Warning" + Error = "Error" class UDFType(Enum): - String = 'String' - Enumeration = 'Enumeration' - Boolean = 'Boolean' + String = "String" + Enumeration = "Enumeration" + Boolean = "Boolean" class TestLabelVisibilityType(Enum): - OnlyPrivate = 'OnlyPrivate' - OnlyPublic = 'OnlyPublic' - All = 'All' + OnlyPrivate = "OnlyPrivate" + OnlyPublic = "OnlyPublic" + All = "All" class AutStatus(Enum): - NotPlanned = 'NotPlanned' - Planned = 'Planned' - InProgress = 'InProgress' - InReview = 'InReview' - Released = 'Released' + NotPlanned = "NotPlanned" + Planned = "Planned" + InProgress = "InProgress" + InReview = "InReview" + Released = "Released" class SpecStatus(Enum): - NotPlanned = 'NotPlanned' - Planned = 'Planned' - InProgress = 'InProgress' - InReview = 'InReview' - Released = 'Released' + NotPlanned = "NotPlanned" + Planned = "Planned" + InProgress = "InProgress" + InReview = "InReview" + Released = "Released" class Priority(Enum): - Undefined = 'Undefined' - Low = 'Low' - Middle = 'Middle' - High = 'High' + Undefined = "Undefined" + Low = "Low" + Middle = "Middle" + High = "High" class ActivityStatus(Enum): - NotPlanned = 'NotPlanned' - Planned = 'Planned' - Assigned = 'Assigned' - Running = 'Running' - Skipped = 'Skipped' - Canceled = 'Canceled' - Performed = 'Performed' + NotPlanned = "NotPlanned" + Planned = "Planned" + Assigned = "Assigned" + Running = "Running" + Skipped = "Skipped" + Canceled = "Canceled" + Performed = "Performed" class ExecStatus(Enum): - NotBlocked = 'NotBlocked' - Blocked = 'Blocked' + NotBlocked = "NotBlocked" + Blocked = "Blocked" class VerdictStatus(Enum): - Undefined = 'Undefined' - ToVerify = 'ToVerify' - Fail = 'Fail' - Pass = 'Pass' + Undefined = "Undefined" + ToVerify = "ToVerify" + Fail = "Fail" + Pass = "Pass" class KeywordVerdict(Enum): - Pass = 'Pass' - Fail = 'Fail' - Skipped = 'Skipped' - ToVerify = 'ToVerify' - Warn = 'Warn' - Undefined = 'Undefined' - Blocked = 'Blocked' + Pass = "Pass" + Fail = "Fail" + Skipped = "Skipped" + ToVerify = "ToVerify" + Warn = "Warn" + Undefined = "Undefined" + Blocked = "Blocked" class SequencePhase(Enum): - Setup = 'Setup' - TestStep = 'TestStep' - Teardown = 'Teardown' + Setup = "Setup" + TestStep = "TestStep" + Teardown = "Teardown" class KeywordCallType(Enum): - Flow = 'Flow' - Check = 'Check' + Flow = "Flow" + Check = "Check" class KeywordType(Enum): - Atomic = 'Atomic' - Compound = 'Compound' - Textual = 'Textual' + Atomic = "Atomic" + Compound = "Compound" + Textual = "Textual" class OperationalState(Enum): - Enabled = 'Enabled' - Disabled = 'Disabled' + Enabled = "Enabled" + Disabled = "Disabled" class GlobalHumanRole(Enum): - Administrator = 'Administrator' - ProjectAdministrator = 'ProjectAdministrator' - ProjectUser = 'ProjectUser' + Administrator = "Administrator" + ProjectAdministrator = "ProjectAdministrator" + ProjectUser = "ProjectUser" class ProjectRole(Enum): - TestManager = 'TestManager' - TestDesigner = 'TestDesigner' - TestProgrammer = 'TestProgrammer' - Tester = 'Tester' - ReadOnlyDesigner = 'ReadOnlyDesigner' - ReadOnlyImplementer = 'ReadOnlyImplementer' - ReadOnlyTester = 'ReadOnlyTester' + TestManager = "TestManager" + TestDesigner = "TestDesigner" + TestProgrammer = "TestProgrammer" + Tester = "Tester" + ReadOnlyDesigner = "ReadOnlyDesigner" + ReadOnlyImplementer = "ReadOnlyImplementer" + ReadOnlyTester = "ReadOnlyTester" class TOVExchangeFormat(Enum): - xml = 'xml' - json = 'json' - inherited = 'inherited' + xml = "xml" + json = "json" + inherited = "inherited" class ProjectExchangeFormat(Enum): - default_xml = 'default_xml' - default_json = 'default_json' + default_xml = "default_xml" + default_json = "default_json" class ImportResult(Enum): - Imported = 'Imported' - PartiallyImported = 'PartiallyImported' - NotImported = 'NotImported' + Imported = "Imported" + PartiallyImported = "PartiallyImported" + NotImported = "NotImported" class ExecutionMode(Enum): - execute = 'execute' - continue_ = 'continue' - view = 'view' - simulate = 'simulate' + execute = "execute" + continue_ = "continue" + view = "view" + simulate = "simulate" @dataclass class UserGlobalRoles: - roles: List[GlobalHumanRole] + roles: list[GlobalHumanRole] @dataclass class UserProjectRoles: userKey: str - roles: List[ProjectRole] + roles: list[ProjectRole] @dataclass @@ -211,7 +213,7 @@ class ProjectUser: key: str name: str login: str - projectRoles: List[ProjectRole] + projectRoles: list[ProjectRole] @dataclass @@ -225,8 +227,8 @@ class LicenseWarning: class LoginData: login: str password: str - force: Optional[bool] = None - context: Optional[str] = None + force: bool | None = None + context: str | None = None @dataclass @@ -241,16 +243,16 @@ class LoginSession: userKey: str login: str sessionToken: str - globalRoles: List[str] + globalRoles: list[str] internalUserManagement: bool serverVersion: str - licenseWarning: Optional[LicenseWarning] = None + licenseWarning: LicenseWarning | None = None @dataclass class ActiveUser: login: str - contexts: List[str] + contexts: list[str] @dataclass @@ -259,19 +261,19 @@ class TestLabel: name: str ownerKey: str visibility: bool - libraryKey: Optional[str] = None + libraryKey: str | None = None @dataclass class TestLabelDataForUpdate: - name: Optional[str] = None - visibility: Optional[bool] = None + name: str | None = None + visibility: bool | None = None @dataclass class TestLabelDataForInsert: name: str - adaptName: Optional[bool] = True + adaptName: bool | None = True @dataclass @@ -281,7 +283,7 @@ class ProjectMember: userName: str projectKey: str projectName: str - roles: List[ProjectRole] + roles: list[ProjectRole] @dataclass @@ -301,16 +303,16 @@ class ProjectSummary: tovsCount: int cyclesCount: int description: str - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None + lockerKey: str | None = None + startDate: str | None = None + endDate: str | None = None @dataclass class ProjectContext: tovName: str - cycleName: Optional[str] = None - executionMode: Optional[ExecutionMode] = None + cycleName: str | None = None + executionMode: ExecutionMode | None = None @dataclass @@ -324,10 +326,10 @@ class ProjectDetails: cyclesCount: int description: str exchangeFormat: ProjectExchangeFormat - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - projectContext: Optional[ProjectContext] = None + lockerKey: str | None = None + startDate: str | None = None + endDate: str | None = None + projectContext: ProjectContext | None = None @dataclass @@ -352,17 +354,18 @@ class ProjectCreation: @dataclass class UnknownProject(ProjectCreation): - pass + type: Literal["UnknownProject"] @dataclass class NewProject(ProjectCreation): - pass + type: Literal["NewProject"] @dataclass class ProjectImported(ProjectCreation): project: str + type: Literal["ProjectImported"] @dataclass @@ -372,37 +375,41 @@ class TOVCreation: @dataclass class UnknownTOV(TOVCreation): - pass + type: Literal["UnknownTOV"] @dataclass class NewTOV(TOVCreation): - pass + type: Literal["NewTOV"] @dataclass class TOVCloned(TOVCreation): tov: str - tovKey: Optional[str] = None + tovKey: str | None = None + type: Literal["TOVCloned"] @dataclass class TOVClonedFromSameProject(TOVCreation): tov: str - tovKey: Optional[str] = None + tovKey: str | None = None + type: Literal["TOVClonedFromSameProject"] @dataclass class TOVClonedFromDifferentProject(TOVCreation): project: str tov: str - tovKey: Optional[str] = None + tovKey: str | None = None + type: Literal["TOVClonedFromDifferentProject"] @dataclass class TOVImportedAsNew(TOVCreation): project: str tov: str + type: Literal["TOVImportedAsNew"] @dataclass @@ -410,12 +417,14 @@ class TOVImportedAsClone(TOVCreation): project: str tov: str tovCloned: str + type: Literal["TOVImportedAsClone"] @dataclass class TOVImportedAsNewFromPlugin(TOVCreation): plugin: str tov: str + type: Literal["TOVImportedAsNewFromPlugin"] @dataclass @@ -423,20 +432,23 @@ class TOVImportedAsCloneFromPlugin(TOVCreation): plugin: str tov: str tovCloned: str + type: Literal["TOVImportedAsCloneFromPlugin"] @dataclass class TOVImported(TOVCreation): project: str tov: str + type: Literal["TOVImported"] @dataclass class TOVDerivedFromSameProject(TOVCreation): baseTOV: str variantsDefinition: str - baseTOVKey: Optional[str] = None - variantsDefinitionKey: Optional[str] = None + baseTOVKey: str | None = None + variantsDefinitionKey: str | None = None + type: Literal["TOVDerivedFromSameProject"] @dataclass @@ -444,15 +456,16 @@ class TOVDerivedFromDifferentProject(TOVCreation): project: str baseTOV: str variantsDefinition: str - baseTOVKey: Optional[str] = None - variantsDefinitionKey: Optional[str] = None + baseTOVKey: str | None = None + variantsDefinitionKey: str | None = None + type: Literal["TOVDerivedFromDifferentProject"] @dataclass class ProjectDetailsResponse: projectKey: str name: str - inspection: List[ProjectInspection] + inspection: list[ProjectInspection] testObjectName: str id: str customer: ProjectCustomer @@ -460,13 +473,13 @@ class ProjectDetailsResponse: visible: bool testIntelligence: bool description: str - creation: ProjectCreation + creation: UnknownProject | NewProject | ProjectImported instantOfCreation: str onlyAdminsMayManageUDFs: bool variantsManagementEnabled: str exchangeFormat: ProjectExchangeFormat - startDate: Optional[str] = None - endDate: Optional[str] = None + startDate: str | None = None + endDate: str | None = None @dataclass @@ -479,9 +492,9 @@ class TOVSummary: cyclesCount: int description: str exchangeFormat: TOVExchangeFormat - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None + lockerKey: str | None = None + startDate: str | None = None + endDate: str | None = None @dataclass @@ -494,9 +507,9 @@ class TOVDetails: cyclesCount: int description: str exchangeFormat: TOVExchangeFormat - lockerKey: Optional[str] = None - startDate: Optional[str] = None - endDate: Optional[str] = None + lockerKey: str | None = None + startDate: str | None = None + endDate: str | None = None @dataclass @@ -506,20 +519,33 @@ class TOVResponse: projectKey: str description: str isBaseTov: bool - creation: TOVCreation + creation: ( + UnknownTOV + | NewTOV + | TOVCloned + | TOVClonedFromSameProject + | TOVClonedFromDifferentProject + | TOVImportedAsNew + | TOVImportedAsClone + | TOVImportedAsNewFromPlugin + | TOVImportedAsCloneFromPlugin + | TOVImported + | TOVDerivedFromSameProject + | TOVDerivedFromDifferentProject + ) key: str status: ProjectStatus testingIntelligence: bool instantOfCreation: str cloningVisibility: bool exchangeFormat: TOVExchangeFormat - endDate: Optional[str] = None - startDate: Optional[str] = None + endDate: str | None = None + startDate: str | None = None @dataclass class OptionalLocalDate: - optional: Optional[str] = None + optional: str | None = None @dataclass @@ -530,8 +556,8 @@ class CycleSummary: status: ProjectStatus visibility: bool description: str - startDate: Optional[str] = None - endDate: Optional[str] = None + startDate: str | None = None + endDate: str | None = None @dataclass @@ -542,8 +568,8 @@ class CycleDetails: status: ProjectStatus visibility: bool description: str - startDate: Optional[str] = None - endDate: Optional[str] = None + startDate: str | None = None + endDate: str | None = None @dataclass @@ -576,11 +602,11 @@ class UserDataForInsert: @dataclass class UserDataForUpdate: - login: Optional[str] = None - name: Optional[str] = None - email: Optional[str] = None - passwordExpired: Optional[bool] = None - active: Optional[bool] = None + login: str | None = None + name: str | None = None + email: str | None = None + passwordExpired: bool | None = None + active: bool | None = None @dataclass @@ -610,7 +636,7 @@ class ServerLocations: class ActionFailure: code: int message: str - description: Optional[str] = None + description: str | None = None @dataclass @@ -675,25 +701,7 @@ class ReportExportOptions: class ReportScope: projectKey: str tovKey: str - cycleKey: Optional[str] = None - - -@dataclass -class ReportCreation: - creator: UserInfo - startDate: str - endDate: str - scope: ReportScope - summary: ReportItemsSummary - exportOptions: Optional[ReportExportOptions] = None - - -@dataclass -class ReportMetaInformation: - formatVersion: str - serverLocation: ServerLocation - reportCreation: ReportCreation - serverVersions: TestBenchVersions + cycleKey: str | None = None @dataclass @@ -711,9 +719,9 @@ class MetricsDistribution: @dataclass class DefectsDistribution: - statusDistribution: Optional[List[MetricsDistribution]] = None - priorityDistribution: Optional[List[MetricsDistribution]] = None - classDistribution: Optional[List[MetricsDistribution]] = None + statusDistribution: list[MetricsDistribution] | None = None + priorityDistribution: list[MetricsDistribution] | None = None + classDistribution: list[MetricsDistribution] | None = None @dataclass @@ -722,7 +730,7 @@ class ConditionSummary: uniqueID: str name: str description: str - version: Optional[str] = None + version: str | None = None @dataclass @@ -732,10 +740,10 @@ class SpecificationSummary: reviewComment: str status: SpecStatus priority: Priority - locker: Optional[UserReference] = None - responsible: Optional[UserReference] = None - dueDate: Optional[str] = None - reviewer: Optional[UserReference] = None + locker: UserReference | None = None + responsible: UserReference | None = None + dueDate: str | None = None + reviewer: UserReference | None = None @dataclass @@ -745,25 +753,25 @@ class TestCaseSetSpecificationSummary: reviewComment: str status: SpecStatus priority: Priority - preConditions: List[ConditionSummary] - postConditions: List[ConditionSummary] - udfs: List[UserDefinedField] - tags: List[Tag] - references: List[str] - requirements: List[RequirementReference] - responsible: Optional[UserReference] = None - dueDate: Optional[str] = None - reviewer: Optional[UserReference] = None + preConditions: list[ConditionSummary] + postConditions: list[ConditionSummary] + udfs: list[UserDefinedField] + tags: list[Tag] + references: list[str] + requirements: list[RequirementReference] + responsible: UserReference | None = None + dueDate: str | None = None + reviewer: UserReference | None = None @dataclass class TestCaseSpecificationDetails: key: str comments: str - udfs: List[UserDefinedField] - tags: List[Tag] - requirements: List[RequirementReference] - version: Optional[str] = None + udfs: list[UserDefinedField] + tags: list[Tag] + requirements: list[RequirementReference] + version: str | None = None @dataclass @@ -773,28 +781,28 @@ class TestThemeSpecification: reviewComment: str status: SpecStatus priority: Priority - udfs: List[UserDefinedField] - tags: List[Tag] - requirements: List[RequirementReference] - references: List[str] - dueDate: Optional[str] = None - reviewer: Optional[UserReference] = None - responsible: Optional[UserReference] = None + udfs: list[UserDefinedField] + tags: list[Tag] + requirements: list[RequirementReference] + references: list[str] + dueDate: str | None = None + reviewer: UserReference | None = None + responsible: UserReference | None = None @dataclass class TestCaseSetExecutionSummary: key: str comments: str - udfs: List[UserDefinedField] - tags: List[Tag] + udfs: list[UserDefinedField] + tags: list[Tag] @dataclass class TestCaseSpecificationSummary: key: str comments: str - requirements: List[RequirementReference] + requirements: list[RequirementReference] @dataclass @@ -803,9 +811,9 @@ class TestCaseExecutionSummary: status: ActivityStatus execStatus: ExecStatus verdict: VerdictStatus - defects: List[str] + defects: list[str] comments: str - tester: Optional[UserReference] = None + tester: UserReference | None = None @dataclass @@ -813,7 +821,7 @@ class TestCaseSummary: uniqueID: str index: int spec: TestCaseSpecificationSummary - exec: Optional[TestCaseExecutionSummary] = None + exec: TestCaseExecutionSummary | None = None @dataclass @@ -826,19 +834,19 @@ class TestCaseExecutionDetails: actualDuration: int currentUser: UserReference comments: str - defects: List[str] - udfs: List[UserDefinedField] - tags: List[Tag] - references: List[str] - version: Optional[str] = None - tester: Optional[UserReference] = None + defects: list[str] + udfs: list[UserDefinedField] + tags: list[Tag] + references: list[str] + version: str | None = None + tester: UserReference | None = None class KindOfDataType(Enum): - Regular = 'Regular' - Reference = 'Reference' - Global = 'Global' - AcceptingGlobal = 'AcceptingGlobal' + Regular = "Regular" + Reference = "Reference" + Global = "Global" + AcceptingGlobal = "AcceptingGlobal" @dataclass @@ -848,20 +856,20 @@ class DataTypeSummary: name: str path: str uniqueID: str - version: Optional[str] = None + version: str | None = None class ParameterDefinitionType(Enum): - DetailedInstance = 'DetailedInstance' - InstanceTable = 'InstanceTable' - AtomicInstance = 'AtomicInstance' + DetailedInstance = "DetailedInstance" + InstanceTable = "InstanceTable" + AtomicInstance = "AtomicInstance" @dataclass class ParameterValue: name: str key: str - dtSequenceKeys: List[str] + dtSequenceKeys: list[str] @dataclass @@ -871,79 +879,81 @@ class InstanceArrayValue: class ParameterEvaluationType(Enum): - CallByValue = 'CallByValue' - CallByReference = 'CallByReference' - CallByReferenceMandatory = 'CallByReferenceMandatory' + CallByValue = "CallByValue" + CallByReference = "CallByReference" + CallByReferenceMandatory = "CallByReferenceMandatory" class RepresentativeType(Enum): - Text = 'Text' - Placeholder = 'Placeholder' - Attachment = 'Attachment' - Hyperlink = 'Hyperlink' - Reference = 'Reference' + Text = "Text" + Placeholder = "Placeholder" + Attachment = "Attachment" + Hyperlink = "Hyperlink" + Reference = "Reference" class ArgumentValueType(Enum): - EquivalenceClass = 'EquivalenceClass' - Representative = 'Representative' - InstancesArray = 'InstancesArray' - CBRRepresentative = 'CBRRepresentative' + EquivalenceClass = "EquivalenceClass" + Representative = "Representative" + InstancesArray = "InstancesArray" + CBRRepresentative = "CBRRepresentative" class TestCaseDetailsOrigin(Enum): - Fallback = 'Fallback' - Generated = 'Generated' - Rejected = 'Rejected' - Restored = 'Restored' - Upgraded = 'Upgraded' + Fallback = "Fallback" + Generated = "Generated" + Rejected = "Rejected" + Restored = "Restored" + Upgraded = "Upgraded" class ReferenceKind(Enum): - Reference = 'Reference' - Link = 'Link' - Attachment = 'Attachment' + Reference = "Reference" + Link = "Link" + Attachment = "Attachment" class TestFilterType(Enum): - TestTheme = 'TestTheme' - TestCaseSet = 'TestCaseSet' - TestCase = 'TestCase' + TestTheme = "TestTheme" + TestCaseSet = "TestCaseSet" + TestCase = "TestCase" @dataclass class FilterInfo: name: str filterType: TestFilterType - testThemeUID: Optional[str] = None + testThemeUID: str | None = None @dataclass class TovStructureOptions(ReportExportOptions): - treeRootUID: Optional[str] = None - suppressFilteredData: Optional[bool] = None - suppressEmptyTestThemes: Optional[bool] = None - filters: Optional[List[FilterInfo]] = None + treeRootUID: str | None = None + suppressFilteredData: bool | None = None + suppressEmptyTestThemes: bool | None = None + filters: list[FilterInfo] | None = None + exportType: Literal["tovExport"] @dataclass class CycleStructureOptions: - treeRootUID: Optional[str] = None - basedOnExecution: Optional[bool] = None - suppressFilteredData: Optional[bool] = None - suppressNotExecutable: Optional[bool] = None - suppressEmptyTestThemes: Optional[bool] = None - filters: Optional[List[FilterInfo]] = None + treeRootUID: str | None = None + basedOnExecution: bool | None = None + suppressFilteredData: bool | None = None + suppressNotExecutable: bool | None = None + suppressEmptyTestThemes: bool | None = None + filters: list[FilterInfo] | None = None @dataclass class CycleReportOptions(ReportExportOptions): - treeRootUID: Optional[str] = None - executionMode: Optional[ExecutionMode] = None - suppressFilteredData: Optional[bool] = None - suppressNotExecutable: Optional[bool] = None - suppressEmptyTestThemes: Optional[bool] = None - filters: Optional[List[FilterInfo]] = None + treeRootUID: str | None = None + executionMode: ExecutionMode | None = None + suppressFilteredData: bool | None = None + suppressNotExecutable: bool | None = None + suppressEmptyTestThemes: bool | None = None + filters: list[FilterInfo] | None = None + exportType: Literal["cycleExport"] @dataclass @@ -954,8 +964,8 @@ class DefectAttribute: @dataclass class ProjectDefectField: - values: List[str] - defaultValue: Optional[str] = None + values: list[str] + defaultValue: str | None = None @dataclass @@ -963,7 +973,7 @@ class DefectUDF: name: str udfType: UDFType isMandatory: bool - values: Optional[List[str]] = None + values: list[str] | None = None @dataclass @@ -981,7 +991,7 @@ class TestStructureExecution: @dataclass class TestStructureItemExecution(TestStructureExecution): key: str - locker: Optional[UserReference] = None + locker: UserReference | None = None @dataclass @@ -998,7 +1008,7 @@ class TestStructureSpecification: class TestStructureItemSpecification(TestStructureSpecification): key: str status: SpecStatus - locker: Optional[UserReference] = None + locker: UserReference | None = None @dataclass @@ -1030,14 +1040,14 @@ class TestCaseBaseInformation: class TestStructureAutomation: key: str status: AutStatus - locker: Optional[UserReference] = None + locker: UserReference | None = None class TestStructureElementType(Enum): - RootNode = 'RootNode' - TestThemeNode = 'TestThemeNode' - TestCaseSetNode = 'TestCaseSetNode' - TestCaseNode = 'TestCaseNode' + RootNode = "RootNode" + TestThemeNode = "TestThemeNode" + TestCaseSetNode = "TestCaseSetNode" + TestCaseNode = "TestCaseNode" @dataclass @@ -1048,12 +1058,6 @@ class AttachedFilter: content: str -@dataclass -class TestStructureTree: - nodes: List[TestStructureTreeNode] - root: Optional[TestStructureTreeNode] = None - - @dataclass class CreatedJob: jobID: str @@ -1073,11 +1077,13 @@ class ReportingResult: @dataclass class ReportingFailure(ReportingResult): error: ActionFailure + type: Literal["ReportingFailure"] @dataclass class ReportingSuccess(ReportingResult): reportName: str + type: Literal["ReportingSuccess"] @dataclass @@ -1088,6 +1094,7 @@ class ExecutionImportingResult: @dataclass class ExecutionImportingFailure(ExecutionImportingResult): error: ActionFailure + type: Literal["ExecutionImportingFailure"] @dataclass @@ -1107,7 +1114,7 @@ class CreatedDefect: class CreatedReference: foreignKey: str createdKey: str - newFileName: Optional[str] = None + newFileName: str | None = None @dataclass @@ -1116,21 +1123,21 @@ class TestCaseExecutionImportResult: executionKey: str uid: str importResult: ImportResult - warnings: List[ActionFailure] - error: Optional[ActionFailure] = None + warnings: list[ActionFailure] + error: ActionFailure | None = None @dataclass class CheckInData: comment: str - label: Optional[str] = None + label: str | None = None @dataclass class ExecutionImportSimulationOptions: fileName: str - treeRootUID: Optional[str] = None - filters: Optional[List[FilterInfo]] = None + treeRootUID: str | None = None + filters: list[FilterInfo] | None = None @dataclass @@ -1152,11 +1159,11 @@ class TestThemeExecution: status: ActivityStatus execStatus: ExecStatus verdict: VerdictStatus - udfs: List[UserDefinedField] - tags: List[Tag] - references: List[str] - comments: Optional[str] = None - responsible: Optional[UserReference] = None + udfs: list[UserDefinedField] + tags: list[Tag] + references: list[str] + comments: str | None = None + responsible: UserReference | None = None @dataclass @@ -1167,14 +1174,14 @@ class TestThemeDetails: numbering: str path: str spec: TestThemeSpecification - exec: Optional[TestThemeExecution] = None + exec: TestThemeExecution | None = None @dataclass class TestObjectVersionCSVReportOptions: - reportRootUID: Optional[str] = None - fields: Optional[List[str]] = None - characterEncoding: Optional[str] = None + reportRootUID: str | None = None + fields: list[str] | None = None + characterEncoding: str | None = None @dataclass @@ -1185,24 +1192,24 @@ class JWTResponse: @dataclass class TestCycleCSVReportOptions: - reportRootUID: Optional[str] = None - fields: Optional[List[str]] = None - characterEncoding: Optional[str] = None - basedOn: Optional[str] = None + reportRootUID: str | None = None + fields: list[str] | None = None + characterEncoding: str | None = None + basedOn: str | None = None class UDFLocation(Enum): - TestThemesInSpecification = 'TestThemesInSpecification' - TestCaseSetsInSpecification = 'TestCaseSetsInSpecification' - TestCasesInSpecification = 'TestCasesInSpecification' - TestThemesInExecution = 'TestThemesInExecution' - TestCaseSetsInExecution = 'TestCaseSetsInExecution' - TestCasesInExecution = 'TestCasesInExecution' + TestThemesInSpecification = "TestThemesInSpecification" + TestCaseSetsInSpecification = "TestCaseSetsInSpecification" + TestCasesInSpecification = "TestCasesInSpecification" + TestThemesInExecution = "TestThemesInExecution" + TestCaseSetsInExecution = "TestCaseSetsInExecution" + TestCasesInExecution = "TestCasesInExecution" class UDFAbsolutePosition(Enum): - First = 'First' - Last = 'Last' + First = "First" + Last = "Last" @dataclass @@ -1217,16 +1224,16 @@ class BeforeUDF: @dataclass class UDFForUpdate: - name: Optional[str] = None - isMandatory: Optional[bool] = None - definedFor: Optional[List[UDFLocation]] = None - udfType: Optional[UDFType] = None - enumerationValues: Optional[List[UDFEnumerationValue]] = None + name: str | None = None + isMandatory: bool | None = None + definedFor: list[UDFLocation] | None = None + udfType: UDFType | None = None + enumerationValues: list[UDFEnumerationValue] | None = None @dataclass class UDFsMoveResponse: - targetKeys: List[str] + targetKeys: list[str] @dataclass @@ -1234,7 +1241,7 @@ class TestFilter: key: str name: str owner: str - pluginName: Optional[str] = None + pluginName: str | None = None @dataclass @@ -1261,6 +1268,7 @@ class CycleCreation: @dataclass class CycleCloned(CycleCreation): cycle: str + type: Literal["CycleCloned"] @dataclass @@ -1268,12 +1276,14 @@ class CycleImported(CycleCreation): project: str tov: str cycle: str + type: Literal["CycleImported"] @dataclass class CycleImportedAsNew(CycleCreation): tov: str cycle: str + type: Literal["CycleImportedAsNew"] @dataclass @@ -1281,6 +1291,7 @@ class CycleImportedAsClone(CycleCreation): tov: str cycle: str clonedCycle: str + type: Literal["CycleImportedAsClone"] @dataclass @@ -1288,12 +1299,14 @@ class CycleImportedAsNewFromPlugin(CycleCreation): plugin: str tov: str cycle: str + type: Literal["CycleImportedAsNewFromPlugin"] @dataclass class CycleClonedFromPlugin(CycleCreation): plugin: str cycle: str + type: Literal["CycleClonedFromPlugin"] @dataclass @@ -1302,16 +1315,17 @@ class CycleImportedAsCloneFromPlugin(CycleCreation): tov: str cycle: str clonedCycle: str + type: Literal["CycleImportedAsCloneFromPlugin"] @dataclass class UnknownCycle(CycleCreation): - pass + type: Literal["UnknownCycle"] @dataclass class NewCycle(CycleCreation): - pass + type: Literal["NewCycle"] @dataclass @@ -1334,8 +1348,8 @@ class UdfValueForImport: @dataclass class RichTextForImport: - html: Optional[str] = None - plain: Optional[str] = None + html: str | None = None + plain: str | None = None @dataclass @@ -1343,7 +1357,7 @@ class ExecutionResultForImport: status: ActivityStatus execStatus: ExecStatus verdict: VerdictStatus - timestamp: Optional[str] = None + timestamp: str | None = None @dataclass @@ -1352,11 +1366,11 @@ class TestCaseExecutionForImport: testCaseExecutionKey: str result: ExecutionResultForImport durationMillis: int - testerKey: Optional[str] = None - comments: Optional[RichTextForImport] = None - defects: Optional[List[str]] = None - udfs: Optional[List[UdfValueForImport]] = None - references: Optional[List[str]] = None + testerKey: str | None = None + comments: RichTextForImport | None = None + defects: list[str] | None = None + udfs: list[UdfValueForImport] | None = None + references: list[str] | None = None @dataclass @@ -1364,10 +1378,10 @@ class TestCaseSetExecutionForImport: testCaseSetKey: str executionKey: str durationMillis: int - testCases: List[TestCaseExecutionForImport] - testerKey: Optional[str] = None - comments: Optional[RichTextForImport] = None - udfs: Optional[List[UdfValueForImport]] = None + testCases: list[TestCaseExecutionForImport] + testerKey: str | None = None + comments: RichTextForImport | None = None + udfs: list[UdfValueForImport] | None = None @dataclass @@ -1376,66 +1390,66 @@ class KeywordCallExecution: duration: int currentUser: UserReference comments: str - references: List[str] - defects: List[str] - time: Optional[str] = None - tester: Optional[UserReference] = None + references: list[str] + defects: list[str] + time: str | None = None + tester: UserReference | None = None class Permission(Enum): - AccessSecuredData = 'AccessSecuredData' - AdministerServer = 'AdministerServer' - DeleteUserAccount = 'DeleteUserAccount' - DeleteUserSession = 'DeleteUserSession' - DownloadReportFile = 'DownloadReportFile' - ExecuteOthersTestCaseSets = 'ExecuteOthersTestCaseSets' - ImportExecutionResults = 'ImportExecutionResults' - ModifyGlobalTestLabels = 'ModifyGlobalTestLabels' - ModifyProjectDetails = 'ModifyProjectDetails' - ModifyProjectUDFs = 'ModifyProjectUDFs' - ModifySpecifications = 'ModifySpecifications' - ModifySpecManagementInfo = 'ModifySpecManagementInfo' - ModifySpecPriorityAndDueDate = 'ModifySpecPriorityAndDueDate' - ModifyTestElements = 'ModifyTestElements' - ModifyTestLabels = 'ModifyTestLabels' - ModifyUserData = 'ModifyUserData' - ModifyUserRolesInProject = 'ModifyUserRolesInProject' - PrivatizeGlobalTestLabels = 'PrivatizeGlobalTestLabels' - ReadActiveUsersList = 'ReadActiveUsersList' - ReadCompleteProjectsList = 'ReadCompleteProjectsList' - ReadCompleteUsersList = 'ReadCompleteUsersList' - ReadCycleReport = 'ReadCycleReport' - ReadCycleReportOverRMI = 'ReadCycleReportOverRMI' - ReadCycleRequirements = 'ReadCycleRequirements' - ReadDefectsMetricDistribution = 'ReadDefectsMetricDistribution' - ReadExecutionImportingJobDetails = 'ReadExecutionImportingJobDetails' - ReadInvisibleProjectContent = 'ReadInvisibleProjectContent' - ReadOwnProjectsList = 'ReadOwnProjectsList' - ReadOwnUserDetails = 'ReadOwnUserDetails' - ReadProjectDefectsAndTheirAssignments = 'ReadProjectDefectsAndTheirAssignments' - ReadProjectDetails = 'ReadProjectDetails' - ReadProjectExportOverRMI = 'ReadProjectExportOverRMI' - ReadProjectHierarchy = 'ReadProjectHierarchy' - ReadProjectMembers = 'ReadProjectMembers' - ReadProjectUDFs = 'ReadProjectUDFs' - ReadReportingJobDetails = 'ReadReportingJobDetails' - ReadTestCaseDetails = 'ReadTestCaseDetails' - ReadTestCaseSetDetails = 'ReadTestCaseSetDetails' - ReadTestElements = 'ReadTestElements' - ReadTestLabels = 'ReadTestLabels' - ReadTestThemeDetails = 'ReadTestThemeDetails' - ReadTestThemeStatusDistribution = 'ReadTestThemeStatusDistribution' - ReadTestThemeTree = 'ReadTestThemeTree' - ReadTovReport = 'ReadTovReport' - ReadTovReportOverRMI = 'ReadTovReportOverRMI' - ReadTovRequirements = 'ReadTovRequirements' - ReadUserDetails = 'ReadUserDetails' - ReadUserMemberships = 'ReadUserMemberships' - ReadUserSessions = 'ReadUserSessions' - RestrictProjectUDFs = 'RestrictProjectUDFs' - SynchronizeUsers = 'SynchronizeUsers' - UnlockForeignSpecs = 'UnlockForeignSpecs' - UnlockForeignTestElements = 'UnlockForeignTestElements' + AccessSecuredData = "AccessSecuredData" + AdministerServer = "AdministerServer" + DeleteUserAccount = "DeleteUserAccount" + DeleteUserSession = "DeleteUserSession" + DownloadReportFile = "DownloadReportFile" + ExecuteOthersTestCaseSets = "ExecuteOthersTestCaseSets" + ImportExecutionResults = "ImportExecutionResults" + ModifyGlobalTestLabels = "ModifyGlobalTestLabels" + ModifyProjectDetails = "ModifyProjectDetails" + ModifyProjectUDFs = "ModifyProjectUDFs" + ModifySpecifications = "ModifySpecifications" + ModifySpecManagementInfo = "ModifySpecManagementInfo" + ModifySpecPriorityAndDueDate = "ModifySpecPriorityAndDueDate" + ModifyTestElements = "ModifyTestElements" + ModifyTestLabels = "ModifyTestLabels" + ModifyUserData = "ModifyUserData" + ModifyUserRolesInProject = "ModifyUserRolesInProject" + PrivatizeGlobalTestLabels = "PrivatizeGlobalTestLabels" + ReadActiveUsersList = "ReadActiveUsersList" + ReadCompleteProjectsList = "ReadCompleteProjectsList" + ReadCompleteUsersList = "ReadCompleteUsersList" + ReadCycleReport = "ReadCycleReport" + ReadCycleReportOverRMI = "ReadCycleReportOverRMI" + ReadCycleRequirements = "ReadCycleRequirements" + ReadDefectsMetricDistribution = "ReadDefectsMetricDistribution" + ReadExecutionImportingJobDetails = "ReadExecutionImportingJobDetails" + ReadInvisibleProjectContent = "ReadInvisibleProjectContent" + ReadOwnProjectsList = "ReadOwnProjectsList" + ReadOwnUserDetails = "ReadOwnUserDetails" + ReadProjectDefectsAndTheirAssignments = "ReadProjectDefectsAndTheirAssignments" + ReadProjectDetails = "ReadProjectDetails" + ReadProjectExportOverRMI = "ReadProjectExportOverRMI" + ReadProjectHierarchy = "ReadProjectHierarchy" + ReadProjectMembers = "ReadProjectMembers" + ReadProjectUDFs = "ReadProjectUDFs" + ReadReportingJobDetails = "ReadReportingJobDetails" + ReadTestCaseDetails = "ReadTestCaseDetails" + ReadTestCaseSetDetails = "ReadTestCaseSetDetails" + ReadTestElements = "ReadTestElements" + ReadTestLabels = "ReadTestLabels" + ReadTestThemeDetails = "ReadTestThemeDetails" + ReadTestThemeStatusDistribution = "ReadTestThemeStatusDistribution" + ReadTestThemeTree = "ReadTestThemeTree" + ReadTovReport = "ReadTovReport" + ReadTovReportOverRMI = "ReadTovReportOverRMI" + ReadTovRequirements = "ReadTovRequirements" + ReadUserDetails = "ReadUserDetails" + ReadUserMemberships = "ReadUserMemberships" + ReadUserSessions = "ReadUserSessions" + RestrictProjectUDFs = "RestrictProjectUDFs" + SynchronizeUsers = "SynchronizeUsers" + UnlockForeignSpecs = "UnlockForeignSpecs" + UnlockForeignTestElements = "UnlockForeignTestElements" @dataclass @@ -1451,17 +1465,17 @@ class SubdivisionDetails: uniqueID: str description: str path: str - references: List[str] - locker: Optional[UserReference] = None - parentUniqueID: Optional[str] = None - libraryKey: Optional[str] = None + references: list[str] + locker: UserReference | None = None + parentUniqueID: str | None = None + libraryKey: str | None = None @dataclass class KeywordParameterForInsert: name: str - dataTypeKey: Optional[str] = None - evaluationType: Optional[ParameterEvaluationType] = None + dataTypeKey: str | None = None + evaluationType: ParameterEvaluationType | None = None @dataclass @@ -1470,9 +1484,9 @@ class ParameterDetails: name: str definitionType: ParameterDefinitionType evaluationType: ParameterEvaluationType - dataTypeKey: Optional[str] = None - defaultValue: Optional[DefaultValue] = None - signatureID: Optional[str] = None + dataTypeKey: str | None = None + defaultValue: DefaultValue | None = None + signatureID: str | None = None @dataclass @@ -1484,7 +1498,7 @@ class TOVNode: status: ProjectStatus visibility: bool exchangeFormat: TOVExchangeFormat - children: List[CycleNode] + children: list[CycleNode] @dataclass @@ -1493,7 +1507,7 @@ class UDF: name: str projectKey: str isMandatory: bool - definedFor: List[UDFLocation] + definedFor: list[UDFLocation] @dataclass @@ -1503,7 +1517,7 @@ class BooleanUDF(UDF): @dataclass class EnumerationUDF(UDF): - enumerationValues: List[UDFEnumerationValue] + enumerationValues: list[UDFEnumerationValue] @dataclass @@ -1516,7 +1530,25 @@ class ReferenceAssignment: key: str value: str referenceType: ReferenceKind - versionName: Optional[str] = None + versionName: str | None = None + + +@dataclass +class ReportCreation: + creator: UserInfo + startDate: str + endDate: str + scope: ReportScope + summary: ReportItemsSummary + exportOptions: TovStructureOptions | CycleReportOptions | None = None + + +@dataclass +class ReportMetaInformation: + formatVersion: str + serverLocation: ServerLocation + reportCreation: ReportCreation + serverVersions: TestBenchVersions @dataclass @@ -1537,21 +1569,21 @@ class AssignedDefect: priority: str classification: str creationTime: str - references: List[str] - udfs: List[DefectAttribute] - version: Optional[str] = None - tester: Optional[str] = None - lastEditTime: Optional[str] = None - lastEditorKey: Optional[str] = None - defectManagementSystem: Optional[str] = None - defectManagementProject: Optional[str] = None + references: list[str] + udfs: list[DefectAttribute] + version: str | None = None + tester: str | None = None + lastEditTime: str | None = None + lastEditorKey: str | None = None + defectManagementSystem: str | None = None + defectManagementProject: str | None = None @dataclass class ExternalDefectManagement: system: str project: str - udfs: List[DefectUDF] + udfs: list[DefectUDF] @dataclass @@ -1559,49 +1591,54 @@ class DefectConfig: status: ProjectDefectField classification: ProjectDefectField priority: ProjectDefectField - external: Optional[ExternalDefectManagement] = None + external: ExternalDefectManagement | None = None @dataclass class RootNode(TestStructureTreeNode): base: TestStructureItemBaseInformation - filters: List[AttachedFilter] + filters: list[AttachedFilter] + elementType: Literal["RootNode"] @dataclass class TestThemeNode(TestStructureTreeNode): base: TestStructureItemBaseInformation - filters: List[AttachedFilter] - spec: Optional[TestStructureItemSpecification] = None - aut: Optional[TestStructureAutomation] = None - exec: Optional[TestStructureItemExecution] = None + filters: list[AttachedFilter] + spec: TestStructureItemSpecification | None = None + aut: TestStructureAutomation | None = None + exec: TestStructureItemExecution | None = None + elementType: Literal["TestThemeNode"] @dataclass class TestCaseSetNode(TestStructureTreeNode): base: TestStructureItemBaseInformation - spec: Optional[TestStructureItemSpecification] = None - aut: Optional[TestStructureAutomation] = None - exec: Optional[TestStructureItemExecution] = None + filters: Any + spec: TestStructureItemSpecification | None = None + aut: TestStructureAutomation | None = None + exec: TestStructureItemExecution | None = None + elementType: Literal["TestCaseSetNode"] @dataclass class TestCaseNode(TestStructureTreeNode): base: TestCaseBaseInformation - spec: Optional[TestCaseSpecification] = None - exec: Optional[TestCaseExecution] = None + spec: TestCaseSpecification | None = None + exec: TestCaseExecution | None = None + elementType: Literal["TestCaseNode"] @dataclass -class ReportingCompletion: - time: str - result: ReportingResult +class TestStructureTree: + nodes: list[RootNode | TestThemeNode | TestCaseSetNode | TestCaseNode] + root: RootNode | TestThemeNode | TestCaseSetNode | TestCaseNode | None = None @dataclass -class ExecutionImportingCompletion: +class ReportingCompletion: time: str - result: ExecutionImportingResult + result: ReportingFailure | ReportingSuccess @dataclass @@ -1611,61 +1648,61 @@ class TestCaseSetExecutionImportResult: name: str uid: str finished: bool - testCases: List[TestCaseExecutionImportResult] - error: Optional[ActionFailure] = None + testCases: list[TestCaseExecutionImportResult] + error: ActionFailure | None = None @dataclass class ExecutionImportOptions: fileName: str - treeRootUID: Optional[str] = None - useExistingDefect: Optional[bool] = None - discardTesterInformation: Optional[bool] = None - defaultTester: Optional[str] = None - updateReferencesList: Optional[ReferenceUpdateMode] = None - filters: Optional[List[FilterInfo]] = None - checkInData: Optional[CheckInData] = None + treeRootUID: str | None = None + useExistingDefect: bool | None = None + discardTesterInformation: bool | None = None + defaultTester: str | None = None + updateReferencesList: ReferenceUpdateMode | None = None + filters: list[FilterInfo] | None = None + checkInData: CheckInData | None = None @dataclass class JWTDataOptions: - permissions: List[Permission] - projectKey: Optional[str] = None - tovKey: Optional[str] = None - cycleKey: Optional[str] = None - subject: Optional[str] = None - expiresAfterSeconds: Optional[int] = None + permissions: list[Permission] + projectKey: str | None = None + tovKey: str | None = None + cycleKey: str | None = None + subject: str | None = None + expiresAfterSeconds: int | None = None @dataclass class UDFPosition: - after: Optional[AfterUDF] = None - before: Optional[BeforeUDF] = None - absolute: Optional[UDFAbsolutePosition] = None + after: AfterUDF | None = None + before: BeforeUDF | None = None + absolute: UDFAbsolutePosition | None = None @dataclass class UDFForMove: - udfKeys: List[str] + udfKeys: list[str] newPositions: UDFPosition @dataclass class StringUDFUpdatedResponse(UDFUpdatedResponse): stringUDF: StringUDF - affectedFilters: List[TestFilter] + affectedFilters: list[TestFilter] @dataclass class BooleanUDFUpdatedResponse(UDFUpdatedResponse): booleanUdf: BooleanUDF - affectedFilters: List[TestFilter] + affectedFilters: list[TestFilter] @dataclass class EnumerationUDFUpdatedResponse(UDFUpdatedResponse): enumerationUDF: EnumerationUDF - affectedFilters: List[TestFilter] + affectedFilters: list[TestFilter] @dataclass @@ -1674,14 +1711,24 @@ class CycleResponse: visible: bool projectKey: str description: str - creation: CycleCreation + creation: ( + CycleCloned + | CycleImported + | CycleImportedAsNew + | CycleImportedAsClone + | CycleImportedAsNewFromPlugin + | CycleClonedFromPlugin + | CycleImportedAsCloneFromPlugin + | UnknownCycle + | NewCycle + ) key: str tovKey: str status: ProjectStatus testingIntelligence: bool instantOfCreation: str - endDate: Optional[str] = None - startDate: Optional[str] = None + endDate: str | None = None + startDate: str | None = None @dataclass @@ -1699,15 +1746,15 @@ class KeywordDetails: defaultCallType: KeywordCallType description: str path: str - parameters: List[ParameterDetails] - preConditions: List[ConditionSummary] - postConditions: List[ConditionSummary] - references: List[str] - locker: Optional[UserReference] = None - version: Optional[str] = None - parentUniqueID: Optional[str] = None - libraryKey: Optional[str] = None - advancedContent: Optional[AdvancedContent] = None + parameters: list[ParameterDetails] + preConditions: list[ConditionSummary] + postConditions: list[ConditionSummary] + references: list[str] + locker: UserReference | None = None + version: str | None = None + parentUniqueID: str | None = None + libraryKey: str | None = None + advancedContent: AdvancedContent | None = None @dataclass @@ -1719,7 +1766,7 @@ class ProjectNode: status: ProjectStatus visibility: bool exchangeFormat: ProjectExchangeFormat - children: List[TOVNode] + children: list[TOVNode] @dataclass @@ -1728,15 +1775,15 @@ class ParameterSummary: key: str name: str evaluationType: ParameterEvaluationType - dataType: Optional[DataTypeSummary] = None - value: Optional[str] = None - valueType: Optional[RepresentativeType] = None - representativeValue: Optional[RepresentativeValue] = None - parameterValue: Optional[ParameterValue] = None - instanceArrayValue: Optional[InstanceArrayValue] = None - parentAlias: Optional[str] = None - rootAlias: Optional[str] = None - usesCount: Optional[int] = None + dataType: DataTypeSummary | None = None + value: str | None = None + valueType: RepresentativeType | None = None + representativeValue: RepresentativeValue | None = None + parameterValue: ParameterValue | None = None + instanceArrayValue: InstanceArrayValue | None = None + parentAlias: str | None = None + rootAlias: str | None = None + usesCount: int | None = None @dataclass @@ -1745,43 +1792,34 @@ class ReportingJob: projectKey: str owner: str start: str - progress: Optional[JobProgress] = None - completion: Optional[ReportingCompletion] = None - - -@dataclass -class ExecutionImportingJob: - id: str - projectKey: str - owner: str - start: str - progress: Optional[JobProgress] = None - completion: Optional[ExecutionImportingCompletion] = None + progress: JobProgress | None = None + completion: ReportingCompletion | None = None @dataclass class ExecutionImportingSuccess(ExecutionImportingResult): - testCaseSets: List[TestCaseSetExecutionImportResult] - checkedInTestStructureElements: List[TestStructureElement] - checkedInTestElements: List[CheckedInElement] - createdDefects: List[CreatedDefect] - createdReferences: List[CreatedReference] + testCaseSets: list[TestCaseSetExecutionImportResult] + checkedInTestStructureElements: list[TestStructureElement] + checkedInTestElements: list[CheckedInElement] + createdDefects: list[CreatedDefect] + createdReferences: list[CreatedReference] + type: Literal["ExecutionImportingSuccess"] @dataclass class UDFForInsert: name: str isMandatory: bool - definedFor: List[UDFLocation] + definedFor: list[UDFLocation] udfType: UDFType - enumerationName: Optional[List[str]] = None - position: Optional[UDFPosition] = None + enumerationName: list[str] | None = None + position: UDFPosition | None = None @dataclass class RichTextInfo: html: str - images: List[ImageInfo] + images: list[ImageInfo] @dataclass @@ -1791,11 +1829,11 @@ class KeywordCallSpecification: sequencePhase: SequencePhase callType: KeywordCallType comments: str - callParameters: List[ParameterSummary] - keywordType: Optional[KeywordType] = None - description: Optional[str] = None - keywordKey: Optional[str] = None - callingKeywordKey: Optional[str] = None + callParameters: list[ParameterSummary] + keywordType: KeywordType | None = None + description: str | None = None + keywordKey: str | None = None + callingKeywordKey: str | None = None @dataclass @@ -1803,86 +1841,86 @@ class KeywordCall: sequenceID: str numbering: str spec: KeywordCallSpecification - parentID: Optional[str] = None - exec: Optional[KeywordCallExecution] = None + parentID: str | None = None + exec: KeywordCallExecution | None = None @dataclass class KeywordForInsert: parentKey: str name: str - parameters: List[KeywordParameterForInsert] - uid: Optional[str] = None - description: Optional[RichTextInfo] = None - advancedContent: Optional[AdvancedContent] = None - callType: Optional[KeywordCallType] = None + parameters: list[KeywordParameterForInsert] + uid: str | None = None + description: RichTextInfo | None = None + advancedContent: AdvancedContent | None = None + callType: KeywordCallType | None = None @dataclass class SubdivisionForInsert: name: str - parentKey: Optional[str] = None - uid: Optional[str] = None - description: Optional[RichTextInfo] = None + parentKey: str | None = None + uid: str | None = None + description: RichTextInfo | None = None @dataclass class SubdivisionForUpdate: - name: Optional[str] = None - description: Optional[RichTextInfo] = None + name: str | None = None + description: RichTextInfo | None = None @dataclass class KeywordDetailsForUpdate: - name: Optional[str] = None - description: Optional[RichTextInfo] = None - callType: Optional[KeywordCallType] = None - locker: Optional[OptionalUser] = None - advancedContent: Optional[OptionalAdvancedContent] = None + name: str | None = None + description: RichTextInfo | None = None + callType: KeywordCallType | None = None + locker: OptionalUser | None = None + advancedContent: OptionalAdvancedContent | None = None @dataclass class SpecificationDetailsForUpdate: - responsible: Optional[OptionalUser] = None - reviewer: Optional[OptionalUser] = None - locker: Optional[OptionalUser] = None - priority: Optional[Priority] = None - dueDate: Optional[OptionalLocalDate] = None - description: Optional[RichTextInfo] = None - reviewComment: Optional[RichTextInfo] = None + responsible: OptionalUser | None = None + reviewer: OptionalUser | None = None + locker: OptionalUser | None = None + priority: Priority | None = None + dueDate: OptionalLocalDate | None = None + description: RichTextInfo | None = None + reviewComment: RichTextInfo | None = None @dataclass class ProjectDetailsForUpdate: - name: Optional[str] = None - testObjectName: Optional[str] = None - id: Optional[str] = None - customerName: Optional[str] = None - customerAddress: Optional[str] = None - contactPerson: Optional[str] = None - testLab: Optional[str] = None - placeOfInspection: Optional[str] = None - status: Optional[ProjectStatus] = None - isVisibleToTester: Optional[bool] = None - isTestingIntelligenceActive: Optional[bool] = None - description: Optional[RichTextInfo] = None - startDate: Optional[OptionalLocalDate] = None - endDate: Optional[OptionalLocalDate] = None - inspections: Optional[List[str]] = None - variantsManagementEnabled: Optional[OperationalState] = None + name: str | None = None + testObjectName: str | None = None + id: str | None = None + customerName: str | None = None + customerAddress: str | None = None + contactPerson: str | None = None + testLab: str | None = None + placeOfInspection: str | None = None + status: ProjectStatus | None = None + isVisibleToTester: bool | None = None + isTestingIntelligenceActive: bool | None = None + description: RichTextInfo | None = None + startDate: OptionalLocalDate | None = None + endDate: OptionalLocalDate | None = None + inspections: list[str] | None = None + variantsManagementEnabled: OperationalState | None = None @dataclass class TOVForUpdate: - name: Optional[str] = None - endDate: Optional[OptionalLocalDate] = None - visible: Optional[bool] = None - description: Optional[RichTextInfo] = None - isBaseTov: Optional[bool] = None - status: Optional[ProjectStatus] = None - testingIntelligence: Optional[bool] = None - startDate: Optional[OptionalLocalDate] = None - cloningVisibility: Optional[bool] = None + name: str | None = None + endDate: OptionalLocalDate | None = None + visible: bool | None = None + description: RichTextInfo | None = None + isBaseTov: bool | None = None + status: ProjectStatus | None = None + testingIntelligence: bool | None = None + startDate: OptionalLocalDate | None = None + cloningVisibility: bool | None = None @dataclass @@ -1893,30 +1931,46 @@ class TestCaseSetDetails: uniqueID: str name: str spec: TestCaseSetSpecificationSummary - testCases: List[TestCaseSummary] - testSequence: List[KeywordCall] - parameters: List[ParameterDetails] - keywords: List[KeywordDetails] - exec: Optional[TestCaseSetExecutionSummary] = None + testCases: list[TestCaseSummary] + testSequence: list[KeywordCall] + parameters: list[ParameterDetails] + keywords: list[KeywordDetails] + exec: TestCaseSetExecutionSummary | None = None @dataclass class TestCaseDetails: uniqueID: str spec: TestCaseSpecificationDetails - testSequence: List[KeywordCall] - parameters: List[ParameterSummary] - keywords: List[KeywordDetails] - exec: Optional[TestCaseExecutionDetails] = None - origin: Optional[TestCaseDetailsOrigin] = None + testSequence: list[KeywordCall] + parameters: list[ParameterSummary] + keywords: list[KeywordDetails] + exec: TestCaseExecutionDetails | None = None + origin: TestCaseDetailsOrigin | None = None + + +@dataclass +class ExecutionImportingCompletion: + time: str + result: ExecutionImportingFailure | ExecutionImportingSuccess @dataclass class CycleForUpdate: - name: Optional[str] = None - endDate: Optional[OptionalLocalDate] = None - visible: Optional[bool] = None - description: Optional[RichTextInfo] = None - status: Optional[ProjectStatus] = None - testingIntelligence: Optional[bool] = None - startDate: Optional[OptionalLocalDate] = None + name: str | None = None + endDate: OptionalLocalDate | None = None + visible: bool | None = None + description: RichTextInfo | None = None + status: ProjectStatus | None = None + testingIntelligence: bool | None = None + startDate: OptionalLocalDate | None = None + + +@dataclass +class ExecutionImportingJob: + id: str + projectKey: str + owner: str + start: str + progress: JobProgress | None = None + completion: ExecutionImportingCompletion | None = None diff --git a/testbench2robotframework/result_writer.py b/testbench2robotframework/result_writer.py index b9dda75..448461b 100644 --- a/testbench2robotframework/result_writer.py +++ b/testbench2robotframework/result_writer.py @@ -95,9 +95,18 @@ def __init__( self.test_suites: dict[str, TestSuite] = {} self.keywords: list[Keyword] = [] self.itb_test_case_catalog: dict[str, TestCaseDetails] = {} - self.phase_pattern = config.phasePattern + self.phase_pattern = config.phase_pattern self.test_chain: list[TestCase] = [] - self.main_protocol = from_dict(ExecutionImportingSuccess, {"testCaseSets": [], "checkedInTestStructureElements":[], "checkedInTestElements": [], "createdDefects":[], "createdReferences":[]}) + self.main_protocol = from_dict( + ExecutionImportingSuccess, + { + "testCaseSets": [], + "checkedInTestStructureElements": [], + "checkedInTestElements": [], + "createdDefects": [], + "createdReferences": [], + }, + ) def _create_artifact_storage(self): return ExecutionArtifactStorage( @@ -113,9 +122,7 @@ def start_suite(self, suite: TestSuite): self.test_suites[suite.metadata["uniqueID"]] = suite self.protocol_test_cases: list[TestCaseExecutionForImport] = [] - def _get_keywords_by_type( - self, keywords: list[KeywordCall], keyword_type: KeywordType - ): + def _get_keywords_by_type(self, keywords: list[KeywordCall], keyword_type: KeywordType): for keyword in keywords: if not keyword.spec: continue @@ -159,15 +166,13 @@ def end_test(self, test: TestCase): ) self._set_atomic_keywords_execution_result(atomic_keywords, self.test_chain) for keyword in compound_keywords: - self._set_compound_keyword_execution_verdict( - keyword, itb_test_case.testSequence - ) + self._set_compound_keyword_execution_verdict(keyword, itb_test_case.testSequence) textual_steps = list( self._get_keywords_by_type(itb_test_case.testSequence, KeywordType.Textual) ) for step in textual_steps: if step.exec is None: - step.exec = from_dict(KeywordCallExecution, {}) + step.exec = from_dict(KeywordCallExecution, {}) step.exec.verdict = KeywordVerdict.Skipped self._set_itb_testcase_execution_result(itb_test_case, self.test_chain) self._set_itb_testcase_execution_comment(itb_test_case, self.test_chain) @@ -320,12 +325,8 @@ def _set_atomic_keywords_execution_result( atomic_keywords, SequencePhase.Teardown ) self._set_keyword_verdicts(setup_keywords, test_chain_setup, SequencePhase.Setup) - self._set_keyword_verdicts( - test_step_keywords, test_chain_body, SequencePhase.TestStep - ) - self._set_keyword_verdicts( - teardown_keywords, test_chain_teardown, SequencePhase.Teardown - ) + self._set_keyword_verdicts(test_step_keywords, test_chain_body, SequencePhase.TestStep) + self._set_keyword_verdicts(teardown_keywords, test_chain_teardown, SequencePhase.Teardown) def _set_keyword_verdicts( self, @@ -385,9 +386,7 @@ def _get_keyword_exec_from_keyword(self, keyword: Keyword) -> KeywordCallExecuti }, ) - def _check_matching_keyword_name( - self, rf_keyword: Keyword, tb_keyword: KeywordCall - ) -> None: + def _check_matching_keyword_name(self, rf_keyword: Keyword, tb_keyword: KeywordCall) -> None: if not is_normalized_equal( rf_keyword.kwname, tb_keyword.spec.name ) and not is_normalized_equal(rf_keyword.kwname.split(".")[-1], tb_keyword.spec.name): @@ -458,9 +457,7 @@ def _set_compound_keyword_execution_verdict( if compound_keyword.exec is None: compound_keyword.exec = from_dict(KeywordCallExecution, {}) compound_keyword.exec.verdict = KeywordVerdict.Skipped - children = list( - filter(lambda ts: ts.parentID == compound_keyword.sequenceID, test_steps) - ) + children = list(filter(lambda ts: ts.parentID == compound_keyword.sequenceID, test_steps)) for child in children: if child.exec is None: logger.debug( @@ -477,9 +474,7 @@ def _set_compound_keyword_execution_verdict( if child.exec.verdict is KeywordVerdict.Pass: compound_keyword.exec.verdict = KeywordVerdict.Pass - compound_keyword.exec.duration = sum( - [keyword.exec.duration for keyword in children] - ) + compound_keyword.exec.duration = sum([keyword.exec.duration for keyword in children]) compound_keyword.exec.time = children[-1].exec.time @staticmethod diff --git a/testbench2robotframework/testbench2rf.py b/testbench2robotframework/testbench2rf.py index 2eaea7b..eff37a5 100644 --- a/testbench2robotframework/testbench2rf.py +++ b/testbench2robotframework/testbench2rf.py @@ -423,7 +423,7 @@ def to_robot_ast_test_cases( rf_test_cases: list[TestCase] = [] multiple_tests = len(rf_keyword_call_lists) > 1 for index, rf_keywords in enumerate(rf_keyword_call_lists): - phase_pattern = self.config.phasePattern + phase_pattern = self.config.phase_pattern tc_name = ( phase_pattern.format( testcase=self.uid, From 7fa23e1bb73966981687750dba0319c9232e9073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81?= Date: Mon, 18 May 2026 18:48:51 +0200 Subject: [PATCH 07/15] ruff check fix py310 --- create_json_schema.py | 5 ++--- testbench2robotframework/execution_artifacts.py | 17 ++++++++--------- testbench2robotframework/json_reader.py | 9 ++++----- testbench2robotframework/json_writer.py | 3 +-- testbench2robotframework/result_writer.py | 7 +++---- .../robotframework2testbench.py | 5 ++--- testbench2robotframework/utils.py | 9 ++++----- 7 files changed, 24 insertions(+), 31 deletions(-) diff --git a/create_json_schema.py b/create_json_schema.py index ce8cc3f..c678fd8 100644 --- a/create_json_schema.py +++ b/create_json_schema.py @@ -1,8 +1,7 @@ import json import re - -with open("testbench2robotframework/model.py", "r", encoding="utf8") as model_py: +with open("testbench2robotframework/model.py", encoding="utf8") as model_py: model_str = model_py.read() pydantic_model = re.sub(r"(@dataclass\n)(class .*?)(:)", r"\2(BaseModel)\3", model_str) @@ -19,7 +18,7 @@ pydantic_model_py.write(pydantic_model) from pydantic_model import * -from yaml import dump, Dumper +from yaml import Dumper, dump with open("model.json", "w") as schema: schema.write(json.dumps(AllModels.model_json_schema(), indent=2)) diff --git a/testbench2robotframework/execution_artifacts.py b/testbench2robotframework/execution_artifacts.py index 6081933..7bcb20b 100644 --- a/testbench2robotframework/execution_artifacts.py +++ b/testbench2robotframework/execution_artifacts.py @@ -1,7 +1,6 @@ import shutil import sys from pathlib import Path -from typing import Optional from urllib.parse import unquote from .config import AttachmentConflictBehaviour, ReferenceBehaviour @@ -26,9 +25,9 @@ def __init__( self.tb_references: list[ReferenceAssignment] = tb_references self.output_xml = output_xml self.attachment_folder = attachment_folder - self._key: Optional[int] = None + self._key: int | None = None - def add_artifact(self, artifact: str) -> Optional[str]: + def add_artifact(self, artifact: str) -> str | None: """ Adds an artifact to the storage based on the reference behaviour. @@ -135,7 +134,7 @@ def _copy_attachment(self, artifact_value: str) -> str: return self._use_new_attachment(filename, artifact_value, attachment_folder_path) return self._dispatch_attachment_copy(filename, artifact_value, attachment_folder_path) - def _process_artifact(self, artifact: str) -> Optional[str]: + def _process_artifact(self, artifact: str) -> str | None: artifact_info = ExecutionArtifactInfo(artifact, self.output_xml) artifact_value = artifact_info.get_attachment_value() if not artifact_value: @@ -150,7 +149,7 @@ def _process_artifact(self, artifact: str) -> Optional[str]: return None return self._copy_attachment(artifact_value) - def _process_reference(self, artifact: str) -> Optional[str]: + def _process_reference(self, artifact: str) -> str | None: artifact_info = ExecutionArtifactInfo(artifact, self.output_xml) artifact_value = artifact_info.get_reference_value() if not artifact_value: @@ -160,14 +159,14 @@ def _process_reference(self, artifact: str) -> Optional[str]: return None return artifact_value - def _process_unknown(self, artifact: str) -> Optional[str]: + def _process_unknown(self, artifact: str) -> str | None: logger.error( f"Unknown reference behaviour '{self.reference_behaviour}'." f"Cannot add artifact '{artifact}'." ) return None - def _process_no_references_allowed(self, artifact: str) -> Optional[str]: + def _process_no_references_allowed(self, artifact: str) -> str | None: logger.warning( f"Reference behaviour is set to NONE." f"Reference '{artifact}' will not be added to report." @@ -214,7 +213,7 @@ def __init__(self, artifact: str, output_xml: str) -> None: self.artifact = unquoted_artifact self.output_xml = output_xml - def get_reference_value(self) -> Optional[str]: + def get_reference_value(self) -> str | None: unquoted_path = Path(self.artifact) if not unquoted_path.exists(): robot_output_dir = Path(self.output_xml).parent @@ -230,7 +229,7 @@ def get_reference_value(self) -> Optional[str]: return None return str(unquoted_path) - def get_attachment_value(self) -> Optional[str]: + def get_attachment_value(self) -> str | None: unquoted_path = Path(self.artifact) if not unquoted_path.exists(): robot_output_dir = Path(self.output_xml).parent diff --git a/testbench2robotframework/json_reader.py b/testbench2robotframework/json_reader.py index 7199efe..394e57e 100644 --- a/testbench2robotframework/json_reader.py +++ b/testbench2robotframework/json_reader.py @@ -3,7 +3,6 @@ from dataclasses import dataclass from json import JSONDecodeError from pathlib import Path -from typing import Optional from .log import logger from .model import ( @@ -36,7 +35,7 @@ def metadata(self) -> dict[str, str]: class TestBenchJsonReader: def __init__(self, json_dir) -> None: self.json_dir = json_dir - self._test_theme_tree: Optional[TestStructureTree] = None + self._test_theme_tree: TestStructureTree | None = None self._test_case_sets: dict[str, TestCaseSetDetails] = {} self._test_cases: dict[str, TestCaseDetails] = {} if not json_dir: @@ -116,20 +115,20 @@ def get_test_case_uids(self, test_case_set_uid: str) -> list[str]: test_case_set = self.test_case_sets[test_case_set_uid] return [tc.uniqueID for tc in test_case_set.testCases] - def read_test_case_set(self, uid) -> Optional[TestCaseSetDetails]: + def read_test_case_set(self, uid) -> TestCaseSetDetails | None: tcs_dict = read_json(str(Path(self.json_dir, f"{uid}.json"))) if tcs_dict is None: return None return from_dict(TestCaseSetDetails, tcs_dict) - def read_test_case(self, uid) -> Optional[TestCaseDetails]: + def read_test_case(self, uid) -> TestCaseDetails | None: tc_dict = read_json(str(Path(self.json_dir, f"{uid}.json"))) if tc_dict is None: return None # return None # TODO: wenn nicht da dann Fehler? return from_dict(TestCaseDetails, tc_dict) - def read_test_theme_tree(self, is_tov=False) -> Optional[TestStructureTree]: + def read_test_theme_tree(self, is_tov=False) -> TestStructureTree | None: test_structure_tree = read_json(str(Path(self.json_dir, TEST_STRUCTURE_TREE_FILE))) if test_structure_tree is None: return None diff --git a/testbench2robotframework/json_writer.py b/testbench2robotframework/json_writer.py index 151794e..5819a3d 100644 --- a/testbench2robotframework/json_writer.py +++ b/testbench2robotframework/json_writer.py @@ -2,7 +2,6 @@ from dataclasses import asdict from enum import Enum from pathlib import Path -from typing import Union from .config import Configuration from .log import logger @@ -19,7 +18,7 @@ def write_test_structure_element( json_dir: str, - test_structure_element: Union[TestStructureTree, TestCaseSetDetails, TestCaseDetails], + test_structure_element: TestStructureTree | TestCaseSetDetails | TestCaseDetails, ) -> None: if isinstance(test_structure_element, TestStructureTree): filepath = Path(json_dir) / Path(TEST_STRUCTURE_TREE_FILE + ".json") diff --git a/testbench2robotframework/result_writer.py b/testbench2robotframework/result_writer.py index 448461b..6bdb598 100644 --- a/testbench2robotframework/result_writer.py +++ b/testbench2robotframework/result_writer.py @@ -7,7 +7,6 @@ from datetime import datetime, timedelta, timezone from pathlib import Path from shutil import copytree -from typing import Optional from robot.result import Keyword, ResultVisitor, TestCase, TestSuite @@ -67,7 +66,7 @@ class ResultWriter(ResultVisitor): def __init__( self, json_report: str, - json_result: Optional[str], + json_result: str | None, config: Configuration, output_xml, listener_uid=None, @@ -78,7 +77,7 @@ def __init__( self.reference_behaviour = config.referenceBehaviour self.attachment_conflict_behaviour = config.attachmentConflictBehaviour self.tempdir = tempfile.TemporaryDirectory(dir=os.curdir) - self._test_setup_passed: Optional[bool] = None + self._test_setup_passed: bool | None = None if json_result is None: self.json_result = self.json_dir self.json_result_path = self.json_dir @@ -679,7 +678,7 @@ def __init__(self, name, index, length): self.length = int(length) -def get_test_chain(test_name: str, phase_pattern: str) -> Optional[TestChain]: +def get_test_chain(test_name: str, phase_pattern: str) -> TestChain | None: matcher = re.match(get_test_chain_pattern(phase_pattern), test_name) if matcher: return TestChain(*matcher.groups()) diff --git a/testbench2robotframework/robotframework2testbench.py b/testbench2robotframework/robotframework2testbench.py index 81326a3..276233a 100644 --- a/testbench2robotframework/robotframework2testbench.py +++ b/testbench2robotframework/robotframework2testbench.py @@ -1,6 +1,5 @@ import sys from pathlib import Path -from typing import Optional from robot.api import ExecutionResult @@ -12,8 +11,8 @@ def robot2testbench( json_input_report: str, robot_result_xml: str, - json_output_result: Optional[str] = None, - config: Optional[dict] = None, + json_output_result: str | None = None, + config: dict | None = None, ): if not Path(json_input_report).exists(): sys.exit("Could not find json directory or zip file at the given path.") diff --git a/testbench2robotframework/utils.py b/testbench2robotframework/utils.py index 16bce86..881b221 100644 --- a/testbench2robotframework/utils.py +++ b/testbench2robotframework/utils.py @@ -2,7 +2,6 @@ import shutil import sys from pathlib import Path, PurePath -from typing import Optional from zipfile import ZipFile from testbench2robotframework.model import ( @@ -19,7 +18,7 @@ from .log import logger -def robot_tag_from_udf(udf: UserDefinedField) -> Optional[str]: +def robot_tag_from_udf(udf: UserDefinedField) -> str | None: if (udf.udfType == UDFType.Enumeration and udf.value) or ( udf.udfType == UDFType.String and udf.value ): @@ -115,7 +114,7 @@ def _get_padded_index(self, tse) -> str: # return resolve(tree.body) -def get_directory(json_report_path: Optional[str]) -> str: +def get_directory(json_report_path: str | None) -> str: if json_report_path is None: return "" if not Path(json_report_path).exists(): @@ -156,14 +155,14 @@ def get_tse_index(tse: TestStructureTreeNode) -> str: return tse.base.numbering.rsplit(".", 1)[-1] -def directory_to_zip(directory: Path, new_path: Optional[str] = None): +def directory_to_zip(directory: Path, new_path: str | None = None): if new_path: shutil.make_archive(str(new_path), "zip", str(directory)) else: shutil.make_archive(str(directory), "zip", str(directory)) -def get_list_item(lst, index, default: Optional[str]): +def get_list_item(lst, index, default: str | None): try: return lst[index] except IndexError: From 24cddf6b52e83b4085db715b8adba81af73abe54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rene=CC=81?= Date: Wed, 20 May 2026 01:56:53 +0200 Subject: [PATCH 08/15] clean up and updated Model generation and added robustnes agains updates --- .gitignore | 1 + CreatePiPWheel.bat | 7 - CreatePiPWheel.sh | 5 - DEVELOPMENT.md | 48 +- ExampleConfiguration/json_config.json | 21 +- ExampleConfiguration/pyproject_example.toml | 5 +- ExampleConfiguration/toml_config.toml | 5 +- docs/configuration/cli_options.md | 3 +- docs/configuration/overview.md | 21 +- docs/configuration/pyproject_config.md | 19 +- docs/usage/generate_tests.md | 1 + oldModel.py | 1153 ------------------- pydantic_model.py | 469 -------- pyproject.toml | 10 +- tasks.py | 44 +- testbench2robotframework/cli.py | 6 +- testbench2robotframework/model.py | 82 +- testbench2robotframework/model_utils.py | 31 +- testbench2robotframework/result_writer.py | 23 +- 19 files changed, 222 insertions(+), 1732 deletions(-) delete mode 100755 CreatePiPWheel.bat delete mode 100755 CreatePiPWheel.sh delete mode 100644 oldModel.py delete mode 100644 pydantic_model.py diff --git a/.gitignore b/.gitignore index 676f3d6..0c02b1f 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,4 @@ cli-export-config.json cli-import-config.json robot_tests/ reports/ +openapi.yml diff --git a/CreatePiPWheel.bat b/CreatePiPWheel.bat deleted file mode 100755 index 956b7de..0000000 --- a/CreatePiPWheel.bat +++ /dev/null @@ -1,7 +0,0 @@ -check-manifest --update -pause -python -m build -pause -twine check dist\* -pause -twine upload dist/* diff --git a/CreatePiPWheel.sh b/CreatePiPWheel.sh deleted file mode 100755 index fd87eb0..0000000 --- a/CreatePiPWheel.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -check-manifest --update -python -m build -twine check dist\* -twine upload dist/* diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2684f51..ce9ba51 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,25 +1,35 @@ # Contributing -## Setting up project for the first time -1. Write all dependencies into pyproject.toml -2. Create venv (python -m venv .venv) and activate it (".venv\scripts\activate" or "source .venv/bin/activate") -3. Update pip (python -m pip install -U pip) -3. Install pip-tools (pip install pip-tools) -4. Update/Create `requirements.txt` (when dependencies have been updated in pyproject.toml) - - with Development dependencies - > pip-compile --extra dev - - or only with Runtime dependencies - > pip-compile -5. Install dependencies (pip install -U -r requirements.txt) -6. Install project into local venv (pip install -e .[dev]) -## Creating whl and publish -1. Install 'setuptools' (pip install setuptools) -2. Run CreatePipWheel Script +## Setting up project for the first time +1. Create venv and activate it: + ```bash + python -m venv .venv + source .venv/bin/activate # Linux/macOS + .venv\scripts\activate # Windows + ``` +2. Install project with dev dependencies: + ```bash + pip install -e .[dev] + ``` -## Generate documentation in Word format - [Install Pandoc](https://pandoc.org/installing.html) +## Building and publishing -```shell -pandoc -s README.md -M title="imbus TestBench - Robot Code Generator" -M subtitle=Benutzerhandbuch -M toc-title=Inhaltsverzeichnis --toc -o Benutzerhandbuch.docx +```bash +check-manifest --update +python -m build +twine check dist/* +twine upload dist/* ``` + +## Updating the data model + +`testbench2robotframework/model.py` is generated from the TestBench OpenAPI spec using [datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator). + +1. Download the OpenAPI YAML from the TestBench Swagger documentation (e.g. `openapi.yml`). +2. Generate the model (settings are in `pyproject.toml` under `[tool.datamodel-codegen]`): + ```bash + invoke generate-model --input openapi.yml + ``` + This runs `datamodel-codegen` and injects `__VERSION__` from the spec's `info.version` field into `model.py`. +3. Review the generated file diff --git a/ExampleConfiguration/json_config.json b/ExampleConfiguration/json_config.json index 9edf3dc..327a62f 100644 --- a/ExampleConfiguration/json_config.json +++ b/ExampleConfiguration/json_config.json @@ -10,8 +10,14 @@ "output-directory": "{root}/Generated", "log-suite-numbering": true, "resource-directory": "{root}/Resources", + "resource-directory-regex": ".*\\[Robot-Resources\\].*", "clean": true, "compound-keyword-logging": "GROUP", + "reference-behaviour": "ATTACHMENT", + "attachment-conflict-behaviour": "USE_EXISTING", + "testcase-splitting-regex": "^StopWithRestart\\..*", + "phase-pattern": "{testcase} : Phase {index}/{length}", + "metadata": {}, "library-mapping": { "SeleniumLibrary": "SeleniumLibrary timeout=10 implicit_wait=1 run_on_failure=Capture Page Screenshot", "SuperRemoteLibrary": "Remote http://127.0.0.1:8270 WITH NAME SuperRemoteLibrary" @@ -20,20 +26,15 @@ "MyKeywords": "{root}/../MyKeywords.resource", "MyOtherKeywords": "{resourceDirectory}/subdir/MyOtherKeywords.resource" }, - "forcedImport": { - "libraries": [ - ], - "resources": - [ - ], - "variables": [ - ] + "forced-import": { + "libraries": [], + "resources": [], + "variables": [] }, - "testCaseSplitPathRegEx": "^StopWithRestart\\..*", "console-logging": { "logLevel": "debug" }, "file-logging": { "logLevel": "info" - } + } } diff --git a/ExampleConfiguration/pyproject_example.toml b/ExampleConfiguration/pyproject_example.toml index 3ea3f3f..1ab38c7 100644 --- a/ExampleConfiguration/pyproject_example.toml +++ b/ExampleConfiguration/pyproject_example.toml @@ -7,9 +7,10 @@ fully-qualified = false output-directory = "{root}/Generated" log-suite-numbering = false clean = true -compound-keyword-logging = true +compound-keyword-logging = "GROUP" resource-directory = "{root}/Resources" -testcase-split-regex = ".*StopWithRestart.*" +resource-directory-regex = ".*\\[Robot-Resources\\].*" +testcase-splitting-regex = ".*StopWithRestart.*" phase-pattern = "{testcase} : Phase {index}/{length}" reference-behaviour = "ATTACHMENT" attachment-conflict-behaviour = "USE_EXISTING" diff --git a/ExampleConfiguration/toml_config.toml b/ExampleConfiguration/toml_config.toml index 3ea3f3f..1ab38c7 100644 --- a/ExampleConfiguration/toml_config.toml +++ b/ExampleConfiguration/toml_config.toml @@ -7,9 +7,10 @@ fully-qualified = false output-directory = "{root}/Generated" log-suite-numbering = false clean = true -compound-keyword-logging = true +compound-keyword-logging = "GROUP" resource-directory = "{root}/Resources" -testcase-split-regex = ".*StopWithRestart.*" +resource-directory-regex = ".*\\[Robot-Resources\\].*" +testcase-splitting-regex = ".*StopWithRestart.*" phase-pattern = "{testcase} : Phase {index}/{length}" reference-behaviour = "ATTACHMENT" attachment-conflict-behaviour = "USE_EXISTING" diff --git a/docs/configuration/cli_options.md b/docs/configuration/cli_options.md index db2ed30..3b91e06 100644 --- a/docs/configuration/cli_options.md +++ b/docs/configuration/cli_options.md @@ -34,8 +34,7 @@ Options specific to the `generate-tests` subcommand: | `--compound-keyword-logging` | Choice | Mode for logging compound keywords. Options: `GROUP`, `COMMENT`, or `NONE`. | | `--fully-qualified` | Flag | Calls Robot Framework keywords by their fully qualified names in the generated test suites. | | `--log-suite-numbering` | Flag | Enables logging of the test suite numbering. | - - +| `--metadata TEXT` | Text | Add extra metadata to the settings of the generated Robot Framework test suite. Provide entries as `key:value` pairs. | ### Resource & Library Options diff --git a/docs/configuration/overview.md b/docs/configuration/overview.md index 1f1c95b..7dd63b5 100644 --- a/docs/configuration/overview.md +++ b/docs/configuration/overview.md @@ -58,6 +58,8 @@ When multiple configuration methods are used, settings are applied in the follow - `fully-qualified` - Enable/disable fully qualified keyword names in generated test suites - `compound-keyword-logging` - Control compound TestBench keyword logging (`GROUP`, `COMMENT`, `NONE`) - `log-suite-numbering` - Enable/disable suite numbering in generated file names +- `testcase-splitting-regex` - Regular expression to split test cases at matching interactions +- `phase-pattern` - Pattern for naming test case phases when splitting ### Library & Resource Mapping @@ -67,10 +69,25 @@ When multiple configuration methods are used, settings are applied in the follow - `resource-root` - Root subdivision for resources - `library-mapping` - Custom library import statements - `resource-mapping` - Custom resource import statements +- `resource-directory` - Directory containing Robot Framework resource files +- `resource-directory-regex` - Regex to identify the TestBench subdivision corresponding to the resource directory + +### Metadata + +- `metadata` - Extra metadata key-value pairs added to generated test suite settings + +### Forced Imports + +- `forced-import` - Force import of specific libraries, resources, or variables in every generated suite ### Attachment & Reference Handling -- `reference-behaviour` - How to handle references -- `attachment-conflict-behaviour` - How to handle attachment conflicts +- `reference-behaviour` - How to handle references (`ATTACHMENT`, `REFERENCE`, `NONE`) +- `attachment-conflict-behaviour` - How to handle attachment conflicts (`ERROR`, `USE_NEW`, `USE_EXISTING`, `RENAME_NEW`) + +### Logging + +- `console-logging` - Console output log level and format +- `file-logging` - File-based log level, format, and file name diff --git a/docs/configuration/pyproject_config.md b/docs/configuration/pyproject_config.md index 92f8364..cb3171a 100644 --- a/docs/configuration/pyproject_config.md +++ b/docs/configuration/pyproject_config.md @@ -34,6 +34,11 @@ resource-directory = "{root}/Resources" resource-directory-regex = ".*\\[Robot-Resources\\].*" reference-behaviour = "ATTACHMENT" attachment-conflict-behaviour = "USE_EXISTING" +testcase-splitting-regex = ".*StopWithRestart.*" +phase-pattern = "{testcase} : Phase {index}/{length}" + +[tool.testbench2robotframework.metadata] +# Example: MyKey = "my value" [tool.testbench2robotframework.library-mapping] SeleniumLibrary = "SeleniumLibrary timeout=10 implicit_wait=1 run_on_failure=Capture Page Screenshot" @@ -71,6 +76,17 @@ clean = true fully-qualified = false log-suite-numbering = false compound-keyword-logging = "GROUP" +testcase-splitting-regex = ".*StopWithRestart.*" +phase-pattern = "{testcase} : Phase {index}/{length}" +``` + +### Metadata + +Add custom metadata to the settings of generated Robot Framework test suites: + +```toml +[tool.testbench2robotframework.metadata] +MyMetadata = "some value" ``` ### Library Mapping @@ -143,8 +159,7 @@ resource-directory = "{root}/Resources" Instead of `pyproject.toml`, you can use `robot.toml` with the same structure: ```toml -[testbench2robotframework] -# Same options as in pyproject.toml, but without the "tool." prefix +[tool.testbench2robotframework] output-directory = "./Generated" clean = true ``` diff --git a/docs/usage/generate_tests.md b/docs/usage/generate_tests.md index 62ed8d1..d77f09b 100644 --- a/docs/usage/generate_tests.md +++ b/docs/usage/generate_tests.md @@ -36,6 +36,7 @@ The `generate-tests` command supports the following options: | `--resource-root TEXT` | TestBench root subdivision whose direct children correspond to Robot Framework resources. | | `--library-mapping TEXT` | Library import statement to use when a keyword from the specified TestBench subdivision is encountered. | | `--resource-mapping TEXT` | Resource import statement to use when a keyword from the specified TestBench subdivision is encountered. | +| `--metadata TEXT` | Add extra metadata to the settings of the generated Robot Framework test suite. Provide entries as `key:value` pairs. | | `--help` | Displays the help message and exits. | | `--version` | Writes the TestBench2RobotFramework, Robot Framework and Python version to console. | diff --git a/oldModel.py b/oldModel.py deleted file mode 100644 index e26c32f..0000000 --- a/oldModel.py +++ /dev/null @@ -1,1153 +0,0 @@ -# generated by datamodel-codegen: -# filename: model.yml -# timestamp: 2023-09-25T09:28:04+00:00 - -from __future__ import annotations - -from typing import List, Optional - -from pydantic import BaseModel, Field - - -class ActivityStatus(BaseModel): - enum: List[str] - title: str - type: str - - -class Content(BaseModel): - title: str - type: str - - -class FilterType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Key(BaseModel): - title: str - type: str - - -class Name(BaseModel): - title: str - type: str - - -class Properties(BaseModel): - content: Content - filterType: FilterType - key: Key - name: Name - - -class AttachedFilter(BaseModel): - properties: Properties - required: List[str] - title: str - type: str - - -class CallType(BaseModel): - enum: List[str] - title: str - type: str - - -class Description(BaseModel): - title: str - type: str - - -class UniqueId(BaseModel): - title: str - type: str - - -class AnyOfItem(BaseModel): - type: str - - -class Version(BaseModel): - anyOf: List[AnyOfItem] - default: None - title: str - - -class Properties1(BaseModel): - description: Description - key: Key - name: Name - uniqueId: UniqueId - version: Version - - -class ConditionSummary(BaseModel): - properties: Properties1 - required: List[str] - title: str - type: str - - -class DataTypeKind(BaseModel): - enum: List[str] - title: str - type: str - - -class Kind(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Path(BaseModel): - title: str - type: str - - -class UniqueID(BaseModel): - title: str - type: str - - -class Version1(BaseModel): - anyOf: List[AnyOfItem] - title: str - - -class Properties2(BaseModel): - key: Key - kind: Kind - name: Name - path: Path - uniqueID: UniqueID - version: Version1 - - -class DataTypeSummary(BaseModel): - properties: Properties2 - required: List[str] - title: str - type: str - - -class ExecutionStatus(BaseModel): - enum: List[str] - title: str - type: str - - -class ExecutionVerdict(BaseModel): - enum: List[str] - title: str - type: str - - -class FilterType1(BaseModel): - enum: List[str] - title: str - type: str - - -class AnyOfItem2(BaseModel): - field_ref: Optional[str] = Field(None, alias='$ref') - type: Optional[str] = None - - -class Exec(BaseModel): - anyOf: List[AnyOfItem2] - - -class InteractionType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Items(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Interactions(BaseModel): - items: Items - title: str - type: str - - -class Parameters(BaseModel): - items: Items - title: str - type: str - - -class Spec(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class AnyOfItem3(BaseModel): - type: str - - -class Version2(BaseModel): - anyOf: List[AnyOfItem3] - title: str - - -class Properties3(BaseModel): - exec: Exec - interactionType: InteractionType - interactions: Interactions - key: Key - name: Name - parameters: Parameters - path: Path - spec: Spec - uniqueID: UniqueID - version: Version2 - - -class InteractionDetails(BaseModel): - properties: Properties3 - required: List[str] - title: str - type: str - - -class Comments(BaseModel): - title: str - type: str - - -class CurrentUser(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Duration(BaseModel): - title: str - type: str - - -class References(BaseModel): - items: Items - title: str - type: str - - -class Tester(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Time(BaseModel): - title: str - type: str - - -class Verdict(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties4(BaseModel): - comments: Comments - currentUser: CurrentUser - duration: Duration - references: References - tester: Tester - time: Time - verdict: Verdict - - -class InteractionExecutionSummary(BaseModel): - properties: Properties4 - required: List[str] - title: str - type: str - - -class CallId(BaseModel): - title: str - type: str - - -class CallType1(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class PostConditions(BaseModel): - items: Items - title: str - type: str - - -class PreConditions(BaseModel): - items: Items - title: str - type: str - - -class References1(BaseModel): - items: Items - title: str - type: str - - -class SequencePhase(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties5(BaseModel): - callId: CallId - callType: CallType1 - comments: Comments - description: Description - postConditions: PostConditions - preConditions: PreConditions - references: References1 - sequencePhase: SequencePhase - - -class InteractionSpecificationSummary(BaseModel): - properties: Properties5 - required: List[str] - title: str - type: str - - -class InteractionType1(BaseModel): - enum: List[str] - title: str - type: str - - -class InteractionVerdict(BaseModel): - enum: List[str] - title: str - type: str - - -class IsVariantsMarker(BaseModel): - title: str - type: str - - -class Properties6(BaseModel): - isVariantsMarker: IsVariantsMarker - key: Key - name: Name - - -class Keyword(BaseModel): - properties: Properties6 - required: List[str] - title: str - type: str - - -class ParameterDefinitionType(BaseModel): - enum: List[str] - title: str - type: str - - -class DataType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class DefinitionType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class UseType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Value(BaseModel): - anyOf: List[AnyOfItem3] - title: str - - -class ValueType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties7(BaseModel): - dataType: DataType - definitionType: DefinitionType - key: Key - name: Name - useType: UseType - value: Value - valueType: ValueType - - -class ParameterSummary(BaseModel): - properties: Properties7 - required: List[str] - title: str - type: str - - -class ParameterUseType(BaseModel): - enum: List[str] - title: str - type: str - - -class ParameterValueType(BaseModel): - enum: List[str] - title: str - type: str - - -class Priority(BaseModel): - enum: List[str] - title: str - type: str - - -class Type(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties8(BaseModel): - path: Path - type: Type - - -class Reference(BaseModel): - properties: Properties8 - required: List[str] - title: str - type: str - - -class ReferenceType(BaseModel): - enum: List[str] - title: str - type: str - - -class Edited(BaseModel): - title: str - type: str - - -class Properties9(BaseModel): - edited: Edited - key: Key - - -class RequirementReference(BaseModel): - properties: Properties9 - required: List[str] - title: str - type: str - - -class SequencePhase1(BaseModel): - enum: List[str] - title: str - type: str - - -class SpecificationStatus(BaseModel): - enum: List[str] - title: str - type: str - - -class AnyOfItem5(BaseModel): - field_ref: Optional[str] = Field(None, alias='$ref') - type: Optional[str] = None - - -class Exec1(BaseModel): - anyOf: List[AnyOfItem5] - - -class Interactions1(BaseModel): - items: Items - title: str - type: str - - -class Parameters1(BaseModel): - items: Items - title: str - type: str - - -class Properties10(BaseModel): - exec: Exec1 - interactions: Interactions1 - parameters: Parameters1 - spec: Spec - uniqueID: UniqueID - - -class TestCaseDetails(BaseModel): - properties: Properties10 - required: List[str] - title: str - type: str - - -class ActualDuration(BaseModel): - title: str - type: str - - -class Items8(BaseModel): - type: str - - -class Defects(BaseModel): - items: Items8 - title: str - type: str - - -class ExecStatus(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Items9(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Keywords(BaseModel): - items: Items9 - title: str - type: str - - -class PlannedDuration(BaseModel): - title: str - type: str - - -class References2(BaseModel): - items: Items9 - title: str - type: str - - -class Status(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Tester1(BaseModel): - anyOf: List[AnyOfItem5] - - -class Udfs(BaseModel): - items: Items9 - title: str - type: str - - -class AnyOfItem7(BaseModel): - type: str - - -class Version3(BaseModel): - anyOf: List[AnyOfItem7] - title: str - - -class Properties11(BaseModel): - actualDuration: ActualDuration - comments: Comments - currentUser: CurrentUser - defects: Defects - execStatus: ExecStatus - key: Key - keywords: Keywords - plannedDuration: PlannedDuration - references: References2 - status: Status - tester: Tester1 - udfs: Udfs - verdict: Verdict - version: Version3 - - -class TestCaseExecutionDetails(BaseModel): - properties: Properties11 - required: List[str] - title: str - type: str - - -class Comments3(BaseModel): - default: str - title: str - type: str - - -class Items12(BaseModel): - type: str - - -class AnyOfItem8(BaseModel): - items: Optional[Items12] = None - type: str - - -class Defects1(BaseModel): - anyOf: List[AnyOfItem8] - default: None - title: str - - -class AllOfItem(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class ExecStatus1(BaseModel): - allOf: List[AllOfItem] - default: str - - -class Status1(BaseModel): - allOf: List[AllOfItem] - default: str - - -class AnyOfItem9(BaseModel): - field_ref: Optional[str] = Field(None, alias='$ref') - type: Optional[str] = None - - -class Tester2(BaseModel): - anyOf: List[AnyOfItem9] - default: None - - -class Verdict2(BaseModel): - allOf: List[AllOfItem] - default: str - - -class Properties12(BaseModel): - comments: Comments3 - defects: Defects1 - execStatus: ExecStatus1 - key: Key - status: Status1 - tester: Tester2 - verdict: Verdict2 - - -class TestCaseExecutionSummary(BaseModel): - properties: Properties12 - required: List[str] - title: str - type: str - - -class Exec2(BaseModel): - anyOf: List[AnyOfItem9] - - -class Numbering(BaseModel): - title: str - type: str - - -class Items13(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class TestCases(BaseModel): - items: Items13 - title: str - type: str - - -class Properties13(BaseModel): - exec: Exec2 - key: Key - name: Name - numbering: Numbering - spec: Spec - testCases: TestCases - uniqueID: UniqueID - - -class TestCaseSetDetails(BaseModel): - properties: Properties13 - required: List[str] - title: str - type: str - - -class Comments4(BaseModel): - title: str - type: str - - -class Keywords1(BaseModel): - items: Items13 - title: str - type: str - - -class Udfs1(BaseModel): - items: Items13 - title: str - type: str - - -class Properties14(BaseModel): - comments: Comments4 - key: Key - keywords: Keywords1 - udfs: Udfs1 - - -class TestCaseSetExecutionSummary(BaseModel): - properties: Properties14 - required: List[str] - title: str - type: str - - -class AnyOfItem11(BaseModel): - type: str - - -class DueDate(BaseModel): - anyOf: List[AnyOfItem11] - title: str - - -class Keywords2(BaseModel): - items: Items13 - title: str - type: str - - -class PostConditions1(BaseModel): - items: Items13 - title: str - type: str - - -class PreConditions1(BaseModel): - items: Items13 - title: str - type: str - - -class AnyOfItem12(BaseModel): - field_ref: Optional[str] = Field(None, alias='$ref') - type: Optional[str] = None - - -class Priority1(BaseModel): - anyOf: List[AnyOfItem12] - - -class References3(BaseModel): - items: Items13 - title: str - type: str - - -class Requirements(BaseModel): - items: Items13 - title: str - type: str - - -class Responsible(BaseModel): - anyOf: List[AnyOfItem12] - - -class ReviewComment(BaseModel): - title: str - type: str - - -class Reviewer(BaseModel): - anyOf: List[AnyOfItem12] - - -class Status2(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Udfs2(BaseModel): - items: Items13 - title: str - type: str - - -class Properties15(BaseModel): - description: Description - dueDate: DueDate - key: Key - keywords: Keywords2 - postConditions: PostConditions1 - preConditions: PreConditions1 - priority: Priority1 - references: References3 - requirements: Requirements - responsible: Responsible - reviewComment: ReviewComment - reviewer: Reviewer - status: Status2 - udfs: Udfs2 - - -class TestCaseSetSpecificationSummary(BaseModel): - properties: Properties15 - required: List[str] - title: str - type: str - - -class Keywords3(BaseModel): - items: Items13 - title: str - type: str - - -class Requirements1(BaseModel): - items: Items13 - title: str - type: str - - -class Udfs3(BaseModel): - items: Items13 - title: str - type: str - - -class Properties16(BaseModel): - comments: Comments4 - key: Key - keywords: Keywords3 - requirements: Requirements1 - udfs: Udfs3 - - -class TestCaseSpecificationDetails(BaseModel): - properties: Properties16 - required: List[str] - title: str - type: str - - -class Requirements2(BaseModel): - items: Items13 - title: str - type: str - - -class Properties17(BaseModel): - comments: Comments4 - key: Key - requirements: Requirements2 - - -class TestCaseSpecificationSummary(BaseModel): - properties: Properties17 - required: List[str] - title: str - type: str - - -class Exec3(BaseModel): - anyOf: List[AnyOfItem12] - default: None - - -class Index(BaseModel): - title: str - type: str - - -class Properties18(BaseModel): - exec: Exec3 - index: Index - spec: Spec - uniqueID: UniqueID - - -class TestCaseSummary(BaseModel): - properties: Properties18 - required: List[str] - title: str - type: str - - -class Locker(BaseModel): - anyOf: List[AnyOfItem12] - - -class Properties19(BaseModel): - key: Key - locker: Locker - status: Status2 - - -class TestStructureAutomation(BaseModel): - properties: Properties19 - required: List[str] - title: str - type: str - - -class ExecStatus2(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Locker1(BaseModel): - anyOf: List[AnyOfItem12] - - -class Verdict3(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties20(BaseModel): - execStatus: ExecStatus2 - key: Key - locker: Locker1 - status: Status2 - verdict: Verdict3 - - -class TestStructureExecution(BaseModel): - properties: Properties20 - required: List[str] - title: str - type: str - - -class Locker2(BaseModel): - anyOf: List[AnyOfItem12] - - -class Properties21(BaseModel): - key: Key - locker: Locker2 - status: Status2 - - -class TestStructureSpecification(BaseModel): - properties: Properties21 - required: List[str] - title: str - type: str - - -class Nodes(BaseModel): - items: Items13 - title: str - type: str - - -class Root(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties22(BaseModel): - nodes: Nodes - root: Root - - -class TestStructureTree(BaseModel): - properties: Properties22 - required: List[str] - title: str - type: str - - -class Automation(BaseModel): - anyOf: List[AnyOfItem12] - - -class BaseInformation(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class ElementType(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Execution(BaseModel): - anyOf: List[AnyOfItem12] - - -class Filters(BaseModel): - items: Items13 - title: str - type: str - - -class Specification(BaseModel): - anyOf: List[AnyOfItem12] - - -class Properties23(BaseModel): - automation: Automation - baseInformation: BaseInformation - elementType: ElementType - execution: Execution - filters: Filters - specification: Specification - - -class TestStructureTreeNode(BaseModel): - properties: Properties23 - required: List[str] - title: str - type: str - - -class MatchesFilter(BaseModel): - title: str - type: str - - -class OrderPos(BaseModel): - title: str - type: str - - -class ParentKey(BaseModel): - title: str - type: str - - -class Properties24(BaseModel): - key: Key - matchesFilter: MatchesFilter - name: Name - numbering: Numbering - orderPos: OrderPos - parentKey: ParentKey - uniqueID: UniqueID - - -class TestStructureTreeNodeInformation(BaseModel): - properties: Properties24 - required: List[str] - title: str - type: str - - -class TestStructureTreeNodeType(BaseModel): - enum: List[str] - title: str - type: str - - -class UdfType(BaseModel): - enum: List[str] - title: str - type: str - - -class Value1(BaseModel): - title: str - type: str - - -class Properties25(BaseModel): - key: Key - name: Name - value: Value1 - valueType: ValueType - - -class UserDefinedField(BaseModel): - properties: Properties25 - required: List[str] - title: str - type: str - - -class Properties26(BaseModel): - key: Key - name: Name - - -class UserReference(BaseModel): - properties: Properties26 - required: List[str] - title: str - type: str - - -class FieldDefs(BaseModel): - ActivityStatus: ActivityStatus - AttachedFilter: AttachedFilter - CallType: CallType - ConditionSummary: ConditionSummary - DataTypeKind: DataTypeKind - DataTypeSummary: DataTypeSummary - ExecutionStatus: ExecutionStatus - ExecutionVerdict: ExecutionVerdict - FilterType: FilterType1 - InteractionDetails: InteractionDetails - InteractionExecutionSummary: InteractionExecutionSummary - InteractionSpecificationSummary: InteractionSpecificationSummary - InteractionType: InteractionType1 - InteractionVerdict: InteractionVerdict - Keyword: Keyword - ParameterDefinitionType: ParameterDefinitionType - ParameterSummary: ParameterSummary - ParameterUseType: ParameterUseType - ParameterValueType: ParameterValueType - Priority: Priority - Reference: Reference - ReferenceType: ReferenceType - RequirementReference: RequirementReference - SequencePhase: SequencePhase1 - SpecificationStatus: SpecificationStatus - TestCaseDetails: TestCaseDetails - TestCaseExecutionDetails: TestCaseExecutionDetails - TestCaseExecutionSummary: TestCaseExecutionSummary - TestCaseSetDetails: TestCaseSetDetails - TestCaseSetExecutionSummary: TestCaseSetExecutionSummary - TestCaseSetSpecificationSummary: TestCaseSetSpecificationSummary - TestCaseSpecificationDetails: TestCaseSpecificationDetails - TestCaseSpecificationSummary: TestCaseSpecificationSummary - TestCaseSummary: TestCaseSummary - TestStructureAutomation: TestStructureAutomation - TestStructureExecution: TestStructureExecution - TestStructureSpecification: TestStructureSpecification - TestStructureTree: TestStructureTree - TestStructureTreeNode: TestStructureTreeNode - TestStructureTreeNodeInformation: TestStructureTreeNodeInformation - TestStructureTreeNodeType: TestStructureTreeNodeType - UdfType: UdfType - UserDefinedField: UserDefinedField - UserReference: UserReference - - -class TestCaseDetails1(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class TestCaseSetDetails1(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class TestStructureTree1(BaseModel): - field_ref: str = Field(..., alias='$ref') - - -class Properties27(BaseModel): - TestCaseDetails: TestCaseDetails1 - TestCaseSetDetails: TestCaseSetDetails1 - TestStructureTree: TestStructureTree1 - - -class Model(BaseModel): - field_defs: FieldDefs = Field(..., alias='$defs') - properties: Properties27 - required: List[str] - title: str - type: str diff --git a/pydantic_model.py b/pydantic_model.py deleted file mode 100644 index 74a6d05..0000000 --- a/pydantic_model.py +++ /dev/null @@ -1,469 +0,0 @@ -# pylint: skip-file -from __future__ import annotations - -from pydantic import BaseModel -from enum import Enum, auto -from typing import List, Optional - - -class StrEnum(str, Enum): - def __new__(cls, *args): - for arg in args: - if not isinstance(arg, (str, auto)): - raise TypeError(f"Values of StrEnums must be strings: {repr(arg)} is a {type(arg)}") - return super().__new__(cls, *args) - - def __str__(self): - return self.value - - def _generate_next_value_(name, *_): - return name - - -class TestFilterType(StrEnum): - TestTheme = "TestTheme" - TestCaseSet = "TestCaseSet" - TestCase = "TestCase" - - -class TestStructureElementType(StrEnum): - Root = "Root" - TestTheme = "TestTheme" - TestCaseSet = "TestCaseSet" - - -class Priority(StrEnum): - Undefined = "Undefined" - Low = "Low" - Middle = "Middle" - High = "High" - - -class ReferenceType(StrEnum): - Reference = "Reference" - Hyperlink = "Hyperlink" - Attachment = "Attachment" - - -class SpecStatus(StrEnum): - NotPlanned = "NotPlanned" - Planned = "Planned" - InProgress = "InProgress" - InReview = "InReview" - Released = "Released" - - -class InteractionVerdict(StrEnum): - Pass = "Pass" - Fail = "Fail" - Skipped = "Skipped" - ToVerify = "ToVerify" - Warn = "Warn" - Undefined = "Undefined" - Blocked = "Blocked" - - -class VerdictStatus(StrEnum): - Undefined = "Undefined" - ToVerify = "ToVerify" - Fail = "Fail" - Pass = "Pass" - - -class ActivityStatus(StrEnum): - NotPlanned = "NotPlanned" - Planned = "Planned" - Assigned = "Assigned" - Running = "Running" - Canceled = "Canceled" - Skipped = "Skipped" - Performed = "Performed" - - -class ExecStatus(StrEnum): - NotBlocked = "NotBlocked" - Blocked = "Blocked" - - -class UDFType(StrEnum): - String = "String" - Enumeration = "Enumeration" - Boolean = "Boolean" - - -class SequencePhase(StrEnum): - Setup = "Setup" - TestStep = "TestStep" - Teardown = "Teardown" - - -class InteractionCallType(StrEnum): - Check = "Check" - Flow = "Flow" - - -class InteractionType(StrEnum): - Compound = "Compound" - Atomic = "Atomic" - Textual = "Textual" - - -class ParameterDefinitionType(StrEnum): - DETAILED = "DETAILED" - ARRAY = "ARRAY" - ATOMIC = "ATOMIC" - - -class ParameterEvaluationType(StrEnum): - CallByReference = "CallByReference" - CallByValue = "CallByValue" - CallByReferenceMandatory = "CallByReferenceMandatory" - - -class RepresentativeType(StrEnum): - Text = "Text" - Placeholder = "Placeholder" - Attachment = "Attachment" - Hyperlink = "Hyperlink" - Reference = "Reference" - - -class KindOfDataType(StrEnum): - Regular = "Regular" - Reference = "Reference" - Global = "Global" - AcceptingGlobal = "AcceptingGlobal" - - -class ProjectMember(BaseModel): - userkey: str - userLogin: str - userName: str - projectkey: str - projectName: str - roles: List[str] - - -class ProjectDetails(BaseModel): - key: str - creationTime: str - name: str - status: str - visibility: bool - tovsCount: int - cyclesCount: int - description: str - lockerKey: Optional[int] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - - -class TOVDetails(BaseModel): - key: str - creationTime: str - name: str - status: str - visibility: bool - cyclesCount: int - description: str - lockerKey: Optional[int] = None - startDate: Optional[str] = None - endDate: Optional[str] = None - - -class CycleDetails(BaseModel): - key: str - creationTime: str - name: str - status: str - visibility: bool - description: str - startDate: Optional[str] = None - endDate: Optional[str] = None - - -class UserDetails(BaseModel): - key: str - login: str - name: str - email: str - passwordExpired: bool - active: bool - - -class UserSummary(BaseModel): - key: str - login: str - name: str - active: bool - - -class UserDefinedField(BaseModel): - key: str - name: str - value: str - udfType: UDFType - - -class Keyword(BaseModel): - key: str - name: str - isVariantsMarker: bool - - -class Reference(BaseModel): # TODO: May be changed. Differs to OpenApi.YML - type: ReferenceType - path: str - - -class UserReference(BaseModel): - key: str - name: str - - -class RequirementReference(BaseModel): - key: str - edited: bool - - -class ConditionSummary(BaseModel): - key: str - uniqueID: str - name: str - description: str - version: Optional[str] = None - - -class TestCaseSetSpecificationSummary(BaseModel): - key: str - description: str - reviewComment: str - status: SpecStatus - priority: Priority - responsible: Optional[UserReference] - dueDate: Optional[str] - reviewer: Optional[UserReference] - udfs: List[UserDefinedField] - keywords: List[Keyword] - # references: List[Reference] #TODO: MUST BE CHANGED IN THE FUTURE AGAIN!!! - references: List[str] - requirements: List[RequirementReference] - preConditions: List[ConditionSummary] - postConditions: List[ConditionSummary] - - -class TestCaseSpecificationDetails(BaseModel): - key: str - comments: str - udfs: List[UserDefinedField] - keywords: List[Keyword] - requirements: List[RequirementReference] - - -class TestCaseSetExecutionSummary(BaseModel): - key: str - comments: str - udfs: List[UserDefinedField] - keywords: List[Keyword] - - -class TestCaseSpecificationSummary(BaseModel): - key: str - comments: str - requirements: List[RequirementReference] - - -class TestCaseExecutionSummary(BaseModel): - key: str - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - comments: str - defects: List[str] - tester: Optional[UserReference] = None - - -class TestCaseSummary(BaseModel): - uniqueID: str - index: int - spec: TestCaseSpecificationSummary - exec: Optional[TestCaseExecutionSummary] = None - - -class TestCaseExecutionDetails(BaseModel): - key: str - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - plannedDuration: int - actualDuration: int - currentUser: UserReference - comments: str # TODO: Insert htmlComment - version: Optional[str] - defects: List[str] - udfs: List[UserDefinedField] - keywords: List[Keyword] - # references: List[Reference] #TODO: MUST BE CHANGED IN THE FUTURE AGAIN!!! - references: List[str] - tester: Optional[UserReference] = None - - -class TestCaseSetDetails(BaseModel): - key: str - numbering: str - uniqueID: str - name: str - spec: TestCaseSetSpecificationSummary - testCases: List[TestCaseSummary] - exec: Optional[TestCaseSetExecutionSummary] = None - - -class InteractionExecutionSummary(BaseModel): - verdict: InteractionVerdict - time: str - duration: int - currentUser: UserReference - tester: Optional[UserReference] - comments: str - # references: List[Reference] #TODO: MUST BE CHANGED IN THE FUTURE AGAIN!!! - references: List[str] - - -class InteractionSpecificationSummary(BaseModel): - callKey: str - sequencePhase: SequencePhase - callType: InteractionCallType - description: str - comments: str - # references: List[Reference] #TODO: MUST BE CHANGED IN THE FUTURE AGAIN!!! - references: List[str] - preConditions: List[ConditionSummary] - postConditions: List[ConditionSummary] - - -class DataTypeSummary(BaseModel): - key: str - name: str - kind: KindOfDataType - version: Optional[str] - path: str - uniqueID: str - - -class ParameterSummary(BaseModel): - key: str - name: str - value: Optional[str] - valueType: RepresentativeType - definitionType: ParameterDefinitionType - evaluationType: ParameterEvaluationType - dataType: Optional[DataTypeSummary] - - -class InteractionDetails(BaseModel): - key: str - uniqueID: str - name: str - version: Optional[str] - interactionType: InteractionType - path: str - spec: InteractionSpecificationSummary - exec: Optional[InteractionExecutionSummary] - parameters: List[ParameterSummary] - interactions: List[InteractionDetails] - - -class TestCaseDetails(BaseModel): - uniqueID: str - spec: TestCaseSpecificationDetails - interactions: List[InteractionDetails] - parameters: List[ParameterSummary] - exec: Optional[TestCaseExecutionDetails] = None - - -class TestStructureSpecification(BaseModel): - key: str - locker: Optional[UserReference] - status: SpecStatus - - -class TestStructureAutomation(BaseModel): - key: str - locker: Optional[UserReference] - status: SpecStatus - - -class TestStructureExecution(BaseModel): - key: str - locker: Optional[UserReference] - status: ActivityStatus - execStatus: ExecStatus - verdict: VerdictStatus - - -class AttachedFilter(BaseModel): - key: str - name: str - filterType: TestFilterType - content: str - - -class TestStructureTreeNodeInformation(BaseModel): - key: str - numbering: str - parentKey: str - name: str - uniqueID: str - orderPos: int - matchesFilter: bool - - -class TestStructureTreeNode(BaseModel): - elementType: TestStructureElementType - base: TestStructureTreeNodeInformation - spec: Optional[TestStructureSpecification] - aut: Optional[TestStructureAutomation] - exec: Optional[TestStructureExecution] - filters: List[AttachedFilter] - - -class TestStructureTree(BaseModel): - root: Optional[TestStructureTreeNode] - nodes: List[TestStructureTreeNode] - - -class AllModels(BaseModel): - ProjectMember: ProjectMember - ProjectDetails: ProjectDetails - TOVDetails: TOVDetails - CycleDetails: CycleDetails - UserDetails: UserDetails - TestStructureTree: TestStructureTree - TestCaseDetails: TestCaseDetails - InteractionDetails: InteractionDetails - InteractionSpecificationSummary: InteractionSpecificationSummary - InteractionExecutionSummary: InteractionExecutionSummary - ParameterSummary: ParameterSummary - TestCaseSpecificationDetails: TestCaseSpecificationDetails - TestCaseExecutionDetails: TestCaseExecutionDetails - TestStructureSpecification: TestStructureSpecification - TestStructureAutomation: TestStructureAutomation - TestStructureExecution: TestStructureExecution - AttachedFilter: AttachedFilter - TestStructureTreeNodeInformation: TestStructureTreeNodeInformation - TestStructureTreeNode: TestStructureTreeNode - TestCaseExecutionSummary: TestCaseExecutionSummary - TestCaseSetExecutionSummary: TestCaseSetExecutionSummary - ActivityStatus: ActivityStatus - ExecStatus: ExecStatus - VerdictStatus: VerdictStatus - SpecStatus: SpecStatus - DataTypeSummary: DataTypeSummary - TestCaseSetDetails: TestCaseSetDetails - TestCaseSetSpecificationSummary: TestCaseSetSpecificationSummary - TestCaseSpecificationSummary: TestCaseSpecificationSummary - TestCaseSummary: TestCaseSummary - - - - diff --git a/pyproject.toml b/pyproject.toml index 44e0fd5..09fa074 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,6 +33,7 @@ dev = [ "pytest-cov", "pytest-spec", "robotframework-tidy", + "datamodel-code-generator", "ruff", "twine", ] @@ -66,7 +67,7 @@ pretty = true [tool.ruff] -target-version = "py39" +target-version = "py310" line-length = 100 lint.select = [ "A", @@ -147,6 +148,13 @@ disable = ''' max-nested-blocks = 3 +[tool.datamodel-codegen] +input-file-type = "openapi" +output-model-type = "dataclasses.dataclass" +target-python-version = "3.10" +output = "testbench2robotframework/model.py" +formatters = ["ruff-format", "ruff-check"] + [tool.robotidy] transform = [ "InlineIf", diff --git a/tasks.py b/tasks.py index a35af3b..6c45cd7 100644 --- a/tasks.py +++ b/tasks.py @@ -1,8 +1,48 @@ -from invoke import Context, task import os +import re from pathlib import Path + +from invoke import Context, task from robot.run import run_cli +MODEL_OUTPUT = "testbench2robotframework/model.py" + + +@task +def generate_model(c: Context, input="openapi.yml") -> None: + """Regenerates model.py from an OpenAPI spec and injects __VERSION__.""" + if not Path(input).exists(): + raise FileNotFoundError(f"OpenAPI spec not found: {input}") + c.run(f"datamodel-codegen --input {input}") + with open(input, encoding="utf-8") as f: + content = f.read() + match = re.search(r"version:\s*['\"]?([^'\"\n]+)", content) + version = match.group(1).strip() if match else "unknown" + model_path = Path(MODEL_OUTPUT) + model_content = model_path.read_text(encoding="utf-8") + # Inject __VERSION__ + model_content = model_content.replace( + "from __future__ import annotations", + f'from __future__ import annotations\n\n__VERSION__ = "{version}"', + ) + # datamodel-codegen generates `field: Literal["X"]` without a default, which + # violates the dataclass rule that non-default fields can't follow default fields. + # Using field(default=..., init=False) so inherited classes (where Python preserves + # the parent field's position in the MRO dict) don't break either. + model_content = model_content.replace( + "from dataclasses import dataclass\n", + "from dataclasses import dataclass, field\n", + ) + model_content = re.sub( + r'(\w+): (Literal\["([^"]+)"\])(\s*$)', + r'\1: \2 = field(default="\3", init=False)\4', + model_content, + flags=re.MULTILINE, + ) + model_path.write_text(model_content, encoding="utf-8") + print(f"Generated {MODEL_OUTPUT} with __VERSION__ = '{version}'") + + @task def run_atest(c: Context) -> None: """Runs Robot Framework atests.""" @@ -26,4 +66,4 @@ def run_atest(c: Context) -> None: ] ) finally: - os.chdir(orgdir) \ No newline at end of file + os.chdir(orgdir) diff --git a/testbench2robotframework/cli.py b/testbench2robotframework/cli.py index 696e298..7477155 100644 --- a/testbench2robotframework/cli.py +++ b/testbench2robotframework/cli.py @@ -123,9 +123,9 @@ def testbench2robotframework_cli(): "--metadata", multiple=True, callback=parse_subdivision_mapping, - help="""Add extra metadata to the settings of the generated Robot Framework test suite. - Provide entries as key:value pairs, where *key* is the metadata name and *value* is the corresponding value. - Values may also be Python expressions. + help="""Add extra metadata to the settings of the generated Robot Framework test suite. + Provide entries as key:value pairs, where *key* is the metadata name and *value* is the corresponding value. + Values may also be Python expressions. The special variable '$tcs' gives access to the TestBench Python model of the test case set.""", ) @click.option( diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index b5dcec0..c0578fd 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -1,12 +1,12 @@ # generated by datamodel-codegen: # filename: openapi.yml -# timestamp: 2026-05-18T16:35:43+00:00 +# timestamp: 2026-05-19T23:21:09+00:00 from __future__ import annotations __VERSION__ = "4.0.45" -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum from typing import Any, Literal @@ -354,18 +354,18 @@ class ProjectCreation: @dataclass class UnknownProject(ProjectCreation): - type: Literal["UnknownProject"] + type: Literal["UnknownProject"] = field(default="UnknownProject", init=False) @dataclass class NewProject(ProjectCreation): - type: Literal["NewProject"] + type: Literal["NewProject"] = field(default="NewProject", init=False) @dataclass class ProjectImported(ProjectCreation): project: str - type: Literal["ProjectImported"] + type: Literal["ProjectImported"] = field(default="ProjectImported", init=False) @dataclass @@ -375,26 +375,26 @@ class TOVCreation: @dataclass class UnknownTOV(TOVCreation): - type: Literal["UnknownTOV"] + type: Literal["UnknownTOV"] = field(default="UnknownTOV", init=False) @dataclass class NewTOV(TOVCreation): - type: Literal["NewTOV"] + type: Literal["NewTOV"] = field(default="NewTOV", init=False) @dataclass class TOVCloned(TOVCreation): tov: str tovKey: str | None = None - type: Literal["TOVCloned"] + type: Literal["TOVCloned"] = field(default="TOVCloned", init=False) @dataclass class TOVClonedFromSameProject(TOVCreation): tov: str tovKey: str | None = None - type: Literal["TOVClonedFromSameProject"] + type: Literal["TOVClonedFromSameProject"] = field(default="TOVClonedFromSameProject", init=False) @dataclass @@ -402,14 +402,14 @@ class TOVClonedFromDifferentProject(TOVCreation): project: str tov: str tovKey: str | None = None - type: Literal["TOVClonedFromDifferentProject"] + type: Literal["TOVClonedFromDifferentProject"] = field(default="TOVClonedFromDifferentProject", init=False) @dataclass class TOVImportedAsNew(TOVCreation): project: str tov: str - type: Literal["TOVImportedAsNew"] + type: Literal["TOVImportedAsNew"] = field(default="TOVImportedAsNew", init=False) @dataclass @@ -417,14 +417,14 @@ class TOVImportedAsClone(TOVCreation): project: str tov: str tovCloned: str - type: Literal["TOVImportedAsClone"] + type: Literal["TOVImportedAsClone"] = field(default="TOVImportedAsClone", init=False) @dataclass class TOVImportedAsNewFromPlugin(TOVCreation): plugin: str tov: str - type: Literal["TOVImportedAsNewFromPlugin"] + type: Literal["TOVImportedAsNewFromPlugin"] = field(default="TOVImportedAsNewFromPlugin", init=False) @dataclass @@ -432,14 +432,14 @@ class TOVImportedAsCloneFromPlugin(TOVCreation): plugin: str tov: str tovCloned: str - type: Literal["TOVImportedAsCloneFromPlugin"] + type: Literal["TOVImportedAsCloneFromPlugin"] = field(default="TOVImportedAsCloneFromPlugin", init=False) @dataclass class TOVImported(TOVCreation): project: str tov: str - type: Literal["TOVImported"] + type: Literal["TOVImported"] = field(default="TOVImported", init=False) @dataclass @@ -448,7 +448,7 @@ class TOVDerivedFromSameProject(TOVCreation): variantsDefinition: str baseTOVKey: str | None = None variantsDefinitionKey: str | None = None - type: Literal["TOVDerivedFromSameProject"] + type: Literal["TOVDerivedFromSameProject"] = field(default="TOVDerivedFromSameProject", init=False) @dataclass @@ -458,7 +458,7 @@ class TOVDerivedFromDifferentProject(TOVCreation): variantsDefinition: str baseTOVKey: str | None = None variantsDefinitionKey: str | None = None - type: Literal["TOVDerivedFromDifferentProject"] + type: Literal["TOVDerivedFromDifferentProject"] = field(default="TOVDerivedFromDifferentProject", init=False) @dataclass @@ -932,7 +932,7 @@ class TovStructureOptions(ReportExportOptions): suppressFilteredData: bool | None = None suppressEmptyTestThemes: bool | None = None filters: list[FilterInfo] | None = None - exportType: Literal["tovExport"] + exportType: Literal["tovExport"] = field(default="tovExport", init=False) @dataclass @@ -953,7 +953,7 @@ class CycleReportOptions(ReportExportOptions): suppressNotExecutable: bool | None = None suppressEmptyTestThemes: bool | None = None filters: list[FilterInfo] | None = None - exportType: Literal["cycleExport"] + exportType: Literal["cycleExport"] = field(default="cycleExport", init=False) @dataclass @@ -976,11 +976,6 @@ class DefectUDF: values: list[str] | None = None -@dataclass -class TestStructureTreeNode: - pass - - @dataclass class TestStructureExecution: status: ActivityStatus @@ -1077,13 +1072,13 @@ class ReportingResult: @dataclass class ReportingFailure(ReportingResult): error: ActionFailure - type: Literal["ReportingFailure"] + type: Literal["ReportingFailure"] = field(default="ReportingFailure", init=False) @dataclass class ReportingSuccess(ReportingResult): reportName: str - type: Literal["ReportingSuccess"] + type: Literal["ReportingSuccess"] = field(default="ReportingSuccess", init=False) @dataclass @@ -1094,7 +1089,7 @@ class ExecutionImportingResult: @dataclass class ExecutionImportingFailure(ExecutionImportingResult): error: ActionFailure - type: Literal["ExecutionImportingFailure"] + type: Literal["ExecutionImportingFailure"] = field(default="ExecutionImportingFailure", init=False) @dataclass @@ -1268,7 +1263,7 @@ class CycleCreation: @dataclass class CycleCloned(CycleCreation): cycle: str - type: Literal["CycleCloned"] + type: Literal["CycleCloned"] = field(default="CycleCloned", init=False) @dataclass @@ -1276,14 +1271,14 @@ class CycleImported(CycleCreation): project: str tov: str cycle: str - type: Literal["CycleImported"] + type: Literal["CycleImported"] = field(default="CycleImported", init=False) @dataclass class CycleImportedAsNew(CycleCreation): tov: str cycle: str - type: Literal["CycleImportedAsNew"] + type: Literal["CycleImportedAsNew"] = field(default="CycleImportedAsNew", init=False) @dataclass @@ -1291,7 +1286,7 @@ class CycleImportedAsClone(CycleCreation): tov: str cycle: str clonedCycle: str - type: Literal["CycleImportedAsClone"] + type: Literal["CycleImportedAsClone"] = field(default="CycleImportedAsClone", init=False) @dataclass @@ -1299,14 +1294,14 @@ class CycleImportedAsNewFromPlugin(CycleCreation): plugin: str tov: str cycle: str - type: Literal["CycleImportedAsNewFromPlugin"] + type: Literal["CycleImportedAsNewFromPlugin"] = field(default="CycleImportedAsNewFromPlugin", init=False) @dataclass class CycleClonedFromPlugin(CycleCreation): plugin: str cycle: str - type: Literal["CycleClonedFromPlugin"] + type: Literal["CycleClonedFromPlugin"] = field(default="CycleClonedFromPlugin", init=False) @dataclass @@ -1315,17 +1310,17 @@ class CycleImportedAsCloneFromPlugin(CycleCreation): tov: str cycle: str clonedCycle: str - type: Literal["CycleImportedAsCloneFromPlugin"] + type: Literal["CycleImportedAsCloneFromPlugin"] = field(default="CycleImportedAsCloneFromPlugin", init=False) @dataclass class UnknownCycle(CycleCreation): - type: Literal["UnknownCycle"] + type: Literal["UnknownCycle"] = field(default="UnknownCycle", init=False) @dataclass class NewCycle(CycleCreation): - type: Literal["NewCycle"] + type: Literal["NewCycle"] = field(default="NewCycle", init=False) @dataclass @@ -1594,11 +1589,16 @@ class DefectConfig: external: ExternalDefectManagement | None = None +@dataclass +class TestStructureTreeNode: + elementType: TestStructureElementType + + @dataclass class RootNode(TestStructureTreeNode): base: TestStructureItemBaseInformation filters: list[AttachedFilter] - elementType: Literal["RootNode"] + elementType: Literal["RootNode"] = field(default="RootNode", init=False) @dataclass @@ -1608,7 +1608,7 @@ class TestThemeNode(TestStructureTreeNode): spec: TestStructureItemSpecification | None = None aut: TestStructureAutomation | None = None exec: TestStructureItemExecution | None = None - elementType: Literal["TestThemeNode"] + elementType: Literal["TestThemeNode"] = field(default="TestThemeNode", init=False) @dataclass @@ -1618,7 +1618,7 @@ class TestCaseSetNode(TestStructureTreeNode): spec: TestStructureItemSpecification | None = None aut: TestStructureAutomation | None = None exec: TestStructureItemExecution | None = None - elementType: Literal["TestCaseSetNode"] + elementType: Literal["TestCaseSetNode"] = field(default="TestCaseSetNode", init=False) @dataclass @@ -1626,7 +1626,7 @@ class TestCaseNode(TestStructureTreeNode): base: TestCaseBaseInformation spec: TestCaseSpecification | None = None exec: TestCaseExecution | None = None - elementType: Literal["TestCaseNode"] + elementType: Literal["TestCaseNode"] = field(default="TestCaseNode", init=False) @dataclass @@ -1803,7 +1803,7 @@ class ExecutionImportingSuccess(ExecutionImportingResult): checkedInTestElements: list[CheckedInElement] createdDefects: list[CreatedDefect] createdReferences: list[CreatedReference] - type: Literal["ExecutionImportingSuccess"] + type: Literal["ExecutionImportingSuccess"] = field(default="ExecutionImportingSuccess", init=False) @dataclass diff --git a/testbench2robotframework/model_utils.py b/testbench2robotframework/model_utils.py index 5ced8f0..0d9578e 100644 --- a/testbench2robotframework/model_utils.py +++ b/testbench2robotframework/model_utils.py @@ -1,4 +1,5 @@ -from dataclasses import fields, is_dataclass +import logging +from dataclasses import MISSING, fields, is_dataclass from enum import Enum from types import UnionType as TypesUnion from typing import Any, TypeVar, get_args, get_origin, get_type_hints @@ -6,13 +7,15 @@ T = TypeVar("T") +logger = logging.getLogger(__name__) + ERROR_NOT_A_DATACLASS = "The provided class '{dataclass}' is not a dataclass." ERROR_UNKNOWN_TYPE_HINT_ORIGIN = "Unknown type hint origin." ERROR_UNION_MISMATCH = "Value does not match any of the union types." ERROR_NOT_A_LIST = "Value is not of type list." ERROR_LIST_ARGUMENTS_MISMATCH = "List type hint must have exactly one argument, got {args}." ERROR_UNION_WITHOUT_ARGUMENTS = "Union type hint must have at least one argument." -ERROR_TOO_MANY_DATA_FIELDS = "Data dictionary contains more fields than the dataclass has." +ERROR_MISSING_FIELDS = "Data dictionary for '{dataclass}' is missing mandatory fields: {fields}." ERROR_NONETYPE_DATA = "Data cannot be None." @@ -36,17 +39,35 @@ def get_origin_from_type_hint(type_hint): raise ValueError(ERROR_UNKNOWN_TYPE_HINT_ORIGIN) +def _has_default(cls_field) -> bool: + return cls_field.default is not MISSING or cls_field.default_factory is not MISSING + + def from_dict(cls: type[T], data: dict) -> T: if not is_dataclass(cls): raise ValueError(ERROR_NOT_A_DATACLASS.format(dataclass=cls.__name__)) if data is None: raise ValueError(ERROR_NONETYPE_DATA) - cls_dict = {} class_type_hints = get_type_hints(cls) class_fields = fields(cls) - if len(class_fields) < len(data): - raise ValueError(ERROR_TOO_MANY_DATA_FIELDS) + init_field_names = {f.name for f in class_fields if f.init} + extra_keys = set(data.keys()) - init_field_names + if extra_keys: + logger.warning( + "%s: ignoring unknown fields: %s", cls.__name__, ", ".join(sorted(extra_keys)) + ) + mandatory_fields = {f.name for f in class_fields if f.init and not _has_default(f)} + missing_fields = mandatory_fields - set(data.keys()) + if missing_fields: + raise ValueError( + ERROR_MISSING_FIELDS.format( + dataclass=cls.__name__, fields=", ".join(sorted(missing_fields)) + ) + ) + cls_dict = {} for cls_field in class_fields: + if not cls_field.init: + continue if cls_field.name not in data: continue field_value = data.get(cls_field.name) diff --git a/testbench2robotframework/result_writer.py b/testbench2robotframework/result_writer.py index 6bdb598..a2084e2 100644 --- a/testbench2robotframework/result_writer.py +++ b/testbench2robotframework/result_writer.py @@ -29,9 +29,9 @@ RichTextForImport, SequencePhase, TestCaseDetails, - TestCaseExecutionDetails, TestCaseExecutionForImport, TestCaseSetExecutionForImport, + UserReference, VerdictStatus, ) from .utils import directory_to_zip, get_directory @@ -41,6 +41,17 @@ except ImportError: Group = None +def _empty_keyword_call_execution() -> KeywordCallExecution: + return KeywordCallExecution( + verdict=KeywordVerdict.Undefined, + duration=0, + currentUser=UserReference(key="", name=""), + comments="", + references=[], + defects=[], + ) + + BACKGROUND_COLOR = { "PASS": "#04AF91", "FAIL": "#ce3e01", @@ -149,8 +160,6 @@ def end_test(self, test: TestCase): self.protocol_test_case: TestCaseExecutionForImport = TestCaseExecutionForImport( test_uid, itb_test_case.exec.key, None, None, None ) - if itb_test_case.exec is None: - itb_test_case.exec = from_dict(TestCaseExecutionDetails, {}) if itb_test_case.exec.key in ["", "-1"]: logger.warning( f"Test case {itb_test_case.uniqueID} was not exported based on " @@ -171,7 +180,7 @@ def end_test(self, test: TestCase): ) for step in textual_steps: if step.exec is None: - step.exec = from_dict(KeywordCallExecution, {}) + 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_execution_comment(itb_test_case, self.test_chain) @@ -335,7 +344,7 @@ def _set_keyword_verdicts( ): for index, tb_keyword in enumerate(keyword_list): if tb_keyword.exec is None: - tb_keyword.exec = from_dict(KeywordCallExecution, {}) + tb_keyword.exec = _empty_keyword_call_execution() if sequence_phase == SequencePhase.TestStep and not self._test_setup_passed: tb_keyword.exec.verdict = KeywordVerdict.Skipped continue @@ -454,7 +463,7 @@ def _set_compound_keyword_execution_verdict( self, compound_keyword: KeywordCall, test_steps: list[KeywordCall] ): if compound_keyword.exec is None: - compound_keyword.exec = from_dict(KeywordCallExecution, {}) + compound_keyword.exec = _empty_keyword_call_execution() compound_keyword.exec.verdict = KeywordVerdict.Skipped children = list(filter(lambda ts: ts.parentID == compound_keyword.sequenceID, test_steps)) for child in children: @@ -462,7 +471,7 @@ def _set_compound_keyword_execution_verdict( logger.debug( f"Child keyword {child.uniqueID} had no exec details and therefore ignored." ) - child.exec = from_dict(KeywordCallExecution, {}) + child.exec = _empty_keyword_call_execution() if child.spec.keywordType == KeywordType.Compound: self._set_compound_keyword_execution_verdict(child, test_steps) if child.spec.keywordType == KeywordType.Textual: From 52a01887a417cb99a5f3943b4c2217da06b01529 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Wed, 20 May 2026 17:26:02 +0200 Subject: [PATCH 09/15] model revert - generation with version <0.51.0 --- testbench2robotframework/model.py | 147 ++++++++++-------------------- 1 file changed, 47 insertions(+), 100 deletions(-) diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index c0578fd..df9c8a1 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -1,14 +1,13 @@ # generated by datamodel-codegen: # filename: openapi.yml -# timestamp: 2026-05-19T23:21:09+00:00 +# timestamp: 2026-05-20T15:22:23+00:00 from __future__ import annotations __VERSION__ = "4.0.45" -from dataclasses import dataclass, field +from dataclasses import dataclass from enum import Enum -from typing import Any, Literal class TestElementStatus(Enum): @@ -354,18 +353,17 @@ class ProjectCreation: @dataclass class UnknownProject(ProjectCreation): - type: Literal["UnknownProject"] = field(default="UnknownProject", init=False) + pass @dataclass class NewProject(ProjectCreation): - type: Literal["NewProject"] = field(default="NewProject", init=False) + pass @dataclass class ProjectImported(ProjectCreation): project: str - type: Literal["ProjectImported"] = field(default="ProjectImported", init=False) @dataclass @@ -375,26 +373,24 @@ class TOVCreation: @dataclass class UnknownTOV(TOVCreation): - type: Literal["UnknownTOV"] = field(default="UnknownTOV", init=False) + pass @dataclass class NewTOV(TOVCreation): - type: Literal["NewTOV"] = field(default="NewTOV", init=False) + pass @dataclass class TOVCloned(TOVCreation): tov: str tovKey: str | None = None - type: Literal["TOVCloned"] = field(default="TOVCloned", init=False) @dataclass class TOVClonedFromSameProject(TOVCreation): tov: str tovKey: str | None = None - type: Literal["TOVClonedFromSameProject"] = field(default="TOVClonedFromSameProject", init=False) @dataclass @@ -402,14 +398,12 @@ class TOVClonedFromDifferentProject(TOVCreation): project: str tov: str tovKey: str | None = None - type: Literal["TOVClonedFromDifferentProject"] = field(default="TOVClonedFromDifferentProject", init=False) @dataclass class TOVImportedAsNew(TOVCreation): project: str tov: str - type: Literal["TOVImportedAsNew"] = field(default="TOVImportedAsNew", init=False) @dataclass @@ -417,14 +411,12 @@ class TOVImportedAsClone(TOVCreation): project: str tov: str tovCloned: str - type: Literal["TOVImportedAsClone"] = field(default="TOVImportedAsClone", init=False) @dataclass class TOVImportedAsNewFromPlugin(TOVCreation): plugin: str tov: str - type: Literal["TOVImportedAsNewFromPlugin"] = field(default="TOVImportedAsNewFromPlugin", init=False) @dataclass @@ -432,14 +424,12 @@ class TOVImportedAsCloneFromPlugin(TOVCreation): plugin: str tov: str tovCloned: str - type: Literal["TOVImportedAsCloneFromPlugin"] = field(default="TOVImportedAsCloneFromPlugin", init=False) @dataclass class TOVImported(TOVCreation): project: str tov: str - type: Literal["TOVImported"] = field(default="TOVImported", init=False) @dataclass @@ -448,7 +438,6 @@ class TOVDerivedFromSameProject(TOVCreation): variantsDefinition: str baseTOVKey: str | None = None variantsDefinitionKey: str | None = None - type: Literal["TOVDerivedFromSameProject"] = field(default="TOVDerivedFromSameProject", init=False) @dataclass @@ -458,7 +447,6 @@ class TOVDerivedFromDifferentProject(TOVCreation): variantsDefinition: str baseTOVKey: str | None = None variantsDefinitionKey: str | None = None - type: Literal["TOVDerivedFromDifferentProject"] = field(default="TOVDerivedFromDifferentProject", init=False) @dataclass @@ -473,7 +461,7 @@ class ProjectDetailsResponse: visible: bool testIntelligence: bool description: str - creation: UnknownProject | NewProject | ProjectImported + creation: ProjectCreation instantOfCreation: str onlyAdminsMayManageUDFs: bool variantsManagementEnabled: str @@ -519,20 +507,7 @@ class TOVResponse: projectKey: str description: str isBaseTov: bool - creation: ( - UnknownTOV - | NewTOV - | TOVCloned - | TOVClonedFromSameProject - | TOVClonedFromDifferentProject - | TOVImportedAsNew - | TOVImportedAsClone - | TOVImportedAsNewFromPlugin - | TOVImportedAsCloneFromPlugin - | TOVImported - | TOVDerivedFromSameProject - | TOVDerivedFromDifferentProject - ) + creation: TOVCreation key: str status: ProjectStatus testingIntelligence: bool @@ -704,6 +679,24 @@ class ReportScope: cycleKey: str | None = None +@dataclass +class ReportCreation: + creator: UserInfo + startDate: str + endDate: str + scope: ReportScope + summary: ReportItemsSummary + exportOptions: ReportExportOptions | None = None + + +@dataclass +class ReportMetaInformation: + formatVersion: str + serverLocation: ServerLocation + reportCreation: ReportCreation + serverVersions: TestBenchVersions + + @dataclass class RequirementReference: key: str @@ -932,7 +925,6 @@ class TovStructureOptions(ReportExportOptions): suppressFilteredData: bool | None = None suppressEmptyTestThemes: bool | None = None filters: list[FilterInfo] | None = None - exportType: Literal["tovExport"] = field(default="tovExport", init=False) @dataclass @@ -953,7 +945,6 @@ class CycleReportOptions(ReportExportOptions): suppressNotExecutable: bool | None = None suppressEmptyTestThemes: bool | None = None filters: list[FilterInfo] | None = None - exportType: Literal["cycleExport"] = field(default="cycleExport", init=False) @dataclass @@ -1072,13 +1063,11 @@ class ReportingResult: @dataclass class ReportingFailure(ReportingResult): error: ActionFailure - type: Literal["ReportingFailure"] = field(default="ReportingFailure", init=False) @dataclass class ReportingSuccess(ReportingResult): reportName: str - type: Literal["ReportingSuccess"] = field(default="ReportingSuccess", init=False) @dataclass @@ -1089,7 +1078,6 @@ class ExecutionImportingResult: @dataclass class ExecutionImportingFailure(ExecutionImportingResult): error: ActionFailure - type: Literal["ExecutionImportingFailure"] = field(default="ExecutionImportingFailure", init=False) @dataclass @@ -1263,7 +1251,6 @@ class CycleCreation: @dataclass class CycleCloned(CycleCreation): cycle: str - type: Literal["CycleCloned"] = field(default="CycleCloned", init=False) @dataclass @@ -1271,14 +1258,12 @@ class CycleImported(CycleCreation): project: str tov: str cycle: str - type: Literal["CycleImported"] = field(default="CycleImported", init=False) @dataclass class CycleImportedAsNew(CycleCreation): tov: str cycle: str - type: Literal["CycleImportedAsNew"] = field(default="CycleImportedAsNew", init=False) @dataclass @@ -1286,7 +1271,6 @@ class CycleImportedAsClone(CycleCreation): tov: str cycle: str clonedCycle: str - type: Literal["CycleImportedAsClone"] = field(default="CycleImportedAsClone", init=False) @dataclass @@ -1294,14 +1278,12 @@ class CycleImportedAsNewFromPlugin(CycleCreation): plugin: str tov: str cycle: str - type: Literal["CycleImportedAsNewFromPlugin"] = field(default="CycleImportedAsNewFromPlugin", init=False) @dataclass class CycleClonedFromPlugin(CycleCreation): plugin: str cycle: str - type: Literal["CycleClonedFromPlugin"] = field(default="CycleClonedFromPlugin", init=False) @dataclass @@ -1310,17 +1292,16 @@ class CycleImportedAsCloneFromPlugin(CycleCreation): tov: str cycle: str clonedCycle: str - type: Literal["CycleImportedAsCloneFromPlugin"] = field(default="CycleImportedAsCloneFromPlugin", init=False) @dataclass class UnknownCycle(CycleCreation): - type: Literal["UnknownCycle"] = field(default="UnknownCycle", init=False) + pass @dataclass class NewCycle(CycleCreation): - type: Literal["NewCycle"] = field(default="NewCycle", init=False) + pass @dataclass @@ -1528,24 +1509,6 @@ class ReferenceAssignment: versionName: str | None = None -@dataclass -class ReportCreation: - creator: UserInfo - startDate: str - endDate: str - scope: ReportScope - summary: ReportItemsSummary - exportOptions: TovStructureOptions | CycleReportOptions | None = None - - -@dataclass -class ReportMetaInformation: - formatVersion: str - serverLocation: ServerLocation - reportCreation: ReportCreation - serverVersions: TestBenchVersions - - @dataclass class RepresentativeValue: name: str @@ -1598,7 +1561,6 @@ class TestStructureTreeNode: class RootNode(TestStructureTreeNode): base: TestStructureItemBaseInformation filters: list[AttachedFilter] - elementType: Literal["RootNode"] = field(default="RootNode", init=False) @dataclass @@ -1608,17 +1570,14 @@ class TestThemeNode(TestStructureTreeNode): spec: TestStructureItemSpecification | None = None aut: TestStructureAutomation | None = None exec: TestStructureItemExecution | None = None - elementType: Literal["TestThemeNode"] = field(default="TestThemeNode", init=False) @dataclass class TestCaseSetNode(TestStructureTreeNode): base: TestStructureItemBaseInformation - filters: Any spec: TestStructureItemSpecification | None = None aut: TestStructureAutomation | None = None exec: TestStructureItemExecution | None = None - elementType: Literal["TestCaseSetNode"] = field(default="TestCaseSetNode", init=False) @dataclass @@ -1626,19 +1585,24 @@ class TestCaseNode(TestStructureTreeNode): base: TestCaseBaseInformation spec: TestCaseSpecification | None = None exec: TestCaseExecution | None = None - elementType: Literal["TestCaseNode"] = field(default="TestCaseNode", init=False) @dataclass class TestStructureTree: - nodes: list[RootNode | TestThemeNode | TestCaseSetNode | TestCaseNode] + nodes: list[TestThemeNode | TestCaseSetNode | TestCaseNode] root: RootNode | TestThemeNode | TestCaseSetNode | TestCaseNode | None = None @dataclass class ReportingCompletion: time: str - result: ReportingFailure | ReportingSuccess + result: ReportingResult + + +@dataclass +class ExecutionImportingCompletion: + time: str + result: ExecutionImportingResult @dataclass @@ -1711,17 +1675,7 @@ class CycleResponse: visible: bool projectKey: str description: str - creation: ( - CycleCloned - | CycleImported - | CycleImportedAsNew - | CycleImportedAsClone - | CycleImportedAsNewFromPlugin - | CycleClonedFromPlugin - | CycleImportedAsCloneFromPlugin - | UnknownCycle - | NewCycle - ) + creation: CycleCreation key: str tovKey: str status: ProjectStatus @@ -1796,6 +1750,16 @@ class ReportingJob: completion: ReportingCompletion | None = None +@dataclass +class ExecutionImportingJob: + id: str + projectKey: str + owner: str + start: str + progress: JobProgress | None = None + completion: ExecutionImportingCompletion | None = None + + @dataclass class ExecutionImportingSuccess(ExecutionImportingResult): testCaseSets: list[TestCaseSetExecutionImportResult] @@ -1803,7 +1767,6 @@ class ExecutionImportingSuccess(ExecutionImportingResult): checkedInTestElements: list[CheckedInElement] createdDefects: list[CreatedDefect] createdReferences: list[CreatedReference] - type: Literal["ExecutionImportingSuccess"] = field(default="ExecutionImportingSuccess", init=False) @dataclass @@ -1949,12 +1912,6 @@ class TestCaseDetails: origin: TestCaseDetailsOrigin | None = None -@dataclass -class ExecutionImportingCompletion: - time: str - result: ExecutionImportingFailure | ExecutionImportingSuccess - - @dataclass class CycleForUpdate: name: str | None = None @@ -1964,13 +1921,3 @@ class CycleForUpdate: status: ProjectStatus | None = None testingIntelligence: bool | None = None startDate: OptionalLocalDate | None = None - - -@dataclass -class ExecutionImportingJob: - id: str - projectKey: str - owner: str - start: str - progress: JobProgress | None = None - completion: ExecutionImportingCompletion | None = None From cf130606bbb2f404ddfccefc3970b895b9658532 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Wed, 20 May 2026 21:22:36 +0200 Subject: [PATCH 10/15] updated version --- testbench2robotframework/__init__.py | 2 +- testbench2robotframework/model.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/testbench2robotframework/__init__.py b/testbench2robotframework/__init__.py index f24e6f4..da18653 100644 --- a/testbench2robotframework/__init__.py +++ b/testbench2robotframework/__init__.py @@ -17,4 +17,4 @@ from .testbench2robotframework import testbench2robotframework # noqa: F401 -__version__ = "0.9.2b3" +__version__ = "0.9.2b4" diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index df9c8a1..0ca473a 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -903,6 +903,7 @@ class TestCaseDetailsOrigin(Enum): class ReferenceKind(Enum): Reference = "Reference" Link = "Link" + Hyperlink = "Hyperlink" Attachment = "Attachment" From 92e8095055ce63b0191a4dbade271df787245d60 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Thu, 21 May 2026 09:45:55 +0200 Subject: [PATCH 11/15] fix: missing model info --- testbench2robotframework/model.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index 0ca473a..e43c4c3 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -1562,6 +1562,9 @@ class TestStructureTreeNode: class RootNode(TestStructureTreeNode): base: TestStructureItemBaseInformation filters: list[AttachedFilter] + spec: TestStructureItemSpecification | None = None + aut: TestStructureAutomation | None = None + exec: TestStructureItemExecution | None = None @dataclass From 1e8d23d7f88a5e8bfb37f0278cd76f0a02270519 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Thu, 21 May 2026 09:46:59 +0200 Subject: [PATCH 12/15] updated version --- testbench2robotframework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbench2robotframework/__init__.py b/testbench2robotframework/__init__.py index da18653..2cfc7ad 100644 --- a/testbench2robotframework/__init__.py +++ b/testbench2robotframework/__init__.py @@ -17,4 +17,4 @@ from .testbench2robotframework import testbench2robotframework # noqa: F401 -__version__ = "0.9.2b4" +__version__ = "0.9.2b5" From 6e023cf00d43c06381d3a14a0bd5452d79132298 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Thu, 21 May 2026 17:01:16 +0200 Subject: [PATCH 13/15] revert modelutils --- testbench2robotframework/__init__.py | 2 +- testbench2robotframework/model.py | 5 +--- testbench2robotframework/model_utils.py | 33 +++++-------------------- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/testbench2robotframework/__init__.py b/testbench2robotframework/__init__.py index 2cfc7ad..3ba9c8c 100644 --- a/testbench2robotframework/__init__.py +++ b/testbench2robotframework/__init__.py @@ -17,4 +17,4 @@ from .testbench2robotframework import testbench2robotframework # noqa: F401 -__version__ = "0.9.2b5" +__version__ = "0.9.2b6" diff --git a/testbench2robotframework/model.py b/testbench2robotframework/model.py index e43c4c3..4939a13 100644 --- a/testbench2robotframework/model.py +++ b/testbench2robotframework/model.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: openapi.yml -# timestamp: 2026-05-20T15:22:23+00:00 +# timestamp: 2026-05-21T13:56:04+00:00 from __future__ import annotations @@ -1562,9 +1562,6 @@ class TestStructureTreeNode: class RootNode(TestStructureTreeNode): base: TestStructureItemBaseInformation filters: list[AttachedFilter] - spec: TestStructureItemSpecification | None = None - aut: TestStructureAutomation | None = None - exec: TestStructureItemExecution | None = None @dataclass diff --git a/testbench2robotframework/model_utils.py b/testbench2robotframework/model_utils.py index 0d9578e..c7da161 100644 --- a/testbench2robotframework/model_utils.py +++ b/testbench2robotframework/model_utils.py @@ -1,5 +1,4 @@ -import logging -from dataclasses import MISSING, fields, is_dataclass +from dataclasses import fields, is_dataclass from enum import Enum from types import UnionType as TypesUnion from typing import Any, TypeVar, get_args, get_origin, get_type_hints @@ -7,15 +6,13 @@ T = TypeVar("T") -logger = logging.getLogger(__name__) - ERROR_NOT_A_DATACLASS = "The provided class '{dataclass}' is not a dataclass." ERROR_UNKNOWN_TYPE_HINT_ORIGIN = "Unknown type hint origin." ERROR_UNION_MISMATCH = "Value does not match any of the union types." ERROR_NOT_A_LIST = "Value is not of type list." ERROR_LIST_ARGUMENTS_MISMATCH = "List type hint must have exactly one argument, got {args}." ERROR_UNION_WITHOUT_ARGUMENTS = "Union type hint must have at least one argument." -ERROR_MISSING_FIELDS = "Data dictionary for '{dataclass}' is missing mandatory fields: {fields}." +ERROR_TOO_MANY_DATA_FIELDS = "Data dictionary contains more fields than the dataclass has." ERROR_NONETYPE_DATA = "Data cannot be None." @@ -39,35 +36,17 @@ def get_origin_from_type_hint(type_hint): raise ValueError(ERROR_UNKNOWN_TYPE_HINT_ORIGIN) -def _has_default(cls_field) -> bool: - return cls_field.default is not MISSING or cls_field.default_factory is not MISSING - - def from_dict(cls: type[T], data: dict) -> T: if not is_dataclass(cls): raise ValueError(ERROR_NOT_A_DATACLASS.format(dataclass=cls.__name__)) if data is None: raise ValueError(ERROR_NONETYPE_DATA) + cls_dict = {} class_type_hints = get_type_hints(cls) class_fields = fields(cls) - init_field_names = {f.name for f in class_fields if f.init} - extra_keys = set(data.keys()) - init_field_names - if extra_keys: - logger.warning( - "%s: ignoring unknown fields: %s", cls.__name__, ", ".join(sorted(extra_keys)) - ) - mandatory_fields = {f.name for f in class_fields if f.init and not _has_default(f)} - missing_fields = mandatory_fields - set(data.keys()) - if missing_fields: - raise ValueError( - ERROR_MISSING_FIELDS.format( - dataclass=cls.__name__, fields=", ".join(sorted(missing_fields)) - ) - ) - cls_dict = {} + if len(class_fields) < len(data): + raise ValueError(ERROR_TOO_MANY_DATA_FIELDS) for cls_field in class_fields: - if not cls_field.init: - continue if cls_field.name not in data: continue field_value = data.get(cls_field.name) @@ -125,4 +104,4 @@ def convert_value(value: Any, type_hint: Any) -> Any: return convert_value_without_origin(value, type_hint) if origin is Origin.UNION: return convert_value_with_union_type(value, type_hint) - return convert_value_with_list_type(value, type_hint) + return convert_value_with_list_type(value, type_hint) \ No newline at end of file From a51068cf88e53f0a039b4960ba1a358052e4279b Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Fri, 29 May 2026 11:30:51 +0200 Subject: [PATCH 14/15] added version check --- testbench2robotframework/__init__.py | 2 +- testbench2robotframework/cli.py | 14 ++++-- testbench2robotframework/model_utils.py | 2 +- testbench2robotframework/result_writer.py | 1 + .../robotframework2testbench.py | 4 ++ .../testbench2robotframework.py | 8 ++- testbench2robotframework/utils.py | 49 +++++++++++++++++++ 7 files changed, 74 insertions(+), 6 deletions(-) diff --git a/testbench2robotframework/__init__.py b/testbench2robotframework/__init__.py index 3ba9c8c..e15c997 100644 --- a/testbench2robotframework/__init__.py +++ b/testbench2robotframework/__init__.py @@ -17,4 +17,4 @@ from .testbench2robotframework import testbench2robotframework # noqa: F401 -__version__ = "0.9.2b6" +__version__ = "0.9.2b7" diff --git a/testbench2robotframework/cli.py b/testbench2robotframework/cli.py index 7477155..3891917 100644 --- a/testbench2robotframework/cli.py +++ b/testbench2robotframework/cli.py @@ -21,10 +21,17 @@ ) from .json_reader import read_json from .testbench2robotframework import testbench2robotframework +from .utils import ALLOWED_SERVER_VERSIONS -TESTBENCH2ROBOTFRAMEWORK_DESCRIPTION = """TestBench2RobotFramework converts a TestBench JSON-report +TESTBENCH2ROBOTFRAMEWORK_DESCRIPTION = ( + """TestBench2RobotFramework converts a TestBench JSON-report to Robot Framework test suites and enhances the TestBench Report - with the execution results provided by Robot Framework.""" + with the execution results provided by Robot Framework. The version your TestBench report + was generated with must be compatible with the version of testbench2robotframework you are using. + Supported versions for this version of testbench2robotframework are: """ + + ", ".join(ALLOWED_SERVER_VERSIONS) + + "." +) GENERATE_HELP = """Command to convert a TestBench JSON-report to Robot Framework test suites.""" FETCH_HELP = """Command to fetch execution results from a Robot Framework result XML and to write the results to a TestBench JSON-report.""" @@ -56,7 +63,8 @@ def parse_subdivision_mapping( help="Writes the TestBench2RobotFramework, Robot Framework and Python version to console.", message=( f"TestBench2RobotFramework {__version__} with " - f"Robot Framework {robot.version.get_full_version()}" + f"Robot Framework {robot.version.get_full_version()}. " + f"Compatible with TestBench server versions: {', '.join(ALLOWED_SERVER_VERSIONS)}." ), ) @click.help_option("-h", "--help") diff --git a/testbench2robotframework/model_utils.py b/testbench2robotframework/model_utils.py index c7da161..5ced8f0 100644 --- a/testbench2robotframework/model_utils.py +++ b/testbench2robotframework/model_utils.py @@ -104,4 +104,4 @@ def convert_value(value: Any, type_hint: Any) -> Any: return convert_value_without_origin(value, type_hint) if origin is Origin.UNION: return convert_value_with_union_type(value, type_hint) - return convert_value_with_list_type(value, type_hint) \ No newline at end of file + return convert_value_with_list_type(value, type_hint) diff --git a/testbench2robotframework/result_writer.py b/testbench2robotframework/result_writer.py index a2084e2..b8d70ac 100644 --- a/testbench2robotframework/result_writer.py +++ b/testbench2robotframework/result_writer.py @@ -41,6 +41,7 @@ except ImportError: Group = None + def _empty_keyword_call_execution() -> KeywordCallExecution: return KeywordCallExecution( verdict=KeywordVerdict.Undefined, diff --git a/testbench2robotframework/robotframework2testbench.py b/testbench2robotframework/robotframework2testbench.py index 276233a..4fc2909 100644 --- a/testbench2robotframework/robotframework2testbench.py +++ b/testbench2robotframework/robotframework2testbench.py @@ -3,6 +3,8 @@ from robot.api import ExecutionResult +from testbench2robotframework.utils import perform_version_check + from .config import Configuration from .log import logger, setup_logger from .result_writer import ResultWriter @@ -18,6 +20,8 @@ def robot2testbench( sys.exit("Could not find json directory or zip file at the given path.") if not Path(robot_result_xml).exists(): sys.exit("Robot result xml does not exist at the given path.") + perform_version_check(Path(json_input_report)) + configuration = Configuration.from_dict(config) setup_logger(configuration) logger.debug("Configuration loaded.") diff --git a/testbench2robotframework/testbench2robotframework.py b/testbench2robotframework/testbench2robotframework.py index 6e41945..e10cc94 100644 --- a/testbench2robotframework/testbench2robotframework.py +++ b/testbench2robotframework/testbench2robotframework.py @@ -7,10 +7,16 @@ from .log import logger, setup_logger from .testbench2rf import create_test_suites from .testsuite_write import write_test_suites -from .utils import PathResolver, extract_to_working_directory, is_zip_file +from .utils import ( + PathResolver, + extract_to_working_directory, + is_zip_file, + perform_version_check, +) def testbench2robotframework(testbench_report: str, config: dict | Configuration): + perform_version_check(Path(testbench_report)) configuration = Configuration.from_dict(config) if isinstance(config, dict) else config setup_logger(configuration) logger.debug("Configuration loaded.") diff --git a/testbench2robotframework/utils.py b/testbench2robotframework/utils.py index 881b221..13396f4 100644 --- a/testbench2robotframework/utils.py +++ b/testbench2robotframework/utils.py @@ -1,3 +1,4 @@ +import json import re import shutil import sys @@ -17,6 +18,54 @@ from .log import logger +ALLOWED_SERVER_VERSIONS = ["4.0"] +ERROR_COULD_NOT_READ_VERSION = ( + "Could not read TestBench report version. The report must be generated with one of the supported versions: " + + ", ".join(ALLOWED_SERVER_VERSIONS) +) +ERROR_INCOMPATIBLE_VERSION = ( + "The version of testbench2robotframework is not compatible with the TestBench report version '{server_version}'. " + f"Supported versions are: {', '.join(ALLOWED_SERVER_VERSIONS)}. " +) + + +def perform_version_check(testbench_report: Path): + try: + manifest = read_manifest_json_from_testbench_report(testbench_report) + except Exception as e: + sys.exit(ERROR_COULD_NOT_READ_VERSION) + server_version = manifest.get("serverVersions", {}).get("version", None) + if not server_version: + sys.exit(ERROR_COULD_NOT_READ_VERSION) + + if server_version not in ALLOWED_SERVER_VERSIONS: + sys.exit(ERROR_INCOMPATIBLE_VERSION.format(server_version=server_version)) + + +def read_manifest_json_from_testbench_report(testbench_report: Path) -> dict: + if testbench_report.is_dir(): + manifest = testbench_report / "manifest.json" + if not manifest.exists(): + raise FileNotFoundError("manifest.json not found") + with open(manifest, "r", encoding="utf-8") as f: + return json.load(f) + + # ZIP archive + elif testbench_report.suffix == ".zip": + with ZipFile(testbench_report) as zf: + try: + with zf.open("manifest.json") as f: + return json.load(f) + except KeyError: + raise FileNotFoundError("manifest.json not found in zip") + + # Direct file + elif testbench_report.is_file() and testbench_report.name == "manifest.json": + with open(testbench_report, "r", encoding="utf-8") as f: + return json.load(f) + else: + raise FileNotFoundError(testbench_report) + def robot_tag_from_udf(udf: UserDefinedField) -> str | None: if (udf.udfType == UDFType.Enumeration and udf.value) or ( From e6f0eede2161b467ebd372a26c6352096f4e6c75 Mon Sep 17 00:00:00 2001 From: HenrikSchuette Date: Mon, 1 Jun 2026 08:28:39 +0200 Subject: [PATCH 15/15] increase version --- testbench2robotframework/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbench2robotframework/__init__.py b/testbench2robotframework/__init__.py index e15c997..128e63f 100644 --- a/testbench2robotframework/__init__.py +++ b/testbench2robotframework/__init__.py @@ -17,4 +17,4 @@ from .testbench2robotframework import testbench2robotframework # noqa: F401 -__version__ = "0.9.2b7" +__version__ = "1.0.0"