Skip to content
Draft
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
3 changes: 1 addition & 2 deletions allure-python-commons/src/allure_commons/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ def _last_item_uuid(self, item_type=None):

@contextmanager
def schedule_test_case(self, uuid=None):
test_result = TestResult()
test_result.uuid = uuid or uuid4()
test_result = TestResult(uuid=uuid or uuid4())
self._items[test_result.uuid] = test_result
yield test_result

Expand Down
17 changes: 13 additions & 4 deletions allure-python-commons/src/allure_commons/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@
import json
import uuid
import shutil
from attr import asdict
from typing import Any
from enum import Enum
from attr import asdict, Attribute
from allure_commons import hookimpl

INDENT = 4


def _enum_value_serializer(inst: Any, field: Attribute, value: Any) -> Any:
"""Convert enum values to their string representation for serialization."""
if isinstance(value, Enum):
return value.value
return value


class AllureFileLogger:

def __init__(self, report_dir, clean=False):
Expand All @@ -21,7 +30,7 @@ def __init__(self, report_dir, clean=False):
def _report_item(self, item):
indent = INDENT if os.environ.get("ALLURE_INDENT_OUTPUT") else None
filename = item.file_pattern.format(prefix=uuid.uuid4())
data = asdict(item, filter=lambda _, v: v or v is False)
data = asdict(item, filter=lambda _, v: v or v is False, value_serializer=_enum_value_serializer)
with io.open(self._report_dir / filename, 'w', encoding='utf8') as json_file:
json.dump(data, json_file, indent=indent, ensure_ascii=False)

Expand Down Expand Up @@ -57,12 +66,12 @@ def __init__(self):

@hookimpl
def report_result(self, result):
data = asdict(result, filter=lambda _, v: v or v is False)
data = asdict(result, filter=lambda _, v: v or v is False, value_serializer=_enum_value_serializer)
self.test_cases.append(data)

@hookimpl
def report_container(self, container):
data = asdict(container, filter=lambda _, v: v or v is False)
data = asdict(container, filter=lambda _, v: v or v is False, value_serializer=_enum_value_serializer)
self.test_containers.append(data)

@hookimpl
Expand Down
24 changes: 12 additions & 12 deletions allure-python-commons/src/allure_commons/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,28 @@ def __is(kind, t):
def parse_tag(tag, issue_pattern=None, link_pattern=None):
"""
>>> parse_tag("blocker")
Label(name='severity', value='blocker')
Label(name=<LabelType.SEVERITY: 'severity'>, value='blocker')

>>> parse_tag("allure.issue:http://example.com/BUG-42")
Link(type='issue', url='http://example.com/BUG-42', name='http://example.com/BUG-42')
Link(type=<LinkType.ISSUE: 'issue'>, url='http://example.com/BUG-42', name='http://example.com/BUG-42')

>>> parse_tag("allure.link.home:http://qameta.io")
Link(type='link', url='http://qameta.io', name='home')
Link(type=<LinkType.LINK: 'link'>, url='http://qameta.io', name='home')

>>> parse_tag("allure.suite:mapping")
Label(name='suite', value='mapping')
Label(name=<LabelType.SUITE: 'suite'>, value='mapping')

>>> parse_tag("allure.suite:mapping")
Label(name='suite', value='mapping')
Label(name=<LabelType.SUITE: 'suite'>, value='mapping')

>>> parse_tag("allure.label.owner:me")
Label(name='owner', value='me')

>>> parse_tag("foo.label:1")
Label(name='tag', value='foo.label:1')
Label(name=<LabelType.TAG: 'tag'>, value='foo.label:1')

>>> parse_tag("allure.foo:1")
Label(name='tag', value='allure.foo:1')
Label(name=<LabelType.TAG: 'tag'>, value='allure.foo:1')
"""
sep = allure_tag_sep(tag)
schema, value = islice(chain(tag.split(sep, 1), [None]), 2)
Expand All @@ -63,10 +63,10 @@ def parse_tag(tag, issue_pattern=None, link_pattern=None):
value = issue_pattern.format(value)
if link_pattern and kind == "link" and not value.startswith("http"):
value = link_pattern.format(value)
return Link(type=kind, name=name or value, url=value)
return Link(type=LinkType(kind), name=name or value, url=value)

if __is(kind, LabelType):
return Label(name=kind, value=value)
return Label(name=LabelType(kind), value=value)

if kind == "id":
return Label(name=LabelType.ID, value=value)
Expand All @@ -82,7 +82,7 @@ def labels_set(labels):
>>> labels_set([Label(name=LabelType.SEVERITY, value=Severity.NORMAL),
... Label(name=LabelType.SEVERITY, value=Severity.BLOCKER)
... ])
[Label(name='severity', value=<Severity.BLOCKER: 'blocker'>)]
[Label(name=<LabelType.SEVERITY: 'severity'>, value=<Severity.BLOCKER: 'blocker'>)]

