diff --git a/.revapi/api-changes.json b/.revapi/api-changes.json index 0a1b3c3f57..a93cc621be 100644 --- a/.revapi/api-changes.json +++ b/.revapi/api-changes.json @@ -71,22 +71,12 @@ ], "internal": [ { - "extension": "revapi.differences", - "id": "internal-api-issues", "ignore": true, - "configuration": { - "differences": [ - { - "ignore": true, - "code": "java.method.visibilityIncreased", - "old": "method io.cucumber.core.eventbus.UuidGenerator io.cucumber.core.runtime.UuidGeneratorServiceLoader::loadUuidGenerator()", - "new": "method io.cucumber.core.eventbus.UuidGenerator io.cucumber.core.runtime.UuidGeneratorServiceLoader::loadUuidGenerator()", - "oldVisibility": "package", - "newVisibility": "public", - "justification": "Expose internal API to other internal components" - } - ] - } + "code": "java.class.nowImplementsInterface", + "old": "class io.cucumber.core.plugin.JsonFormatter", + "new": "class io.cucumber.core.plugin.JsonFormatter", + "interface": "io.cucumber.plugin.ConcurrentEventListener", + "justification": "The JsonFormatter is consumed by Cucumber" } ], "testng": [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f60bfd76f..bf5cbcb7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- [Core] Use a [message based Cucumber JSON Formatter](https://github.com/cucumber/cucumber-json-formatter) ([##2888](https://github.com/cucumber/cucumber-jvm/pull/#2888) M.P. Korstanje) ## [7.27.0] - 2025-07-27 ### Changed diff --git a/cucumber-bom/pom.xml b/cucumber-bom/pom.xml index c2eb6ee2b2..6472c42079 100644 --- a/cucumber-bom/pom.xml +++ b/cucumber-bom/pom.xml @@ -15,6 +15,7 @@ 10.0.1 18.0.1 + 0.1.1 33.1.0 21.13.0 0.8.0 @@ -38,6 +39,11 @@ cucumber-expressions ${cucumber-expressions.version} + + io.cucumber + cucumber-json-formatter + ${cucumber-json-formatter.version} + io.cucumber gherkin diff --git a/cucumber-core/pom.xml b/cucumber-core/pom.xml index 679d6221a9..55363b9eb9 100644 --- a/cucumber-core/pom.xml +++ b/cucumber-core/pom.xml @@ -80,6 +80,10 @@ io.cucumber cucumber-expressions + + io.cucumber + cucumber-json-formatter + io.cucumber datatable diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java index f36526e4ea..4334cb920f 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/JsonFormatter.java @@ -1,442 +1,48 @@ package io.cucumber.core.plugin; -import io.cucumber.messages.types.Background; -import io.cucumber.messages.types.Feature; -import io.cucumber.messages.types.Scenario; -import io.cucumber.messages.types.Step; -import io.cucumber.plugin.EventListener; -import io.cucumber.plugin.event.Argument; -import io.cucumber.plugin.event.DataTableArgument; -import io.cucumber.plugin.event.DocStringArgument; -import io.cucumber.plugin.event.EmbedEvent; +import io.cucumber.jsonformatter.MessagesToJsonWriter; +import io.cucumber.messages.types.Envelope; +import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.event.EventPublisher; -import io.cucumber.plugin.event.HookTestStep; -import io.cucumber.plugin.event.HookType; -import io.cucumber.plugin.event.PickleStepTestStep; -import io.cucumber.plugin.event.Result; -import io.cucumber.plugin.event.Status; -import io.cucumber.plugin.event.StepArgument; -import io.cucumber.plugin.event.TestCase; -import io.cucumber.plugin.event.TestCaseStarted; -import io.cucumber.plugin.event.TestRunFinished; -import io.cucumber.plugin.event.TestSourceRead; -import io.cucumber.plugin.event.TestStep; -import io.cucumber.plugin.event.TestStepFinished; -import io.cucumber.plugin.event.TestStepStarted; -import io.cucumber.plugin.event.WriteEvent; +import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.io.Writer; import java.net.URI; -import java.time.Instant; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import static io.cucumber.core.exception.ExceptionUtils.printStackTrace; -import static io.cucumber.core.plugin.TestSourcesModel.getBackgroundForTestCase; -import static java.util.Collections.singletonList; -import static java.util.Locale.ROOT; -import static java.util.stream.Collectors.toList; +import static io.cucumber.jsonformatter.MessagesToJsonWriter.builder; -public final class JsonFormatter implements EventListener { +public final class JsonFormatter implements ConcurrentEventListener { - private static final String before = "before"; - private static final String after = "after"; - private final List> featureMaps = new ArrayList<>(); - private final Map currentBeforeStepHookList = new HashMap<>(); - private final Writer writer; - private final TestSourcesModel testSources = new TestSourcesModel(); - private URI currentFeatureFile; - private List> currentElementsList; - private Map currentElementMap; - private Map currentTestCaseMap; - private List> currentStepsList; - private Map currentStepOrHookMap; + private final MessagesToJsonWriter writer; - @SuppressWarnings("WeakerAccess") // Used by PluginFactory public JsonFormatter(OutputStream out) { - this.writer = new UTF8OutputStreamWriter(out); + URI cwdUri = new File("").toURI(); + this.writer = builder(Jackson.OBJECT_MAPPER::writeValue) + .relativizeAgainst(cwdUri) + .build(out); } @Override public void setEventPublisher(EventPublisher publisher) { - publisher.registerHandlerFor(TestSourceRead.class, this::handleTestSourceRead); - publisher.registerHandlerFor(TestCaseStarted.class, this::handleTestCaseStarted); - publisher.registerHandlerFor(TestStepStarted.class, this::handleTestStepStarted); - publisher.registerHandlerFor(TestStepFinished.class, this::handleTestStepFinished); - publisher.registerHandlerFor(WriteEvent.class, this::handleWrite); - publisher.registerHandlerFor(EmbedEvent.class, this::handleEmbed); - publisher.registerHandlerFor(TestRunFinished.class, this::finishReport); + publisher.registerHandlerFor(Envelope.class, this::write); } - private void handleTestSourceRead(TestSourceRead event) { - testSources.addTestSourceReadEvent(event.getUri(), event); - } - - @SuppressWarnings("unchecked") - private void handleTestCaseStarted(TestCaseStarted event) { - if (currentFeatureFile == null || !currentFeatureFile.equals(event.getTestCase().getUri())) { - currentFeatureFile = event.getTestCase().getUri(); - Map currentFeatureMap = createFeatureMap(event.getTestCase()); - featureMaps.add(currentFeatureMap); - currentElementsList = (List>) currentFeatureMap.get("elements"); - } - currentTestCaseMap = createTestCase(event); - if (testSources.hasBackground(currentFeatureFile, event.getTestCase().getLocation().getLine())) { - currentElementMap = createBackground(event.getTestCase()); - currentElementsList.add(currentElementMap); - } else { - currentElementMap = currentTestCaseMap; - } - currentElementsList.add(currentTestCaseMap); - currentStepsList = (List>) currentElementMap.get("steps"); - } - - @SuppressWarnings("unchecked") - private void handleTestStepStarted(TestStepStarted event) { - if (event.getTestStep() instanceof PickleStepTestStep) { - PickleStepTestStep testStep = (PickleStepTestStep) event.getTestStep(); - if (isFirstStepAfterBackground(testStep)) { - currentElementMap = currentTestCaseMap; - currentStepsList = (List>) currentElementMap.get("steps"); - } - currentStepOrHookMap = createTestStep(testStep); - // add beforeSteps list to current step - if (currentBeforeStepHookList.containsKey(before)) { - currentStepOrHookMap.put(before, currentBeforeStepHookList.get(before)); - currentBeforeStepHookList.clear(); - } - currentStepsList.add(currentStepOrHookMap); - } else if (event.getTestStep() instanceof HookTestStep) { - HookTestStep hookTestStep = (HookTestStep) event.getTestStep(); - currentStepOrHookMap = createHookStep(hookTestStep); - addHookStepToTestCaseMap(currentStepOrHookMap, hookTestStep.getHookType()); - } else { - throw new IllegalStateException(); - } - } - - private void handleTestStepFinished(TestStepFinished event) { - currentStepOrHookMap.put("match", createMatchMap(event.getTestStep(), event.getResult())); - currentStepOrHookMap.put("result", createResultMap(event.getResult())); - } - - private void handleWrite(WriteEvent event) { - addOutputToHookMap(event.getText()); - } - - private void handleEmbed(EmbedEvent event) { - addEmbeddingToHookMap(event.getData(), event.getMediaType(), event.getName()); - } - - private void finishReport(TestRunFinished event) { - Throwable exception = event.getResult().getError(); - if (exception != null) { - featureMaps.add(createDummyFeatureForFailure(event)); - } - + private void write(Envelope event) { try { - Jackson.OBJECT_MAPPER.writeValue(writer, featureMaps); - writer.close(); + writer.write(event); } catch (IOException e) { - throw new RuntimeException(e); + throw new IllegalStateException(e); } - } - private Map createFeatureMap(TestCase testCase) { - Map featureMap = new HashMap<>(); - featureMap.put("uri", TestSourcesModel.relativize(testCase.getUri())); - featureMap.put("elements", new ArrayList>()); - Feature feature = testSources.getFeature(testCase.getUri()); - if (feature != null) { - featureMap.put("keyword", feature.getKeyword()); - featureMap.put("name", feature.getName()); - featureMap.put("description", feature.getDescription() != null ? feature.getDescription() : ""); - featureMap.put("line", feature.getLocation().getLine()); - featureMap.put("id", TestSourcesModel.convertToId(feature.getName())); - featureMap.put("tags", feature.getTags().stream().map( - tag -> { - Map json = new LinkedHashMap<>(); - json.put("name", tag.getName()); - json.put("type", "Tag"); - Map location = new LinkedHashMap<>(); - location.put("line", tag.getLocation().getLine()); - location.put("column", tag.getLocation().getColumn()); - json.put("location", location); - return json; - }).collect(toList())); - - } - return featureMap; - } - - private Map createTestCase(TestCaseStarted event) { - Map testCaseMap = new HashMap<>(); - - testCaseMap.put("start_timestamp", getDateTimeFromTimeStamp(event.getInstant())); - - TestCase testCase = event.getTestCase(); - - testCaseMap.put("name", testCase.getName()); - testCaseMap.put("line", testCase.getLine()); - testCaseMap.put("type", "scenario"); - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testCase.getLine()); - if (astNode != null) { - testCaseMap.put("id", TestSourcesModel.calculateId(astNode)); - Scenario scenarioDefinition = TestSourcesModel.getScenarioDefinition(astNode); - testCaseMap.put("keyword", scenarioDefinition.getKeyword()); - testCaseMap.put("description", - scenarioDefinition.getDescription() != null ? scenarioDefinition.getDescription() : ""); - } - testCaseMap.put("steps", new ArrayList>()); - if (!testCase.getTags().isEmpty()) { - List> tagList = new ArrayList<>(); - for (String tag : testCase.getTags()) { - Map tagMap = new HashMap<>(); - tagMap.put("name", tag); - tagList.add(tagMap); + // TODO: Plugins should implement the closable interface + // and be closed by Cucumber + if (event.getTestRunFinished().isPresent()) { + try { + writer.close(); + } catch (IOException e) { + throw new IllegalStateException(e); } - testCaseMap.put("tags", tagList); } - return testCaseMap; } - - private Map createBackground(TestCase testCase) { - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testCase.getLocation().getLine()); - if (astNode != null) { - Background background = getBackgroundForTestCase(astNode).get(); - Map testCaseMap = new HashMap<>(); - testCaseMap.put("name", background.getName()); - testCaseMap.put("line", background.getLocation().getLine()); - testCaseMap.put("type", "background"); - testCaseMap.put("keyword", background.getKeyword()); - testCaseMap.put("description", background.getDescription() != null ? background.getDescription() : ""); - testCaseMap.put("steps", new ArrayList>()); - return testCaseMap; - } - return null; - } - - private boolean isFirstStepAfterBackground(PickleStepTestStep testStep) { - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testStep.getStepLine()); - if (astNode == null) { - return false; - } - return currentElementMap != currentTestCaseMap && !TestSourcesModel.isBackgroundStep(astNode); - } - - private Map createTestStep(PickleStepTestStep testStep) { - Map stepMap = new HashMap<>(); - stepMap.put("name", testStep.getStepText()); - stepMap.put("line", testStep.getStepLine()); - TestSourcesModel.AstNode astNode = testSources.getAstNode(currentFeatureFile, testStep.getStepLine()); - StepArgument argument = testStep.getStepArgument(); - if (argument != null) { - if (argument instanceof DocStringArgument) { - DocStringArgument docStringArgument = (DocStringArgument) argument; - stepMap.put("doc_string", createDocStringMap(docStringArgument)); - } else if (argument instanceof DataTableArgument) { - DataTableArgument dataTableArgument = (DataTableArgument) argument; - stepMap.put("rows", createDataTableList(dataTableArgument)); - } - } - if (astNode != null) { - Step step = (Step) astNode.node; - stepMap.put("keyword", step.getKeyword()); - } - - return stepMap; - } - - private Map createHookStep(HookTestStep hookTestStep) { - return new HashMap<>(); - } - - private void addHookStepToTestCaseMap(Map currentStepOrHookMap, HookType hookType) { - String hookName; - if (hookType == HookType.AFTER || hookType == HookType.AFTER_STEP) - hookName = after; - else - hookName = before; - - Map mapToAddTo; - switch (hookType) { - case BEFORE: - mapToAddTo = currentTestCaseMap; - break; - case AFTER: - mapToAddTo = currentTestCaseMap; - break; - case BEFORE_STEP: - mapToAddTo = currentBeforeStepHookList; - break; - case AFTER_STEP: - mapToAddTo = currentStepsList.get(currentStepsList.size() - 1); - break; - default: - mapToAddTo = currentTestCaseMap; - } - - if (!mapToAddTo.containsKey(hookName)) { - mapToAddTo.put(hookName, new ArrayList>()); - } - ((List>) mapToAddTo.get(hookName)).add(currentStepOrHookMap); - } - - private Map createMatchMap(TestStep step, Result result) { - Map matchMap = new HashMap<>(); - if (step instanceof PickleStepTestStep) { - PickleStepTestStep testStep = (PickleStepTestStep) step; - if (!testStep.getDefinitionArgument().isEmpty()) { - List> argumentList = new ArrayList<>(); - for (Argument argument : testStep.getDefinitionArgument()) { - Map argumentMap = new HashMap<>(); - if (argument.getValue() != null) { - argumentMap.put("val", argument.getValue()); - argumentMap.put("offset", argument.getStart()); - } - argumentList.add(argumentMap); - } - matchMap.put("arguments", argumentList); - } - } - if (!result.getStatus().is(Status.UNDEFINED)) { - matchMap.put("location", step.getCodeLocation()); - } - return matchMap; - } - - private Map createResultMap(Result result) { - Map resultMap = new HashMap<>(); - resultMap.put("status", result.getStatus().name().toLowerCase(ROOT)); - if (result.getError() != null) { - resultMap.put("error_message", printStackTrace(result.getError())); - } - if (!result.getDuration().isZero()) { - resultMap.put("duration", result.getDuration().toNanos()); - } - return resultMap; - } - - private void addOutputToHookMap(String text) { - if (!currentStepOrHookMap.containsKey("output")) { - currentStepOrHookMap.put("output", new ArrayList()); - } - ((List) currentStepOrHookMap.get("output")).add(text); - } - - private void addEmbeddingToHookMap(byte[] data, String mediaType, String name) { - if (!currentStepOrHookMap.containsKey("embeddings")) { - currentStepOrHookMap.put("embeddings", new ArrayList>()); - } - Map embedMap = createEmbeddingMap(data, mediaType, name); - ((List>) currentStepOrHookMap.get("embeddings")).add(embedMap); - } - - private Map createDummyFeatureForFailure(TestRunFinished event) { - Throwable exception = event.getResult().getError(); - - Map feature = new LinkedHashMap<>(); - feature.put("line", 1); - { - Map scenario = new LinkedHashMap<>(); - feature.put("elements", singletonList(scenario)); - - scenario.put("start_timestamp", getDateTimeFromTimeStamp(event.getInstant())); - scenario.put("line", 2); - scenario.put("name", "Failure while executing Cucumber"); - scenario.put("description", ""); - scenario.put("id", "failure;failure-while-executing-cucumber"); - scenario.put("type", "scenario"); - scenario.put("keyword", "Scenario"); - - Map when = new LinkedHashMap<>(); - Map then = new LinkedHashMap<>(); - scenario.put("steps", Arrays.asList(when, then)); - { - - { - Map whenResult = new LinkedHashMap<>(); - when.put("result", whenResult); - whenResult.put("duration", 0); - whenResult.put("status", "passed"); - } - when.put("line", 3); - when.put("name", "Cucumber failed while executing"); - Map whenMatch = new LinkedHashMap<>(); - when.put("match", whenMatch); - whenMatch.put("arguments", new ArrayList<>()); - whenMatch.put("location", "io.cucumber.core.Failure.failure_while_executing_cucumber()"); - when.put("keyword", "When "); - - { - Map thenResult = new LinkedHashMap<>(); - then.put("result", thenResult); - thenResult.put("duration", 0); - thenResult.put("error_message", printStackTrace(exception)); - thenResult.put("status", "failed"); - } - then.put("line", 4); - then.put("name", "Cucumber will report this error:"); - Map thenMatch = new LinkedHashMap<>(); - then.put("match", thenMatch); - thenMatch.put("arguments", new ArrayList<>()); - thenMatch.put("location", "io.cucumber.core.Failure.cucumber_reports_this_error()"); - then.put("keyword", "Then "); - } - - feature.put("name", "Test run failed"); - feature.put("description", "There were errors during the execution"); - feature.put("id", "failure"); - feature.put("keyword", "Feature"); - feature.put("uri", "classpath:io/cucumber/core/failure.feature"); - feature.put("tags", new ArrayList<>()); - } - - return feature; - } - - private String getDateTimeFromTimeStamp(Instant instant) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX") - .withZone(ZoneOffset.UTC); - return formatter.format(instant); - } - - private Map createDocStringMap(DocStringArgument docString) { - Map docStringMap = new HashMap<>(); - docStringMap.put("value", docString.getContent()); - docStringMap.put("line", docString.getLine()); - docStringMap.put("content_type", docString.getMediaType()); - return docStringMap; - } - - private List>> createDataTableList(DataTableArgument argument) { - List>> rowList = new ArrayList<>(); - for (List row : argument.cells()) { - Map> rowMap = new HashMap<>(); - rowMap.put("cells", new ArrayList<>(row)); - rowList.add(rowMap); - } - return rowList; - } - - private Map createEmbeddingMap(byte[] data, String mediaType, String name) { - Map embedMap = new HashMap<>(); - embedMap.put("mime_type", mediaType); // Should be media-type but not - // worth migrating for - embedMap.put("data", Base64.getEncoder().encodeToString(data)); - if (name != null) { - embedMap.put("name", name); - } - return embedMap; - } - } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourceReadResource.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourceReadResource.java deleted file mode 100644 index d9fd4104ce..0000000000 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourceReadResource.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.cucumber.core.plugin; - -import io.cucumber.core.resource.Resource; -import io.cucumber.plugin.event.TestSourceRead; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.net.URI; - -import static java.nio.charset.StandardCharsets.UTF_8; - -final class TestSourceReadResource implements Resource { - - private final TestSourceRead event; - - TestSourceReadResource(TestSourceRead event) { - this.event = event; - } - - @Override - public URI getUri() { - return event.getUri(); - } - - @Override - public InputStream getInputStream() { - return new ByteArrayInputStream(event.getSource().getBytes(UTF_8)); - } - -} diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java deleted file mode 100644 index b3e87d4d7c..0000000000 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java +++ /dev/null @@ -1,253 +0,0 @@ -package io.cucumber.core.plugin; - -import io.cucumber.gherkin.GherkinParser; -import io.cucumber.messages.types.Background; -import io.cucumber.messages.types.Envelope; -import io.cucumber.messages.types.Examples; -import io.cucumber.messages.types.Feature; -import io.cucumber.messages.types.FeatureChild; -import io.cucumber.messages.types.GherkinDocument; -import io.cucumber.messages.types.Rule; -import io.cucumber.messages.types.RuleChild; -import io.cucumber.messages.types.Scenario; -import io.cucumber.messages.types.Source; -import io.cucumber.messages.types.SourceMediaType; -import io.cucumber.messages.types.Step; -import io.cucumber.messages.types.TableRow; -import io.cucumber.plugin.event.TestSourceRead; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -final class TestSourcesModel { - - private final Map pathToReadEventMap = new HashMap<>(); - private final Map pathToAstMap = new HashMap<>(); - private final Map> pathToNodeMap = new HashMap<>(); - - static Scenario getScenarioDefinition(AstNode astNode) { - AstNode candidate = astNode; - while (candidate != null && !(candidate.node instanceof Scenario)) { - candidate = candidate.parent; - } - return candidate == null ? null : (Scenario) candidate.node; - } - - static boolean isBackgroundStep(AstNode astNode) { - return astNode.parent.node instanceof Background; - } - - static String calculateId(AstNode astNode) { - Object node = astNode.node; - if (node instanceof Rule) { - return calculateId(astNode.parent) + ";" + convertToId(((Rule) node).getName()); - } - if (node instanceof Scenario) { - return calculateId(astNode.parent) + ";" + convertToId(((Scenario) node).getName()); - } - if (node instanceof ExamplesRowWrapperNode) { - return calculateId(astNode.parent) + ";" + (((ExamplesRowWrapperNode) node).bodyRowIndex + 2); - } - if (node instanceof TableRow) { - return calculateId(astNode.parent) + ";" + 1; - } - if (node instanceof Examples) { - return calculateId(astNode.parent) + ";" + convertToId(((Examples) node).getName()); - } - if (node instanceof Feature) { - return convertToId(((Feature) node).getName()); - } - return ""; - } - - private static final Pattern replacementPattern = Pattern.compile("[\\s'_,!]"); - - static String convertToId(String name) { - return replacementPattern.matcher(name).replaceAll("-").toLowerCase(); - } - - static URI relativize(URI uri) { - if (!"file".equals(uri.getScheme())) { - return uri; - } - if (!uri.isAbsolute()) { - return uri; - } - - try { - URI root = new File("").toURI(); - URI relative = root.relativize(uri); - // Scheme is lost by relativize - return new URI("file", relative.getSchemeSpecificPart(), relative.getFragment()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - void addTestSourceReadEvent(URI path, TestSourceRead event) { - pathToReadEventMap.put(path, event); - } - - Feature getFeature(URI path) { - if (!pathToAstMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToAstMap.containsKey(path)) { - return pathToAstMap.get(path).getFeature().orElse(null); - } - return null; - } - - private void parseGherkinSource(URI path) { - if (!pathToReadEventMap.containsKey(path)) { - return; - } - String source = pathToReadEventMap.get(path).getSource(); - - GherkinParser parser = GherkinParser.builder() - .build(); - - Stream envelopes = parser.parse( - Envelope.of(new Source(path.toString(), source, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN))); - - // TODO: What about empty gherkin docs? - GherkinDocument gherkinDocument = envelopes - .map(Envelope::getGherkinDocument) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst() - .orElse(null); - - pathToAstMap.put(path, gherkinDocument); - Map nodeMap = new HashMap<>(); - // TODO: What about gherkin docs with no features? - Feature feature = gherkinDocument.getFeature().get(); - AstNode currentParent = new AstNode(feature, null); - for (FeatureChild child : feature.getChildren()) { - processFeatureDefinition(nodeMap, child, currentParent); - } - pathToNodeMap.put(path, nodeMap); - - } - - private void processFeatureDefinition(Map nodeMap, FeatureChild child, AstNode currentParent) { - child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent)); - child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent)); - child.getRule().ifPresent(rule -> { - AstNode childNode = new AstNode(rule, currentParent); - nodeMap.put(rule.getLocation().getLine(), childNode); - rule.getChildren().forEach(ruleChild -> processRuleDefinition(nodeMap, ruleChild, childNode)); - }); - } - - private void processBackgroundDefinition( - Map nodeMap, Background background, AstNode currentParent - ) { - AstNode childNode = new AstNode(background, currentParent); - nodeMap.put(background.getLocation().getLine(), childNode); - for (Step step : background.getSteps()) { - nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)); - } - } - - private void processScenarioDefinition(Map nodeMap, Scenario child, AstNode currentParent) { - AstNode childNode = new AstNode(child, currentParent); - nodeMap.put(child.getLocation().getLine(), childNode); - for (io.cucumber.messages.types.Step step : child.getSteps()) { - nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)); - } - if (!child.getExamples().isEmpty()) { - processScenarioOutlineExamples(nodeMap, child, childNode); - } - } - - private void processRuleDefinition(Map nodeMap, RuleChild child, AstNode currentParent) { - child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent)); - child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent)); - } - - private void processScenarioOutlineExamples( - Map nodeMap, Scenario scenarioOutline, AstNode parent - ) { - for (Examples examples : scenarioOutline.getExamples()) { - AstNode examplesNode = new AstNode(examples, parent); - // TODO: Can tables without headers even exist? - TableRow headerRow = examples.getTableHeader().get(); - AstNode headerNode = new AstNode(headerRow, examplesNode); - nodeMap.put(headerRow.getLocation().getLine(), headerNode); - for (int i = 0; i < examples.getTableBody().size(); ++i) { - TableRow examplesRow = examples.getTableBody().get(i); - Object rowNode = new ExamplesRowWrapperNode(examplesRow, i); - AstNode expandedScenarioNode = new AstNode(rowNode, examplesNode); - nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode); - } - } - } - - AstNode getAstNode(URI path, int line) { - if (!pathToNodeMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToNodeMap.containsKey(path)) { - return pathToNodeMap.get(path).get((long) line); - } - return null; - } - - boolean hasBackground(URI path, int line) { - if (!pathToNodeMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToNodeMap.containsKey(path)) { - AstNode astNode = pathToNodeMap.get(path).get((long) line); - return getBackgroundForTestCase(astNode).isPresent(); - } - return false; - } - - static Optional getBackgroundForTestCase(AstNode astNode) { - Feature feature = getFeatureForTestCase(astNode); - return feature.getChildren() - .stream() - .map(FeatureChild::getBackground) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); - } - - private static Feature getFeatureForTestCase(AstNode astNode) { - while (astNode.parent != null) { - astNode = astNode.parent; - } - return (Feature) astNode.node; - } - - static class ExamplesRowWrapperNode { - - final int bodyRowIndex; - - ExamplesRowWrapperNode(Object examplesRow, int bodyRowIndex) { - this.bodyRowIndex = bodyRowIndex; - } - - } - - static class AstNode { - - final Object node; - final AstNode parent; - - AstNode(Object node, AstNode parent) { - this.node = node; - this.parent = parent; - } - - } - -} diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java index 532e444942..0cf532a79b 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java @@ -6,49 +6,42 @@ public class StubHookDefinition implements HookDefinition { private static final String STUBBED_LOCATION_WITH_DETAILS = "{stubbed location with details}"; - private final String location; + private final Located location; private final RuntimeException exception; private final Consumer action; - private final SourceReference sourceReference; private final HookType hookType; public StubHookDefinition( - String location, RuntimeException exception, Consumer action, - SourceReference sourceReference, HookType hookType + Located location, RuntimeException exception, Consumer action, HookType hookType ) { this.location = location; this.exception = exception; this.action = action; - this.sourceReference = sourceReference; this.hookType = hookType; } - public StubHookDefinition(String location, Consumer action) { - this(location, null, action, null, null); + public StubHookDefinition(SourceReference location, HookType hookType, Consumer action) { + this(new StubLocation(location), null, action, hookType); } public StubHookDefinition() { - this(STUBBED_LOCATION_WITH_DETAILS, null, null, null, null); + this(new StubLocation(STUBBED_LOCATION_WITH_DETAILS), null, null, null); } public StubHookDefinition(Consumer action) { - this(STUBBED_LOCATION_WITH_DETAILS, null, action, null, null); + this(new StubLocation(STUBBED_LOCATION_WITH_DETAILS), null, action, null); } public StubHookDefinition(RuntimeException exception) { - this(STUBBED_LOCATION_WITH_DETAILS, exception, null, null, null); - } - - public StubHookDefinition(String location) { - this(location, null, null, null, null); + this(new StubLocation(STUBBED_LOCATION_WITH_DETAILS), exception, null, null); } public StubHookDefinition(SourceReference sourceReference, HookType hookType) { - this(null, null, null, sourceReference, hookType); + this(new StubLocation(sourceReference), null, null, hookType); } public StubHookDefinition(SourceReference sourceReference, HookType hookType, RuntimeException exception) { - this(null, exception, null, sourceReference, hookType); + this(new StubLocation(sourceReference), exception, null, hookType); } @Override @@ -78,12 +71,12 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) { @Override public String getLocation() { - return location; + return location.getLocation(); } @Override public Optional getSourceReference() { - return Optional.ofNullable(sourceReference); + return location.getSourceReference(); } @Override diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java index 63b03f418b..7263313f06 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java @@ -1,11 +1,37 @@ package io.cucumber.core.backend; +import java.lang.reflect.Method; +import java.util.Optional; + public class StubLocation implements Located { private final String location; + private final SourceReference sourceReference; public StubLocation(String location) { this.location = location; + this.sourceReference = null; + } + + public StubLocation(Method method) { + this.location = null; + this.sourceReference = SourceReference.fromMethod(method); + } + + public StubLocation(SourceReference sourceReference) { + this.sourceReference = sourceReference; + this.location = formatLocation(sourceReference); + } + + private static String formatLocation(SourceReference sourceReference) { + if (sourceReference instanceof JavaMethodReference) { + JavaMethodReference javaMethodReference = (JavaMethodReference) sourceReference; + String className = javaMethodReference.className(); + String methodName = javaMethodReference.methodName(); + String parameterTypes = String.join(",", javaMethodReference.methodParameterTypes()); + return String.format("%s#%s(%s)", className, methodName, parameterTypes); + } + return null; } @Override @@ -13,6 +39,11 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) { return false; } + @Override + public Optional getSourceReference() { + return Optional.ofNullable(sourceReference); + } + @Override public String getLocation() { return location; diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java index d47e8d06a1..afa3830dde 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java @@ -16,36 +16,31 @@ public class StubStepDefinition implements StepDefinition { private final String expression; private final Throwable exception; private final Located location; - private SourceReference sourceReference; public StubStepDefinition(String pattern, String location, Type... types) { - this(pattern, location, null, types); + this(pattern, new StubLocation(location), null, types); } - public StubStepDefinition(String pattern, Type... types) { - this(pattern, STUBBED_LOCATION_WITH_DETAILS, null, types); + public StubStepDefinition(String pattern, SourceReference location, Type... types) { + this(pattern, new StubLocation(location), null, types); } - public StubStepDefinition(String pattern, Throwable exception, Type... types) { - this(pattern, STUBBED_LOCATION_WITH_DETAILS, exception, types); + public StubStepDefinition(String pattern, Type... types) { + this(pattern, new StubLocation(STUBBED_LOCATION_WITH_DETAILS), null, types); } - public StubStepDefinition(String pattern, String location, Throwable exception, Type... types) { - this.parameterInfos = Stream.of(types).map(StubParameterInfo::new).collect(Collectors.toList()); - this.expression = pattern; - this.location = new StubLocation(location); - this.exception = exception; + public StubStepDefinition(String pattern, Throwable exception, Type... types) { + this(pattern, new StubLocation(STUBBED_LOCATION_WITH_DETAILS), exception, types); } - public StubStepDefinition(String pattern, SourceReference sourceReference, Type... types) { - this(pattern, sourceReference, null, types); + public StubStepDefinition(String pattern, SourceReference location, Throwable exception, Type... types) { + this(pattern, new StubLocation(location), exception, types); } - public StubStepDefinition(String pattern, SourceReference sourceReference, Throwable exception, Type... types) { + private StubStepDefinition(String pattern, StubLocation location, Throwable exception, Type... types) { this.parameterInfos = Stream.of(types).map(StubParameterInfo::new).collect(Collectors.toList()); this.expression = pattern; - this.location = new StubLocation(""); - this.sourceReference = sourceReference; + this.location = location; this.exception = exception; } @@ -86,7 +81,7 @@ public String getPattern() { @Override public Optional getSourceReference() { - return Optional.ofNullable(sourceReference); + return location.getSourceReference(); } private static final class StubParameterInfo implements ParameterInfo { diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java index 0a13c2cf50..5dec0d1d4a 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java @@ -1,158 +1,38 @@ package io.cucumber.core.plugin; -import io.cucumber.core.backend.StubHookDefinition; +import io.cucumber.core.backend.SourceReference; import io.cucumber.core.backend.StubStepDefinition; +import io.cucumber.core.eventbus.IncrementingUuidGenerator; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.gherkin.Feature; -import io.cucumber.core.options.RuntimeOptionsBuilder; import io.cucumber.core.runner.StepDurationTimeService; import io.cucumber.core.runtime.Runtime; -import io.cucumber.core.runtime.Runtime.Builder; import io.cucumber.core.runtime.StubBackendSupplier; import io.cucumber.core.runtime.StubFeatureSupplier; import io.cucumber.core.runtime.TimeServiceEventBus; -import io.cucumber.datatable.DataTable; -import io.cucumber.docstring.DocString; import org.json.JSONException; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.util.Scanner; -import java.util.UUID; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.time.Clock.fixed; import static java.time.Duration.ofMillis; -import static java.time.Instant.EPOCH; -import static java.time.ZoneId.of; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; class JsonFormatterTest { - @Test - void featureWithOutlineTest() throws JSONException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - createRuntime(out) - .build() - .run(); - - InputStream resourceAsStream = getClass().getResourceAsStream("JsonPrettyFormatterTest.json"); - String expected = new Scanner(resourceAsStream, "UTF-8") - .useDelimiter("\\A") - .next(); - - assertJsonEquals(expected, out); - } - - private Builder createRuntime(ByteArrayOutputStream out) { - Feature feature = TestFeatureParser.parse( - "classpath:io/cucumber/core/plugin/JsonPrettyFormatterTest.feature", - getClass().getResourceAsStream("JsonPrettyFormatterTest.feature")); - - return Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition()), - asList( - new StubStepDefinition("bg_1"), - new StubStepDefinition("bg_2"), - new StubStepDefinition("bg_3"), - new StubStepDefinition("step_1"), - new StubStepDefinition("step_2"), - new StubStepDefinition("step_3"), - new StubStepDefinition("cliché"), - new StubStepDefinition("so_1 {int}", Integer.class), - new StubStepDefinition("so_2 {int} cucumbers", Integer.class), - new StubStepDefinition("{int} so_3", Integer.class), - new StubStepDefinition("a"), - new StubStepDefinition("b"), - new StubStepDefinition("c")), - emptyList())) - .withAdditionalPlugins(new JsonFormatter(out)); - } - - @Test - void featureWithOutlineTestParallel() throws JSONException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - createRuntime(out) - .withRuntimeOptions(new RuntimeOptionsBuilder().setThreads(2).build()) - .build() - .run(); - - InputStream resourceAsStream = getClass().getResourceAsStream("JsonPrettyFormatterTest.json"); - String expected = new Scanner(resourceAsStream, "UTF-8") - .useDelimiter("\\A") - .next(); - - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_an_undefined_step() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier()) - .build() - .run(); + final SourceReference thereAreBananas = getMethod("there_are_bananas"); - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {},\n" + - " \"result\": {\n" + - " \"status\": \"undefined\"\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); + private static SourceReference getMethod(String name) { + try { + return SourceReference.fromMethod(JsonFormatterTestStepDefinitions.class.getMethod(name)); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } } private void assertJsonEquals(String expected, ByteArrayOutputStream actual) throws JSONException { - assertJsonEquals(expected, new String(actual.toByteArray(), UTF_8)); - - } - - private void assertJsonEquals(String expected, String actual) throws JSONException { - assertEquals(expected, actual, true); + assertEquals(expected, new String(actual.toByteArray(), UTF_8), true); } @Test @@ -168,69 +48,9 @@ void should_format_scenario_with_a_passed_step() throws JSONException { Runtime.builder() .withFeatureSupplier(new StubFeatureSupplier(feature)) .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_a_failed_step() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) + .withEventBus(new TimeServiceEventBus(timeService, new IncrementingUuidGenerator())) .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()", - new StubException("the stack trace")))) + new StubStepDefinition("there are bananas", thereAreBananas))) .build() .run(); @@ -258,1219 +78,8 @@ void should_format_scenario_with_a_failed_step() throws JSONException { " \"name\": \"there are bananas\",\n" + " \"line\": 4,\n" + " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"failed\",\n" + - " \"error_message\": \"the stack trace\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_a_rule() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Rule: This is all monkey business\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"line\": 1,\n" + - " \"elements\": [\n" + - " {\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"line\": 4,\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party;this-is-all-monkey-business;monkey-eats-bananas\",\n" + - " \"type\": \"scenario\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"line\": 5,\n" + - " \"name\": \"there are bananas\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"keyword\": \"Given \"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"name\": \"Banana party\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_a_rule_and_background() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Background: \n" + - " Given there are bananas\n" + - "\n" + - " Rule: This is all monkey business\n" + - "\n" + - " Background: \n" + - " Given there are bananas\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"line\": 1,\n" + - " \"elements\": [\n" + - " {\n" + - " \"line\": 3,\n" + - " \"name\": \"\",\n" + - " \"description\": \"\",\n" + - " \"type\": \"background\",\n" + - " \"keyword\": \"Background\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"line\": 4,\n" + - " \"name\": \"there are bananas\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"keyword\": \"Given \"\n" + - " },\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"line\": 9,\n" + - " \"name\": \"there are bananas\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"keyword\": \"Given \"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"line\": 11,\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party;this-is-all-monkey-business;monkey-eats-bananas\",\n" + - " \"type\": \"scenario\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"line\": 12,\n" + - " \"name\": \"there are bananas\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"keyword\": \"Given \"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"name\": \"Banana party\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_outline_with_one_example() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Fruit party\n" + - "\n" + - " Scenario Outline: Monkey eats fruits\n" + - " Given there are \n" + - " Examples: Fruit table\n" + - " | fruits |\n" + - " | bananas |\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"fruit-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Fruit party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"fruit-party;monkey-eats-fruits;fruit-table;2\",\n" + - " \"keyword\": \"Scenario Outline\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats fruits\",\n" + - " \"line\": 7,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_feature_with_background() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Background: There are bananas\n" + - " Given there are bananas\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Then the monkey eats bananas\n" + - "\n" + - " Scenario: Monkey eats more bananas\n" + - " Then the monkey eats more bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"), - new StubStepDefinition("the monkey eats bananas", "StepDefs.monkey_eats_bananas()"), - new StubStepDefinition("the monkey eats more bananas", "StepDefs.monkey_eats_more_bananas()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"keyword\": \"Background\",\n" + - " \"name\": \"There are bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"background\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 6,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Then \",\n" + - " \"name\": \"the monkey eats bananas\",\n" + - " \"line\": 7,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.monkey_eats_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"keyword\": \"Background\",\n" + - " \"name\": \"There are bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"background\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-more-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.002Z\",\n" + - " \"name\": \"Monkey eats more bananas\",\n" + - " \"line\": 9,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Then \",\n" + - " \"name\": \"the monkey eats more bananas\",\n" + - " \"line\": 10,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.monkey_eats_more_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_feature_and_scenario_with_tags() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "@Party @Banana\n" + - "Feature: Banana party\n" + - " @Monkey\n" + - " Scenario: Monkey eats more bananas\n" + - " Then the monkey eats more bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("the monkey eats more bananas", "StepDefs.monkey_eats_more_bananas()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"line\": 2,\n" + - " \"elements\": [\n" + - " {\n" + - " \"line\": 4,\n" + - " \"name\": \"Monkey eats more bananas\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party;monkey-eats-more-bananas\",\n" + - " \"type\": \"scenario\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"line\": 5,\n" + - " \"name\": \"the monkey eats more bananas\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.monkey_eats_more_bananas()\"\n" + - " },\n" + - " \"keyword\": \"Then \"\n" + - " }\n" + - " ],\n" + - " \"tags\": [\n" + - " {\n" + - " \"name\": \"@Party\"\n" + - " },\n" + - " {\n" + - " \"name\": \"@Banana\"\n" + - " },\n" + - " {\n" + - " \"name\": \"@Monkey\"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"name\": \"Banana party\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"tags\": [\n" + - " {\n" + - " \"name\": \"@Party\",\n" + - " \"type\": \"Tag\",\n" + - " \"location\": {\n" + - " \"line\": 1,\n" + - " \"column\": 1\n" + - " }\n" + - " },\n" + - " {\n" + - " \"name\": \"@Banana\",\n" + - " \"type\": \"Tag\",\n" + - " \"location\": {\n" + - " \"line\": 1,\n" + - " \"column\": 8\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_hooks() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition("Hooks.before_hook_1()")), - singletonList(new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()")), - singletonList(new StubHookDefinition("Hooks.after_hook_1()")))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"before\": [\n" + - " {\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.before_hook_1()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"after\": [\n" + - " {\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.after_hook_1()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_add_step_hooks_to_step() throws JSONException { - Feature feature = TestFeatureParser.parse("file:path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n" + - " When monkey arrives\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - emptyList(), - singletonList(new StubHookDefinition("Hooks.beforestep_hooks_1()")), - asList( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"), - new StubStepDefinition("monkey arrives", "StepDefs.monkey_arrives()")), - asList( - new StubHookDefinition("Hooks.afterstep_hooks_1()"), - new StubHookDefinition("Hooks.afterstep_hooks_2()")), - emptyList())) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"line\": 1,\n" + - " \"elements\": [\n" + - " {\n" + - " \"line\": 3,\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"type\": \"scenario\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"before\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.beforestep_hooks_1()\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"line\": 4,\n" + - " \"name\": \"there are bananas\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"after\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.afterstep_hooks_2()\"\n" + - " }\n" + - " },\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.afterstep_hooks_1()\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"keyword\": \"Given \"\n" + - " },\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"before\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.beforestep_hooks_1()\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"line\": 5,\n" + - " \"name\": \"monkey arrives\",\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.monkey_arrives()\"\n" + - " },\n" + - " \"after\": [\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.afterstep_hooks_2()\"\n" + - " }\n" + - " },\n" + - " {\n" + - " \"result\": {\n" + - " \"duration\": 1000000,\n" + - " \"status\": \"passed\"\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.afterstep_hooks_1()\"\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"keyword\": \"When \"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"name\": \"Banana party\",\n" + - " \"description\": \"\",\n" + - " \"id\": \"banana-party\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_handle_write_from_a_hook() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition("Hooks.before_hook_1()", - testCaseState -> testCaseState.log("printed from hook"))), - singletonList(new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()")), - emptyList())) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"before\": [\n" + - " {\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.before_hook_1()\"\n" + - " },\n" + - " \"output\": [\n" + - " \"printed from hook\"\n" + - " ],\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_handle_embed_from_a_hook() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition("Hooks.before_hook_1()", - testCaseState -> testCaseState - .attach(new byte[] { 1, 2, 3 }, "mime-type;base64", null))), - singletonList(new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()")), - emptyList())) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"before\": [\n" + - " {\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.before_hook_1()\"\n" + - " },\n" + - " \"embeddings\": [\n" + - " {\n" + - " \"mime_type\": \"mime-type;base64\",\n" + - " \"data\": \"AQID\"\n" + - " }\n" + - " ],\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_handle_embed_with_name_from_a_hook() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition("Hooks.before_hook_1()", - testCaseState -> testCaseState.attach(new byte[] { 1, 2, 3 }, "mime-type;base64", - "someEmbedding"))), - singletonList(new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()")), - emptyList())) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"before\": [\n" + - " {\n" + - " \"match\": {\n" + - " \"location\": \"Hooks.before_hook_1()\"\n" + - " },\n" + - " \"embeddings\": [\n" + - " {\n" + - " \"mime_type\": \"mime-type;base64\",\n" + - " \"data\": \"AQID\",\n" + - " \"name\": \"someEmbedding\"\n" + - " }\n" + - " ],\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ],\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_a_step_with_a_doc_string() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n" + - " \"\"\"\n" + - " doc string content\n" + - " \"\"\"\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()", String.class))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"doc_string\": {\n" + - " \"value\": \"doc string content\",\n" + - " \"line\": 5\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_a_step_with_a_doc_string_and_content_type() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n" + - " \"\"\"text/plain\n" + - " doc string content\n" + - " \"\"\"\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()", DocString.class))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"doc_string\": {\n" + - " \"content_type\": \"text/plain\",\n" + - " \"value\": \"doc string content\",\n" + - " \"line\": 5\n" + - " },\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_format_scenario_with_a_step_with_a_data_table() throws JSONException { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n" + - " | aa | 11 |\n" + - " | bb | 22 |\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()", DataTable.class))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"rows\": [\n" + - " {\n" + - " \"cells\": [\n" + - " \"aa\",\n" + - " \"11\"\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"cells\": [\n" + - " \"bb\",\n" + - " \"22\"\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " }\n" + - "]"; - assertJsonEquals(expected, out); - } - - @Test - void should_handle_several_features() throws JSONException { - Feature feature1 = TestFeatureParser.parse("path/test1.feature", "" + - "Feature: Banana party\n" + - "\n" + - " Scenario: Monkey eats bananas\n" + - " Given there are bananas\n"); - Feature feature2 = TestFeatureParser.parse("path/test2.feature", "" + - "Feature: Orange party\n" + - "\n" + - " Scenario: Monkey eats oranges\n" + - " Given there are oranges\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - StepDurationTimeService timeService = new StepDurationTimeService(ofMillis(1)); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature1, feature2)) - .withAdditionalPlugins(timeService, new JsonFormatter(out)) - .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()"), - new StubStepDefinition("there are oranges", "StepDefs.there_are_oranges()"))) - .build() - .run(); - - String expected = "" + - "[\n" + - " {\n" + - " \"id\": \"banana-party\",\n" + - " \"uri\": \"file:path/test1.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Banana party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"banana-party;monkey-eats-bananas\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.000Z\",\n" + - " \"name\": \"Monkey eats bananas\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are bananas\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_bananas()\"\n" + - " },\n" + - " \"result\": {\n" + - " \"status\": \"passed\",\n" + - " \"duration\": 1000000\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"tags\": []\n" + - " },\n" + - " {\n" + - " \"id\": \"orange-party\",\n" + - " \"uri\": \"file:path/test2.feature\",\n" + - " \"keyword\": \"Feature\",\n" + - " \"name\": \"Orange party\",\n" + - " \"line\": 1,\n" + - " \"description\": \"\",\n" + - " \"elements\": [\n" + - " {\n" + - " \"id\": \"orange-party;monkey-eats-oranges\",\n" + - " \"keyword\": \"Scenario\",\n" + - " \"start_timestamp\": \"1970-01-01T00:00:00.001Z\",\n" + - " \"name\": \"Monkey eats oranges\",\n" + - " \"line\": 3,\n" + - " \"description\": \"\",\n" + - " \"type\": \"scenario\",\n" + - " \"steps\": [\n" + - " {\n" + - " \"keyword\": \"Given \",\n" + - " \"name\": \"there are oranges\",\n" + - " \"line\": 4,\n" + - " \"match\": {\n" + - " \"location\": \"StepDefs.there_are_oranges()\"\n" + + " \"location\": \"io.cucumber.core.plugin.JsonFormatterTestStepDefinitions.there_are_bananas()\"\n" + + " },\n" + " \"result\": {\n" + " \"status\": \"passed\",\n" + diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTestStepDefinitions.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTestStepDefinitions.java new file mode 100644 index 0000000000..730eccbbec --- /dev/null +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTestStepDefinitions.java @@ -0,0 +1,83 @@ +package io.cucumber.core.plugin; + +class JsonFormatterTestStepDefinitions { + + public void bg_1() { + } + + public void bg_2() { + } + + public void bg_3() { + } + + public void step_1() { + } + + public void step_2() { + } + + public void step_3() { + } + + public void cliche() { + } + + public void so_1() { + } + + public void so_2() { + } + + public void so_3() { + } + + public void a() { + } + + public void b() { + } + + public void c() { + } + + public void before_hook_1() { + + } + + public void after_hook_1() { + + } + + public void beforestep_hook_1() { + + } + + public void afterstep_hook_1() { + + } + + public void afterstep_hook_2() { + + } + + public void there_are_bananas() { + + } + + public void there_are_oranges() { + + } + + public void monkey_eats_bananas() { + + } + + public void monkey_eats_more_bananas() { + + } + + public void monkey_arrives() { + + } +} diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java deleted file mode 100644 index c6fcd9057d..0000000000 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonParallelRuntimeTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.cucumber.core.plugin; - -import io.cucumber.core.feature.FeatureWithLines; -import io.cucumber.core.options.RuntimeOptionsBuilder; -import io.cucumber.core.runner.ClockStub; -import io.cucumber.core.runtime.Runtime; -import io.cucumber.core.runtime.TimeServiceEventBus; -import org.json.JSONException; -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; -import java.util.UUID; - -import static java.time.Duration.ZERO; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; - -class JsonParallelRuntimeTest { - - @Test - void testSingleFeature() throws JSONException { - ByteArrayOutputStream parallel = new ByteArrayOutputStream(); - - Runtime.builder() - .withRuntimeOptions( - new RuntimeOptionsBuilder() - .setThreads(3) - .addFeature(FeatureWithLines.parse( - "src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature")) - .build()) - .withAdditionalPlugins(new JsonFormatter(parallel)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) - .build() - .run(); - - ByteArrayOutputStream serial = new ByteArrayOutputStream(); - - Runtime.builder() - .withRuntimeOptions( - new RuntimeOptionsBuilder() - .setThreads(1) - .addFeature(FeatureWithLines.parse( - "src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature")) - .build()) - .withAdditionalPlugins(new JsonFormatter(serial)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) - .build() - .run(); - - assertEquals(serial.toString(), parallel.toString(), false); - } - - @Test - void testMultipleFeatures() throws JSONException { - ByteArrayOutputStream parallel = new ByteArrayOutputStream(); - - Runtime.builder() - .withRuntimeOptions( - new RuntimeOptionsBuilder() - .setThreads(3) - .addFeature(FeatureWithLines.parse( - "src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature")) - .addFeature(FeatureWithLines - .parse("src/test/resources/io/cucumber/core/plugin/FormatterInParallel.feature")) - .build()) - .withAdditionalPlugins(new JsonFormatter(parallel)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) - .build() - .run(); - - ByteArrayOutputStream serial = new ByteArrayOutputStream(); - - Runtime.builder() - .withRuntimeOptions( - new RuntimeOptionsBuilder() - .setThreads(1) - .addFeature(FeatureWithLines.parse( - "src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature")) - .addFeature(FeatureWithLines - .parse("src/test/resources/io/cucumber/core/plugin/FormatterInParallel.feature")) - .build()) - .withAdditionalPlugins(new JsonFormatter(serial)) - .withEventBus(new TimeServiceEventBus(new ClockStub(ZERO), UUID::randomUUID)) - .build() - .run(); - - assertEquals(serial.toString(), parallel.toString(), false); - } - -} diff --git a/cucumber-core/src/test/resources/io/cucumber/core/plugin/FormatterInParallel.feature b/cucumber-core/src/test/resources/io/cucumber/core/plugin/FormatterInParallel.feature deleted file mode 100644 index 35d38f6fa0..0000000000 --- a/cucumber-core/src/test/resources/io/cucumber/core/plugin/FormatterInParallel.feature +++ /dev/null @@ -1,28 +0,0 @@ -Feature: Feature executed in parallel - - Background: - Given bg_1_parallel - When bg_2_parallel - Then bg_3_parallel - - Scenario: Scenario_1 - Given step_1_parallel - When step_2_parallel - Then step_3_parallel - Then cliché_parallel - - Scenario Outline: ScenarioOutline_1_parallel - Given so_1 _parallel - When so_2 cucumbers_parallel - Then so_3_parallel - - Examples: - | a | b | c | - | 12 | 5 | 7 | - | 20 | 5 | 15 | - - Scenario: Scenario_2 - Given a_parallel - Then b_parallel - When c_parallel - diff --git a/cucumber-core/src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature b/cucumber-core/src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature deleted file mode 100644 index 77cd1c49b8..0000000000 --- a/cucumber-core/src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.feature +++ /dev/null @@ -1,28 +0,0 @@ -Feature: Feature_3 - - Background: - Given bg_1 - When bg_2 - Then bg_3 - - Scenario: Scenario_1 - Given step_1 - When step_2 - Then step_3 - Then cliché - - Scenario Outline: ScenarioOutline_1 - Given so_1 - When so_2 cucumbers - Then so_3 - - Examples: - | a | b | c | - | 12 | 5 | 7 | - | 20 | 5 | 15 | - - Scenario: Scenario_2 - Given a - Then b - When c - diff --git a/cucumber-core/src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.json b/cucumber-core/src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.json deleted file mode 100644 index b86942ff95..0000000000 --- a/cucumber-core/src/test/resources/io/cucumber/core/plugin/JsonPrettyFormatterTest.json +++ /dev/null @@ -1,444 +0,0 @@ -[ - { - "line": 1, - "elements": [ - { - "line": 3, - "name": "", - "description": "", - "type": "background", - "keyword": "Background", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 4, - "name": "bg_1", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 5, - "name": "bg_2", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 6, - "name": "bg_3", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "start_timestamp": "1970-01-01T00:00:00.000Z", - "before": [ - { - "result": { - "status": "passed" - }, - "match": { - "location": "{stubbed location with details}" - } - } - ], - "line": 8, - "name": "Scenario_1", - "description": "", - "id": "feature-3;scenario-1", - "type": "scenario", - "keyword": "Scenario", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 9, - "name": "step_1", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 10, - "name": "step_2", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 11, - "name": "step_3", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - }, - { - "result": { - "status": "passed" - }, - "line": 12, - "name": "cliché", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "line": 3, - "name": "", - "description": "", - "type": "background", - "keyword": "Background", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 4, - "name": "bg_1", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 5, - "name": "bg_2", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 6, - "name": "bg_3", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "start_timestamp": "1970-01-01T00:00:00.000Z", - "before": [ - { - "result": { - "status": "passed" - }, - "match": { - "location": "{stubbed location with details}" - } - } - ], - "line": 21, - "name": "ScenarioOutline_1", - "description": "", - "id": "feature-3;scenariooutline-1;;2", - "type": "scenario", - "keyword": "Scenario Outline", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 15, - "name": "so_1 12", - "match": { - "arguments": [ - { - "val": "12", - "offset": 5 - } - ], - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 16, - "name": "so_2 7 cucumbers", - "match": { - "arguments": [ - { - "val": "7", - "offset": 5 - } - ], - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 17, - "name": "5 so_3", - "match": { - "arguments": [ - { - "val": "5", - "offset": 0 - } - ], - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "line": 3, - "name": "", - "description": "", - "type": "background", - "keyword": "Background", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 4, - "name": "bg_1", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 5, - "name": "bg_2", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 6, - "name": "bg_3", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "start_timestamp": "1970-01-01T00:00:00.000Z", - "before": [ - { - "result": { - "status": "passed" - }, - "match": { - "location": "{stubbed location with details}" - } - } - ], - "line": 22, - "name": "ScenarioOutline_1", - "description": "", - "id": "feature-3;scenariooutline-1;;3", - "type": "scenario", - "keyword": "Scenario Outline", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 15, - "name": "so_1 20", - "match": { - "arguments": [ - { - "val": "20", - "offset": 5 - } - ], - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 16, - "name": "so_2 15 cucumbers", - "match": { - "arguments": [ - { - "val": "15", - "offset": 5 - } - ], - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 17, - "name": "5 so_3", - "match": { - "arguments": [ - { - "val": "5", - "offset": 0 - } - ], - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "line": 3, - "name": "", - "description": "", - "type": "background", - "keyword": "Background", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 4, - "name": "bg_1", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 5, - "name": "bg_2", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "When " - }, - { - "result": { - "status": "passed" - }, - "line": 6, - "name": "bg_3", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - } - ] - }, - { - "start_timestamp": "1970-01-01T00:00:00.000Z", - "before": [ - { - "result": { - "status": "passed" - }, - "match": { - "location": "{stubbed location with details}" - } - } - ], - "line": 24, - "name": "Scenario_2", - "description": "", - "id": "feature-3;scenario-2", - "type": "scenario", - "keyword": "Scenario", - "steps": [ - { - "result": { - "status": "passed" - }, - "line": 25, - "name": "a", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Given " - }, - { - "result": { - "status": "passed" - }, - "line": 26, - "name": "b", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "Then " - }, - { - "result": { - "status": "passed" - }, - "line": 27, - "name": "c", - "match": { - "location": "{stubbed location with details}" - }, - "keyword": "When " - } - ] - } - ], - "name": "Feature_3", - "description": "", - "id": "feature-3", - "keyword": "Feature", - "uri": "classpath:io/cucumber/core/plugin/JsonPrettyFormatterTest.feature", - "tags": [] - } -] diff --git a/cucumber-core/src/test/resources/io/cucumber/core/plugin/surefire-test-report-3.0.xsd b/cucumber-core/src/test/resources/io/cucumber/core/plugin/surefire-test-report-3.0.xsd deleted file mode 100644 index 69d9c93d11..0000000000 --- a/cucumber-core/src/test/resources/io/cucumber/core/plugin/surefire-test-report-3.0.xsd +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -