Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support allure decorators (not all) for allure-pytest-bdd (fixes #726) #818

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
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:
delatrie marked this conversation as resolved.
Show resolved Hide resolved
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'
delatrie marked this conversation as resolved.
Show resolved Hide resolved
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