From e5323f76c43366dade125388faaef85764721d23 Mon Sep 17 00:00:00 2001 From: acuanico-tr-galt Date: Wed, 16 Jul 2025 16:20:38 +0800 Subject: [PATCH 1/5] Update for release 1.11.0 --- CHANGELOG.MD | 7 +++++++ README.md | 8 ++++---- trcli/__init__.py | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 1c1ef6c..851eb56 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -6,6 +6,13 @@ This project adheres to [Semantic Versioning](https://semver.org/). Version numb - **MINOR**: New features that are backward-compatible. - **PATCH**: Bug fixes or minor changes that do not affect backward compatibility. +## [1.11.0] + +_released 07-18-2025 + +### Added + - Added feature to optionally close test run using --auto-close-run in add_run command + ## [1.10.1] _released 07-11-2025 diff --git a/README.md b/README.md index afb2e37..67b3fd3 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ trcli ``` You should get something like this: ``` -TestRail CLI v1.10.1 +TestRail CLI v1.11.0 Copyright 2025 Gurock Software GmbH - www.gurock.com Supported and loaded modules: - parse_junit: JUnit XML Files (& Similar) @@ -45,7 +45,7 @@ CLI general reference -------- ```shell $ trcli --help -TestRail CLI v1.10.1 +TestRail CLI v1.11.0 Copyright 2025 Gurock Software GmbH - www.gurock.com Usage: trcli [OPTIONS] COMMAND [ARGS]... @@ -287,7 +287,7 @@ will be used to upload all results into the same test run. ### Reference ```shell $ trcli add_run --help -TestRail CLI v1.10.1 +TestRail CLI v1.11.0 Copyright 2025 Gurock Software GmbH - www.gurock.com Usage: trcli add_run [OPTIONS] @@ -326,7 +326,7 @@ providing you with a solid base of test cases, which you can further expand on T ### Reference ```shell $ trcli parse_openapi --help -TestRail CLI v1.10.1 +TestRail CLI v1.11.0 Copyright 2025 Gurock Software GmbH - www.gurock.com Usage: trcli parse_openapi [OPTIONS] diff --git a/trcli/__init__.py b/trcli/__init__.py index a0865bb..f84c53b 100644 --- a/trcli/__init__.py +++ b/trcli/__init__.py @@ -1 +1 @@ -__version__ = "1.10.1" +__version__ = "1.11.0" From 3ea0605accfc1491daaa3edf9f8c0553c0a9b111 Mon Sep 17 00:00:00 2001 From: acuanico-tr-galt Date: Mon, 28 Jul 2025 21:35:35 +0800 Subject: [PATCH 2/5] TRCLI-33 [GIT #282] Add commands to close a test run created with add_run --- README.md | 1 + tests/test_project_based_client.py | 2 ++ tests/test_results_uploader.py | 6 ++---- trcli/api/project_based_client.py | 7 +++++++ trcli/cli.py | 1 + trcli/commands/cmd_add_run.py | 6 ++++++ 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index afb2e37..6633a2e 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,7 @@ Options: --run-assigned-to-id The ID of the user the test run should be assigned to. [x>=1] --run-include-all Use this option to include all test cases in this test run. + --auto-close-run Use this option to automatically close the created run. --run-case-ids Comma separated list of test case IDs to include in the test run (i.e.: 1,2,3,4). --run-refs A comma-separated list of references/requirements diff --git a/tests/test_project_based_client.py b/tests/test_project_based_client.py index 6d45c1f..a0efc00 100644 --- a/tests/test_project_based_client.py +++ b/tests/test_project_based_client.py @@ -390,6 +390,7 @@ def test_create_or_update_test_run_calls_add_run(self, project_based_client_data project_based_client, ) = project_based_client_data_provider environment.run_id = None + environment.auto_close_run = False api_request_handler.add_run.return_value = (1, "") project_based_client.resolve_project() run_id, error_message = project_based_client.create_or_update_test_run() @@ -411,6 +412,7 @@ def test_create_or_update_test_run_calls_update_run(self, project_based_client_d project_based_client, ) = project_based_client_data_provider environment.run_id = 1 + environment.auto_close_run = False api_request_handler.update_run.return_value = (1, "") project_based_client.resolve_project() run_id, error_message = project_based_client.create_or_update_test_run() diff --git a/tests/test_results_uploader.py b/tests/test_results_uploader.py index ae1e6f0..235bea5 100644 --- a/tests/test_results_uploader.py +++ b/tests/test_results_uploader.py @@ -208,16 +208,14 @@ def test_upload_results_successful( 2: mocker.call("Removing unnecessary empty sections that may have been created earlier. ", new_line=False), 3: mocker.call("Removed 1 unused/empty section(s)."), 4: mocker.call("Creating test run. ", new_line=False), - 5: mocker.call("Test run: https://fake_host.com/index.php?/runs/view/100"), - 6: mocker.call("Closing test run. ", new_line=False), + 5: mocker.call("Closing run. ", new_line=False), } else: calls = { 2: mocker.call("Removing unnecessary empty sections that may have been created earlier. ", new_line=False), 3: mocker.call("Removed 1 unused/empty section(s)."), 4: mocker.call("Updating test run. ", new_line=False), - 5: mocker.call("Test run: https://fake_host.com/index.php?/runs/view/101"), - 6: mocker.call("Closing test run. ", new_line=False), + 5: mocker.call("Closing run. ", new_line=False), } results_uploader.upload_results() diff --git a/trcli/api/project_based_client.py b/trcli/api/project_based_client.py index ba230b2..9a19cf1 100644 --- a/trcli/api/project_based_client.py +++ b/trcli/api/project_based_client.py @@ -228,6 +228,13 @@ def create_or_update_test_run(self) -> Tuple[int, str]: run, error_message = self.api_request_handler.update_run( run_id, self.run_name, self.environment.milestone_id ) + if self.environment.auto_close_run: + self.environment.log("Closing run. ", new_line=False) + close_run, error_message = self.api_request_handler.close_run(run_id) + if close_run: + self.environment.log("Run closed successfully.") + else: + self.environment.elog(f"Failed to close run: {error_message}") if error_message: self.environment.elog("\n" + error_message) else: diff --git a/trcli/cli.py b/trcli/cli.py index a38b9f9..af52c5e 100755 --- a/trcli/cli.py +++ b/trcli/cli.py @@ -65,6 +65,7 @@ def __init__(self, cmd="parse_junit"): self.run_assigned_to_id = None self.run_case_ids = None self.run_include_all = None + self.auto_close_run = None self.run_refs = None self.proxy = None # Add proxy related attributes self.noproxy = None diff --git a/trcli/commands/cmd_add_run.py b/trcli/commands/cmd_add_run.py index c618d37..a47f649 100644 --- a/trcli/commands/cmd_add_run.py +++ b/trcli/commands/cmd_add_run.py @@ -66,6 +66,12 @@ def write_run_to_file(environment: Environment, run_id: int): default=False, help="Use this option to include all test cases in this test run." ) +@click.option( + "--auto-close-run", + is_flag=True, + default=False, + help="Use this option to automatically close the created run." +) @click.option( "--run-case-ids", metavar="", From 050dcfde9072e89b489f57242f07f2151b3fd34c Mon Sep 17 00:00:00 2001 From: acuanico-tr-galt Date: Mon, 28 Jul 2025 21:58:59 +0800 Subject: [PATCH 3/5] Updated changelog for 1.11.0 release --- CHANGELOG.MD | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 851eb56..8596f01 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -8,10 +8,11 @@ This project adheres to [Semantic Versioning](https://semver.org/). Version numb ## [1.11.0] -_released 07-18-2025 +_released 07-30-2025 ### Added - Added feature to optionally close test run using --auto-close-run in add_run command + - Added support for Start and End Date API changes for Test Runs and Test Plans ## [1.10.1] From f909f5411925ef2caeadba703f9ea827ea2f3d25 Mon Sep 17 00:00:00 2001 From: acuanico-tr-galt Date: Tue, 29 Jul 2025 18:18:35 +0800 Subject: [PATCH 4/5] TRCLI-77 : Added functional test for add run and plan with due date --- README.md | 2 ++ tests_e2e/test_end2end.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/README.md b/README.md index 67b3fd3..55e5572 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,8 @@ Options: --run-description Summary text to be added to the test run. --milestone-id Milestone ID to which the Test Run should be associated to. [x>=1] + --run-start-date The expected or scheduled start date of this test run in MM/DD/YYYY format + --run-end-date The expected or scheduled end date of this test run in MM/DD/YYYY format --run-assigned-to-id The ID of the user the test run should be assigned to. [x>=1] --run-include-all Use this option to include all test cases in this test run. diff --git a/tests_e2e/test_end2end.py b/tests_e2e/test_end2end.py index bc1b989..8d18e4f 100644 --- a/tests_e2e/test_end2end.py +++ b/tests_e2e/test_end2end.py @@ -444,6 +444,25 @@ def test_cli_add_run_upload_results(self): "Submitted 6 test results" ] ) + + def test_cli_add_run_and_plan_with_due_date(self): + output = _run_cmd(f""" +trcli -y \\ + -h {self.TR_INSTANCE} \\ + --project "SA - (DO NOT DELETE) TRCLI-E2E-Tests" \\ + add_run --run-include-all \\ + --title "[CLI-E2E-Tests] ADD RUN TEST: Test Run with Due Date" \\ + --run-start-date "03/01/2030" --run-end-date "03/12/2030" + """) + _assert_contains( + output, + [ + "Creating test run.", + f"Test run: {self.TR_INSTANCE}index.php?/runs/view", + "title: [CLI-E2E-Tests] ADD RUN TEST: Test Run with Due Date" + ] + ) + def bug_test_cli_robot_description_bug(self): output = _run_cmd(f""" From 88290b8f032b5cbb6668294e92a8b3346b59e093 Mon Sep 17 00:00:00 2001 From: acuanico-tr-galt Date: Tue, 29 Jul 2025 18:20:22 +0800 Subject: [PATCH 5/5] TRCLI-77 Support Start and End Dates changes in the API for Test Runs and Test Plans --- trcli/api/api_request_handler.py | 10 ++++++++-- trcli/api/project_based_client.py | 2 ++ trcli/cli.py | 2 ++ trcli/commands/cmd_add_run.py | 16 ++++++++++++++++ trcli/data_providers/api_data_provider.py | 15 +++++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/trcli/api/api_request_handler.py b/trcli/api/api_request_handler.py index 7e54d94..b5623df 100644 --- a/trcli/api/api_request_handler.py +++ b/trcli/api/api_request_handler.py @@ -404,6 +404,8 @@ def add_run( project_id: int, run_name: str, milestone_id: int = None, + start_date: str = None, + end_date: str = None, plan_id: int = None, config_ids: List[int] = None, assigned_to_id: int = None, @@ -420,6 +422,8 @@ def add_run( add_run_data = self.data_provider.add_run( run_name, case_ids=case_ids, + start_date=start_date, + end_date=end_date, milestone_id=milestone_id, assigned_to_id=assigned_to_id, include_all=include_all, @@ -443,7 +447,8 @@ def add_run( run_id = response.response_text["runs"][0]["id"] return run_id, response.error_message - def update_run(self, run_id: int, run_name: str, milestone_id: int = None) -> Tuple[dict, str]: + def update_run(self, run_id: int, run_name: str, start_date: str = None, + end_date: str = None, milestone_id: int = None) -> Tuple[dict, str]: """ Updates an existing run :run_id: run id @@ -453,7 +458,8 @@ def update_run(self, run_id: int, run_name: str, milestone_id: int = None) -> Tu run_response = self.client.send_get(f"get_run/{run_id}") existing_description = run_response.response_text.get("description", "") - add_run_data = self.data_provider.add_run(run_name, milestone_id=milestone_id) + add_run_data = self.data_provider.add_run(run_name, start_date=start_date, + end_date=end_date, milestone_id=milestone_id) add_run_data["description"] = existing_description # Retain the current description run_tests, error_message = self.__get_all_tests_in_run(run_id) diff --git a/trcli/api/project_based_client.py b/trcli/api/project_based_client.py index ba230b2..9b1147e 100644 --- a/trcli/api/project_based_client.py +++ b/trcli/api/project_based_client.py @@ -214,6 +214,8 @@ def create_or_update_test_run(self) -> Tuple[int, str]: project_id=self.project.project_id, run_name=self.run_name, milestone_id=self.environment.milestone_id, + start_date=self.environment.run_start_date, + end_date=self.environment.run_end_date, plan_id=self.environment.plan_id, config_ids=self.environment.config_ids, assigned_to_id=self.environment.run_assigned_to_id, diff --git a/trcli/cli.py b/trcli/cli.py index a38b9f9..82b55ad 100755 --- a/trcli/cli.py +++ b/trcli/cli.py @@ -51,6 +51,8 @@ def __init__(self, cmd="parse_junit"): self.plan_id = None self.config_ids = None self.milestone_id = None + self.run_start_date = None + self.run_end_date = None self.section_id = None self.auto_creation_response = None self.silent = None diff --git a/trcli/commands/cmd_add_run.py b/trcli/commands/cmd_add_run.py index c618d37..f3705c4 100644 --- a/trcli/commands/cmd_add_run.py +++ b/trcli/commands/cmd_add_run.py @@ -14,6 +14,8 @@ def print_config(env: Environment): f"\n> Suite ID: {env.suite_id}" f"\n> Description: {env.run_description}" f"\n> Milestone ID: {env.milestone_id}" + f"\n> Start Date: {env.run_start_date}" + f"\n> End Date: {env.run_end_date}" f"\n> Assigned To ID: {env.run_assigned_to_id}" f"\n> Include All: {env.run_include_all}" f"\n> Case IDs: {env.run_case_ids}" @@ -54,6 +56,20 @@ def write_run_to_file(environment: Environment, run_id: int): metavar="", help="Milestone ID to which the Test Run should be associated to.", ) +@click.option( + "--run-start-date", + metavar="", + default=None, + type=lambda x: [int(i) for i in x.split("/") if len(x.split("/")) == 3], + help="The expected or scheduled start date of this test run in MM/DD/YYYY format" +) +@click.option( + "--run-end-date", + metavar="", + default=None, + type=lambda x: [int(i) for i in x.split("/") if len(x.split("/")) == 3], + help="The expected or scheduled end date of this test run in MM/DD/YYYY format" +) @click.option( "--run-assigned-to-id", type=click.IntRange(min=1), diff --git a/trcli/data_providers/api_data_provider.py b/trcli/data_providers/api_data_provider.py index 0501510..3b98c14 100644 --- a/trcli/data_providers/api_data_provider.py +++ b/trcli/data_providers/api_data_provider.py @@ -1,6 +1,7 @@ from beartype.typing import List, Dict, Optional from serde.json import to_dict +from datetime import datetime, timezone from trcli.constants import OLD_SYSTEM_NAME_AUTOMATION_ID, UPDATED_SYSTEM_NAME_AUTOMATION_ID from trcli.data_classes.dataclass_testrail import TestRailSuite @@ -66,6 +67,8 @@ def add_run( self, run_name: Optional[str], case_ids=None, + start_date=None, + end_date=None, milestone_id=None, assigned_to_id=None, include_all=None, @@ -93,6 +96,18 @@ def add_run( "milestone_id": milestone_id, "case_ids": case_ids } + if isinstance(start_date, list) and start_date is not None: + try: + dt = datetime(start_date[2], start_date[0], start_date[1], tzinfo=timezone.utc) + body["start_on"] = int(dt.timestamp()) + except ValueError: + body["start_on"] = None + if isinstance(end_date, list) and end_date is not None: + try: + dt = datetime(end_date[2], end_date[0], end_date[1], tzinfo=timezone.utc) + body["due_on"] = int(dt.timestamp()) + except ValueError: + body["due_on"] = None if include_all is not None: body["include_all"] = include_all if assigned_to_id is not None: