Skip to content
Open
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
52 changes: 47 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,55 @@ You can use **directory** option to specify directory to use for test file struc
pytest --testomatio sync --directory imported_tests
```
Note: **keep-structure** option takes precedence over **directory** option. If both are used **keep-structure** will be used.
#### Filter tests by id
You can filter tests by testomat.io id, using **test-id** option. You pass single or multiple ids to this option. Use this option with **report** command:
```bash
pytest --testomatio report --test-id "Tc0880217|Tfd1c595c"
#### Filter tests
You can filter tests that will be reported, using **testomatio-filter** option. Filter format: *filter_type=value*. Use this option with **report** command.

**Note**: Only one filter can be applied at a time

**Filter types**:
- **test_id**. Filter test by Testomat.io id. You can pass single or multiple ids for this filter using *|* as separator. Ex: "test_id=@T3h2r432|T2e34e342|b234fr254"
- **plan**. Filter by plan id.
- **jira**. Filter by jira issue id.
- **tag**. Filter by tag name.
- **label**. Filter by label name or label-id.

**Examples**:

**Filter by test id**
```bash
pytest --testomatio report --testomatio-filter="test_id=Tc0880217|Tfd1c595c"
```
**Filter by tag**

If your test have '@smoke' tag on testomat.io, then value for this filter == smoke
```bash
pytest --testomatio report --testomatio-filter="tag=smoke"
```

**Filter by label**

```bash
# by label name
pytest --testomatio report --testomatio-filter="label=important"

# by label id
pytest --testomatio report --testomatio-filter="label=important-f435-e"

# based on Severity type
pytest --testomatio report --testomatio-filter="label=severity-f124r-3:⚠️ Critical"
```
Note: Test id should be started from letter "T"

**Filter by plan**

```bash
pytest --testomatio report --testomatio-filter="plan=ca34gf3t"
```

**Filter by Jira Issue**

```bash
pytest --testomatio report --testomatio-filter="jira=TES1"
```

### Configuration with environment variables
You can use environment variable to control certain features of testomat.io
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ version_provider = "pep621"
update_changelog_on_bump = false
[project]
name = "pytestomatio"
version = "2.10.2"
version = "2.11.0b1"


dependencies = [
Expand Down
17 changes: 17 additions & 0 deletions pytestomatio/connect/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,23 @@ def get_tests(self, test_metadata: list[TestItem]) -> dict:
response = self.session.get(f'{self.base_url}/api/test_data?api_key={self.api_key}')
return response.json()

def get_filtered_tests(self, filter_type, filter_value):
"""
Returns list of filtered tests from Testomat.io
"""
# TODO: add retry logic
url = f'{self.base_url}/api/test_grep?api_key={self.api_key}&type={filter_type}&id={filter_value}'
try:
response = self.session.get(url)
if response.status_code < 400:
log.info(f'Received tests filtered by {filter_type}={filter_value} from {self.base_url}')
return response.json()
else:
log.error(f'Failed to receive tests from {self.base_url}. Status code: {response.status_code}')
except Exception as e:
log.error(f'An unexpected exception occurred. Please report an issue: {e}')
return

def create_test_run(self, access_event: str, title: str, group_title, env: str, label: str, shared_run: bool, shared_run_timeout: str,
parallel, ci_build_url: str) -> dict | None:
request = {
Expand Down
96 changes: 76 additions & 20 deletions pytestomatio/testomatio/filter_plugin.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,90 @@
import logging
import pytest

log = logging.getLogger('pytestomatio')


class TestomatioFilterPlugin:
@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(self, session, config, items):
# By now all other filters (like -m, -k, name-based) have been applied
# and `items` is the filtered set after all their conditions.
test_ids_str = config.getoption("test_id")
if not test_ids_str:
# No custom IDs specified, nothing to do
return
# todo: check multiple values for filter, apply several filters
allowed_filters = {'test_id', 'jira', 'label', 'plan', 'tag'}

test_ids = test_ids_str.split("|")
def get_matched_test_ids(self, f_type, f_value) -> list:
"""
Returns test ids that matches given filter
:param f_type: filter type(test_id, label)
:param f_value: filter value
"""
if f_type != 'test_id':
test_ids = self.filter_by_testomatio_related_fields(f_type, f_value)
else:
test_ids = f_value.split("|")
# Remove "@" from the start of test IDs if present
test_ids = [test_id.lstrip("@T") for test_id in test_ids]
if not test_ids:
return
cleared_ids = [test_id.lstrip("@T") for test_id in test_ids]
return cleared_ids

# Now let's find all tests that match these test IDs from the original full list.
# We use the originally collected tests to avoid losing tests filtered out by others.
original_items = session._pytestomatio_original_collected_items
testomatio_matched = []

for item in original_items:
def filter_by_testomatio_related_fields(self, filter_type, filter_value):
"""Get ids of tests that matches filter from Testomatio. Used for Testomatio related filters"""
connector = pytest.testomatio.connector
tests = connector.get_filtered_tests(filter_type, filter_value)
return tests.get('tests') if tests else []

def match_tests_by_id(self, test_ids, items) -> list:
matched_tests = []

for item in items:
# Check for testomatio marker
for marker in item.iter_markers(name="testomatio"):

marker_id = marker.args[0].lstrip("@T") # Strip "@" from the marker argument
if marker_id in test_ids:
testomatio_matched.append(item)
break
matched_tests.append(item)
break
return matched_tests

def filter_tests(self, filter_opts, original_items):
try:
f_type, f_value = filter_opts.split('=')
if not (f_type and f_value):
log.error(f'Failed to retrieve filter data. Filter type: {f_type} Filter value: {f_value}')
return

if f_type not in self.allowed_filters:
log.error(f"Filter '{f_type}' not allowed. Choose of these filters: {self.allowed_filters}")
return

log.info(f"Filtering tests using the '{f_type}' filter with '{f_value}' value")
test_ids = self.get_matched_test_ids(f_type, f_value)
if not test_ids:
return

return self.match_tests_by_id(test_ids, original_items)
except ValueError as e:
log.error(f"Incorrect filter format. Filter must be in type=value format. Received: {filter_opts}")

@pytest.hookimpl(trylast=True)
def pytest_collection_modifyitems(self, session, config, items):
testomatio_option = config.getoption('testomatio')
if testomatio_option is None or testomatio_option != 'report':
return

filter_opts = config.getoption('testomatio_filter') or config.getoption('test_id')
if not filter_opts:
return

# compatibility with deprecated filter option
if config.getoption('test_id') and not config.getoption('testomatio_filter'):
filter_opts = f'test_id={filter_opts}'
log.warning('--test-id filter option is deprecated. Use --testomatio-filter instead')

log.info('Testomatio Filter enabled')
# By now all other filters (like -m, -k, name-based) have been applied
# and `items` is the filtered set after all their conditions.
# We use the originally collected tests to avoid losing tests filtered out by others.
original_items = session._pytestomatio_original_collected_items
testomatio_matched = self.filter_tests(filter_opts, original_items)
if not testomatio_matched:
log.info('No tests were found matching the filter provided. Filtering is skipped')
return

# We'll check common filters: -k, -m and a few others.
# If they are empty or None, they are not active.
Expand Down
9 changes: 8 additions & 1 deletion pytestomatio/utils/parser_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,18 @@ def parser_options(parser: Parser, testomatio='testomatio') -> None:
Note: --structure option takes precedence over --directory option. If both are used --structure will be used.
"""
)
group.addoption('--testomatio-filter',
default=None,
dest="testomatio_filter",
help="""
help="Filter tests by Test IDs (e.g., single test id 'T00C73028' or multiply 'T00C73028|T00C73029'), Labels, Tags, Plan or Jira issue ids
"""
)
group.addoption('--test-id',
default=None,
dest="test_id",
help="""
help="Filter tests by Test IDs (e.g., single test id 'T00C73028' or multiply 'T00C73028|T00C73029')
help="DEPRECATED. Filter tests by Test IDs (e.g., single test id 'T00C73028' or multiply 'T00C73028|T00C73029')
"""
)
parser.addini('testomatio_url', 'testomat.io base url')
Loading
Loading