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
2 changes: 2 additions & 0 deletions smart_tests/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from smart_tests.commands.subset import subset
from smart_tests.commands.update import update
from smart_tests.commands.verify import verify
from smart_tests.commands.view import view

cli = Group(name="cli", callback=Application)
cli.add_command(record)
Expand All @@ -29,6 +30,7 @@
cli.add_command(detect_flakes)
cli.add_command(gate)
cli.add_command(get)
cli.add_command(view)


def _load_test_runners():
Expand Down
13 changes: 13 additions & 0 deletions smart_tests/commands/view/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from ... import args4p
from ...app import Application
from .flaky_tests import flaky_tests
from .test_results import test_results


@args4p.group(help="View historical test data and insights")
def view(app: Application):
return app


view.add_command(flaky_tests)
view.add_command(test_results)
107 changes: 107 additions & 0 deletions smart_tests/commands/view/flaky_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import json
import re
import sys
from http import HTTPStatus
from typing import Annotated

import click

import smart_tests.args4p.typer as typer
from smart_tests.args4p.converters import intType
from smart_tests.args4p.exceptions import BadCmdLineException

from ... import args4p
from ...app import Application
from ...utils.smart_tests_client import SmartTestsClient
from ...utils.typer_types import DateTimeWithTimezone, parse_datetime_with_timezone


def validate_iso_week(value: str) -> str:
"""Validate ISO week format YYYY-Www"""
pattern = r'^\d{4}-W\d{2}$'
if not re.match(pattern, value):
raise BadCmdLineException(
f"Invalid year-week format: '{value}'. Expected format: YYYY-Www (e.g., 2026-W15)"
)
return value


@args4p.command(help="View flaky test data with weekly scores")
def flaky_tests(
app: Application,
year_week: Annotated[str | None, typer.Option(
"--year-week",
help="Specific ISO week for flaky tests (e.g., '2026-W15')",
type=validate_iso_week,
metavar="YYYY-Www"
)] = None,
weeks: Annotated[int | None, typer.Option(
"--weeks",
help="Number of weeks to retrieve (default: 1, max: 12)",
type=intType(min=1, max=12),
metavar="N"
)] = None,
from_date: Annotated[DateTimeWithTimezone | None, typer.Option(
"--from",
help="Start date/time (ISO 8601 format, e.g., '2026-04-08' or '2026-04-08T00:00:00Z')",
type=parse_datetime_with_timezone,
metavar="DATE"
)] = None,
to_date: Annotated[DateTimeWithTimezone | None, typer.Option(
"--to",
help="End date/time (ISO 8601 format, e.g., '2026-04-14' or '2026-04-14T23:59:59Z')",
type=parse_datetime_with_timezone,
metavar="DATE"
)] = None,
test_suite: Annotated[str | None, typer.Option(
"--test-suite",
help="Test suite name filter (e.g., 'unit-tests')",
metavar="NAME"
)] = None,
limit: Annotated[int | None, typer.Option(
"--limit",
help="Max results to return per week (default: 50, max: 500)",
type=intType(min=1, max=500),
metavar="N"
)] = None,
):
"""View flaky tests with weekly scores and trends"""
client = SmartTestsClient(app=app)

# Build query parameters
params = {}
if year_week:
params["year-week"] = year_week
if weeks:
params["weeks"] = weeks
if from_date:
params["from"] = str(from_date)
if to_date:
params["to"] = str(to_date)
if test_suite:
params["test-suite"] = test_suite
if limit:
params["limit"] = limit

try:
res = client.request("get", "view/flaky-tests", params=params)

if res.status_code == HTTPStatus.NOT_FOUND:
click.secho(
"No flaky test data found. Check your filters and try again.",
fg='yellow', err=True
)
sys.exit(1)

res.raise_for_status()
response_json = res.json()

# Output JSON format
click.echo(json.dumps(response_json, indent=2))

