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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ 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-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]

_released 07-11-2025
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]...

Expand Down Expand Up @@ -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]

Expand All @@ -298,9 +298,12 @@ 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.
--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
Expand All @@ -326,7 +329,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]

Expand Down
2 changes: 2 additions & 0 deletions tests/test_project_based_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down
6 changes: 2 additions & 4 deletions tests/test_results_uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
19 changes: 19 additions & 0 deletions tests_e2e/test_end2end.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down
2 changes: 1 addition & 1 deletion trcli/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.10.1"
__version__ = "1.11.0"
10 changes: 8 additions & 2 deletions trcli/api/api_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand 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
Expand All @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions trcli/api/project_based_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -228,6 +230,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:
Expand Down
3 changes: 3 additions & 0 deletions trcli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -65,6 +67,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
Expand Down
22 changes: 22 additions & 0 deletions trcli/commands/cmd_add_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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),
Expand All @@ -66,6 +82,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="",
Expand Down
15 changes: 15 additions & 0 deletions trcli/data_providers/api_data_provider.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down