Skip to content
Closed
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
32 changes: 32 additions & 0 deletions allure-pytest-bdd/src/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest
import allure_commons
from .utils import ALLURE_DESCRIPTION_MARK, ALLURE_DESCRIPTION_HTML_MARK
from .utils import ALLURE_LABEL_MARK, ALLURE_LINK_MARK
from .utils import format_allure_link


class AllureTestHelper:
def __init__(self, config):
self.config = config

@allure_commons.hookimpl
def decorate_as_description(self, test_description):
allure_description = getattr(pytest.mark, ALLURE_DESCRIPTION_MARK)
return allure_description(test_description)

@allure_commons.hookimpl
def decorate_as_description_html(self, test_description_html):
allure_description_html = getattr(pytest.mark, ALLURE_DESCRIPTION_HTML_MARK)
return allure_description_html(test_description_html)

@allure_commons.hookimpl
def decorate_as_label(self, label_type, labels):
allure_label = getattr(pytest.mark, ALLURE_LABEL_MARK)
return allure_label(*labels, label_type=label_type)

@allure_commons.hookimpl
def decorate_as_link(self, url, link_type, name):
url = format_allure_link(self.config, url, link_type)
allure_link = getattr(pytest.mark, ALLURE_LINK_MARK)
name = url if name is None else name
return allure_link(url, name=name, link_type=link_type)
31 changes: 30 additions & 1 deletion allure-pytest-bdd/src/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import allure_commons
import argparse
import os

import allure_commons
from allure_commons.logger import AllureFileLogger

from .helper import AllureTestHelper
from .pytest_bdd_listener import PytestBDDListener


Expand All @@ -17,11 +21,32 @@ def pytest_addoption(parser):
dest="clean_alluredir",
help="Clean alluredir folder if it exists")

parser.getgroup("general").addoption('--allure-link-pattern',
action="append",
dest="allure_link_pattern",
metavar="LINK_TYPE:LINK_PATTERN",
default=[],
type=link_pattern,
help="""Url pattern for link type. Allows short links in test,
like 'issue-1'. Text will be formatted to full url with python
str.format().""")


def link_pattern(string):
pattern = string.split(':', 1)
if not pattern[0]:
raise argparse.ArgumentTypeError('Link type is mandatory.')

if len(pattern) != 2:
raise argparse.ArgumentTypeError('Link pattern is mandatory')
return pattern


def cleanup_factory(plugin):
def clean_up():
name = allure_commons.plugin_manager.get_name(plugin)
allure_commons.plugin_manager.unregister(name=name)

return clean_up


Expand All @@ -32,6 +57,10 @@ def pytest_configure(config):
if report_dir:
report_dir = os.path.abspath(report_dir)

test_helper = AllureTestHelper(config)
allure_commons.plugin_manager.register(test_helper)
config.add_cleanup(cleanup_factory(test_helper))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, register custom marks like allure-pytest does it here

pytest_bdd_listener = PytestBDDListener()
config.pluginmanager.register(pytest_bdd_listener)
allure_commons.plugin_manager.register(pytest_bdd_listener)
Expand Down
43 changes: 30 additions & 13 deletions allure-pytest-bdd/src/pytest_bdd_listener.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import pytest
from functools import partial

import allure_commons
from allure_commons.utils import now
from allure_commons.utils import uuid4
import pytest
from allure_commons.lifecycle import AllureLifecycle
from allure_commons.model2 import Label
from allure_commons.model2 import Status

from allure_commons.model2 import StatusDetails
from allure_commons.model2 import Link
from allure_commons.types import LabelType, AttachmentType
from allure_commons.utils import platform_label
from allure_commons.utils import host_tag, thread_tag
from allure_commons.utils import md5
from .utils import get_uuid
from .utils import get_step_name
from .utils import get_status_details
from .utils import get_pytest_report_status
from allure_commons.model2 import StatusDetails
from functools import partial
from allure_commons.lifecycle import AllureLifecycle
from .utils import get_full_name, get_name, get_params
from allure_commons.utils import now
from allure_commons.utils import platform_label
from allure_commons.utils import uuid4

from .utils import (
get_uuid,
get_step_name,
get_status_details,
get_pytest_report_status,
get_full_name,
get_name,
get_params,
allure_labels,
pytest_markers,
allure_description,
allure_links,
)