>>> labels_set([Label(name=LabelType.SEVERITY, value=Severity.NORMAL),
... Label(name='severity', value='minor')
Expand All @@ -92,12 +92,12 @@ def labels_set(labels):
>>> labels_set([Label(name=LabelType.EPIC, value="Epic"),
... Label(name=LabelType.EPIC, value="Epic")
... ])
[Label(name='epic', value='Epic')]
[Label(name=<LabelType.EPIC: 'epic'>, value='Epic')]

>>> labels_set([Label(name=LabelType.EPIC, value="Epic1"),
... Label(name=LabelType.EPIC, value="Epic2")
... ])
[Label(name='epic', value='Epic1'), Label(name='epic', value='Epic2')]
[Label(name=<LabelType.EPIC: 'epic'>, value='Epic1'), Label(name=<LabelType.EPIC: 'epic'>, value='Epic2')]
"""
class Wl:
def __init__(self, label):
Expand Down
105 changes: 59 additions & 46 deletions allure-python-commons/src/allure_commons/model2.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from __future__ import annotations

from enum import Enum

from attr import attrs, attrib
from attr import Factory

from allure_commons.types import AttachmentType, LabelType, LinkType, ParameterMode

TEST_GROUP_PATTERN = "{prefix}-container.json"
TEST_CASE_PATTERN = "{prefix}-result.json"
Expand All @@ -12,49 +17,49 @@
class TestResultContainer:
file_pattern = TEST_GROUP_PATTERN

uuid = attrib(default=None)
name = attrib(default=None)
children = attrib(default=Factory(list))
description = attrib(default=None)
descriptionHtml = attrib(default=None)
befores = attrib(default=Factory(list))
afters = attrib(default=Factory(list))
links = attrib(default=Factory(list))
start = attrib(default=None)
stop = attrib(default=None)
uuid: str = attrib(default=None)
name: str | None = attrib(default=None)
children: list[str] = attrib(default=Factory(list))
description: str | None = attrib(default=None)
descriptionHtml: str | None = attrib(default=None)
befores: list[TestBeforeResult] = attrib(default=Factory(list))
afters: list[TestAfterResult] = attrib(default=Factory(list))
links: list[Link] = attrib(default=Factory(list))
start: int | None = attrib(default=None)
stop: int | None = attrib(default=None)


@attrs
class ExecutableItem:
name = attrib(default=None)
status = attrib(default=None)
statusDetails = attrib(default=None)
stage = attrib(default=None)
description = attrib(default=None)
descriptionHtml = attrib(default=None)
steps = attrib(default=Factory(list))
attachments = attrib(default=Factory(list))
parameters = attrib(default=Factory(list))
start = attrib(default=None)
stop = attrib(default=None)
name: str | None = attrib(default=None)
status: Status | None = attrib(default=None)
statusDetails: StatusDetails | None = attrib(default=None)
stage: Stage | None = attrib(default=None)
description: str | None = attrib(default=None)
descriptionHtml: str | None = attrib(default=None)
steps: list[TestStepResult] = attrib(default=Factory(list))
attachments: list[Attachment] = attrib(default=Factory(list))
parameters: list[Parameter] = attrib(default=Factory(list))
start: int | None = attrib(default=None)
stop: int | None = attrib(default=None)


@attrs
class TestResult(ExecutableItem):
file_pattern = TEST_CASE_PATTERN

uuid = attrib(default=None)
historyId = attrib(default=None)
testCaseId = attrib(default=None)
fullName = attrib(default=None)
labels = attrib(default=Factory(list))
links = attrib(default=Factory(list))
titlePath = attrib(default=Factory(list))
uuid: str = attrib(default=None)
historyId: str | None = attrib(default=None)
testCaseId: str | None = attrib(default=None)
fullName: str | None = attrib(default=None)
labels: list[Label] = attrib(default=Factory(list))
links: list[Link] = attrib(default=Factory(list))
titlePath: list[str] = attrib(default=Factory(list))


@attrs
class TestStepResult(ExecutableItem):
id = attrib(default=None) # noqa: A003
id: str | None = attrib(default=None) # noqa: A003


@attrs
Expand All @@ -69,43 +74,51 @@ class TestAfterResult(ExecutableItem):

@attrs
class Parameter:
name = attrib(default=None)
value = attrib(default=None)
excluded = attrib(default=None)
mode = attrib(default=None)
name: str = attrib(default=None)
value: str = attrib(default=None)
excluded: bool | None = attrib(default=None)
mode: ParameterMode | None = attrib(default=None)


@attrs
class Label:
name = attrib(default=None)
value = attrib(default=None)
name: LabelType | str = attrib(default=None)
value: str = attrib(default=None)


@attrs
class Link:
type = attrib(default=None) # noqa: A003
url = attrib(default=None)
name = attrib(default=None)
type: LinkType | str | None = attrib(default=None) # noqa: A003
url: str = attrib(default=None)
name: str | None = attrib(default=None)


@attrs
class StatusDetails:
known = attrib(default=None)
flaky = attrib(default=None)
message = attrib(default=None)
trace = attrib(default=None)
known: bool | None = attrib(default=None)
flaky: bool | None = attrib(default=None)
message: str | None = attrib(default=None)
trace: str | None = attrib(default=None)


@attrs
class Attachment:
name = attrib(default=None)
source = attrib(default=None)
type = attrib(default=None) # noqa: A003
name: str = attrib(default=None)
source: str = attrib(default=None)
type: AttachmentType | str | None = attrib(default=None) # noqa: A003


class Status:
class Status(str, Enum):
FAILED = 'failed'
BROKEN = 'broken'
PASSED = 'passed'
SKIPPED = 'skipped'
UNKNOWN = 'unknown'


class Stage(str, Enum):
SCHEDULED = "scheduled"
RUNNING = "running"
FINISHED = "finished"
PENDING = "pending"
INTERRUPTED = "interrupted"
17 changes: 10 additions & 7 deletions allure-python-commons/src/allure_commons/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from enum import Enum
from __future__ import annotations

ALLURE_UNIQUE_LABELS = ['severity', 'thread', 'host']
from enum import Enum


class Severity(str, Enum):
Expand All @@ -11,13 +11,13 @@ class Severity(str, Enum):
TRIVIAL = 'trivial'


class LinkType:
class LinkType(str, Enum):
LINK = 'link'
ISSUE = 'issue'
TEST_CASE = 'tms'


class LabelType(str):
class LabelType(str, Enum):
EPIC = 'epic'
FEATURE = 'feature'
STORY = 'story'
Expand All @@ -34,9 +34,12 @@ class LabelType(str):
MANUAL = 'ALLURE_MANUAL'


ALLURE_UNIQUE_LABELS = [LabelType.SEVERITY, LabelType.THREAD, LabelType.HOST]


class AttachmentType(Enum):

def __init__(self, mime_type, extension):
def __init__(self, mime_type: str, extension: str) -> None:
self.mime_type = mime_type
self.extension = extension

Expand Down Expand Up @@ -66,7 +69,7 @@ def __init__(self, mime_type, extension):
PDF = ("application/pdf", "pdf")


class ParameterMode(Enum):
class ParameterMode(str, Enum):
HIDDEN = 'hidden'
MASKED = 'masked'
DEFAULT = None
DEFAULT = 'default'
2 changes: 1 addition & 1 deletion allure-robotframework/src/listener/allure_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def stop_test(self, _, attributes, messages):
test_result.labels.append(Label(name=LabelType.SEVERITY, value=Severity.CRITICAL))

for link_type in (LinkType.ISSUE, LinkType.TEST_CASE, LinkType.LINK):
test_result.links.extend(allure_links(attributes, link_type))
test_result.links.extend(allure_links(attributes, link_type.value))

self._current_tb, self._current_msg = None, None

Expand Down
11 changes: 6 additions & 5 deletions allure-robotframework/src/listener/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ def get_allure_parameters(parameters):
def get_allure_suites(longname):
"""
>>> get_allure_suites('Suite1.Test')
[Label(name='suite', value='Suite1')]
[Label(name=<LabelType.SUITE: 'suite'>, value='Suite1')]
>>> get_allure_suites('Suite1.Suite2.Test') # doctest: +NORMALIZE_WHITESPACE
[Label(name='suite', value='Suite1'), Label(name='subSuite', value='Suite2')]
[Label(name=<LabelType.SUITE: 'suite'>, value='Suite1'),
Label(name=<LabelType.SUB_SUITE: 'subSuite'>, value='Suite2')]
>>> get_allure_suites('Suite1.Suite2.Suite3.Test') # doctest: +NORMALIZE_WHITESPACE
[Label(name='parentSuite', value='Suite1'),
Label(name='suite', value='Suite2'),
Label(name='subSuite', value='Suite3')]
[Label(name=<LabelType.PARENT_SUITE: 'parentSuite'>, value='Suite1'),
Label(name=<LabelType.SUITE: 'suite'>, value='Suite2'),
Label(name=<LabelType.SUB_SUITE: 'subSuite'>, value='Suite3')]
"""
labels = []
suites = longname.split('.')
Expand Down