class AllureListener(object): def __init__(self, config): self.config = config self.allure_logger = AllureReporter() self._cache = ItemCache() self._host = host_tag() self._thread = thread_tag() @allure_commons.hookimpl def start_step(self, uuid, title, params): parameters = [Parameter(name=name, value=value) for name, value in params.items()] step = TestStepResult(name=title, start=now(), parameters=parameters) self.allure_logger.start_step(None, uuid, step) @allure_commons.hookimpl def stop_step(self, uuid, exc_type, exc_val, exc_tb): self.allure_logger.stop_step(uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details(exc_type, exc_val, exc_tb)) @allure_commons.hookimpl def start_fixture(self, parent_uuid, uuid, name): after_fixture = TestAfterResult(name=name, start=now()) self.allure_logger.start_after_fixture(parent_uuid, uuid, after_fixture) @allure_commons.hookimpl def stop_fixture(self, parent_uuid, uuid, name, exc_type, exc_val, exc_tb): self.allure_logger.stop_after_fixture(uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details(exc_type, exc_val, exc_tb)) @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(self, item, nextitem): uuid = self._cache.push(item.nodeid) test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now()) self.allure_logger.schedule_test(uuid, test_result) yield @pytest.hookimpl(hookwrapper=True) def pytest_runtest_setup(self, item): if not self._cache.get(item.nodeid): uuid = self._cache.push(item.nodeid) test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now()) self.allure_logger.schedule_test(uuid, test_result) yield uuid = self._cache.get(item.nodeid) test_result = self.allure_logger.get_test(uuid) for fixturedef in _test_fixtures(item): group_uuid = self._cache.get(fixturedef) if not group_uuid: group_uuid = self._cache.push(fixturedef) group = TestResultContainer(uuid=group_uuid) self.allure_logger.start_group(group_uuid, group) self.allure_logger.update_group(group_uuid, children=uuid) params = item.callspec.params if hasattr(item, 'callspec') else {} test_result.name = allure_name(item, params) full_name = allure_full_name(item) test_result.fullName = full_name test_result.historyId = md5(item.nodeid) test_result.testCaseId = md5(full_name) test_result.description = allure_description(item) test_result.descriptionHtml = allure_description_html(item) test_result.parameters.extend( [Parameter(name=name, value=represent(value)) for name, value in params.items()]) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): uuid = self._cache.get(item.nodeid) test_result = self.allure_logger.get_test(uuid) if test_result: self.allure_logger.drop_test(uuid) self.allure_logger.schedule_test(uuid, test_result) test_result.start = now() yield if test_result: test_result.stop = now() @pytest.hookimpl(hookwrapper=True) def pytest_runtest_teardown(self, item): yield uuid = self._cache.get(item.nodeid) test_result = self.allure_logger.get_test(uuid) test_result.labels.extend([Label(name=name, value=value) for name, value in allure_labels(item)]) test_result.labels.extend([Label(name=LabelType.TAG, value=value) for value in pytest_markers(item)]) test_result.labels.extend([Label(name=name, value=value) for name, value in allure_suite_labels(item)]) 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')) test_result.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label())) test_result.labels.append(Label(name='package', value=allure_package(item))) test_result.links.extend([Link(link_type, url, name) for link_type, url, name in allure_links(item)]) @pytest.hookimpl(hookwrapper=True) def pytest_fixture_setup(self, fixturedef, request): fixture_name = getattr(fixturedef.func, '__allure_display_name__', fixturedef.argname) container_uuid = self._cache.get(fixturedef) if not container_uuid: container_uuid = self._cache.push(fixturedef) container = TestResultContainer(uuid=container_uuid) self.allure_logger.start_group(container_uuid, container) self.allure_logger.update_group(container_uuid, start=now()) before_fixture_uuid = uuid4() before_fixture = TestBeforeResult(name=fixture_name, start=now()) self.allure_logger.start_before_fixture(container_uuid, before_fixture_uuid, before_fixture) outcome = yield self.allure_logger.stop_before_fixture(before_fixture_uuid, stop=now(), status=get_outcome_status(outcome), statusDetails=get_outcome_status_details(outcome)) finalizers = getattr(fixturedef, '_finalizers', []) for index, finalizer in enumerate(finalizers): name = '{fixture}::{finalizer}'.format(fixture=fixture_name, finalizer=getattr(finalizer, "__name__", index)) finalizers[index] = allure_commons.fixture(finalizer, parent_uuid=container_uuid, name=name) @pytest.hookimpl(hookwrapper=True) def pytest_fixture_post_finalizer(self, fixturedef): yield if hasattr(fixturedef, 'cached_result') and self._cache.get(fixturedef): container_uuid = self._cache.pop(fixturedef) self.allure_logger.stop_group(container_uuid, stop=now()) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(self, item, call): uuid = self._cache.get(item.nodeid) report = (yield).get_result() test_result = self.allure_logger.get_test(uuid) status = get_pytest_report_status(report) status_details = None if call.excinfo: message = escape_non_unicode_symbols(call.excinfo.exconly()) if hasattr(report, 'wasxfail'): reason = report.wasxfail message = ('XFAIL {}'.format(reason) if reason else 'XFAIL') + '\n\n' + message trace = escape_non_unicode_symbols(report.longreprtext) status_details = StatusDetails( message=message, trace=trace) if (status != Status.SKIPPED and not (call.excinfo.errisinstance(AssertionError) or call.excinfo.errisinstance(pytest.fail.Exception))): status = Status.BROKEN if status == Status.PASSED and hasattr(report, 'wasxfail'): reason = report.wasxfail message = 'XPASS {reason}'.format(reason=reason) if reason else 'XPASS' status_details = StatusDetails(message=message) if report.when == 'setup': test_result.status = status test_result.statusDetails = status_details if report.when == 'call': if test_result.status == Status.PASSED: test_result.status = status test_result.statusDetails = status_details if report.when == 'teardown': if status in (Status.FAILED, Status.BROKEN) and test_result.status == Status.PASSED: test_result.status = status test_result.statusDetails = status_details if self.config.option.attach_capture: if report.caplog: self.attach_data(report.caplog, "log", AttachmentType.TEXT, None) if report.capstdout: self.attach_data(report.capstdout, "stdout", AttachmentType.TEXT, None) if report.capstderr: self.attach_data(report.capstderr, "stderr", AttachmentType.TEXT, None) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_logfinish(self, nodeid, location): yield uuid = self._cache.pop(nodeid) if uuid: self.allure_logger.close_test(uuid) @allure_commons.hookimpl def attach_data(self, body, name, attachment_type, extension): self.allure_logger.attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def attach_file(self, source, name, attachment_type, extension): self.allure_logger.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def add_title(self, test_title): test_result = self.allure_logger.get_test(None) if test_result: test_result.name = test_title @allure_commons.hookimpl def add_description(self, test_description): test_result = self.allure_logger.get_test(None) if test_result: test_result.description = test_description @allure_commons.hookimpl def add_description_html(self, test_description_html): test_result = self.allure_logger.get_test(None) if test_result: test_result.descriptionHtml = test_description_html @allure_commons.hookimpl def add_link(self, url, link_type, name): test_result = self.allure_logger.get_test(None) if test_result: pattern = dict(self.config.option.allure_link_pattern).get(link_type, u'{}') link_url = pattern.format(url) new_link = Link(link_type, link_url, link_url if name is None else name) for link in test_result.links: if link.url == new_link.url: return test_result.links.append(new_link) @allure_commons.hookimpl def add_label(self, label_type, labels): test_result = self.allure_logger.get_test(None) for label in labels if test_result else (): test_result.labels.append(Label(label_type, label))
class AllureListener(object): def __init__(self, behave_config): self.behave_config = behave_config self.logger = AllureReporter() self.current_step_uuid = None self.current_scenario_uuid = None self.execution_context = Context() self.fixture_context = Context() self.steps = deque() def __del__(self): for group in self.fixture_context.exit(): group.children.extend(self.execution_context) self.logger.stop_group(group.uuid) @allure_commons.hookimpl def start_fixture(self, parent_uuid, uuid, name, parameters): parameters = [Parameter(name=param_name, value=param_value) for param_name, param_value in parameters.items()] if name in FIXTURES and not self.fixture_context: group = TestResultContainer(uuid=uuid4()) self.logger.start_group(group.uuid, group) self.fixture_context.append(group) if name in BEFORE_FIXTURES: fixture = TestBeforeResult(name=name, start=now(), parameters=parameters) for group in self.fixture_context: self.logger.start_before_fixture(group.uuid, uuid, fixture) elif name in AFTER_FIXTURES: fixture = TestAfterResult(name=name, start=now(), parameters=parameters) for group in self.fixture_context: self.logger.start_after_fixture(group.uuid, uuid, fixture) @allure_commons.hookimpl def stop_fixture(self, parent_uuid, uuid, name, exc_type, exc_val, exc_tb): if name in FIXTURES: self.logger.stop_before_fixture(uuid=uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details(exc_type, exc_val, exc_tb)) def start_feature(self): self.execution_context.enter() self.fixture_context.enter() def stop_feature(self): uuids = self.execution_context.exit() for group in self.fixture_context.exit(): group.children.extend(uuids) self.logger.stop_group(group.uuid) self.execution_context.extend(uuids) @allure_commons.hookimpl def start_test(self, parent_uuid, uuid, name, parameters, context): self.start_scenario(context['scenario']) def start_scenario(self, scenario): self.current_scenario_uuid = uuid4() self.fixture_context.enter() self.execution_context.enter() self.execution_context.append(self.current_scenario_uuid) test_case = TestResult(uuid=self.current_scenario_uuid, start=now()) test_case.name = scenario_name(scenario) test_case.historyId = scenario_history_id(scenario) test_case.description = '\n'.join(scenario.description) test_case.parameters = scenario_parameters(scenario) test_case.labels.extend([Label(name=LabelType.TAG, value=tag) for tag in scenario_tags(scenario)]) test_case.labels.append(Label(name=LabelType.SEVERITY, value=scenario_severity(scenario).value)) test_case.labels.append(Label(name=LabelType.FEATURE, value=scenario.feature.name)) test_case.labels.append(Label(name=LabelType.FRAMEWORK, value='behave')) test_case.labels.append(Label(name=LabelType.LANGUAGE, value=platform_label())) self.logger.schedule_test(self.current_scenario_uuid, test_case) @allure_commons.hookimpl def stop_test(self, parent_uuid, uuid, name, context, exc_type, exc_val, exc_tb): self.stop_scenario(context['scenario']) def stop_scenario(self, scenario): if scenario.status == 'skipped' and not self.behave_config.show_skipped: self.logger.drop_test(self.current_scenario_uuid) else: status = scenario_status(scenario) status_details = scenario_status_details(scenario) self.flush_steps() test_result = self.logger.get_test(self.current_scenario_uuid) test_result.stop = now() test_result.status = status test_result.statusDetails = status_details self.logger.close_test(self.current_scenario_uuid) self.current_step_uuid = None for group in self.fixture_context.exit(): group.children.append(self.current_scenario_uuid) self.logger.stop_group(group.uuid) self.execution_context.exit() self.execution_context.append(self.current_scenario_uuid) self.current_scenario_uuid = None def schedule_step(self, step): self.steps.append(step) def match_step(self, match): step = self.steps.popleft() self.start_behave_step(step) def start_behave_step(self, step): self.current_step_uuid = uuid4() name = u'{keyword} {title}'.format(keyword=step.keyword, title=step.name) allure_step = TestStepResult(name=name, start=now()) self.logger.start_step(None, self.current_step_uuid, allure_step) if step.text: self.logger.attach_data(uuid4(), step.text, name='.text', attachment_type=AttachmentType.TEXT) if step.table: self.logger.attach_data(uuid4(), step_table(step), name='.table', attachment_type=AttachmentType.CSV) def stop_behave_step(self, result): status = step_status(result) status_details = step_status_details(result) self.logger.stop_step(self.current_step_uuid, stop=now(), status=status, statusDetails=status_details) def flush_steps(self): while self.steps: step = self.steps.popleft() self.start_behave_step(step) self.stop_behave_step(step) @allure_commons.hookimpl def start_step(self, uuid, title, params): parameters = [Parameter(name=name, value=value) for name, value in params.items()] step = TestStepResult(name=title, start=now(), parameters=parameters) self.logger.start_step(None, uuid, step) @allure_commons.hookimpl def stop_step(self, uuid, exc_type, exc_val, exc_tb): self.logger.stop_step(uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details(exc_type, exc_val, exc_tb)) @allure_commons.hookimpl def attach_data(self, body, name, attachment_type, extension): self.logger.attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def attach_file(self, source, name, attachment_type, extension): self.logger.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension)
class AllureListener(object): def __init__(self, behave_config): self.behave_config = behave_config self.logger = AllureReporter() self.current_step_uuid = None self.current_scenario_uuid = None self.execution_context = Context() self.fixture_context = Context() self.steps = deque() def __del__(self): for group in self.fixture_context.exit(): group.children.extend(self.execution_context) self.logger.stop_group(group.uuid) @allure_commons.hookimpl def start_fixture(self, parent_uuid, uuid, name, parameters): parameters = [ Parameter(name=param_name, value=param_value) for param_name, param_value in parameters.items() ] if name in FIXTURES and not self.fixture_context: group = TestResultContainer(uuid=uuid4()) self.logger.start_group(group.uuid, group) self.fixture_context.append(group) if name in BEFORE_FIXTURES: fixture = TestBeforeResult(name=name, start=now(), parameters=parameters) for group in self.fixture_context: self.logger.start_before_fixture(group.uuid, uuid, fixture) elif name in AFTER_FIXTURES: fixture = TestAfterResult(name=name, start=now(), parameters=parameters) for group in self.fixture_context: self.logger.start_after_fixture(group.uuid, uuid, fixture) @allure_commons.hookimpl def stop_fixture(self, parent_uuid, uuid, name, exc_type, exc_val, exc_tb): if name in FIXTURES: self.logger.stop_before_fixture(uuid=uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details( exc_type, exc_val, exc_tb)) def start_feature(self): self.execution_context.enter() self.fixture_context.enter() def stop_feature(self): uuids = self.execution_context.exit() for group in self.fixture_context.exit(): group.children.extend(uuids) self.logger.stop_group(group.uuid) self.execution_context.extend(uuids) @allure_commons.hookimpl def start_test(self, parent_uuid, uuid, name, parameters, context): self.start_scenario(context['scenario']) def start_scenario(self, scenario): self.current_scenario_uuid = uuid4() self.fixture_context.enter() self.execution_context.enter() self.execution_context.append(self.current_scenario_uuid) test_case = TestResult(uuid=self.current_scenario_uuid, start=now()) test_case.name = scenario_name(scenario) test_case.historyId = scenario_history_id(scenario) test_case.description = '\n'.join(scenario.description) test_case.parameters = scenario_parameters(scenario) test_case.links.extend(scenario_links(scenario)) test_case.labels.extend(scenario_labels(scenario)) test_case.labels.append( Label(name=LabelType.FEATURE, value=scenario.feature.name)) test_case.labels.append(Label(name=LabelType.FRAMEWORK, value='behave')) test_case.labels.append( Label(name=LabelType.LANGUAGE, value=platform_label())) self.logger.schedule_test(self.current_scenario_uuid, test_case) @allure_commons.hookimpl def stop_test(self, parent_uuid, uuid, name, context, exc_type, exc_val, exc_tb): self.stop_scenario(context['scenario']) def stop_scenario(self, scenario): if scenario.status == 'skipped' and not self.behave_config.show_skipped: self.logger.drop_test(self.current_scenario_uuid) else: status = scenario_status(scenario) status_details = scenario_status_details(scenario) self.flush_steps() test_result = self.logger.get_test(self.current_scenario_uuid) test_result.stop = now() test_result.status = status test_result.statusDetails = status_details self.logger.close_test(self.current_scenario_uuid) self.current_step_uuid = None for group in self.fixture_context.exit(): group.children.append(self.current_scenario_uuid) self.logger.stop_group(group.uuid) self.execution_context.exit() self.execution_context.append(self.current_scenario_uuid) self.current_scenario_uuid = None def schedule_step(self, step): self.steps.append(step) def match_step(self, match): step = self.steps.popleft() self.start_behave_step(step) def start_behave_step(self, step): self.current_step_uuid = uuid4() name = u'{keyword} {title}'.format(keyword=step.keyword, title=step.name) allure_step = TestStepResult(name=name, start=now()) self.logger.start_step(None, self.current_step_uuid, allure_step) if step.text: self.logger.attach_data(uuid4(), step.text, name='.text', attachment_type=AttachmentType.TEXT) if step.table: self.logger.attach_data(uuid4(), step_table(step), name='.table', attachment_type=AttachmentType.CSV) def stop_behave_step(self, result): status = step_status(result) status_details = step_status_details(result) self.logger.stop_step(self.current_step_uuid, stop=now(), status=status, statusDetails=status_details) def flush_steps(self): while self.steps: step = self.steps.popleft() self.start_behave_step(step) self.stop_behave_step(step) @allure_commons.hookimpl def start_step(self, uuid, title, params): parameters = [ Parameter(name=name, value=value) for name, value in params.items() ] step = TestStepResult(name=title, start=now(), parameters=parameters) self.logger.start_step(None, uuid, step) @allure_commons.hookimpl def stop_step(self, uuid, exc_type, exc_val, exc_tb): self.logger.stop_step(uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details( exc_type, exc_val, exc_tb)) @allure_commons.hookimpl def attach_data(self, body, name, attachment_type, extension): self.logger.attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def attach_file(self, source, name, attachment_type, extension): self.logger.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension)
class AllureListener(object): def __init__(self, behave_config): self.behave_config = behave_config self.issue_pattern = behave_config.userdata.get( 'AllureFormatter.issue_pattern', None) self.link_pattern = behave_config.userdata.get( 'AllureFormatter.link_pattern', None) self.hide_excluded = behave_config.userdata.get( 'AllureFormatter.hide_excluded', False) self.logger = AllureReporter() self.current_step_uuid = None self.current_scenario_uuid = None self.group_context = GroupContext(self.logger) self.group_context.enter() self.steps = deque() def start_file(self): self.group_context.enter() @allure_commons.hookimpl def start_fixture(self, parent_uuid, uuid, name, parameters): # parameters = [Parameter(name=param_name, value=param_value) for param_name, param_value in parameters.items()] if name.startswith("before_"): name = get_hook_name(name, parameters) fixture = TestBeforeResult(name=name, start=now(), parameters=None) group = self.group_context.current_group() self.logger.start_before_fixture(group.uuid, uuid, fixture) elif name.startswith("after_"): name = get_hook_name(name, parameters) fixture = TestAfterResult(name=name, start=now(), parameters=None) group = self.group_context.current_group() self.logger.start_after_fixture(group.uuid, uuid, fixture) @allure_commons.hookimpl def stop_fixture(self, parent_uuid, uuid, name, exc_type, exc_val, exc_tb): self.logger.stop_before_fixture(uuid=uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details( exc_type, exc_val, exc_tb)) def stop_feature(self): self.group_context.exit() @allure_commons.hookimpl def start_test(self, parent_uuid, uuid, name, parameters, context): self.start_scenario(context['scenario']) def start_scenario(self, scenario): self.current_scenario_uuid = uuid4() self.group_context.enter() test_case = TestResult(uuid=self.current_scenario_uuid, start=now()) test_case.name = scenario_name(scenario) test_case.fullName = get_fullname(scenario) test_case.historyId = scenario_history_id(scenario) test_case.description = '\n'.join(scenario.description) test_case.parameters = scenario_parameters(scenario) test_case.links.extend( scenario_links(scenario, issue_pattern=self.issue_pattern, link_pattern=self.link_pattern)) test_case.labels.extend(scenario_labels(scenario)) test_case.labels.append( Label(name=LabelType.FEATURE, value=scenario.feature.name)) test_case.labels.append(Label(name=LabelType.FRAMEWORK, value='behave')) test_case.labels.append( Label(name=LabelType.LANGUAGE, value=platform_label())) self.logger.schedule_test(self.current_scenario_uuid, test_case) @allure_commons.hookimpl def stop_test(self, parent_uuid, uuid, name, context, exc_type, exc_val, exc_tb): self.stop_scenario(context['scenario']) def stop_scenario(self, scenario): should_run = (scenario.should_run_with_tags(self.behave_config.tags) and scenario.should_run_with_name_select( self.behave_config)) should_drop_skipped_by_option = scenario.status == 'skipped' and not self.behave_config.show_skipped should_drop_excluded = self.hide_excluded and ( scenario.skip_reason == TEST_PLAN_SKIP_REASON or not should_run) if should_drop_skipped_by_option or should_drop_excluded: self.logger.drop_test(self.current_scenario_uuid) else: status = scenario_status(scenario) status_details = scenario_status_details(scenario) self.flush_steps() test_result = self.logger.get_test(self.current_scenario_uuid) test_result.stop = now() test_result.status = status test_result.statusDetails = status_details self.logger.close_test(self.current_scenario_uuid) self.current_step_uuid = None self.group_context.append_test(self.current_scenario_uuid) self.group_context.exit() self.current_scenario_uuid = None def schedule_step(self, step): self.steps.append(step) def match_step(self, match): step = self.steps.popleft() self.start_behave_step(step) def start_behave_step(self, step): self.current_step_uuid = uuid4() name = u'{keyword} {title}'.format(keyword=step.keyword, title=step.name) allure_step = TestStepResult(name=name, start=now()) self.logger.start_step(None, self.current_step_uuid, allure_step) if step.text: self.logger.attach_data(uuid4(), step.text, name='.text', attachment_type=AttachmentType.TEXT) if step.table: self.logger.attach_data(uuid4(), step_table(step), name='.table', attachment_type=AttachmentType.CSV) def stop_behave_step(self, result): status = step_status(result) status_details = step_status_details(result) self.logger.stop_step(self.current_step_uuid, stop=now(), status=status, statusDetails=status_details) def flush_steps(self): while self.steps: step = self.steps.popleft() self.start_behave_step(step) self.stop_behave_step(step) @allure_commons.hookimpl def start_step(self, uuid, title, params): parameters = [ Parameter(name=name, value=value) for name, value in params.items() ] step = TestStepResult(name=title, start=now(), parameters=parameters) self.logger.start_step(None, uuid, step) @allure_commons.hookimpl def stop_step(self, uuid, exc_type, exc_val, exc_tb): self.logger.stop_step(uuid, stop=now(), status=get_status(exc_val), statusDetails=get_status_details( exc_type, exc_val, exc_tb)) @allure_commons.hookimpl def attach_data(self, body, name, attachment_type, extension): self.logger.attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def attach_file(self, source, name, attachment_type, extension): self.logger.attach_file(uuid4(), source, name=name, attachment_type=attachment_type, extension=extension) @allure_commons.hookimpl def add_description(self, test_description): test_result = self.logger.get_test(None) if test_result: test_result.description = test_description @allure_commons.hookimpl def add_description_html(self, test_description_html): test_result = self.logger.get_test(None) if test_result: test_result.descriptionHtml = test_description_html @allure_commons.hookimpl def add_link(self, url, link_type, name): test_result = self.logger.get_test(None) if test_result: pattern = u'{}' if link_type == LinkType.ISSUE and self.issue_pattern: pattern = self.issue_pattern elif link_type == LinkType.LINK and self.link_pattern: pattern = self.link_pattern link_url = pattern.format(url) new_link = Link(link_type, link_url, link_url if name is None else name) for link in test_result.links: if link.url == new_link.url: return test_result.links.append(new_link) def stop_session(self): self.group_context.exit()