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
597 changes: 597 additions & 0 deletions README.md

Large diffs are not rendered by default.

431 changes: 431 additions & 0 deletions tests/test_cmd_cases.py

Large diffs are not rendered by default.

424 changes: 424 additions & 0 deletions tests/test_cmd_plans.py

Large diffs are not rendered by default.

397 changes: 397 additions & 0 deletions tests/test_cmd_sections.py

Large diffs are not rendered by default.

363 changes: 363 additions & 0 deletions tests/test_cmd_suites.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions trcli/api/api_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from trcli.api.run_handler import RunHandler
from trcli.api.bdd_handler import BddHandler
from trcli.api.case_handler import CaseHandler
from trcli.api.plan_handler import PlanHandler
from trcli.cli import Environment
from trcli.constants import (
ProjectErrors,
Expand Down Expand Up @@ -84,6 +85,7 @@ def __init__(
handle_futures_callback=self.handle_futures,
retrieve_results_callback=ApiRequestHandler.retrieve_results_after_cancelling,
)
self.plan_handler = PlanHandler(api_client, environment)

# BDD case cache for feature name matching (shared by CucumberParser and JunitParser)
# Structure: {"{project_id}_{suite_id}": {normalized_name: [case_dict, case_dict, ...]}}
Expand Down
57 changes: 57 additions & 0 deletions trcli/api/case_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,60 @@ def update_case_automation_id(self, case_id: int, automation_id: str) -> Tuple[b
update_response.error_message or f"Failed to update automation_id (HTTP {update_response.status_code})"
)
return False, error_msg

def get_case(self, case_id: int) -> Tuple[dict, str]:
"""
Retrieve a single test case by ID

:param case_id: TestRail case ID
:returns: Tuple with (case_data_dict, error_message)
"""
response = self.client.send_get(f"get_case/{case_id}")
if response.error_message:
return {}, response.error_message
return response.response_text, ""

def get_cases(
self,
project_id: int,
suite_id: int = None,
priority_id: str = None,
filter_text: str = None,
limit: int = 250,
offset: int = 0,
) -> Tuple[dict, str]:
"""
Retrieve test cases for a project with optional filters

:param project_id: TestRail project ID
:param suite_id: Optional suite ID filter
:param priority_id: Optional priority ID filter (comma-separated for multiple)
:param filter_text: Optional text search filter
:param limit: Maximum number of cases to return (default: 250)
:param offset: Offset for pagination (default: 0)
:returns: Tuple with (paginated_response_dict, error_message)
Response dict contains: cases, offset, limit, size, _links
"""
# Build query parameters
params = []
if suite_id is not None:
params.append(f"suite_id={suite_id}")
if priority_id is not None:
params.append(f"priority_id={priority_id}")
if filter_text is not None:
params.append(f"filter={filter_text}")
if limit != 250:
params.append(f"limit={limit}")
if offset > 0:
params.append(f"offset={offset}")

# Build URL
query_string = "&".join(params) if params else ""
url = f"get_cases/{project_id}"
if query_string:
url = f"{url}&{query_string}"

response = self.client.send_get(url)
if response.error_message:
return {}, response.error_message
return response.response_text, ""
75 changes: 75 additions & 0 deletions trcli/api/plan_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""
PlanHandler - Handles all plan-related operations for TestRail

It manages all plan operations including:
- Retrieving individual plans
- Listing plans with pagination
"""

from beartype.typing import Tuple

from trcli.api.api_client import APIClient
from trcli.cli import Environment


class PlanHandler:
"""Handles all plan-related operations for TestRail"""

def __init__(
self,
client: APIClient,
environment: Environment,
):
"""
Initialize the PlanHandler

:param client: APIClient instance for making API calls
:param environment: Environment configuration
"""
self.client = client
self.environment = environment

def get_plan(self, plan_id: int) -> Tuple[dict, str]:
"""
Retrieve a single test plan by ID

:param plan_id: TestRail plan ID
:returns: Tuple with (plan_data_dict, error_message)
"""
response = self.client.send_get(f"get_plan/{plan_id}")
if response.error_message:
return {}, response.error_message
return response.response_text, ""

def get_plans(
self,
project_id: int,
limit: int = 250,
offset: int = 0,
) -> Tuple[dict, str]:
"""
Retrieve test plans for a project with pagination

:param project_id: TestRail project ID
:param limit: Maximum number of plans to return (default: 250)
:param offset: Offset for pagination (default: 0)
:returns: Tuple with (paginated_response_dict, error_message)
Response dict contains: plans, offset, limit, size, _links
"""
# Build query parameters
params = []
if limit != 250:
params.append(f"limit={limit}")
if offset > 0:
params.append(f"offset={offset}")

# Build URL
query_string = "&".join(params) if params else ""
url = f"get_plans/{project_id}"
if query_string:
url = f"{url}&{query_string}"

response = self.client.send_get(url)
if response.error_message:
return {}, response.error_message
return response.response_text, ""
45 changes: 45 additions & 0 deletions trcli/api/section_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,48 @@ def delete_sections(self, added_sections: List[Dict]) -> Tuple[List, str]:
error_message = response.error_message
break
return responses, error_message

def get_section(self, section_id: int) -> Tuple[dict, str]:
"""
Retrieve a single section by ID

:param section_id: TestRail section ID
:returns: Tuple with (section_data_dict, error_message)
"""
response = self.client.send_get(f"get_section/{section_id}")
if response.error_message:
return {}, response.error_message
return response.response_text, ""

def get_sections(
self,
project_id: int,
suite_id: int,
limit: int = 250,
offset: int = 0,
) -> Tuple[dict, str]:
"""
Retrieve sections for a project and suite with pagination

:param project_id: TestRail project ID
:param suite_id: TestRail suite ID (required)
:param limit: Maximum number of sections to return (default: 250)
:param offset: Offset for pagination (default: 0)
:returns: Tuple with (paginated_response_dict, error_message)
Response dict contains: sections, offset, limit, size, _links
"""
# Build query parameters
params = [f"suite_id={suite_id}"] # suite_id is required
if limit != 250:
params.append(f"limit={limit}")
if offset > 0:
params.append(f"offset={offset}")

# Build URL
query_string = "&".join(params)
url = f"get_sections/{project_id}&{query_string}"

response = self.client.send_get(url)
if response.error_message:
return {}, response.error_message
return response.response_text, ""
45 changes: 45 additions & 0 deletions trcli/api/suite_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,48 @@ def delete_suite(self, suite_id: int) -> Tuple[dict, str]:
"""
response = self.client.send_post(f"delete_suite/{suite_id}", payload={})
return response.response_text, response.error_message

def get_suite(self, suite_id: int) -> Tuple[dict, str]:
"""
Retrieve a single test suite by ID

:param suite_id: TestRail suite ID
:returns: Tuple with (suite_data_dict, error_message)
"""
response = self.client.send_get(f"get_suite/{suite_id}")
if response.error_message:
return {}, response.error_message
return response.response_text, ""

def get_suites(
self,
project_id: int,
limit: int = 250,
offset: int = 0,
) -> Tuple[dict, str]:
"""
Retrieve test suites for a project with pagination

:param project_id: TestRail project ID
:param limit: Maximum number of suites to return (default: 250)
:param offset: Offset for pagination (default: 0)
:returns: Tuple with (paginated_response_dict, error_message)
Response dict contains: suites, offset, limit, size, _links
"""
# Build query parameters
params = []
if limit != 250:
params.append(f"limit={limit}")
if offset > 0:
params.append(f"offset={offset}")

# Build URL
query_string = "&".join(params) if params else ""
url = f"get_suites/{project_id}"
if query_string:
url = f"{url}&{query_string}"

response = self.client.send_get(url)
if response.error_message:
return {}, response.error_message
return response.response_text, ""
Loading
Loading