Skip to content

Commit 8e5e924

Browse files
authored
Apply multiple fixes (#18)
## Added * Mark package as typed * Methods to get attachments ## Changed * Test steps in test results are now sorted by index ## Fixed * Fixed uppercase-lowercase in constants * If a test case is created without dedicated status, it is set to "Not Executed" now
1 parent a6f6515 commit 8e5e924

31 files changed

+222
-138
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Python package
2+
3+
on: [push]
4+
5+
jobs:
6+
7+
format:
8+
name: Check formatting
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout sources
12+
uses: actions/checkout@v2
13+
- name: Set up Python
14+
uses: actions/setup-python@v2
15+
with:
16+
python-version: 3.8
17+
- name: Check formatting
18+
uses: pre-commit/action@v2.0.0
19+
20+
lint:
21+
name: Lint
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout sources
25+
uses: actions/checkout@v2
26+
- name: Set up Python
27+
uses: actions/setup-python@v2
28+
with:
29+
python-version: 3.8
30+
- name: Lint with flake8
31+
run: |
32+
python -m pip install --upgrade pip
33+
python -m pip install flake8
34+
flake8 adaptavist --count --show-source --statistics --max-line-length 160
35+
flake8 . --count --exit-zero --statistics --max-line-length 160
36+
- name: Lint with pylint
37+
run: |
38+
python -m pip install pylint pytest
39+
python -m pip install -e .
40+
pylint --errors-only --score=n adaptavist
41+
pylint --exit-zero --score=n --disable=C,E,R --enable=useless-suppression adaptavist
42+
- name: Lint with mypy
43+
run: |
44+
python -m pip install mypy types-requests
45+
mypy --ignore-missing-imports adaptavist
46+
47+
test:
48+
name: Test with Python ${{ matrix.python-version }}
49+
runs-on: ubuntu-latest
50+
strategy:
51+
matrix:
52+
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
53+
steps:
54+
- name: Checkout sources
55+
uses: actions/checkout@v2
56+
- name: Set up Python ${{ matrix.python-version }}
57+
uses: actions/setup-python@v2
58+
with:
59+
python-version: ${{ matrix.python-version }}
60+
- name: Install dependencies
61+
run: |
62+
python -m pip install --upgrade pip
63+
python -m pip install pytest-cov
64+
python -m pip install .[test]
65+
- name: Test with pytest
66+
run: |
67+
pytest --cov=adaptavist

.pre-commit-config.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/mirrors-yapf
3+
rev: 'v0.31.0'
4+
hooks:
5+
- id: yapf
6+
- repo: https://github.com/pycqa/isort
7+
rev: '5.9.3'
8+
hooks:
9+
- id: isort
10+
- repo: https://github.com/pre-commit/pre-commit-hooks
11+
rev: 'v4.0.1'
12+
hooks:
13+
- id: end-of-file-fixer
14+
- id: trailing-whitespace

CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,21 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [Unreleased]
7+
## [2.1.0] - 2021/11/17
88

9-
## Added
9+
### Added
1010

1111
- Mark package as typed
1212
- Methods to get attachments
1313

14-
## Fixed
14+
### Changed
15+
16+
- Test steps in test results are now sorted by index
17+
18+
### Fixed
1519

1620
- Fixed uppercase-lowercase in constants
21+
- If a test case is created without dedicated status, it is set to "Not Executed" now
1722

1823
## [2.0.0] - 2021/05/31
1924

@@ -54,4 +59,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5459

5560
### Added
5661

57-
- Python package providing functionality for Jira Test Management.
62+
- Python package providing functionality for Jira Test Management.

LICENSE.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
Copyright (c) 2019 Stephan Steinberg <stephan.steinberg@devolo.de>
1+
Copyright (c) 2019 devolo AG
22

33
The MIT License
44

5-
Permission is hereby granted, free of charge, to any person obtaining a copy
6-
of this software and associated documentation files (the "Software"), to deal
7-
in the Software without restriction, including without limitation the rights
8-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9-
copies of the Software, and to permit persons to whom the Software is
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
1010
furnished to do so, subject to the following conditions:
1111

12-
The above copyright notice and this permission notice shall be included in
12+
The above copyright notice and this permission notice shall be included in
1313
all copies or substantial portions of the Software.
1414

15-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18-
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19-
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20-
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2121
OTHER DEALINGS IN THE SOFTWARE.

adaptavist/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
try:
88
from importlib.metadata import PackageNotFoundError, version
99
except ImportError:
10-
from importlib_metadata import PackageNotFoundError, version # type: ignore[misc]
10+
from importlib_metadata import PackageNotFoundError, version # type: ignore
1111

1212
try:
1313
__version__ = version("adaptavist")

adaptavist/adaptavist.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import requests_toolbelt
1010

1111
from ._helper import build_folder_names, get_executor, raise_on_kwargs_not_empty, update_field, update_multiline_field
12-
from .const import PRIORITY_NORMAL, STATUS_APPROVED, STEP_TYPE_BY_STEP, TEST_CASE, TEST_PLAN, TEST_RUN
12+
from .const import PRIORITY_NORMAL, STATUS_APPROVED, STATUS_NOT_EXECUTED, STEP_TYPE_BY_STEP, TEST_CASE, TEST_PLAN, TEST_RUN
1313

1414

1515
class Adaptavist:
@@ -29,15 +29,15 @@ def __init__(self, jira_server: str, jira_username: str, jira_password: str):
2929
self._adaptavist_api_url = self.jira_server + "/rest/atm/1.0"
3030
self._authentication = requests.auth.HTTPBasicAuth(self.jira_username, jira_password)
3131
self._headers = {"Accept": "application/json", "Content-type": "application/json"}
32-
self._logger = logging.getLogger(self.__class__.__name__)
32+
self._logger = logging.getLogger(__name__)
3333

3434
def get_users(self) -> List[str]:
3535
"""
3636
Get a list of users known to Adaptavist/Jira.
3737
3838
:returns: List of user keys
3939
"""
40-
users: List = []
40+
users: List[Dict[str, Any]] = []
4141
i = 0
4242
while True:
4343
request_url = f"{self.jira_server}/rest/api/2/user/search?username=.&startAt={i}&maxResults=200"
@@ -73,7 +73,7 @@ def get_environments(self, project_key: str) -> List[Dict[str, Any]]:
7373
request = self._get(request_url)
7474
return request.json() if request else []
7575

76-
def create_environment(self, project_key: str, environment_name: str, **kwargs) -> Optional[int]:
76+
def create_environment(self, project_key: str, environment_name: str, **kwargs: Any) -> Optional[int]:
7777
"""
7878
Create a new environment.
7979
@@ -157,7 +157,7 @@ def get_test_cases(self, search_mask: str = "folder <= \"/\"") -> List[Dict[str,
157157
:param search_mask: Search mask to match test cases
158158
:returns: List of test cases
159159
"""
160-
test_cases: List = []
160+
test_cases: List[Dict[str, Any]] = []
161161
i = 0
162162
while True:
163163
request_url = f"{self._adaptavist_api_url}/testcase/search?query={quote_plus(search_mask)}&startAt={i}"
@@ -171,7 +171,7 @@ def get_test_cases(self, search_mask: str = "folder <= \"/\"") -> List[Dict[str,
171171

172172
return test_cases
173173

174-
def create_test_case(self, project_key: str, test_case_name: str, **kwargs) -> Optional[int]:
174+
def create_test_case(self, project_key: str, test_case_name: str, **kwargs: Any) -> Optional[int]:
175175
"""
176176
Create a new test case.
177177
@@ -221,7 +221,7 @@ def create_test_case(self, project_key: str, test_case_name: str, **kwargs) -> O
221221
request = self._post(request_url, request_data)
222222
return request.json()["key"] if request else None
223223

224-
def edit_test_case(self, test_case_key: str, **kwargs) -> bool:
224+
def edit_test_case(self, test_case_key: str, **kwargs: Any) -> bool:
225225
"""
226226
Edit given test case.
227227
@@ -379,7 +379,7 @@ def get_test_plans(self, search_mask: str = "folder <= \"/\"") -> List[Dict[str,
379379
:param search_mask: Search mask to match test plans
380380
:returns: List of test plans
381381
"""
382-
test_plans: List = []
382+
test_plans: List[Dict[str, Any]] = []
383383
i = 0
384384
while True:
385385
request_url = f"{self._adaptavist_api_url}/testplan/search?query={quote_plus(search_mask)}&startAt={i}"
@@ -392,7 +392,7 @@ def get_test_plans(self, search_mask: str = "folder <= \"/\"") -> List[Dict[str,
392392
i += len(result)
393393
return test_plans
394394

395-
def create_test_plan(self, project_key: str, test_plan_name: str, **kwargs) -> Optional[str]:
395+
def create_test_plan(self, project_key: str, test_plan_name: str, **kwargs: Any) -> Optional[str]:
396396
"""
397397
Create a new test plan.
398398
@@ -432,7 +432,7 @@ def create_test_plan(self, project_key: str, test_plan_name: str, **kwargs) -> O
432432
request = self._post(request_url, request_data)
433433
return request.json()["key"] if request else None
434434

435-
def edit_test_plan(self, test_plan_key: str, **kwargs) -> bool:
435+
def edit_test_plan(self, test_plan_key: str, **kwargs: Any) -> bool:
436436
"""
437437
Edit given test plan.
438438
@@ -500,7 +500,7 @@ def get_test_run_by_name(self, test_run_name: str) -> Dict[str, Any]:
500500
:param test_run_name: Test run name to look for
501501
:returns: Info about the test run
502502
"""
503-
test_runs: List = []
503+
test_runs: List[Dict[str, Any]] = []
504504
i = 0
505505
search_mask = quote_plus(f"testRun.name = \"{test_run_name}\"")
506506
while True:
@@ -514,7 +514,7 @@ def get_test_run_by_name(self, test_run_name: str) -> Dict[str, Any]:
514514
i += len(results)
515515
return {key: test_runs[-1][key] for key in ["key", "name"]} if test_runs else {}
516516

517-
def get_test_runs(self, search_mask: str = "folder = \"/\"", **kwargs) -> List[Dict[str, Any]]:
517+
def get_test_runs(self, search_mask: str = "folder = \"/\"", **kwargs: Any) -> List[Dict[str, Any]]:
518518
"""
519519
Get a list of test runs matching the search mask.
520520
Unfortunately, /testrun/search does not support empty query, so we use a basic filter here to get all test runs, if no search mask is given.
@@ -529,7 +529,7 @@ def get_test_runs(self, search_mask: str = "folder = \"/\"", **kwargs) -> List[D
529529
fields: str = kwargs.pop("fields", "")
530530
raise_on_kwargs_not_empty(kwargs)
531531

532-
test_runs: List = []
532+
test_runs: List[Dict[str, Any]] = []
533533
i = 0
534534
while True:
535535
request_url = f"{self._adaptavist_api_url}/testrun/search?query={quote_plus(search_mask)}&startAt={i}&maxResults=1000&fields={quote_plus(fields)}"
@@ -553,7 +553,7 @@ def get_test_run_links(self, issue_key: str) -> List[Dict[str, str]]:
553553
self._logger.debug("Looking for test runs linked to %s", issue_key)
554554
return [test_run for test_run in test_runs if test_run["issueKey"] == issue_key]
555555

556-
def create_test_run(self, project_key: str, test_run_name: str, **kwargs) -> Optional[str]:
556+
def create_test_run(self, project_key: str, test_run_name: str, **kwargs: Any) -> Optional[str]:
557557
"""
558558
Create a new test run.
559559
@@ -575,12 +575,10 @@ def create_test_run(self, project_key: str, test_run_name: str, **kwargs) -> Opt
575575

576576
self.create_folder(project_key=project_key, folder_type=TEST_RUN, folder_name=folder)
577577

578-
test_cases_list_of_dicts = [
579-
{
580-
"testCaseKey": test_case_key,
581-
"environment": environment or None,
582-
} for test_case_key in test_cases
583-
]
578+
test_cases_list_of_dicts = [{
579+
"testCaseKey": test_case_key,
580+
"environment": environment or None,
581+
} for test_case_key in test_cases]
584582

585583
request_url = f"{self._adaptavist_api_url}/testrun"
586584
request_data = {
@@ -591,11 +589,11 @@ def create_test_run(self, project_key: str, test_run_name: str, **kwargs) -> Opt
591589
"issueKey": issue_key or None,
592590
"items": test_cases_list_of_dicts,
593591
}
594-
self._logger.debug("Creating new test run in project %s with name '%s'", test_plan_key, test_run_name)
592+
self._logger.debug("Creating new test run in project %s with name '%s'", project_key, test_run_name)
595593
request = self._post(request_url, request_data)
596594
return request.json()["key"] if request else None
597595

598-
def clone_test_run(self, test_run_key: str, test_run_name: str = "", **kwargs) -> Optional[str]:
596+
def clone_test_run(self, test_run_key: str, test_run_name: str = "", **kwargs: Any) -> Optional[str]:
599597
"""
600598
Clone a given test run.
601599
@@ -647,7 +645,7 @@ def get_test_execution_results(self, last_result_only: bool = True) -> List[Dict
647645
If false, returns all test results, i.e. even those ones that have been overwritten
648646
:returns: Test results
649647
"""
650-
test_results: List = []
648+
test_results: List[Dict[str, Any]] = []
651649
i = 0
652650
while True:
653651
request_url = f"{self.jira_server}/rest/tests/1.0/reports/testresults?startAt={i}&maxResults=10000"
@@ -686,9 +684,14 @@ def get_test_results(self, test_run_key: str) -> List[Dict[str, Any]]:
686684
request_url = f"{self._adaptavist_api_url}/testrun/{test_run_key}/testresults"
687685
self._logger.debug("Getting all test results for run %s", test_run_key)
688686
request = self._get(request_url)
689-
return request.json() if request else []
687+
if not request:
688+
return []
689+
results = request.json()
690+
for result in results:
691+
result["scriptResults"] = sorted(result["scriptResults"], key=lambda result: result["index"])
692+
return results
690693

691-
def create_test_results(self, test_run_key: str, results: List[Dict[str, Any]], exclude_existing_test_cases: bool = True, **kwargs) -> List[int]:
694+
def create_test_results(self, test_run_key: str, results: List[Dict[str, Any]], exclude_existing_test_cases: bool = True, **kwargs: Any) -> List[int]:
692695
"""
693696
Create new test results for a given test run.
694697
@@ -744,7 +747,7 @@ def get_test_result(self, test_run_key: str, test_case_key: str) -> Dict[str, An
744747
return item
745748
return {}
746749

747-
def create_test_result(self, test_run_key: str, test_case_key: str, status: str = "", **kwargs) -> Optional[int]:
750+
def create_test_result(self, test_run_key: str, test_case_key: str, status: str = STATUS_NOT_EXECUTED, **kwargs: Any) -> Optional[int]:
748751
"""
749752
Create a new test result for a given test run and test case with the given status.
750753
@@ -787,7 +790,7 @@ def create_test_result(self, test_run_key: str, test_case_key: str, status: str
787790
request = self._post(request_url, request_data)
788791
return request.json()["id"] if request else None
789792

790-
def edit_test_result_status(self, test_run_key: str, test_case_key: str, status: str, **kwargs) -> bool:
793+
def edit_test_result_status(self, test_run_key: str, test_case_key: str, status: str, **kwargs: Any) -> bool:
791794
"""
792795
Edit the last existing test result for a given test run and test case with the given status.
793796
@@ -859,7 +862,7 @@ def add_test_result_attachment(self, test_run_key: str, test_case_key: str, atta
859862
if isinstance(attachment, str) \
860863
else self._upload_file(request_url, attachment, filename)
861864

862-
def edit_test_script_status(self, test_run_key: str, test_case_key: str, step: int, status: str, **kwargs) -> bool:
865+
def edit_test_script_status(self, test_run_key: str, test_case_key: str, step: int, status: str, **kwargs: Any) -> bool:
863866
"""
864867
Edit test script result for a given test run and test case with the given status.
865868

0 commit comments

Comments
 (0)