class PytestBDDListener:
Expand All @@ -43,11 +53,18 @@ def pytest_bdd_before_scenario(self, request, feature, scenario):
test_result.name = name
test_result.start = now()
test_result.historyId = md5(request.node.nodeid)
test_result.labels.extend([Label(name=name, value=value) for name, value in allure_labels(request.node)])
test_result.labels.extend(
[Label(name=LabelType.TAG, value=value) for value in pytest_markers(request.node)])
test_result.labels.append(Label(name=LabelType.HOST, value=self.host))
test_result.labels.append(Label(name=LabelType.THREAD, value=self.thread))
test_result.labels.append(Label(name=LabelType.FRAMEWORK, value="pytest-bdd"))
test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label()))
test_result.labels.append(Label(name=LabelType.FEATURE, value=feature.name))
test_result.description = allure_description(request.node)
test_result.links.extend(
[Link(link_type, url, name) for link_type, url, name in allure_links(request.node)]
)
test_result.parameters = get_params(request.node)

finalizer = partial(self._scenario_finalizer, scenario)
Expand Down
85 changes: 82 additions & 3 deletions allure-pytest-bdd/src/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import os
from uuid import UUID
from allure_commons.utils import md5
from allure_commons.model2 import StatusDetails
from allure_commons.model2 import Status

from allure_commons.model2 import Parameter
from allure_commons.model2 import Status
from allure_commons.model2 import StatusDetails
from allure_commons.types import LabelType
from allure_commons.utils import format_exception
from allure_commons.utils import md5
from allure_commons.utils import represent

ALLURE_DESCRIPTION_MARK = 'allure_description'
ALLURE_DESCRIPTION_HTML_MARK = 'allure_description_html'
ALLURE_LABEL_MARK = 'allure_label'
ALLURE_LINK_MARK = 'allure_link'
ALLURE_UNIQUE_LABELS = [
LabelType.SEVERITY,
LabelType.FRAMEWORK,
LabelType.HOST,
LabelType.SUITE,
LabelType.PARENT_SUITE,
LabelType.SUB_SUITE
]


def get_step_name(step):
Expand Down Expand Up @@ -48,3 +64,66 @@ def get_params(node):
outline_params = params.pop('_pytest_bdd_example', {})
params.update(outline_params)
return [Parameter(name=name, value=value) for name, value in params.items()]


def format_allure_link(config, url, link_type):
pattern = dict(config.option.allure_link_pattern).get(link_type, '{}')
return pattern.format(url)


def allure_labels(item):
unique_labels = dict()
labels = set()
for mark in item.iter_markers(name=ALLURE_LABEL_MARK):
label_type = mark.kwargs["label_type"]
if label_type in ALLURE_UNIQUE_LABELS:
if label_type not in unique_labels.keys():
unique_labels[label_type] = mark.args[0]
else:
for arg in mark.args:
labels.add((label_type, arg))
for k, v in unique_labels.items():
labels.add((k, v))
return labels


def get_marker_value(item, keyword):
marker = item.get_closest_marker(keyword)
return marker.args[0] if marker and marker.args else None


def allure_description(item):
description = get_marker_value(item, ALLURE_DESCRIPTION_MARK)
if description:
return description
elif hasattr(item, 'function'):
return item.function.__doc__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a test on converting the function's doc into the Allure description?



def mark_to_str(marker):
args = [represent(arg) for arg in marker.args]
kwargs = ['{name}={value}'.format(name=key, value=represent(marker.kwargs[key])) for key in marker.kwargs]
if marker.name in ('filterwarnings', 'skip', 'skipif', 'xfail', 'usefixtures', 'tryfirst', 'trylast'):
markstr = '@pytest.mark.{name}'.format(name=marker.name)
else:
markstr = '{name}'.format(name=marker.name)
if args or kwargs:
parameters = ', '.join(args + kwargs)
markstr = '{}({})'.format(markstr, parameters)
return markstr


def pytest_markers(item):
for keyword in item.keywords.keys():
if any([keyword.startswith('allure_'), keyword == 'parametrize']):
continue
marker = item.get_closest_marker(keyword)
if marker is None:
continue

yield mark_to_str(marker)


def allure_links(item):
for mark in item.iter_markers(name=ALLURE_LINK_MARK):
yield mark.kwargs["link_type"], mark.args[0], mark.kwargs["name"]
Loading