Skip to content

Commit e8396c5

Browse files
authored
parse tags to allure items [behave] (partial allure-framework#382 via allure-framework#428)
1 parent b76b4ab commit e8396c5

File tree

7 files changed

+197
-11
lines changed

7 files changed

+197
-11
lines changed

allure-behave/features/label.feature

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Feature: Label
2+
3+
Scenario: Scenario label
4+
Given feature definition
5+
"""
6+
Feature: Step status
7+
8+
@allure.label.owner:me
9+
Scenario: Scenario with passed step
10+
Given simple passed step
11+
"""
12+
When I run behave with allure formatter
13+
Then allure report has a scenario with name "Scenario with passed step"
14+
And scenario has "owner" label with value "me"

allure-behave/features/link.feature

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Feature: Link
2+
3+
Scenario: Scenario issue link
4+
Given feature definition
5+
"""
6+
Feature: Step status
7+
8+
@allure.issue:http://qameta.io
9+
Scenario: Scenario with passed step
10+
Given simple passed step
11+
"""
12+
When I run behave with allure formatter
13+
Then allure report has a scenario with name "Scenario with passed step"
14+
And scenario has "http://qameta.io" link with type "issue"
15+
16+
Scenario: Feature user link
17+
Given feature definition
18+
"""
19+
@allure.link.homepage:http://qameta.io
20+
Feature: Step status
21+
22+
Scenario: Scenario with passed step
23+
Given simple passed step
24+
"""
25+
When I run behave with allure formatter
26+
Then allure report has a scenario with name "Scenario with passed step"
27+
And scenario has "http://qameta.io" link
28+
29+
30+
Scenario: Feature and scenario user link
31+
Given feature definition
32+
"""
33+
@allure.link.homepage:http://qameta.io
34+
Feature: Step status
35+
36+
@allure.issue:http://example.com
37+
Scenario: Scenario with passed step
38+
Given simple passed step
39+
"""
40+
When I run behave with allure formatter
41+
Then allure report has a scenario with name "Scenario with passed step"
42+
And scenario has "http://qameta.io" link
43+
And scenario has "http://example.com" link with type "issue"

allure-behave/features/steps/report_steps.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
from allure_commons_test.result import has_parameter
99
from allure_commons_test.result import has_status_details
1010
from allure_commons_test.result import with_message_contains
11+
from allure_commons_test.result import has_link
1112
from allure_commons_test.container import has_container
1213
from allure_commons_test.container import has_before, has_after
1314
from allure_commons_test.label import has_severity
1415
from allure_commons_test.label import has_tag
16+
from allure_commons_test.label import has_label
1517

1618

1719
def match(matcher, *args):
@@ -113,6 +115,26 @@ def step_tag(context, tag):
113115
assert_that(context.allure_report, matcher())
114116

115117

118+
@then(u'scenario has "{url}" link')
119+
@then(u'this scenario has "{url}" link')
120+
@then(u'scenario has "{url}" link with type "{link_type}"')
121+
@then(u'this scenario has "{url}" link with type "{link_type}"')
122+
@then(u'scenario has "{url}" link with type "{link_type}" and name "{name}"')
123+
@then(u'this scenario has "{url}" link with type "{link_type}" and name "{name}"')
124+
def step_link(context, url, link_type=None, name=None,):
125+
context_matcher = context.scenario
126+
matcher = partial(context_matcher, has_link, url, link_type, name)
127+
assert_that(context.allure_report, matcher())
128+
129+
130+
@then(u'scenario has "{name}" label with value "{value}"')
131+
@then(u'this scenario has "{name}" label with value "{value}"')
132+
def step_label(context, name, value):
133+
context_matcher = context.scenario
134+
matcher = partial(context_matcher, has_label, name, value)
135+
assert_that(context.allure_report, matcher())
136+
137+
116138
@then(u'{item} has parameter "{name}" with value "{value}"')
117139
@then(u'this {item} has parameter "{name}" with value "{value}"')
118140
def step_parameter(context, item, name, value):

allure-behave/src/listener.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
from allure_commons.model2 import TestResultContainer
1414
from allure_commons.model2 import Parameter, Label
1515
from allure_behave.utils import scenario_parameters
16-
from allure_behave.utils import scenario_severity
17-
from allure_behave.utils import scenario_tags
1816
from allure_behave.utils import scenario_name
1917
from allure_behave.utils import scenario_history_id
2018
from allure_behave.utils import step_status, step_status_details
2119
from allure_behave.utils import scenario_status, scenario_status_details
2220
from allure_behave.utils import step_table
2321
from allure_behave.utils import get_status, get_status_details
22+
from allure_behave.utils import scenario_links
23+
from allure_behave.utils import scenario_labels
2424

2525

2626
BEFORE_FIXTURES = ['before_all', 'before_tag', 'before_feature', 'before_scenario']
@@ -96,8 +96,9 @@ def start_scenario(self, scenario):
9696
test_case.historyId = scenario_history_id(scenario)
9797
test_case.description = '\n'.join(scenario.description)
9898
test_case.parameters = scenario_parameters(scenario)
99-
test_case.labels.extend([Label(name=LabelType.TAG, value=tag) for tag in scenario_tags(scenario)])
100-
test_case.labels.append(Label(name=LabelType.SEVERITY, value=scenario_severity(scenario).value))
99+
100+
test_case.links.extend(scenario_links(scenario))
101+
test_case.labels.extend(scenario_labels(scenario))
101102
test_case.labels.append(Label(name=LabelType.FEATURE, value=scenario.feature.name))
102103
test_case.labels.append(Label(name=LabelType.FRAMEWORK, value='behave'))
103104
test_case.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label()))