except Exception as e:
client.print_exception_and_recover(
e,
"Warning: failed to retrieve flaky tests from server"
)
sys.exit(1)
135 changes: 135 additions & 0 deletions smart_tests/commands/view/test_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import json
import sys
from enum import Enum
from http import HTTPStatus
from typing import Annotated

import click

import smart_tests.args4p.typer as typer
from smart_tests.args4p.converters import intType
from smart_tests.args4p.exceptions import BadCmdLineException

from ... import args4p
from ...app import Application
from ...utils.smart_tests_client import SmartTestsClient
from ...utils.typer_types import DateTimeWithTimezone, parse_datetime_with_timezone


class TestStatus(str, Enum):
"""Test execution status"""
PASSED = "PASSED"
FAILED = "FAILED"
SKIPPED = "SKIPPED"
FLAKE = "FLAKE"

@staticmethod
def from_str(value: str) -> "TestStatus":
"""Parse status from string"""
for member in TestStatus:
if member.value.lower() == value.lower():
return member
raise BadCmdLineException(
f"Invalid status: '{value}'. Valid values: PASSED, FAILED, SKIPPED, FLAKE"
)


@args4p.command(help="View detailed test execution results")
def test_results(
app: Application,
test_path: Annotated[str | None, typer.Option(
"--test-path",
help="Filter by test path (exact match)",
metavar="PATH"
)] = None,
status: Annotated[TestStatus | None, typer.Option(
"--status",
help="Filter by test status (PASSED, FAILED, SKIPPED, FLAKE)",
type=TestStatus.from_str,
metavar="STATUS"
)] = None,
branch: Annotated[str | None, typer.Option(
"--branch",
help="Filter by branch/lineage (exact match)",
metavar="BRANCH"
)] = None,
test_suite: Annotated[str | None, typer.Option(
"--test-suite",
help="Filter by test suite name (e.g., 'unit-tests')",
metavar="NAME"
)] = None,
from_date: Annotated[DateTimeWithTimezone | None, typer.Option(
"--from",
help="Start date/time (ISO 8601 format, e.g., '2026-04-08' or '2026-04-08T00:00:00Z')",
type=parse_datetime_with_timezone,
metavar="DATE"
)] = None,
to_date: Annotated[DateTimeWithTimezone | None, typer.Option(
"--to",
help="End date/time (ISO 8601 format, e.g., '2026-04-14' or '2026-04-14T23:59:59Z')",
type=parse_datetime_with_timezone,
metavar="DATE"
)] = None,
limit: Annotated[int | None, typer.Option(
"--limit",
help="Max results to return (default: 50, max: 500)",
type=intType(min=1, max=500),
metavar="N"
)] = None,
offset: Annotated[int | None, typer.Option(
"--offset",
help="Pagination offset (default: 0)",
type=intType(min=0),
metavar="N"
)] = None,
logs: Annotated[bool, typer.Option(
"--logs",
help="Include full stdout/stderr from failed test case executions"
)] = False,
):
"""View detailed test execution results with filters"""
client = SmartTestsClient(app=app)

# Build query parameters
params = {}
if test_path:
params["test-path"] = test_path
if status:
params["status"] = status.value
if branch:
params["branch"] = branch
if test_suite:
params["test-suite"] = test_suite
if from_date:
params["from"] = str(from_date)
if to_date:
params["to"] = str(to_date)
if logs:
params["include-logs"] = "true"
if limit:
params["limit"] = limit
if offset:
params["offset"] = offset

try:
res = client.request("get", "view/test-results", params=params)

if res.status_code == HTTPStatus.NOT_FOUND:
click.secho(
"No test results found. Check your filters and try again.",
fg='yellow', err=True
)
sys.exit(1)

res.raise_for_status()
response_json = res.json()

# Output JSON format
click.echo(json.dumps(response_json, indent=2))

except Exception as e:
client.print_exception_and_recover(
e,
"Warning: failed to retrieve test results from server"
)
sys.exit(1)
Loading
Loading