From 5b44c78ace4c58a8055a45527d0680d34c574aa7 Mon Sep 17 00:00:00 2001 From: noronhaa Date: Mon, 11 Jun 2018 22:18:43 +0100 Subject: [PATCH 1/3] added allure-cucumber3-jvm project --- allure-cucumber3-jvm/build.gradle | 30 ++ .../cucumber3jvm/AllureCucumber3Jvm.java | 316 ++++++++++++++++++ .../cucumber3jvm/CucumberSourceUtils.java | 181 ++++++++++ .../allure/cucumber3jvm/LabelBuilder.java | 134 ++++++++ .../qameta/allure/cucumber3jvm/TagParser.java | 57 ++++ .../io/qameta/allure/cucumber3jvm/Utils.java | 31 ++ .../src/main/resources/META-INF/aop-ajc.xml | 6 + .../allure/cucumber3jvm/CucumberTest.java | 11 + .../qameta/allure/cucumber3jvm/Stepdefs.java | 138 ++++++++ .../io/qameta/allure/cucumber3jvm/Steps.java | 124 +++++++ .../src/test/resources/allure.properties | 1 + .../features/test-background.feature | 16 + .../features/test-datatables.feature | 24 ++ .../resources/features/test-examples.feature | 35 ++ .../features/test-pending-steps.feature | 15 + .../test-steps-with-attachments.feature | 28 ++ .../src/test/resources/features/test.feature | 26 ++ .../images/totally-open-source-kitten.jpeg | Bin 0 -> 37130 bytes settings.gradle | 1 + 19 files changed, 1174 insertions(+) create mode 100644 allure-cucumber3-jvm/build.gradle create mode 100644 allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/AllureCucumber3Jvm.java create mode 100644 allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/CucumberSourceUtils.java create mode 100644 allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/LabelBuilder.java create mode 100644 allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/TagParser.java create mode 100644 allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/Utils.java create mode 100644 allure-cucumber3-jvm/src/main/resources/META-INF/aop-ajc.xml create mode 100644 allure-cucumber3-jvm/src/test/java/io/qameta/allure/cucumber3jvm/CucumberTest.java create mode 100644 allure-cucumber3-jvm/src/test/java/io/qameta/allure/cucumber3jvm/Stepdefs.java create mode 100644 allure-cucumber3-jvm/src/test/java/io/qameta/allure/cucumber3jvm/Steps.java create mode 100644 allure-cucumber3-jvm/src/test/resources/allure.properties create mode 100644 allure-cucumber3-jvm/src/test/resources/features/test-background.feature create mode 100644 allure-cucumber3-jvm/src/test/resources/features/test-datatables.feature create mode 100644 allure-cucumber3-jvm/src/test/resources/features/test-examples.feature create mode 100644 allure-cucumber3-jvm/src/test/resources/features/test-pending-steps.feature create mode 100644 allure-cucumber3-jvm/src/test/resources/features/test-steps-with-attachments.feature create mode 100644 allure-cucumber3-jvm/src/test/resources/features/test.feature create mode 100644 allure-cucumber3-jvm/src/test/resources/images/totally-open-source-kitten.jpeg diff --git a/allure-cucumber3-jvm/build.gradle b/allure-cucumber3-jvm/build.gradle new file mode 100644 index 000000000..fc5b692e8 --- /dev/null +++ b/allure-cucumber3-jvm/build.gradle @@ -0,0 +1,30 @@ +description = 'Allure CucumberJVM 3.0' + +apply from: "${gradleScriptDir}/maven-publish.gradle" +apply from: "${gradleScriptDir}/bintray.gradle" +apply plugin: 'maven' + +configurations { + agent +} + +def cucumber_version = '3.0.0' + +dependencies { + agent 'org.aspectj:aspectjweaver' + + compile project(':allure-java-commons') + compile group: 'io.cucumber', name: 'cucumber-core', version: "${cucumber_version}" + compile group: 'io.cucumber', name: 'cucumber-java', version: "${cucumber_version}" + + testCompile group: 'io.cucumber', name: 'cucumber-testng', version: "${cucumber_version}" +} + +test.doFirst { + jvmArgs "-javaagent:${configurations.agent.singleFile}" +} + +test { + systemProperty 'allure.model.indentOutput', true + systemProperty 'allure.results.directory', 'build/allure-results' +} diff --git a/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/AllureCucumber3Jvm.java b/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/AllureCucumber3Jvm.java new file mode 100644 index 000000000..215cf5615 --- /dev/null +++ b/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/AllureCucumber3Jvm.java @@ -0,0 +1,316 @@ +package io.qameta.allure.cucumber3jvm; + +import cucumber.api.*; +import cucumber.api.event.EventHandler; +import cucumber.api.event.EventPublisher; +import cucumber.api.event.TestSourceRead; +import cucumber.api.event.TestCaseStarted; +import cucumber.api.event.TestCaseFinished; +import cucumber.api.event.TestStepStarted; +import cucumber.api.event.TestStepFinished; +import cucumber.api.formatter.Formatter; + +//import cucumber.runner.UnskipableStep; +import gherkin.ast.Feature; +import gherkin.ast.ScenarioDefinition; +import gherkin.ast.ScenarioOutline; +import gherkin.ast.Examples; +import gherkin.ast.TableRow; +import gherkin.pickles.PickleCell; +import gherkin.pickles.PickleRow; +import gherkin.pickles.PickleTable; +import gherkin.pickles.PickleTag; +import io.qameta.allure.Allure; +import io.qameta.allure.AllureLifecycle; +import io.qameta.allure.model.Parameter; +import io.qameta.allure.model.Status; +import io.qameta.allure.model.TestResult; +import io.qameta.allure.model.StepResult; +import io.qameta.allure.model.StatusDetails; +import io.qameta.allure.util.ResultsUtils; + +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Allure plugin for Cucumber JVM 2.0. + */ +@SuppressWarnings({ + "PMD.ExcessiveImports", + "ClassFanOutComplexity", "ClassDataAbstractionCoupling" +}) +public class AllureCucumber3Jvm implements Formatter { + + private final AllureLifecycle lifecycle; + + private final Map scenarioUuids = new HashMap<>(); + + private final CucumberSourceUtils cucumberSourceUtils = new CucumberSourceUtils(); + private Feature currentFeature; + private String currentFeatureFile; + private TestCase currentTestCase; + + private final EventHandler featureStartedHandler = this::handleFeatureStartedHandler; + private final EventHandler caseStartedHandler = this::handleTestCaseStarted; + private final EventHandler caseFinishedHandler = this::handleTestCaseFinished; + private final EventHandler stepStartedHandler = this::handleTestStepStarted; + private final EventHandler stepFinishedHandler = this::handleTestStepFinished; + + public AllureCucumber3Jvm() { + this.lifecycle = Allure.getLifecycle(); + } + + @Override + public void setEventPublisher(final EventPublisher publisher) { + publisher.registerHandlerFor(TestSourceRead.class, featureStartedHandler); + + publisher.registerHandlerFor(TestCaseStarted.class, caseStartedHandler); + publisher.registerHandlerFor(TestCaseFinished.class, caseFinishedHandler); + + publisher.registerHandlerFor(TestStepStarted.class, stepStartedHandler); + publisher.registerHandlerFor(TestStepFinished.class, stepFinishedHandler); + } + + /* + Event Handlers + */ + + private void handleFeatureStartedHandler(final TestSourceRead event) { + cucumberSourceUtils.addTestSourceReadEvent(event.uri, event); + } + + private void handleTestCaseStarted(final TestCaseStarted event) { + currentFeatureFile = event.testCase.getUri(); + currentFeature = cucumberSourceUtils.getFeature(currentFeatureFile); + + currentTestCase = event.testCase; + + final Deque tags = new LinkedList<>(); + tags.addAll(event.testCase.getTags()); + + final LabelBuilder labelBuilder = new LabelBuilder(currentFeature, event.testCase, tags); + + final TestResult result = new TestResult() + .withUuid(getTestCaseUuid(event.testCase)) + .withHistoryId(getHistoryId(event.testCase)) + .withName(event.testCase.getName()) + .withLabels(labelBuilder.getScenarioLabels()) + .withLinks(labelBuilder.getScenarioLinks()); + + final ScenarioDefinition scenarioDefinition = + cucumberSourceUtils.getScenarioDefinition(currentFeatureFile, currentTestCase.getLine()); + if (scenarioDefinition instanceof ScenarioOutline) { + result.withParameters( + getExamplesAsParameters((ScenarioOutline) scenarioDefinition) + ); + } + + if (currentFeature.getDescription() != null && !currentFeature.getDescription().isEmpty()) { + result.withDescription(currentFeature.getDescription()); + } + + lifecycle.scheduleTestCase(result); + lifecycle.startTestCase(getTestCaseUuid(event.testCase)); + } + + private void handleTestCaseFinished(final TestCaseFinished event) { + final StatusDetails statusDetails = + ResultsUtils.getStatusDetails(event.result.getError()).orElse(new StatusDetails()); + + if (statusDetails.getMessage() != null && statusDetails.getTrace() != null) { + lifecycle.updateTestCase(getTestCaseUuid(event.testCase), scenarioResult -> + scenarioResult + .withStatus(translateTestCaseStatus(event.result)) + .withStatusDetails(statusDetails)); + } else { + lifecycle.updateTestCase(getTestCaseUuid(event.testCase), scenarioResult -> + scenarioResult + .withStatus(translateTestCaseStatus(event.result))); + } + + lifecycle.stopTestCase(getTestCaseUuid(event.testCase)); + lifecycle.writeTestCase(getTestCaseUuid(event.testCase)); + } + + private void handleTestStepStarted(final TestStepStarted event) { + //TODO DEPRECIATED + if (event.testStep instanceof PickleStepTestStep) { + final PickleStepTestStep pickleStepTestStep = (PickleStepTestStep) event.testStep; + + final String stepKeyword = Optional.ofNullable( + cucumberSourceUtils.getKeywordFromSource(currentFeatureFile, pickleStepTestStep.getStepLine()) + ).orElse("UNDEFINED"); + + final StepResult stepResult = new StepResult() + .withName(String.format("%s %s", stepKeyword, pickleStepTestStep.getPickleStep().getText())) + .withStart(System.currentTimeMillis()); + + lifecycle.startStep(getTestCaseUuid(currentTestCase), getStepUuid(event.testStep), stepResult); + + //TODO DEPRECIATED + pickleStepTestStep.getStepArgument().stream() + .filter(argument -> argument instanceof PickleTable) + .findFirst() + .ifPresent(table -> createDataTableAttachment((PickleTable) table)); + } else if (event.testStep instanceof HookTestStep) { + final HookTestStep hookTestStep = (HookTestStep) event.testStep; + final StepResult stepResult = new StepResult() + //TODO DEPRECIATED + .withName(hookTestStep.getHookType().toString()) + .withStart(System.currentTimeMillis()); + + lifecycle.startStep(getTestCaseUuid(currentTestCase), getHookStepUuid(event.testStep), stepResult); + } else { + throw new IllegalStateException(); + } + } + + private void handleTestStepFinished(final TestStepFinished event) { + //TODO DEPRECIATED + if (event.testStep instanceof PickleStepTestStep) { + handlePickleStep(event); + } else if (event.testStep instanceof HookTestStep) { + handleHookStep(event); + } else { + throw new IllegalStateException(); + } + } + + /* + Utility Methods + */ + + private String getTestCaseUuid(final TestCase testCase) { + return scenarioUuids.computeIfAbsent(getHistoryId(testCase), it -> UUID.randomUUID().toString()); + } + + private String getStepUuid(final TestStep step) { + final PickleStepTestStep pickleStep = (PickleStepTestStep) step; + return currentFeature.getName() + getTestCaseUuid(currentTestCase) + //TODO DEPRECIATED + + pickleStep.getStepText() + pickleStep.getStepLine(); + } + + private String getHookStepUuid(final TestStep step) { + final HookTestStep hookTestStep = (HookTestStep) step; + return currentFeature.getName() + getTestCaseUuid(currentTestCase) + //TODO DEPRECIATED + + hookTestStep.getHookType().toString() + step.getCodeLocation(); + } + + private String getHistoryId(final TestCase testCase) { + final String testCaseLocation = testCase.getUri() + ":" + testCase.getLine(); + return Utils.md5(testCaseLocation); + } + + private Status translateTestCaseStatus(final Result testCaseResult) { + Status allureStatus; + if (testCaseResult.getStatus() == Result.Type.UNDEFINED || testCaseResult.getStatus() == Result.Type.PENDING) { + allureStatus = Status.SKIPPED; + } else { + try { + allureStatus = Status.fromValue(testCaseResult.getStatus().lowerCaseName()); + } catch (IllegalArgumentException e) { + allureStatus = Status.BROKEN; + } + } + return allureStatus; + } + + private List getExamplesAsParameters(final ScenarioOutline scenarioOutline) { + final Examples examples = scenarioOutline.getExamples().get(0); + final TableRow row = examples.getTableBody().stream() + .filter(example -> example.getLocation().getLine() == currentTestCase.getLine()) + .findFirst().get(); + return IntStream.range(0, examples.getTableHeader().getCells().size()).mapToObj(index -> { + final String name = examples.getTableHeader().getCells().get(index).getValue(); + final String value = row.getCells().get(index).getValue(); + return new Parameter().withName(name).withValue(value); + }).collect(Collectors.toList()); + } + + private void createDataTableAttachment(final PickleTable pickleTable) { + final List rows = pickleTable.getRows(); + + final StringBuilder dataTableCsv = new StringBuilder(); + if (!rows.isEmpty()) { + rows.forEach(dataTableRow -> { + dataTableCsv.append( + dataTableRow.getCells().stream() + .map(PickleCell::getValue) + .collect(Collectors.joining("\t")) + ); + dataTableCsv.append('\n'); + }); + + final String attachmentSource = lifecycle + .prepareAttachment("Data table", "text/tab-separated-values", "csv"); + lifecycle.writeAttachment(attachmentSource, + new ByteArrayInputStream(dataTableCsv.toString().getBytes(Charset.forName("UTF-8")))); + } + } + + private void handleHookStep(final TestStepFinished event) { + final String uuid = getHookStepUuid(event.testStep); + Consumer stepResult = result -> result.withStatus(translateTestCaseStatus(event.result)); + + if (!Status.PASSED.equals(translateTestCaseStatus(event.result))) { + final StatusDetails statusDetails = ResultsUtils.getStatusDetails(event.result.getError()).get(); + final HookTestStep hookTestStep = (HookTestStep) event.testStep; + if (hookTestStep.getHookType() == HookType.Before) { + final TagParser tagParser = new TagParser(currentFeature, currentTestCase); + statusDetails + .withMessage("Before is failed: " + event.result.getError().getLocalizedMessage()) + .withFlaky(tagParser.isFlaky()) + .withMuted(tagParser.isMuted()) + .withKnown(tagParser.isKnown()); + lifecycle.updateTestCase(getTestCaseUuid(currentTestCase), scenarioResult -> + scenarioResult.withStatus(Status.SKIPPED) + .withStatusDetails(statusDetails)); + } + stepResult = result -> result + .withStatus(translateTestCaseStatus(event.result)) + .withStatusDetails(statusDetails); + } + + lifecycle.updateStep(uuid, stepResult); + lifecycle.stopStep(uuid); + } + + private void handlePickleStep(final TestStepFinished event) { + final StatusDetails statusDetails; + if (event.result.getStatus() == Result.Type.UNDEFINED) { + statusDetails = + ResultsUtils.getStatusDetails(new PendingException("TODO: implement me")) + .orElse(new StatusDetails()); + lifecycle.updateTestCase(getTestCaseUuid(currentTestCase), scenarioResult -> + scenarioResult + .withStatus(translateTestCaseStatus(event.result)) + .withStatusDetails(statusDetails)); + } else { + statusDetails = + ResultsUtils.getStatusDetails(event.result.getError()) + .orElse(new StatusDetails()); + } + + final TagParser tagParser = new TagParser(currentFeature, currentTestCase); + statusDetails + .withFlaky(tagParser.isFlaky()) + .withMuted(tagParser.isMuted()) + .withKnown(tagParser.isKnown()); + + lifecycle.updateStep(getStepUuid(event.testStep), stepResult -> + stepResult.withStatus(translateTestCaseStatus(event.result))); + lifecycle.stopStep(getStepUuid(event.testStep)); + } +} diff --git a/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/CucumberSourceUtils.java b/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/CucumberSourceUtils.java new file mode 100644 index 000000000..6a2d3f8b8 --- /dev/null +++ b/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/CucumberSourceUtils.java @@ -0,0 +1,181 @@ +package io.qameta.allure.cucumber3jvm; + +import cucumber.api.event.TestSourceRead; + +import gherkin.Parser; +import gherkin.AstBuilder; +import gherkin.TokenMatcher; +import gherkin.ParserException; +import gherkin.GherkinDialect; +import gherkin.GherkinDialectProvider; + +import gherkin.ast.GherkinDocument; +import gherkin.ast.Feature; +import gherkin.ast.ScenarioDefinition; +import gherkin.ast.Node; +import gherkin.ast.ScenarioOutline; +import gherkin.ast.TableRow; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.IntStream; + +/** + * Parts of package-private cucumber.runtime.formatter.TestSourcesModel needed for Allure 2 adapter. + */ +public final class CucumberSourceUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(CucumberSourceUtils.class); + + private final Map pathToReadEventMap = new HashMap<>(); + private final Map pathToAstMap = new HashMap<>(); + private final Map> pathToNodeMap = new HashMap<>(); + + public void addTestSourceReadEvent(final String path, final TestSourceRead event) { + pathToReadEventMap.put(path, event); + } + + public Feature getFeature(final String path) { + if (!pathToAstMap.containsKey(path)) { + parseGherkinSource(path); + } + if (pathToAstMap.containsKey(path)) { + return pathToAstMap.get(path).getFeature(); + } + return null; + } + + private void parseGherkinSource(final String path) { + if (!pathToReadEventMap.containsKey(path)) { + return; + } + final Parser parser = new Parser<>(new AstBuilder()); + final TokenMatcher matcher = new TokenMatcher(); + try { + final GherkinDocument gherkinDocument = parser.parse(pathToReadEventMap.get(path).source, matcher); + pathToAstMap.put(path, gherkinDocument); + final Map nodeMap = new HashMap<>(); + final AstNode currentParent = new AstNode(gherkinDocument.getFeature(), null); + for (ScenarioDefinition child : gherkinDocument.getFeature().getChildren()) { + processScenarioDefinition(nodeMap, child, currentParent); + } + pathToNodeMap.put(path, nodeMap); + } catch (ParserException e) { + LOGGER.trace(e.getMessage(), e); + } + } + + private void processScenarioDefinition( + final Map nodeMap, final ScenarioDefinition child, final AstNode currentParent + ) { + final AstNode childNode = new AstNode(child, currentParent); + nodeMap.put(child.getLocation().getLine(), childNode); + + child.getSteps().forEach( + step -> nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)) + ); + + if (child instanceof ScenarioOutline) { + processScenarioOutlineExamples(nodeMap, (ScenarioOutline) child, childNode); + } + } + + private void processScenarioOutlineExamples( + final Map nodeMap, final ScenarioOutline scenarioOutline, final AstNode childNode + ) { + scenarioOutline.getExamples().forEach(examples -> { + final AstNode examplesNode = new AstNode(examples, childNode); + final TableRow headerRow = examples.getTableHeader(); + final AstNode headerNode = new AstNode(headerRow, examplesNode); + nodeMap.put(headerRow.getLocation().getLine(), headerNode); + IntStream.range(0, examples.getTableBody().size()).forEach(i -> { + final TableRow examplesRow = examples.getTableBody().get(i); + final Node rowNode = new CucumberSourceUtils.ExamplesRowWrapperNode(examplesRow, i); + final AstNode expandedScenarioNode = new AstNode(rowNode, examplesNode); + nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode); + }); + }); + } + + private AstNode getAstNode(final String path, final int line) { + if (!pathToNodeMap.containsKey(path)) { + parseGherkinSource(path); + } + if (pathToNodeMap.containsKey(path)) { + return pathToNodeMap.get(path).get(line); + } + return null; + } + + public ScenarioDefinition getScenarioDefinition(final String path, final int line) { + return getScenarioDefinition(getAstNode(path, line)); + } + + private ScenarioDefinition getScenarioDefinition(final AstNode astNode) { + return astNode.getNode() instanceof ScenarioDefinition + ? (ScenarioDefinition) astNode.getNode() + : (ScenarioDefinition) astNode.getParent().getParent().getNode(); + } + + public String getKeywordFromSource(final String uri, final int stepLine) { + final Feature feature = getFeature(uri); + if (feature != null) { + final TestSourceRead event = getTestSourceReadEvent(uri); + final String trimmedSourceLine = event.source.split("\n")[stepLine - 1].trim(); + final GherkinDialect dialect = new GherkinDialectProvider(feature.getLanguage()).getDefaultDialect(); + for (String keyword : dialect.getStepKeywords()) { + if (trimmedSourceLine.startsWith(keyword)) { + return keyword; + } + } + } + return ""; + } + + private TestSourceRead getTestSourceReadEvent(final String uri) { + if (pathToReadEventMap.containsKey(uri)) { + return pathToReadEventMap.get(uri); + } + return null; + } + + /** + * Representation of Examples row. + */ + private static class ExamplesRowWrapperNode extends Node { + private final int bodyRowIndex; + + ExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) { + super(examplesRow.getLocation()); + this.bodyRowIndex = bodyRowIndex; + } + + public int getBodyRowIndex() { + return bodyRowIndex; + } + } + + /** + * Representation of leaf node. + */ + private static class AstNode { + private final Node node; + private final AstNode parent; + + AstNode(final Node node, final AstNode parent) { + this.node = node; + this.parent = parent; + } + + public Node getNode() { + return node; + } + + public AstNode getParent() { + return parent; + } + } +} diff --git a/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/LabelBuilder.java b/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/LabelBuilder.java new file mode 100644 index 000000000..ac6e2d9a8 --- /dev/null +++ b/allure-cucumber3-jvm/src/main/java/io/qameta/allure/cucumber3jvm/LabelBuilder.java @@ -0,0 +1,134 @@ +package io.qameta.allure.cucumber3jvm; + +import cucumber.api.TestCase; +import gherkin.ast.Feature; +import gherkin.pickles.PickleTag; +import io.qameta.allure.model.Label; +import io.qameta.allure.model.Link; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.regex.Pattern; + +import static io.qameta.allure.util.ResultsUtils.createFeatureLabel; +import static io.qameta.allure.util.ResultsUtils.createStoryLabel; +import static io.qameta.allure.util.ResultsUtils.createSeverityLabel; +import static io.qameta.allure.util.ResultsUtils.createTmsLink; +import static io.qameta.allure.util.ResultsUtils.createIssueLink; +import static io.qameta.allure.util.ResultsUtils.createLink; +import static io.qameta.allure.util.ResultsUtils.getHostName; +import static io.qameta.allure.util.ResultsUtils.getThreadName; +import static io.qameta.allure.util.ResultsUtils.createTagLabel; + +/** + * Scenario labels and links builder. + */ +@SuppressWarnings("CyclomaticComplexity") +class LabelBuilder { + private static final Logger LOGGER = LoggerFactory.getLogger(LabelBuilder.class); + private static final String COMPOSITE_TAG_DELIMITER = "="; + + private static final String SEVERITY = "@SEVERITY"; + private static final String ISSUE_LINK = "@ISSUE"; + private static final String TMS_LINK = "@TMSLINK"; + private static final String PLAIN_LINK = "@LINK"; + + private final List