allure-behave/src/utils.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33

44
from enum import Enum
55
from behave.runner_util import make_undefined_step_snippet
6-
from allure_commons.types import Severity
6+
from allure_commons.types import Severity, LabelType
77
from allure_commons.model2 import Status, Parameter
8+
from allure_commons.model2 import Link, Label
89
from allure_commons.model2 import StatusDetails
910
from allure_commons.utils import md5
1011
from allure_commons.utils import format_exception, format_traceback
12+
from allure_commons.mapping import parse_tag, labels_set
13+
1114

1215
STATUS = {
1316
'passed': Status.PASSED,
@@ -35,16 +38,17 @@ def scenario_parameters(scenario):
3538
return [Parameter(name=name, value=value) for name, value in zip(row.headings, row.cells)] if row else None
3639

3740

38-
def scenario_severity(scenario):
41+
def scenario_links(scenario):
3942
tags = scenario.feature.tags + scenario.tags
40-
severities = list(filter(lambda tag: tag in [severity.value for severity in Severity], tags))
41-
return Severity(severities[-1]) if severities else Severity.NORMAL
43+
parsed = [parse_tag(item) for item in tags]
44+
return filter(lambda x: isinstance(x, Link), parsed)
4245

4346

44-
def scenario_tags(scenario):
47+
def scenario_labels(scenario):
4548
tags = scenario.feature.tags + scenario.tags
46-
tags = list(filter(lambda tag: tag not in [severity.value for severity in Severity], tags))
47-
return set(tags) if tags else []
49+
default_labels = [Label(name=LabelType.SEVERITY, value=Severity.NORMAL)]
50+
parsed = [parse_tag(item) for item in tags]
51+
return labels_set(list(filter(lambda x: isinstance(x, Label), default_labels + parsed)))
4852

4953

5054
def scenario_status(scenario):

allure-python-commons/src/mapping.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from itertools import chain, islice
2+
import attr
3+
4+
from allure_commons.types import Severity, LabelType, LinkType
5+
from allure_commons.types import ALLURE_UNIQUE_LABELS
6+
from allure_commons.model2 import Label, Link
7+
8+
9+
TAG_PREFIX = "allure"
10+
11+
12+
def __is(kind, t):
13+
return kind in [v for k, v in t.__dict__.items() if not k.startswith('__')]
14+
15+
16+
def parse_tag(tag):
17+
"""
18+
>>> parse_tag("blocker")
19+
Label(name='severity', value='blocker')
20+
21+
>>> parse_tag("allure.issue:http://example.com/BUG-42")
22+
Link(type='issue', url='http://example.com/BUG-42', name='http://example.com/BUG-42')
23+
24+
>>> parse_tag("allure.link.home:http://qameta.io")
25+
Link(type='link', url='http://qameta.io', name='home')
26+
27+
>>> parse_tag("allure.suite:mapping")
28+
Label(name='suite', value='mapping')
29+
30+
>>> parse_tag("allure.suite:mapping")
31+
Label(name='suite', value='mapping')
32+
33+
>>> parse_tag("allure.label.owner:me")
34+
Label(name='owner', value='me')
35+
36+
>>> parse_tag("foo.label:1")
37+
Label(name='tag', value='foo.label:1')
38+
39+
>>> parse_tag("allure.foo:1")
40+
Label(name='tag', value='allure.foo:1')
41+
"""
42+
schema, value = islice(chain(tag.split(':', 1), [None]), 2)
43+
prefix, kind, name = islice(chain(schema.split('.'), [None], [None]), 3)
44+
45+
if tag in [severity for severity in Severity]:
46+
return Label(name=LabelType.SEVERITY, value=tag)
47+
48+
if prefix == TAG_PREFIX and value is not None:
49+
50+
if __is(kind, LinkType):
51+
return Link(type=kind, name=name or value, url=value)
52+
53+
if __is(kind, LabelType):
54+
return Label(name=kind, value=value)
55+
56+
if kind == "label" and name is not None:
57+
return Label(name=name, value=value)
58+
59+
return Label(name=LabelType.TAG, value=tag)
60+
61+
62+
def labels_set(labels):
63+
"""
64+
>>> labels_set([Label(name=LabelType.SEVERITY, value=Severity.NORMAL),
65+
... Label(name=LabelType.SEVERITY, value=Severity.BLOCKER)
66+
... ])
67+
[Label(name='severity', value=<Severity.BLOCKER: 'blocker'>)]
68+
69+
>>> labels_set([Label(name=LabelType.SEVERITY, value=Severity.NORMAL),
70+
... Label(name='severity', value='minor')
71+
... ])
72+
[Label(name='severity', value='minor')]
73+
74+
>>> labels_set([Label(name=LabelType.EPIC, value="Epic"),
75+
... Label(name=LabelType.EPIC, value="Epic")
76+
... ])
77+
[Label(name='epic', value='Epic')]
78+
79+
>>> labels_set([Label(name=LabelType.EPIC, value="Epic1"),
80+
... Label(name=LabelType.EPIC, value="Epic2")
81+
... ])
82+
[Label(name='epic', value='Epic1'), Label(name='epic', value='Epic2')]
83+
"""
84+
class Wl(object):
85+
def __init__(self, label):
86+
self.label = label
87+
88+
def __repr__(self):
89+
return "{name}{value}".format(**attr.asdict(self.label))
90+
91+
def __eq__(self, other):
92+
if self.label.name in ALLURE_UNIQUE_LABELS:
93+
return self.label.name == other.label.name
94+
return repr(self) == repr(other)
95+
96+
def __hash__(self):
97+
if self.label.name in ALLURE_UNIQUE_LABELS:
98+
return hash(self.label.name)
99+
return hash(repr(self))
100+
101+
return sorted([wl.label for wl in set([Wl(l) for l in reversed(labels)])])

allure-python-commons/tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ deps=
1414
commands=
1515
python setup.py develop
1616
python -m doctest ./src/utils.py
17+
python -m doctest ./src/mapping.py
1718

1819

1920
[testenv:static_check]

0 commit comments

Comments
 (0)