From 1ab16a1d3a89507a385be416cf9bb194f31702a9 Mon Sep 17 00:00:00 2001 From: Dmitrii Gridnev Date: Wed, 25 Mar 2026 16:09:47 +0300 Subject: [PATCH] chore: update version to 2.0.5 and enhance documentation for test case steps - Bump version from 2.0.4 to 2.0.5 in pyproject.toml. - Add `steps_type` field to test case models and documentation, allowing specification of step formats as 'classic' or 'gherkin'. - Implement validation for `steps_type` in relevant models to ensure correct usage. - Update descriptions in `TestStepCreate` to clarify usage for both step formats. --- qase-api-client/docs/TestCaseCreate.md | 1 + qase-api-client/docs/TestCaseUpdate.md | 1 + qase-api-client/docs/TestCasebulkCasesInner.md | 1 + qase-api-client/docs/TestStepCreate.md | 3 ++- qase-api-client/pyproject.toml | 2 +- .../qase/api_client_v1/models/qql_test_case.py | 12 +++++++++++- .../src/qase/api_client_v1/models/test_case.py | 12 +++++++++++- .../api_client_v1/models/test_case_create.py | 16 ++++++++++++++-- .../qase/api_client_v1/models/test_case_query.py | 12 +++++++++++- .../api_client_v1/models/test_case_update.py | 16 ++++++++++++++-- .../models/test_casebulk_cases_inner.py | 16 ++++++++++++++-- .../api_client_v1/models/test_step_create.py | 6 ++++-- 12 files changed, 85 insertions(+), 13 deletions(-) diff --git a/qase-api-client/docs/TestCaseCreate.md b/qase-api-client/docs/TestCaseCreate.md index 5be29cb8..1374de0c 100644 --- a/qase-api-client/docs/TestCaseCreate.md +++ b/qase-api-client/docs/TestCaseCreate.md @@ -19,6 +19,7 @@ Name | Type | Description | Notes **milestone_id** | **int** | | [optional] **automation** | **int** | | [optional] **status** | **int** | | [optional] +**steps_type** | **str** | Determines the format of the steps field. When \"classic\", steps use the standard action/expected_result/data format. When \"gherkin\", steps use the {value: \"Given...\\nWhen...\\nThen...\"} format. | [optional] [default to 'classic'] **attachments** | **List[str]** | A list of Attachment hashes. | [optional] **steps** | [**List[TestStepCreate]**](TestStepCreate.md) | | [optional] **tags** | **List[str]** | | [optional] diff --git a/qase-api-client/docs/TestCaseUpdate.md b/qase-api-client/docs/TestCaseUpdate.md index a21d3849..c2501e29 100644 --- a/qase-api-client/docs/TestCaseUpdate.md +++ b/qase-api-client/docs/TestCaseUpdate.md @@ -19,6 +19,7 @@ Name | Type | Description | Notes **milestone_id** | **int** | | [optional] **automation** | **int** | | [optional] **status** | **int** | | [optional] +**steps_type** | **str** | Determines the format of the steps field. When \"classic\", steps use the standard action/expected_result/data format. When \"gherkin\", steps use the {value: \"Given...\\nWhen...\\nThen...\"} format. | [optional] [default to 'classic'] **attachments** | **List[str]** | A list of Attachment hashes. | [optional] **steps** | [**List[TestStepCreate]**](TestStepCreate.md) | | [optional] **tags** | **List[str]** | | [optional] diff --git a/qase-api-client/docs/TestCasebulkCasesInner.md b/qase-api-client/docs/TestCasebulkCasesInner.md index 3e3a70a5..787f11e0 100644 --- a/qase-api-client/docs/TestCasebulkCasesInner.md +++ b/qase-api-client/docs/TestCasebulkCasesInner.md @@ -19,6 +19,7 @@ Name | Type | Description | Notes **milestone_id** | **int** | | [optional] **automation** | **int** | | [optional] **status** | **int** | | [optional] +**steps_type** | **str** | Determines the format of the steps field. When \"classic\", steps use the standard action/expected_result/data format. When \"gherkin\", steps use the {value: \"Given...\\nWhen...\\nThen...\"} format. | [optional] [default to 'classic'] **attachments** | **List[str]** | A list of Attachment hashes. | [optional] **steps** | [**List[TestStepCreate]**](TestStepCreate.md) | | [optional] **tags** | **List[str]** | | [optional] diff --git a/qase-api-client/docs/TestStepCreate.md b/qase-api-client/docs/TestStepCreate.md index 331321c8..55d05c57 100644 --- a/qase-api-client/docs/TestStepCreate.md +++ b/qase-api-client/docs/TestStepCreate.md @@ -5,9 +5,10 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**action** | **str** | | [optional] +**action** | **str** | Step action text. Used for classic steps. For gherkin steps, use the \"value\" property instead. | [optional] **expected_result** | **str** | | [optional] **data** | **str** | | [optional] +**value** | **str** | Gherkin scenario text. Used when steps_type is \"gherkin\". Example: \"Given a user exists\\nWhen they log in\\nThen they see the dashboard\" | [optional] **position** | **int** | | [optional] **attachments** | **List[str]** | A list of Attachment hashes. | [optional] **steps** | **List[object]** | Nested steps may be passed here. Use same structure for them. | [optional] diff --git a/qase-api-client/pyproject.toml b/qase-api-client/pyproject.toml index bf68df6e..21d68208 100644 --- a/qase-api-client/pyproject.toml +++ b/qase-api-client/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "qase-api-client" -version = "2.0.4" +version = "2.0.5" description = "Qase TestOps API V1 client for Python" readme = "README.md" authors = [{name = "Qase Team", email = "support@qase.io"}] diff --git a/qase-api-client/src/qase/api_client_v1/models/qql_test_case.py b/qase-api-client/src/qase/api_client_v1/models/qql_test_case.py index f67967ad..d6d33eda 100644 --- a/qase-api-client/src/qase/api_client_v1/models/qql_test_case.py +++ b/qase-api-client/src/qase/api_client_v1/models/qql_test_case.py @@ -19,7 +19,7 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional from qase.api_client_v1.models.attachment import Attachment from qase.api_client_v1.models.custom_field_value import CustomFieldValue @@ -63,6 +63,16 @@ class QqlTestCase(BaseModel): updated_by: Optional[StrictInt] = Field(default=None, description="Author ID of the last update.") __properties: ClassVar[List[str]] = ["id", "test_case_id", "position", "title", "description", "preconditions", "postconditions", "severity", "priority", "type", "layer", "is_flaky", "behavior", "automation", "status", "milestone_id", "suite_id", "custom_fields", "attachments", "steps_type", "steps", "params", "tags", "member_id", "author_id", "created_at", "updated_at", "updated_by"] + @field_validator('steps_type') + def steps_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classic', 'gherkin']): + raise ValueError("must be one of enum values ('classic', 'gherkin')") + return value + model_config = ConfigDict( populate_by_name=True, validate_assignment=True, diff --git a/qase-api-client/src/qase/api_client_v1/models/test_case.py b/qase-api-client/src/qase/api_client_v1/models/test_case.py index 13ee1cc6..7060925d 100644 --- a/qase-api-client/src/qase/api_client_v1/models/test_case.py +++ b/qase-api-client/src/qase/api_client_v1/models/test_case.py @@ -19,7 +19,7 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional from qase.api_client_v1.models.attachment import Attachment from qase.api_client_v1.models.custom_field_value import CustomFieldValue @@ -68,6 +68,16 @@ class TestCase(BaseModel): external_issues: Optional[List[ExternalIssue]] = None __properties: ClassVar[List[str]] = ["id", "position", "title", "description", "preconditions", "postconditions", "severity", "priority", "type", "layer", "is_flaky", "behavior", "automation", "status", "milestone_id", "suite_id", "custom_fields", "attachments", "steps_type", "steps", "params", "parameters", "tags", "member_id", "author_id", "created_at", "updated_at", "deleted", "created", "updated", "external_issues"] + @field_validator('steps_type') + def steps_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classic', 'gherkin']): + raise ValueError("must be one of enum values ('classic', 'gherkin')") + return value + model_config = ConfigDict( populate_by_name=True, validate_assignment=True, diff --git a/qase-api-client/src/qase/api_client_v1/models/test_case_create.py b/qase-api-client/src/qase/api_client_v1/models/test_case_create.py index 245ec63a..566c35d8 100644 --- a/qase-api-client/src/qase/api_client_v1/models/test_case_create.py +++ b/qase-api-client/src/qase/api_client_v1/models/test_case_create.py @@ -18,7 +18,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional from typing_extensions import Annotated from qase.api_client_v1.models.test_case_parameter_create import TestCaseParameterCreate @@ -44,6 +44,7 @@ class TestCaseCreate(BaseModel): milestone_id: Optional[StrictInt] = None automation: Optional[StrictInt] = None status: Optional[StrictInt] = None + steps_type: Optional[StrictStr] = Field(default='classic', description="Determines the format of the steps field. When \"classic\", steps use the standard action/expected_result/data format. When \"gherkin\", steps use the {value: \"Given...\\nWhen...\\nThen...\"} format.") attachments: Optional[List[StrictStr]] = Field(default=None, description="A list of Attachment hashes.") steps: Optional[List[TestStepCreate]] = None tags: Optional[List[StrictStr]] = None @@ -52,7 +53,17 @@ class TestCaseCreate(BaseModel): custom_field: Optional[Dict[str, StrictStr]] = Field(default=None, description="A map of custom fields values (id => value)") created_at: Optional[StrictStr] = None updated_at: Optional[StrictStr] = None - __properties: ClassVar[List[str]] = ["description", "preconditions", "postconditions", "title", "severity", "priority", "behavior", "type", "layer", "is_flaky", "suite_id", "milestone_id", "automation", "status", "attachments", "steps", "tags", "params", "parameters", "custom_field", "created_at", "updated_at"] + __properties: ClassVar[List[str]] = ["description", "preconditions", "postconditions", "title", "severity", "priority", "behavior", "type", "layer", "is_flaky", "suite_id", "milestone_id", "automation", "status", "steps_type", "attachments", "steps", "tags", "params", "parameters", "custom_field", "created_at", "updated_at"] + + @field_validator('steps_type') + def steps_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classic', 'gherkin']): + raise ValueError("must be one of enum values ('classic', 'gherkin')") + return value model_config = ConfigDict( populate_by_name=True, @@ -143,6 +154,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "milestone_id": obj.get("milestone_id"), "automation": obj.get("automation"), "status": obj.get("status"), + "steps_type": obj.get("steps_type") if obj.get("steps_type") is not None else 'classic', "attachments": obj.get("attachments"), "steps": [TestStepCreate.from_dict(_item) for _item in obj["steps"]] if obj.get("steps") is not None else None, "tags": obj.get("tags"), diff --git a/qase-api-client/src/qase/api_client_v1/models/test_case_query.py b/qase-api-client/src/qase/api_client_v1/models/test_case_query.py index 750ec719..bcbaaae8 100644 --- a/qase-api-client/src/qase/api_client_v1/models/test_case_query.py +++ b/qase-api-client/src/qase/api_client_v1/models/test_case_query.py @@ -19,7 +19,7 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional from qase.api_client_v1.models.attachment import Attachment from qase.api_client_v1.models.custom_field_value import CustomFieldValue @@ -63,6 +63,16 @@ class TestCaseQuery(BaseModel): updated_by: Optional[StrictInt] = Field(default=None, description="Author ID of the last update.") __properties: ClassVar[List[str]] = ["id", "test_case_id", "position", "title", "description", "preconditions", "postconditions", "severity", "priority", "type", "layer", "is_flaky", "behavior", "automation", "status", "milestone_id", "suite_id", "custom_fields", "attachments", "steps_type", "steps", "params", "tags", "member_id", "author_id", "created_at", "updated_at", "updated_by"] + @field_validator('steps_type') + def steps_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classic', 'gherkin']): + raise ValueError("must be one of enum values ('classic', 'gherkin')") + return value + model_config = ConfigDict( populate_by_name=True, validate_assignment=True, diff --git a/qase-api-client/src/qase/api_client_v1/models/test_case_update.py b/qase-api-client/src/qase/api_client_v1/models/test_case_update.py index f3e96531..634eb2dd 100644 --- a/qase-api-client/src/qase/api_client_v1/models/test_case_update.py +++ b/qase-api-client/src/qase/api_client_v1/models/test_case_update.py @@ -18,7 +18,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional from typing_extensions import Annotated from qase.api_client_v1.models.test_case_parameter_create import TestCaseParameterCreate @@ -44,13 +44,24 @@ class TestCaseUpdate(BaseModel): milestone_id: Optional[StrictInt] = None automation: Optional[StrictInt] = None status: Optional[StrictInt] = None + steps_type: Optional[StrictStr] = Field(default='classic', description="Determines the format of the steps field. When \"classic\", steps use the standard action/expected_result/data format. When \"gherkin\", steps use the {value: \"Given...\\nWhen...\\nThen...\"} format.") attachments: Optional[List[StrictStr]] = Field(default=None, description="A list of Attachment hashes.") steps: Optional[List[TestStepCreate]] = None tags: Optional[List[StrictStr]] = None params: Optional[Dict[str, List[StrictStr]]] = Field(default=None, description="Deprecated, use `parameters` instead.") parameters: Optional[List[TestCaseParameterCreate]] = None custom_field: Optional[Dict[str, StrictStr]] = Field(default=None, description="A map of custom fields values (id => value)") - __properties: ClassVar[List[str]] = ["description", "preconditions", "postconditions", "title", "severity", "priority", "behavior", "type", "layer", "is_flaky", "suite_id", "milestone_id", "automation", "status", "attachments", "steps", "tags", "params", "parameters", "custom_field"] + __properties: ClassVar[List[str]] = ["description", "preconditions", "postconditions", "title", "severity", "priority", "behavior", "type", "layer", "is_flaky", "suite_id", "milestone_id", "automation", "status", "steps_type", "attachments", "steps", "tags", "params", "parameters", "custom_field"] + + @field_validator('steps_type') + def steps_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classic', 'gherkin']): + raise ValueError("must be one of enum values ('classic', 'gherkin')") + return value model_config = ConfigDict( populate_by_name=True, @@ -141,6 +152,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "milestone_id": obj.get("milestone_id"), "automation": obj.get("automation"), "status": obj.get("status"), + "steps_type": obj.get("steps_type") if obj.get("steps_type") is not None else 'classic', "attachments": obj.get("attachments"), "steps": [TestStepCreate.from_dict(_item) for _item in obj["steps"]] if obj.get("steps") is not None else None, "tags": obj.get("tags"), diff --git a/qase-api-client/src/qase/api_client_v1/models/test_casebulk_cases_inner.py b/qase-api-client/src/qase/api_client_v1/models/test_casebulk_cases_inner.py index d1151bbd..27081db6 100644 --- a/qase-api-client/src/qase/api_client_v1/models/test_casebulk_cases_inner.py +++ b/qase-api-client/src/qase/api_client_v1/models/test_casebulk_cases_inner.py @@ -18,7 +18,7 @@ import re # noqa: F401 import json -from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr, field_validator from typing import Any, ClassVar, Dict, List, Optional from typing_extensions import Annotated from qase.api_client_v1.models.test_case_parameter_create import TestCaseParameterCreate @@ -44,6 +44,7 @@ class TestCasebulkCasesInner(BaseModel): milestone_id: Optional[StrictInt] = None automation: Optional[StrictInt] = None status: Optional[StrictInt] = None + steps_type: Optional[StrictStr] = Field(default='classic', description="Determines the format of the steps field. When \"classic\", steps use the standard action/expected_result/data format. When \"gherkin\", steps use the {value: \"Given...\\nWhen...\\nThen...\"} format.") attachments: Optional[List[StrictStr]] = Field(default=None, description="A list of Attachment hashes.") steps: Optional[List[TestStepCreate]] = None tags: Optional[List[StrictStr]] = None @@ -53,7 +54,17 @@ class TestCasebulkCasesInner(BaseModel): created_at: Optional[StrictStr] = None updated_at: Optional[StrictStr] = None id: Optional[StrictInt] = None - __properties: ClassVar[List[str]] = ["description", "preconditions", "postconditions", "title", "severity", "priority", "behavior", "type", "layer", "is_flaky", "suite_id", "milestone_id", "automation", "status", "attachments", "steps", "tags", "params", "parameters", "custom_field", "created_at", "updated_at", "id"] + __properties: ClassVar[List[str]] = ["description", "preconditions", "postconditions", "title", "severity", "priority", "behavior", "type", "layer", "is_flaky", "suite_id", "milestone_id", "automation", "status", "steps_type", "attachments", "steps", "tags", "params", "parameters", "custom_field", "created_at", "updated_at", "id"] + + @field_validator('steps_type') + def steps_type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['classic', 'gherkin']): + raise ValueError("must be one of enum values ('classic', 'gherkin')") + return value model_config = ConfigDict( populate_by_name=True, @@ -149,6 +160,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "milestone_id": obj.get("milestone_id"), "automation": obj.get("automation"), "status": obj.get("status"), + "steps_type": obj.get("steps_type") if obj.get("steps_type") is not None else 'classic', "attachments": obj.get("attachments"), "steps": [TestStepCreate.from_dict(_item) for _item in obj["steps"]] if obj.get("steps") is not None else None, "tags": obj.get("tags"), diff --git a/qase-api-client/src/qase/api_client_v1/models/test_step_create.py b/qase-api-client/src/qase/api_client_v1/models/test_step_create.py index 241d3062..3a752a25 100644 --- a/qase-api-client/src/qase/api_client_v1/models/test_step_create.py +++ b/qase-api-client/src/qase/api_client_v1/models/test_step_create.py @@ -27,13 +27,14 @@ class TestStepCreate(BaseModel): """ TestStepCreate """ # noqa: E501 - action: Optional[StrictStr] = None + action: Optional[StrictStr] = Field(default=None, description="Step action text. Used for classic steps. For gherkin steps, use the \"value\" property instead.") expected_result: Optional[StrictStr] = None data: Optional[StrictStr] = None + value: Optional[StrictStr] = Field(default=None, description="Gherkin scenario text. Used when steps_type is \"gherkin\". Example: \"Given a user exists\\nWhen they log in\\nThen they see the dashboard\"") position: Optional[StrictInt] = None attachments: Optional[List[StrictStr]] = Field(default=None, description="A list of Attachment hashes.") steps: Optional[List[Dict[str, Any]]] = Field(default=None, description="Nested steps may be passed here. Use same structure for them.") - __properties: ClassVar[List[str]] = ["action", "expected_result", "data", "position", "attachments", "steps"] + __properties: ClassVar[List[str]] = ["action", "expected_result", "data", "value", "position", "attachments", "steps"] model_config = ConfigDict( populate_by_name=True, @@ -89,6 +90,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "action": obj.get("action"), "expected_result": obj.get("expected_result"), "data": obj.get("data"), + "value": obj.get("value"), "position": obj.get("position"), "attachments": obj.get("attachments"), "steps": obj.get("steps")