From 2c1f8e9878cd17f3a966ec5084a53d90d271207b Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Mon, 15 Jan 2024 15:41:21 +0100
Subject: [PATCH 01/15] Enclose scenarios with AsciiDoc tags
These will be used prepare a hierarchical tag overview.
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../jgiven/report/ReportBlockConverter.java | 45 +++---
.../asciidoc/AsciiDocBlockConverter.java | 22 ++-
.../asciidoc/AsciiDocReportModelVisitor.java | 6 +-
.../AsciiDocReportModelVisitorTest.java | 4 +-
.../AsciiDocScenarioBlockConverterTest.java | 144 +++++++++++-------
5 files changed, 133 insertions(+), 88 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/ReportBlockConverter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/ReportBlockConverter.java
index 88bb8b0fcfd..011ced84e4d 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/ReportBlockConverter.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/ReportBlockConverter.java
@@ -39,13 +39,13 @@ String convertStatisticsBlock(ListMultimap featureStat
*
*
* @param name the name of the scenario
- * @param executionStatus was the scenario successful
- * @param duration how long did the scenario run
- * @param tagNames names of the tags if scenario is tagged
+ * @param executionStatus was the scenario successful?
+ * @param duration how long did the scenario run?
+ * @param tags tags the scenario is tagged with
* @param extendedDescription detailed description of the scenario, may be {@code null}
*/
String convertScenarioHeaderBlock(String name, ExecutionStatus executionStatus, long duration,
- List tagNames, String extendedDescription);
+ List tags, String extendedDescription);
/**
* Convert scenario case number and parameters into case header.
@@ -54,7 +54,7 @@ String convertScenarioHeaderBlock(String name, ExecutionStatus executionStatus,
*
* @param caseNr the number of the case, starting from 1
* @param executionStatus whether the case was successful
- * @param duration how long did the scenario case run
+ * @param duration how long did the scenario case run?
* @param description a short description of this case, may be {@code null}
*/
String convertCaseHeaderBlock(int caseNr, ExecutionStatus executionStatus, final long duration, String description);
@@ -62,29 +62,29 @@ String convertScenarioHeaderBlock(String name, ExecutionStatus executionStatus,
/**
* Convert the words that make up the first step into a block.
*
- * @param depth the depth of the step
- * @param words the words to be converted
- * @param status was the step executed successfully
- * @param durationInNanos how long did the step take
- * @param extendedDescription detailed description of the step, may be {@code null}
- * @param caseIsUnsuccessful was the scenario case executed successfully
- * @param currentSectionTitle the current section's title, may be {@code null}
+ * @param depth the depth of the step
+ * @param words the words to be converted
+ * @param status was the step executed successfully
+ * @param durationInNanos how long did the step take?
+ * @param extendedDescription detailed description of the step, may be {@code null}
+ * @param caseIsUnsuccessful was the scenario case executed successfully?
+ * @param currentSectionTitle the current section's title, may be {@code null}
*/
String convertFirstStepBlock(int depth, List words, StepStatus status, long durationInNanos,
- String extendedDescription, boolean caseIsUnsuccessful, String currentSectionTitle);
+ String extendedDescription, boolean caseIsUnsuccessful, String currentSectionTitle);
/**
* Convert the words that make up a step into a block.
*
- * @param depth the depth of the step
- * @param words the words to be converted
- * @param status was the step executed successfully
- * @param durationInNanos how long did the step take
- * @param extendedDescription detailed description of the step, may be {@code null}
- * @param caseIsUnsuccessful was the scenario case executed successfully
+ * @param depth the depth of the step
+ * @param words the words to be converted
+ * @param status was the step executed successfully
+ * @param durationInNanos how long did the step take?
+ * @param extendedDescription detailed description of the step, may be {@code null}
+ * @param caseIsUnsuccessful was the scenario case executed successfully?
*/
String convertStepBlock(int depth, List words, StepStatus status, long durationInNanos,
- String extendedDescription, boolean caseIsUnsuccessful);
+ String extendedDescription, boolean caseIsUnsuccessful);
/**
* Is invoked at the end of a scenario, when the scenario has multiple case and a data table.
@@ -104,8 +104,9 @@ String convertStepBlock(int depth, List words, StepStatus status, long dur
/**
* Is invoked at the end of a scenario.
*
- * @param executionStatus was the scenario successful
+ * @param executionStatus was the scenario successful?
+ * @param tags tags the scenario is tagged with
*/
- String convertScenarioFooterBlock(ExecutionStatus executionStatus);
+ String convertScenarioFooterBlock(ExecutionStatus executionStatus, List tags);
}
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java
index 6b9bcac8f1c..d19a4205650 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java
@@ -4,6 +4,7 @@
import static java.util.stream.Stream.generate;
import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
import com.tngtech.jgiven.impl.util.WordUtil;
import com.tngtech.jgiven.report.ReportBlockConverter;
import com.tngtech.jgiven.report.model.CasesTable;
@@ -87,7 +88,10 @@ public String convertScenarioHeaderBlock(final String name, final ExecutionStatu
final String extendedDescription) {
StringBuilder blockContent = new StringBuilder();
- blockContent.append(MetadataMapper.toAsciiDocTagStart(executionStatus)).append(LINE_BREAK);
+ blockContent.append(MetadataMapper.toAsciiDocStartTag(executionStatus)).append(LINE_BREAK);
+
+ tags.forEach(tag -> blockContent.append(TagMapper.toAsciiDocStartTag(tag)).append(LINE_BREAK));
+
blockContent.append(LINE_BREAK);
blockContent.append("==== ").append(WordUtil.capitalize(name)).append(LINE_BREAK);
@@ -107,7 +111,7 @@ public String convertScenarioHeaderBlock(final String name, final ExecutionStatu
blockContent.append(LINE_BREAK);
blockContent.append(LINE_BREAK);
blockContent.append("Tags: ");
- blockContent.append(tags.stream().map(tag -> "_" + TagMapper.mapTag(tag) + "_").collect(joining(", ")));
+ blockContent.append(tags.stream().map(tag -> "_" + TagMapper.toHumanReadableLabel(tag) + "_").collect(joining(", ")));
}
return blockContent.toString();
@@ -231,8 +235,14 @@ public String convertCaseFooterBlock(final String errorMessage, final List tags) {
+ StringBuilder blockContent = new StringBuilder();
+
+ Lists.reverse(tags).forEach(tag -> blockContent.append(TagMapper.toAsciiDocEndTag(tag)).append(LINE_BREAK));
+
+ blockContent.append(MetadataMapper.toAsciiDocEndTag(executionStatus));
+
+ return blockContent.toString();
}
private static boolean appendWordFragments(final StringBuilder blockContent, final List words,
@@ -285,8 +295,8 @@ private static boolean appendFragment(final StringBuilder blockContent, final bo
final String fragment) {
final String lineContinuation = lastFragmentWasBlockFragment ? "" : " ";
if (fragment.contains(LINE_BREAK)) {
- if (!statusAlreadyAppended && !statusFragment.isBlank()){
- blockContent.append(" ").append(statusFragment);
+ if (!statusAlreadyAppended && !statusFragment.isBlank()) {
+ blockContent.append(" ").append(statusFragment);
}
blockContent.append(fragment);
return true;
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
index 32b444952c7..351e2a5bdcb 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
@@ -131,7 +131,11 @@ public void visitEnd(final ScenarioModel scenarioModel) {
asciiDocBlocks.add(casesTableBlock);
}
- String scenarioFooter = blockConverter.convertScenarioFooterBlock(scenarioModel.getExecutionStatus());
+ final List tags = scenarioModel.getTagIds().stream()
+ .map(this.featureTagMap::get)
+ .collect(Collectors.toList());
+
+ String scenarioFooter = blockConverter.convertScenarioFooterBlock(scenarioModel.getExecutionStatus(), tags);
asciiDocBlocks.add(scenarioFooter);
}
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java
index 24f85e26422..374cb70534d 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java
@@ -256,7 +256,7 @@ public String convertFeatureHeaderBlock(String featureName, ReportStatistics sta
@Override
public String convertScenarioHeaderBlock(String name, ExecutionStatus executionStatus, long duration,
- List tagNames, String extendedDescription) {
+ List tags, String extendedDescription) {
return "ScenarioHeaderBlock";
}
@@ -290,7 +290,7 @@ public String convertCaseFooterBlock(final String errorMessage, final List tags) {
return "ScenarioFooterBlock";
}
}
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocScenarioBlockConverterTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocScenarioBlockConverterTest.java
index 7496c052914..21a80e5152d 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocScenarioBlockConverterTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocScenarioBlockConverterTest.java
@@ -6,7 +6,6 @@
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.jgiven.report.model.ExecutionStatus;
import com.tngtech.jgiven.report.model.Tag;
-import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -18,134 +17,165 @@ public class AsciiDocScenarioBlockConverterTest {
@Test
@DataProvider({"SUCCESS, successful, icon:check-square[role=green]",
- "FAILED, failed, icon:exclamation-circle[role=red]",
- "SCENARIO_PENDING, pending, icon:ban[role=silver]",
- "SOME_STEPS_PENDING, pending, icon:ban[role=silver]"})
+ "FAILED, failed, icon:exclamation-circle[role=red]",
+ "SCENARIO_PENDING, pending, icon:ban[role=silver]",
+ "SOME_STEPS_PENDING, pending, icon:ban[role=silver]"})
public void convert_scenario_header_without_tags_or_description(final ExecutionStatus status,
final String scenarioTag,
final String humanStatus) {
// given
- List tagNames = new ArrayList<>();
- final long oneSecond = 1_000_000_000L;
+ List tags = List.of();
+ long oneSecond = 1_000_000_000L;
// when
- String block = converter.convertScenarioHeaderBlock("my first scenario", status, oneSecond, tagNames, null);
+ String block = converter.convertScenarioHeaderBlock("my first scenario", status, oneSecond, tags, null);
// then
assertThatBlockContainsLines(block,
- "// tag::scenario-" + scenarioTag + "[]",
- "",
- "==== My first scenario",
- "",
- humanStatus + " (1s 0ms)");
+ "// tag::scenario-" + scenarioTag + "[]",
+ "",
+ "==== My first scenario",
+ "",
+ humanStatus + " (1s 0ms)");
}
@Test
public void convert_scenario_header_with_a_tag_and_no_description() {
// given
- List tagNames = new ArrayList<>();
- tagNames.add(mkTag("Best Tag"));
- final long nineMilliseconds = 10_000_000L;
+ List tags = List.of(mkTag("BestTag"));
+ long nineMilliseconds = 10_000_000L;
// when
String block = converter.convertScenarioHeaderBlock("my first scenario",
- ExecutionStatus.SCENARIO_PENDING, nineMilliseconds, tagNames, "");
+ ExecutionStatus.SCENARIO_PENDING, nineMilliseconds, tags, "");
// then
assertThatBlockContainsLines(block,
- "// tag::scenario-pending[]",
- "",
- "==== My first scenario",
- "",
- "icon:ban[role=silver] (10ms)",
- "",
- "Tags: _[.jg-tag-ArbitraryTag]#Best Tag#_");
+ "// tag::scenario-pending[]",
+ "// tag::com.jgiven.ArbitraryTag-BestTag[]",
+ "",
+ "==== My first scenario",
+ "",
+ "icon:ban[role=silver] (10ms)",
+ "",
+ "Tags: _[.jg-tag-ArbitraryTag]#BestTag#_");
}
@Test
public void convert_scenario_header_with_description_and_no_tags() {
// given
- List tagNames = new ArrayList<>();
- final long halfMillisecond = 500_000L;
+ List tags = List.of();
+ long halfMillisecond = 500_000L;
// when
String block = converter.convertScenarioHeaderBlock("my first scenario",
- ExecutionStatus.SOME_STEPS_PENDING, halfMillisecond, tagNames, "Best scenario ever!!!");
+ ExecutionStatus.SOME_STEPS_PENDING, halfMillisecond, tags, "Best scenario ever!!!");
// then
assertThatBlockContainsLines(block,
- "// tag::scenario-pending[]",
- "",
- "==== My first scenario",
- "",
- "icon:ban[role=silver] (0ms)",
- "",
- "+++Best scenario ever!!!+++");
+ "// tag::scenario-pending[]",
+ "",
+ "==== My first scenario",
+ "",
+ "icon:ban[role=silver] (0ms)",
+ "",
+ "+++Best scenario ever!!!+++");
}
@Test
public void convert_scenario_header_with_a_tag_and_description() {
// given
- List tagNames = new ArrayList<>();
- tagNames.add(mkTag("Best Tag"));
- final long threeSeconds = 3_000_000_000L;
+ List tags = List.of(mkTag("BestTag"));
+ long threeSeconds = 3_000_000_000L;
// when
String block =
- converter.convertScenarioHeaderBlock("my first scenario", ExecutionStatus.SUCCESS, threeSeconds, tagNames,
- "Best scenario ever!!!");
+ converter.convertScenarioHeaderBlock("my first scenario", ExecutionStatus.SUCCESS, threeSeconds, tags,
+ "Best scenario ever!!!");
// then
assertThatBlockContainsLines(block,
- "// tag::scenario-successful[]",
- "",
- "==== My first scenario",
- "",
- "icon:check-square[role=green] (3s 0ms)",
- "",
- "+++Best scenario ever!!!+++",
- "",
- "Tags: _[.jg-tag-ArbitraryTag]#Best Tag#_");
+ "// tag::scenario-successful[]",
+ "// tag::com.jgiven.ArbitraryTag-BestTag[]",
+ "",
+ "==== My first scenario",
+ "",
+ "icon:check-square[role=green] (3s 0ms)",
+ "",
+ "+++Best scenario ever!!!+++",
+ "",
+ "Tags: _[.jg-tag-ArbitraryTag]#BestTag#_");
}
@Test
public void convert_scenario_header_with_multiple_tags() {
// given
- List tagNames = new ArrayList<>();
- tagNames.add(mkTag("Best Tag"));
- tagNames.add(mkTag("Other Tag"));
- tagNames.add(mkTag("Nicest Tag"));
- final long threeSeconds = 3_000_000_000L;
+ List tags = List.of(mkTag("BestTag"), mkTag("OtherTag"), mkTag("NicestTag"));
+ long threeSeconds = 3_000_000_000L;
// when
String block =
converter.convertScenarioHeaderBlock("my first scenario", ExecutionStatus.SUCCESS, threeSeconds,
- tagNames, "");
+ tags, "");
// then
assertThatBlockContainsLines(block,
"// tag::scenario-successful[]",
+ "// tag::com.jgiven.ArbitraryTag-BestTag[]",
+ "// tag::com.jgiven.ArbitraryTag-OtherTag[]",
+ "// tag::com.jgiven.ArbitraryTag-NicestTag[]",
"",
"==== My first scenario",
"",
"icon:check-square[role=green] (3s 0ms)",
"",
- "Tags: _[.jg-tag-ArbitraryTag]#Best Tag#_, _[.jg-tag-ArbitraryTag]#Other Tag#_, "
- + "_[.jg-tag-ArbitraryTag]#Nicest Tag#_");
+ "Tags: _[.jg-tag-ArbitraryTag]#BestTag#_, _[.jg-tag-ArbitraryTag]#OtherTag#_, "
+ + "_[.jg-tag-ArbitraryTag]#NicestTag#_");
}
@Test
@DataProvider({"SUCCESS, successful", "FAILED, failed", "SCENARIO_PENDING, pending", "SOME_STEPS_PENDING, pending"})
- public void convert_scenario_footer(final ExecutionStatus status, final String scenarioTag) {
+ public void convert_scenario_footer_without_tags(final ExecutionStatus status, final String scenarioTag) {
// given
+ List tags = List.of();
// when
- String block = converter.convertScenarioFooterBlock(status);
+ String block = converter.convertScenarioFooterBlock(status, tags);
// then
assertThat(block).isEqualTo("// end::scenario-" + scenarioTag + "[]");
}
+ @Test
+ public void convert_scenario_footer_with_a_tag() {
+ // given
+ List tags = List.of(mkTag("BestTag"));
+
+ // when
+ String block = converter.convertScenarioFooterBlock(ExecutionStatus.FAILED, tags);
+
+ // then
+ assertThatBlockContainsLines(block,
+ "// end::com.jgiven.ArbitraryTag-BestTag[]",
+ "// end::scenario-failed[]");
+ }
+
+ @Test
+ public void convert_scenario_footer_with_multiple_tags() {
+ // given
+ List tags = List.of(mkTag("BestTag"), mkTag("OtherTag"), mkTag("NicestTag"));
+
+ // when
+ String block = converter.convertScenarioFooterBlock(ExecutionStatus.FAILED, tags);
+
+ // then
+ assertThatBlockContainsLines(block,
+ "// end::com.jgiven.ArbitraryTag-NicestTag[]",
+ "// end::com.jgiven.ArbitraryTag-OtherTag[]",
+ "// end::com.jgiven.ArbitraryTag-BestTag[]",
+ "// end::scenario-failed[]");
+ }
+
private static Tag mkTag(final String value) {
final Tag tag = new Tag("com.jgiven.ArbitraryTag", value);
tag.setType("ArbitraryTag");
From dcc2273206c3057cf5d03609f92aef75307db66d Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Mon, 15 Jan 2024 15:44:19 +0100
Subject: [PATCH 02/15] Simplify test to ensure both snippets are compatible
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../report/asciidoc/MetadataMapper.java | 8 +++---
.../report/asciidoc/MetadataMapperTest.java | 25 ++++++-------------
2 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/MetadataMapper.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/MetadataMapper.java
index 2ce10917916..41f35fa143f 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/MetadataMapper.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/MetadataMapper.java
@@ -12,18 +12,18 @@ final class MetadataMapper {
private static final int NANOSECONDS_PER_MILLISECOND = 1000000;
private MetadataMapper() {
- // static helper class not intended to be instantiated
+ // static helper class isn't intended to be instantiated
}
- static String toAsciiDocTagStart(ExecutionStatus executionStatus) {
+ static String toAsciiDocStartTag(ExecutionStatus executionStatus) {
return "// tag::" + toAsciiDocTagName(executionStatus) + "[]";
}
- static String toAsciiDocTagEnd(ExecutionStatus executionStatus) {
+ static String toAsciiDocEndTag(ExecutionStatus executionStatus) {
return "// end::" + toAsciiDocTagName(executionStatus) + "[]";
}
- static String toAsciiDocTagName(final ExecutionStatus executionStatus) {
+ private static String toAsciiDocTagName(final ExecutionStatus executionStatus) {
switch (executionStatus) {
case SCENARIO_PENDING:
case SOME_STEPS_PENDING:
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/MetadataMapperTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/MetadataMapperTest.java
index 92fe07804bb..a7aaf01b326 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/MetadataMapperTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/MetadataMapperTest.java
@@ -14,21 +14,6 @@
*/
@RunWith(DataProviderRunner.class)
public class MetadataMapperTest {
-
- @Test
- public void toAsciiDocTagStart() {
- final String actualName = MetadataMapper.toAsciiDocTagStart(ExecutionStatus.SUCCESS);
-
- assertThat(actualName).isEqualTo("// tag::scenario-successful[]");
- }
-
- @Test
- public void toAsciiDocTagEnd() {
- final String actualName = MetadataMapper.toAsciiDocTagEnd(ExecutionStatus.SUCCESS);
-
- assertThat(actualName).isEqualTo("// end::scenario-successful[]");
- }
-
@Test
@DataProvider({
"SUCCESS, scenario-successful",
@@ -36,9 +21,13 @@ public void toAsciiDocTagEnd() {
"SCENARIO_PENDING, scenario-pending",
"SOME_STEPS_PENDING, scenario-pending"})
public void toAsciiDocTagName(final ExecutionStatus executionStatus, final String expectedName) {
- final String actualName = MetadataMapper.toAsciiDocTagName(executionStatus);
+ // when
+ final String startSnippet = MetadataMapper.toAsciiDocStartTag(executionStatus);
+ final String endSnippet = MetadataMapper.toAsciiDocEndTag(executionStatus);
- assertThat(actualName).isEqualTo(expectedName);
+ // then
+ assertThat(startSnippet).isEqualTo("// tag::" + expectedName + "[]");
+ assertThat(endSnippet).isEqualTo("// end::" + expectedName + "[]");
}
@Test
@@ -86,7 +75,7 @@ public void toScenarioDurationForDurationOver1ms(final long nanoseconds, final S
}
@Test
- public void toStepDurationBelow1ms() {
+ public void toStepDurationBelow10ms() {
final String actualDuration = MetadataMapper.toHumanReadableStepDuration(9_999_999);
assertThat(actualDuration).isEmpty();
From 240daf572b5dc696f9f5fc2988e73e4e8a294621 Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Mon, 22 Jan 2024 18:09:17 +0100
Subject: [PATCH 03/15] Add mapping JGiven tags to AsciiDoc tags
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../jgiven/report/asciidoc/TagMapper.java | 18 ++-
.../jgiven/report/asciidoc/TagMapperTest.java | 122 +++++++++++++-----
2 files changed, 107 insertions(+), 33 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
index 38e36cf411f..09f0a51d98c 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
@@ -5,11 +5,23 @@
final class TagMapper {
private TagMapper() {
- // static helper class not intended to be instantiated
+ // static helper class isn't intended to be instantiated
}
- static String mapTag(final Tag tag) {
+ static String toHumanReadableLabel(final Tag tag) {
final String cssClassOrType = tag.getCssClass() == null ? "jg-tag-" + tag.getType() : tag.getCssClass();
- return "[." + cssClassOrType + "]#" + tag.toString() + "#";
+ return "[." + cssClassOrType + "]#" + tag + "#";
+ }
+
+ static String toAsciiDocStartTag(final Tag tag) {
+ return "// tag::" + toAsciiDocTagName(tag) + "[]";
+ }
+
+ static String toAsciiDocEndTag(final Tag tag) {
+ return "// end::" + toAsciiDocTagName(tag) + "[]";
+ }
+
+ private static String toAsciiDocTagName(final Tag tag) {
+ return tag.toIdString();
}
}
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java
index dc14e53cd74..11db56952ea 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java
@@ -1,88 +1,150 @@
package com.tngtech.jgiven.report.asciidoc;
+import static org.assertj.core.api.Assertions.assertThat;
+
import com.tngtech.jgiven.report.model.Tag;
-import java.util.List;
-import org.assertj.core.api.Assertions;
import org.junit.Test;
+import java.util.List;
+
public class TagMapperTest {
@Test
- public void map_simple_tag() {
+ public void simple_tag_to_label() {
// given
final Tag tag = new Tag("com.tngtech.jgiven.tags.Feature");
tag.setType("Feature");
// when
- final String snippet = TagMapper.mapTag(tag);
+ final String snippet = TagMapper.toHumanReadableLabel(tag);
// then
- Assertions.assertThat(snippet).isEqualTo("[.jg-tag-Feature]#Feature#");
+ assertThat(snippet).isEqualTo("[.jg-tag-Feature]#Feature#");
}
@Test
- public void map_simple_tag_with_name() {
- // given
- final Tag tag = new Tag("com.tngtech.jgiven.tags.Priority", "Core Features", null);
- tag.setType("FeatureCore");
-
- // when
- final String snippet = TagMapper.mapTag(tag);
-
- // then
- Assertions.assertThat(snippet).isEqualTo("[.jg-tag-FeatureCore]#Core Features#");
- }
-
- @Test
- public void map_simple_tag_with_single_value() {
+ public void single_value_tag_to_label() {
// given
final Tag tag = new Tag("com.tngtech.jgiven.tags.Story", "ACME-1337");
tag.setType("Story");
// when
- final String snippet = TagMapper.mapTag(tag);
+ final String snippet = TagMapper.toHumanReadableLabel(tag);
// then
- Assertions.assertThat(snippet).isEqualTo("[.jg-tag-Story]#ACME-1337#");
+ assertThat(snippet).isEqualTo("[.jg-tag-Story]#ACME-1337#");
}
@Test
- public void map_combined_tag_with_single_value() {
+ public void single_value_tag_with_type_to_label() {
// given
final Tag tag = new Tag("com.tngtech.jgiven.tags.Issue", "#1337");
tag.setType("Issue");
tag.setPrependType(true);
// when
- final String snippet = TagMapper.mapTag(tag);
+ final String snippet = TagMapper.toHumanReadableLabel(tag);
// then
- Assertions.assertThat(snippet).isEqualTo("[.jg-tag-Issue]#Issue-#1337#");
+ assertThat(snippet).isEqualTo("[.jg-tag-Issue]#Issue-#1337#");
}
@Test
- public void map_simple_tag_with_multiple_values() {
+ public void multiple_value_tag_to_label() {
// given
final Tag tag = new Tag("com.tngtech.jgiven.tags.Story", List.of("ACME-1337", "ACME-4221"));
tag.setType("Story");
// when
- final String snippet = TagMapper.mapTag(tag);
+ final String snippet = TagMapper.toHumanReadableLabel(tag);
// then
- Assertions.assertThat(snippet).isEqualTo("[.jg-tag-Story]#ACME-1337, ACME-4221#");
+ assertThat(snippet).isEqualTo("[.jg-tag-Story]#ACME-1337, ACME-4221#");
}
@Test
- public void map_simple_tag_with_css_class() {
+ public void tag_with_css_class_to_label() {
// given
final Tag tag = new Tag("com.tngtech.jgiven.tags.Priority", "1");
tag.setType("Priority");
tag.setCssClass("hidden");
// when
- final String snippet = TagMapper.mapTag(tag);
+ final String snippet = TagMapper.toHumanReadableLabel(tag);
+
+ // then
+ assertThat(snippet).isEqualTo("[.hidden]#1#");
+ }
+
+ @Test
+ public void tag_with_name_to_label() {
+ // given
+ final Tag tag = new Tag("com.tngtech.jgiven.tags.FeatureCore", "Core Features", null);
+ tag.setType("FeatureCore");
+
+ // when
+ final String snippet = TagMapper.toHumanReadableLabel(tag);
+
+ // then
+ assertThat(snippet).isEqualTo("[.jg-tag-FeatureCore]#Core Features#");
+ }
+
+ @Test
+ public void simple_tag_to_AsciiDoc_tag() {
+ // given
+ final Tag tag = new Tag("com.tngtech.jgiven.tags.Feature");
+ tag.setType("Feature");
+
+ // when
+ final String startSnippet = TagMapper.toAsciiDocStartTag(tag);
+ final String endSnippet = TagMapper.toAsciiDocEndTag(tag);
+
+ // then
+ assertThat(startSnippet).isEqualTo("// tag::com.tngtech.jgiven.tags.Feature[]");
+ assertThat(endSnippet).isEqualTo("// end::com.tngtech.jgiven.tags.Feature[]");
+ }
+
+ @Test
+ public void single_value_tag_to_AsciiDoc_tag() {
+ // given
+ final Tag tag = new Tag("com.tngtech.jgiven.tags.Feature", "AsciiDoc");
+ tag.setType("Feature");
+
+ // when
+ final String startSnippet = TagMapper.toAsciiDocStartTag(tag);
+ final String endSnippet = TagMapper.toAsciiDocEndTag(tag);
+
+ // then
+ assertThat(startSnippet).isEqualTo("// tag::com.tngtech.jgiven.tags.Feature-AsciiDoc[]");
+ assertThat(endSnippet).isEqualTo("// end::com.tngtech.jgiven.tags.Feature-AsciiDoc[]");
+ }
+
+ @Test
+ public void single_value_tag_with_type_to_AsciiDoc_tag() {
+ // given
+ final Tag tag = new Tag("com.tngtech.jgiven.tags.Issue", "#1337");
+ tag.setType("Issue");
+ tag.setPrependType(true);
+
+ // when
+ final String startSnippet = TagMapper.toAsciiDocStartTag(tag);
+ final String endSnippet = TagMapper.toAsciiDocEndTag(tag);
+
+ // then
+ assertThat(startSnippet).isEqualTo("// tag::com.tngtech.jgiven.tags.Issue-#1337[]");
+ assertThat(endSnippet).isEqualTo("// end::com.tngtech.jgiven.tags.Issue-#1337[]");
+ }
+
+ @Test
+ public void multiple_value_tag_to_AsciiDoc_tag() {
+ // given
+ final Tag tag = new Tag("com.tngtech.jgiven.tags.Feature", List.of("AsciiDoc", "Markdown"));
+
+ // when
+ final String startSnippet = TagMapper.toAsciiDocStartTag(tag);
+ final String endSnippet = TagMapper.toAsciiDocEndTag(tag);
// then
- Assertions.assertThat(snippet).isEqualTo("[.hidden]#1#");
+ assertThat(startSnippet).isEqualTo("// tag::com.tngtech.jgiven.tags.Feature-AsciiDoc, Markdown[]");
+ assertThat(endSnippet).isEqualTo("// end::com.tngtech.jgiven.tags.Feature-AsciiDoc, Markdown[]");
}
}
From b48c5936689abf1928ae81f117de95cf628c4ac6 Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Tue, 23 Jan 2024 23:14:10 +0100
Subject: [PATCH 04/15] Collect tagged feature files and scenario counts
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../asciidoc/AsciiDocReportModelVisitor.java | 30 ++++++++++++-------
1 file changed, 19 insertions(+), 11 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
index 351e2a5bdcb..393b85b20a0 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
@@ -13,6 +13,7 @@
import com.tngtech.jgiven.report.model.Tag;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -21,23 +22,26 @@
class AsciiDocReportModelVisitor extends ReportModelVisitor {
private final ReportBlockConverter blockConverter;
- private final List asciiDocBlocks;
private final ReportStatistics featureStatistics;
private final CasesTableCalculator tableCalculator;
+ private final List asciiDocBlocks;
+ private final Map featureTagIds;
private Map featureTagMap;
+ private List tagList;
private boolean scenarioHasDataTable;
+ private boolean scenarioHasMultipleCases;
private boolean skipCurrentCase;
private boolean caseIsUnsuccessful;
- private String currentSectionTitle;
- private boolean scenarioHasMultipleCases;
private boolean isFirstStepInCase;
+ private String currentSectionTitle;
public AsciiDocReportModelVisitor(final ReportBlockConverter blockConverter,
final ReportStatistics featureStatistics) {
this.blockConverter = blockConverter;
- this.asciiDocBlocks = new ArrayList<>();
this.featureStatistics = featureStatistics;
this.tableCalculator = new CasesTableCalculator();
+ this.asciiDocBlocks = new ArrayList<>();
+ this.featureTagIds = new HashMap<>();
}
@Override
@@ -55,12 +59,14 @@ public void visit(final ReportModel reportModel) {
@Override
public void visit(final ScenarioModel scenarioModel) {
- final List tags = scenarioModel.getTagIds().stream()
+ scenarioModel.getTagIds().forEach(tagId -> featureTagIds.merge(tagId, 1, Integer::sum));
+
+ tagList = scenarioModel.getTagIds().stream()
.map(this.featureTagMap::get)
.collect(Collectors.toList());
String scenarioHeader = blockConverter.convertScenarioHeaderBlock(scenarioModel.getDescription(),
- scenarioModel.getExecutionStatus(), scenarioModel.getDurationInNanos(), tags,
+ scenarioModel.getExecutionStatus(), scenarioModel.getDurationInNanos(), tagList,
scenarioModel.getExtendedDescription());
asciiDocBlocks.add(scenarioHeader);
@@ -131,15 +137,17 @@ public void visitEnd(final ScenarioModel scenarioModel) {
asciiDocBlocks.add(casesTableBlock);
}
- final List tags = scenarioModel.getTagIds().stream()
- .map(this.featureTagMap::get)
- .collect(Collectors.toList());
-
- String scenarioFooter = blockConverter.convertScenarioFooterBlock(scenarioModel.getExecutionStatus(), tags);
+ String scenarioFooter = blockConverter.convertScenarioFooterBlock(scenarioModel.getExecutionStatus(), tagList);
asciiDocBlocks.add(scenarioFooter);
}
public List getResult() {
return Collections.unmodifiableList(asciiDocBlocks);
}
+
+
+ public Map getUsedTags() {
+ return Collections.unmodifiableMap(featureTagIds);
+ }
+
}
From 7659507701548f42f8ab723d39ab3c2b7fbcbbb1 Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Tue, 23 Jan 2024 23:14:40 +0100
Subject: [PATCH 05/15] Write index files only for used tags
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../asciidoc/AsciiDocReportGenerator.java | 95 ++++++++++++++-----
.../asciidoc/AsciiDocSnippetGenerator.java | 21 ++--
.../jgiven/report/asciidoc/TagMapper.java | 2 +-
.../AsciiDocSnippetGeneratorTest.java | 4 +-
4 files changed, 87 insertions(+), 35 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
index d6981927c06..3b102346cb7 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
@@ -8,17 +8,21 @@
import com.tngtech.jgiven.impl.util.PrintWriterUtil;
import com.tngtech.jgiven.report.AbstractReportConfig;
import com.tngtech.jgiven.report.AbstractReportGenerator;
+import com.tngtech.jgiven.report.model.ReportModel;
import com.tngtech.jgiven.report.model.ReportModelFile;
import com.tngtech.jgiven.report.model.ReportStatistics;
+import com.tngtech.jgiven.report.model.Tag;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,14 +41,20 @@
*/
public class AsciiDocReportGenerator extends AbstractReportGenerator {
+ private static final String FEATURE_PATH = "features";
private static final Logger log = LoggerFactory.getLogger(AsciiDocReportGenerator.class);
private final AsciiDocBlockConverter blockConverter = new AsciiDocBlockConverter();
+ private final HashMap allTags = new HashMap<>();
private final List featureFiles = new ArrayList<>();
private final List failedScenarioFiles = new ArrayList<>();
private final List pendingScenarioFiles = new ArrayList<>();
+ private final HashMap> taggedScenarioFiles = new HashMap<>();
+ private final Map taggedScenarioCounts = new HashMap<>();
+
private File targetDir;
private File featuresDir;
+ private File tagsDir;
@Override
public AsciiDocReportConfig createReportConfig(String... args) {
@@ -73,6 +83,8 @@ public void generate() {
writeIndexFileForPendingScenarios();
+ taggedScenarioFiles.forEach(this::writeIndexFileForTaggedScenarios);
+
writeTotalStatisticsFile();
writeIndexFileForFullReport(config.getTitle());
@@ -90,6 +102,11 @@ private boolean prepareDirectories(final File targetDir) {
return false;
}
+ tagsDir = new File(this.targetDir.getPath() + "/tags");
+ if (!ensureDirectoryExists(this.tagsDir)) {
+ return false;
+ }
+
featuresDir = new File(this.targetDir.getPath() + "/features");
return ensureDirectoryExists(featuresDir);
}
@@ -98,17 +115,21 @@ private void writeFeatureFiles() {
completeReportModel.getAllReportModels().stream()
.sorted(Comparator.comparing(AsciiDocReportGenerator::byFeatureName))
.forEach(reportModelFile -> {
- final String featureFileName = Files.getNameWithoutExtension(
+ final ReportStatistics statistics = completeReportModel.getStatistics(reportModelFile);
+ final String fileName = Files.getNameWithoutExtension(
reportModelFile.file().getName()) + ".asciidoc";
- writeAsciiDocBlocksToFile(new File(featuresDir, featureFileName),
- collectReportBlocks(reportModelFile, featureFileName));
+ final List asciiDocBlocks = collectReportBlocks(fileName, statistics, reportModelFile.model());
+ writeAsciiDocBlocksToFile(featuresDir, fileName, asciiDocBlocks);
});
}
- private List collectReportBlocks(final ReportModelFile reportModelFile, final String featureFileName) {
- featureFiles.add(featureFileName);
+ private List collectReportBlocks(
+ final String featureFileName,
+ final ReportStatistics statistics,
+ final ReportModel model) {
+ allTags.putAll(model.getTagMap());
- final ReportStatistics statistics = completeReportModel.getStatistics(reportModelFile);
+ featureFiles.add(featureFileName);
if (statistics.numFailedScenarios > 0) {
failedScenarioFiles.add(featureFileName);
}
@@ -117,42 +138,62 @@ private List collectReportBlocks(final ReportModelFile reportModelFile,
}
final AsciiDocReportModelVisitor visitor = new AsciiDocReportModelVisitor(blockConverter, statistics);
- reportModelFile.model().accept(visitor);
+ model.accept(visitor);
+
+ visitor.getUsedTags().forEach((tagId, count) -> {
+ taggedScenarioCounts.merge(tagId, count, Integer::sum);
+ taggedScenarioFiles.computeIfAbsent(tagId, key -> new ArrayList<>()).add(featureFileName);
+ });
return visitor.getResult();
}
private void writeIndexFileForAllScenarios() {
+ final int numScenarios = this.completeReportModel.getTotalStatistics().numScenarios;
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "All Scenarios", "scenarios in total", this.featureFiles, "",
- this.completeReportModel.getTotalStatistics().numScenarios);
+ "All Scenarios", "scenarios in total", numScenarios, "",
+ FEATURE_PATH, this.featureFiles
+ );
- writeAsciiDocBlocksToFile(new File(targetDir, "allScenarios.asciidoc"),
- snippetGenerator.generateIndexSnippet());
+ writeAsciiDocBlocksToFile(targetDir, "allScenarios.asciidoc", snippetGenerator.generateIndexSnippet());
}
private void writeIndexFileForFailedScenarios() {
final String scenarioKind = "failed";
+ final int numFailedScenarios = this.completeReportModel.getTotalStatistics().numFailedScenarios;
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "Failed Scenarios", "failed scenarios", this.failedScenarioFiles, scenarioKind,
- this.completeReportModel.getTotalStatistics().numFailedScenarios);
+ "Failed Scenarios", "failed scenarios", numFailedScenarios, "scenario-" + scenarioKind,
+ FEATURE_PATH, this.failedScenarioFiles
+ );
- writeAsciiDocBlocksToFile(new File(targetDir, scenarioKind + "Scenarios.asciidoc"),
+ writeAsciiDocBlocksToFile(targetDir, scenarioKind + "Scenarios.asciidoc",
snippetGenerator.generateIndexSnippet());
}
private void writeIndexFileForPendingScenarios() {
final String scenarioKind = "pending";
+ final int numPendingScenarios = this.completeReportModel.getTotalStatistics().numPendingScenarios;
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "Pending Scenarios", "pending scenarios", this.pendingScenarioFiles, scenarioKind,
- this.completeReportModel.getTotalStatistics().numPendingScenarios);
+ "Pending Scenarios", "pending scenarios", numPendingScenarios, "scenario-" + scenarioKind,
+ FEATURE_PATH, this.pendingScenarioFiles
+ );
- writeAsciiDocBlocksToFile(new File(targetDir, scenarioKind + "Scenarios.asciidoc"),
+ writeAsciiDocBlocksToFile(targetDir, scenarioKind + "Scenarios.asciidoc",
snippetGenerator.generateIndexSnippet());
}
- private void writeTotalStatisticsFile() {
+ private void writeIndexFileForTaggedScenarios(final String tagId, final List files) {
+ final Tag tag = allTags.get(tagId);
+ final String tagName = TagMapper.toAsciiDocTagName(tag);
+ final int numTaggedScenarios = taggedScenarioCounts.get(tagId);
+ final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
+ tag.getName(), "tagged scenarios", numTaggedScenarios, tagName,
+ "../" + FEATURE_PATH, files);
+ writeAsciiDocBlocksToFile(tagsDir, tagName + ".asciidoc", snippetGenerator.generateIndexSnippet());
+ }
+
+ private void writeTotalStatisticsFile() {
final ListMultimap featureStatistics = completeReportModel.getAllReportModels()
.stream()
.collect(Multimaps.toMultimap(
@@ -163,16 +204,16 @@ private void writeTotalStatisticsFile() {
final String statisticsBlock = blockConverter.convertStatisticsBlock(
featureStatistics, completeReportModel.getTotalStatistics());
- writeAsciiDocBlocksToFile(new File(targetDir, "totalStatistics.asciidoc"),
- Collections.singletonList(statisticsBlock));
+ writeAsciiDocBlocksToFile(targetDir, "totalStatistics.asciidoc", Collections.singletonList(statisticsBlock));
}
private void writeIndexFileForFullReport(final String reportTitle) {
final URL resourceUrl = Resources.getResource(this.getClass(), "index.asciidoc");
try {
- final List indexLines = Resources.readLines(resourceUrl, Charset.defaultCharset());
+ final List indexLines = Resources.readLines(resourceUrl, StandardCharsets.UTF_8);
- try (PrintWriter writer = PrintWriterUtil.getPrintWriter(new File(targetDir, "index.asciidoc"))) {
+ final File indexFile = new File(targetDir, "index.asciidoc");
+ try (PrintWriter writer = PrintWriterUtil.getPrintWriter(indexFile)) {
writer.println("= " + reportTitle);
indexLines.forEach(writer::println);
}
@@ -189,13 +230,17 @@ private static boolean ensureDirectoryExists(final File directory) {
return true;
}
- private static String byFeatureName(ReportModelFile modelFile) {
+ private static String byFeatureName(final ReportModelFile modelFile) {
return (null != modelFile.model().getName())
? modelFile.model().getName()
: modelFile.model().getClassName();
}
- private static void writeAsciiDocBlocksToFile(final File file, final List asciiDocBlocks) {
+ private static void writeAsciiDocBlocksToFile(
+ final File directory,
+ final String fileName,
+ final List asciiDocBlocks) {
+ final File file = new File(directory, fileName);
try (final PrintWriter writer = PrintWriterUtil.getPrintWriter(file)) {
for (final String block : asciiDocBlocks) {
writer.println(block);
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
index f075c368f20..d30316a9072 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
@@ -13,14 +13,21 @@ final class AsciiDocSnippetGenerator {
private final List featureFiles;
private final int numScenarios;
private final String tagSelector;
+ private final String featurePath;
- AsciiDocSnippetGenerator(final String title, final String scenarioQualifier,
- final List featureFiles, final String tags, final int numScenarios) {
+ AsciiDocSnippetGenerator(
+ final String title,
+ final String scenarioQualifier,
+ final int numScenarios,
+ final String tags,
+ final String featurePath,
+ final List featureFiles) {
this.title = title;
this.scenarioQualifier = scenarioQualifier;
- this.featureFiles = featureFiles;
- this.tagSelector = Strings.isNullOrEmpty(tags) ? "" : "tag=scenario-" + tags;
this.numScenarios = numScenarios;
+ this.tagSelector = Strings.isNullOrEmpty(tags) ? "" : "tag=" + tags;
+ this.featurePath = featurePath;
+ this.featureFiles = featureFiles;
}
List generateIndexSnippet() {
@@ -45,7 +52,7 @@ private List generateIndexSnippet(final String intro, final String tags)
result.add(":leveloffset: -1");
}
- featureFiles.forEach(fileName -> result.add(includeMacroFor(fileName, tags)));
+ featureFiles.forEach(fileName -> result.add(includeMacroFor(featurePath, fileName, tags)));
if (!Strings.isNullOrEmpty(tags)) {
result.add(":leveloffset: +1");
@@ -54,7 +61,7 @@ private List generateIndexSnippet(final String intro, final String tags)
}
- private static String includeMacroFor(final String fileName, final String tags) {
- return "include::features/" + fileName + "[" + tags + "]";
+ private static String includeMacroFor(final String featurePath, final String fileName, final String tags) {
+ return "include::" + featurePath + "/" + fileName + "[" + tags + "]";
}
}
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
index 09f0a51d98c..50ec64758c4 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
@@ -21,7 +21,7 @@ static String toAsciiDocEndTag(final Tag tag) {
return "// end::" + toAsciiDocTagName(tag) + "[]";
}
- private static String toAsciiDocTagName(final Tag tag) {
+ static String toAsciiDocTagName(final Tag tag) {
return tag.toIdString();
}
}
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java
index a66b7e44ec9..cd22db7b152 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java
@@ -17,7 +17,7 @@ public void writeIndexFileForFailedScenarios() {
// when
AsciiDocSnippetGenerator asciiDocSnippetGenerator = new AsciiDocSnippetGenerator(
- "Failed Scenarios", "failed scenarios", featureFileNames, "failed", 3);
+ "Failed Scenarios", "failed scenarios", 3, "scenario-failed", "features", featureFileNames);
final List blocks = asciiDocSnippetGenerator.generateIndexSnippet();
// then
@@ -41,7 +41,7 @@ public void writeIndexFileForAllScenarios() {
// when
AsciiDocSnippetGenerator asciiDocSnippetGenerator = new AsciiDocSnippetGenerator(
- "All Scenarios", "scenarios in total", featureFileNames, "", 40);
+ "All Scenarios", "scenarios in total", 40, "", "features", featureFileNames);
final List blocks = asciiDocSnippetGenerator.generateIndexSnippet();
// then
From 2a3dfaf5febaf11f35e6192738fee1a5fe15f5ce Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Mon, 5 Feb 2024 22:20:23 +0100
Subject: [PATCH 06/15] Replace spaces in tag names
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../report/asciidoc/AsciiDocBlockConverter.java | 3 ++-
.../report/asciidoc/AsciiDocReportGenerator.java | 2 +-
.../report/asciidoc/AsciiDocReportModelVisitor.java | 3 +--
.../tngtech/jgiven/report/asciidoc/TagMapper.java | 2 +-
.../tngtech/jgiven/report/asciidoc/index.asciidoc | 2 +-
.../asciidoc/AsciiDocReportModelVisitorTest.java | 12 ++++++------
.../jgiven/report/asciidoc/TagMapperTest.java | 4 ++--
7 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java
index d19a4205650..ff6cdbfec82 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocBlockConverter.java
@@ -46,7 +46,8 @@ public String convertStatisticsBlock(final ListMultimap appendStatisticsRowFragment(statisticsTable, entry.getKey(), entry.getValue()));
appendStatisticsRowFragment(statisticsTable, "sum", totalStatistics);
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
index 3b102346cb7..f412a4a5352 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
@@ -145,7 +145,7 @@ private List collectReportBlocks(
taggedScenarioFiles.computeIfAbsent(tagId, key -> new ArrayList<>()).add(featureFileName);
});
- return visitor.getResult();
+ return visitor.getAsciiDocBlocks();
}
private void writeIndexFileForAllScenarios() {
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
index 393b85b20a0..8f419f4b9dd 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitor.java
@@ -141,11 +141,10 @@ public void visitEnd(final ScenarioModel scenarioModel) {
asciiDocBlocks.add(scenarioFooter);
}
- public List getResult() {
+ public List getAsciiDocBlocks() {
return Collections.unmodifiableList(asciiDocBlocks);
}
-
public Map getUsedTags() {
return Collections.unmodifiableMap(featureTagIds);
}
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
index 50ec64758c4..bdd2d1014f0 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/TagMapper.java
@@ -22,6 +22,6 @@ static String toAsciiDocEndTag(final Tag tag) {
}
static String toAsciiDocTagName(final Tag tag) {
- return tag.toIdString();
+ return tag.toIdString().replace(' ', '_');
}
}
diff --git a/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc b/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc
index d8cdbcc293c..75ec86dcd6a 100644
--- a/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc
+++ b/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc
@@ -1,6 +1,6 @@
// Report title is provided via report config.
:toc: left
-:toclevels: 3
+:toclevels: 2
:icons: font
include::totalStatistics.asciidoc[]
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java
index 374cb70534d..62091b4bb8d 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportModelVisitorTest.java
@@ -42,7 +42,7 @@ public void visits_a_simple_report() {
report.accept(reportModelVisitor);
// then
- assertThat(reportModelVisitor.getResult())
+ assertThat(reportModelVisitor.getAsciiDocBlocks())
.isEqualTo(ImmutableList.of(
"FeatureHeaderBlock",
"ScenarioHeaderBlock",
@@ -70,7 +70,7 @@ public void visits_a_report_with_two_scenarios() {
report.accept(reportModelVisitor);
// then
- assertThat(reportModelVisitor.getResult())
+ assertThat(reportModelVisitor.getAsciiDocBlocks())
.isEqualTo(ImmutableList.of(
"FeatureHeaderBlock",
"ScenarioHeaderBlock",
@@ -102,7 +102,7 @@ public void visits_a_scenario_with_two_standalone_cases() {
report.accept(reportModelVisitor);
// then
- assertThat(reportModelVisitor.getResult())
+ assertThat(reportModelVisitor.getAsciiDocBlocks())
.isEqualTo(ImmutableList.of(
"FeatureHeaderBlock",
"ScenarioHeaderBlock",
@@ -134,7 +134,7 @@ public void visits_a_scenario_with_two_cases_as_table() {
report.accept(reportModelVisitor);
// then
- assertThat(reportModelVisitor.getResult())
+ assertThat(reportModelVisitor.getAsciiDocBlocks())
.isEqualTo(ImmutableList.of(
"FeatureHeaderBlock",
"ScenarioHeaderBlock",
@@ -158,7 +158,7 @@ public void visits_a_scenario_with_a_section() {
report.accept(reportModelVisitor);
// then
- assertThat(reportModelVisitor.getResult())
+ assertThat(reportModelVisitor.getAsciiDocBlocks())
.isEqualTo(ImmutableList.of(
"FeatureHeaderBlock",
"ScenarioHeaderBlock",
@@ -185,7 +185,7 @@ public void visits_a_scenario_with_two_sections() {
report.accept(reportModelVisitor);
// then
- assertThat(reportModelVisitor.getResult())
+ assertThat(reportModelVisitor.getAsciiDocBlocks())
.isEqualTo(ImmutableList.of(
"FeatureHeaderBlock",
"ScenarioHeaderBlock",
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java
index 11db56952ea..1c04f96ab5b 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/TagMapperTest.java
@@ -144,7 +144,7 @@ public void multiple_value_tag_to_AsciiDoc_tag() {
final String endSnippet = TagMapper.toAsciiDocEndTag(tag);
// then
- assertThat(startSnippet).isEqualTo("// tag::com.tngtech.jgiven.tags.Feature-AsciiDoc, Markdown[]");
- assertThat(endSnippet).isEqualTo("// end::com.tngtech.jgiven.tags.Feature-AsciiDoc, Markdown[]");
+ assertThat(startSnippet).isEqualTo("// tag::com.tngtech.jgiven.tags.Feature-AsciiDoc,_Markdown[]");
+ assertThat(endSnippet).isEqualTo("// end::com.tngtech.jgiven.tags.Feature-AsciiDoc,_Markdown[]");
}
}
From af5badc0e03f6e0d953f9d87ac78641d42e38f6f Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Mon, 5 Feb 2024 22:28:50 +0100
Subject: [PATCH 07/15] Group scenarios by tags
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../asciidoc/AsciiDocReportGenerator.java | 173 +++++++++---------
.../asciidoc/AsciiDocSnippetGenerator.java | 81 +++++---
.../AsciiDocSnippetGeneratorTest.java | 20 +-
3 files changed, 154 insertions(+), 120 deletions(-)
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
index f412a4a5352..bc0c8a61d47 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
@@ -42,19 +42,21 @@
public class AsciiDocReportGenerator extends AbstractReportGenerator {
private static final String FEATURE_PATH = "features";
+ private static final String ASCIIDOC_FILETYPE = ".asciidoc";
+ private static final String SCENARIO_TAG = "scenario-";
private static final Logger log = LoggerFactory.getLogger(AsciiDocReportGenerator.class);
private final AsciiDocBlockConverter blockConverter = new AsciiDocBlockConverter();
- private final HashMap allTags = new HashMap<>();
- private final List featureFiles = new ArrayList<>();
- private final List failedScenarioFiles = new ArrayList<>();
- private final List pendingScenarioFiles = new ArrayList<>();
- private final HashMap> taggedScenarioFiles = new HashMap<>();
+ private final Map allTags = new HashMap<>();
+ private final List allFeatures = new ArrayList<>();
+ private final List failedScenarioFeatures = new ArrayList<>();
+ private final List pendingScenarioFeatures = new ArrayList<>();
+ private final Map> taggedScenarioFeatures = new HashMap<>();
private final Map taggedScenarioCounts = new HashMap<>();
private File targetDir;
private File featuresDir;
- private File tagsDir;
+
@Override
public AsciiDocReportConfig createReportConfig(String... args) {
@@ -63,18 +65,10 @@ public AsciiDocReportConfig createReportConfig(String... args) {
@Override
public void generate() {
- if (config == null) {
- throw new IllegalStateException("AsciiDocReporter must be configured before generating a report.");
- }
-
- if (!prepareDirectories(config.getTargetDir())) {
+ if (!loadConfigAndModel()) {
return;
}
- if (completeReportModel == null) {
- loadReportModel();
- }
-
writeFeatureFiles();
writeIndexFileForAllScenarios();
@@ -83,32 +77,27 @@ public void generate() {
writeIndexFileForPendingScenarios();
- taggedScenarioFiles.forEach(this::writeIndexFileForTaggedScenarios);
-
writeTotalStatisticsFile();
writeIndexFileForFullReport(config.getTitle());
}
- private boolean prepareDirectories(final File targetDir) {
- this.targetDir = targetDir;
- if (this.targetDir == null) {
- log.error("Target directory was not configured");
- return false;
+
+ private boolean loadConfigAndModel() {
+ if (config == null) {
+ throw new IllegalStateException("AsciiDocReporter must be configured before generating a report.");
}
- if (!ensureDirectoryExists(this.targetDir)) {
+ if (!prepareDirectories(config.getTargetDir())) {
return false;
}
- tagsDir = new File(this.targetDir.getPath() + "/tags");
- if (!ensureDirectoryExists(this.tagsDir)) {
- return false;
+ if (completeReportModel == null) {
+ loadReportModel();
}
- featuresDir = new File(this.targetDir.getPath() + "/features");
- return ensureDirectoryExists(featuresDir);
+ return true;
}
private void writeFeatureFiles() {
@@ -116,81 +105,49 @@ private void writeFeatureFiles() {
.sorted(Comparator.comparing(AsciiDocReportGenerator::byFeatureName))
.forEach(reportModelFile -> {
final ReportStatistics statistics = completeReportModel.getStatistics(reportModelFile);
- final String fileName = Files.getNameWithoutExtension(
- reportModelFile.file().getName()) + ".asciidoc";
- final List asciiDocBlocks = collectReportBlocks(fileName, statistics, reportModelFile.model());
- writeAsciiDocBlocksToFile(featuresDir, fileName, asciiDocBlocks);
- });
- }
-
- private List collectReportBlocks(
- final String featureFileName,
- final ReportStatistics statistics,
- final ReportModel model) {
- allTags.putAll(model.getTagMap());
-
- featureFiles.add(featureFileName);
- if (statistics.numFailedScenarios > 0) {
- failedScenarioFiles.add(featureFileName);
- }
- if (statistics.numPendingScenarios > 0) {
- pendingScenarioFiles.add(featureFileName);
- }
-
- final AsciiDocReportModelVisitor visitor = new AsciiDocReportModelVisitor(blockConverter, statistics);
- model.accept(visitor);
-
- visitor.getUsedTags().forEach((tagId, count) -> {
- taggedScenarioCounts.merge(tagId, count, Integer::sum);
- taggedScenarioFiles.computeIfAbsent(tagId, key -> new ArrayList<>()).add(featureFileName);
- });
+ final String featureName = Files.getNameWithoutExtension(reportModelFile.file().getName());
+ final List asciiDocBlocks = collectReportBlocks(featureName, statistics, reportModelFile.model());
- return visitor.getAsciiDocBlocks();
+ writeAsciiDocBlocksToFile(featuresDir, featureName, asciiDocBlocks);
+ });
}
private void writeIndexFileForAllScenarios() {
final int numScenarios = this.completeReportModel.getTotalStatistics().numScenarios;
+
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "All Scenarios", "scenarios in total", numScenarios, "",
- FEATURE_PATH, this.featureFiles
- );
+ "All Scenarios", "scenarios in total", numScenarios);
+
+ final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
+ asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(FEATURE_PATH, this.allFeatures, "", 0));
- writeAsciiDocBlocksToFile(targetDir, "allScenarios.asciidoc", snippetGenerator.generateIndexSnippet());
+ writeAsciiDocBlocksToFile(targetDir, "allScenarios", asciiDocBlocks);
}
private void writeIndexFileForFailedScenarios() {
- final String scenarioKind = "failed";
final int numFailedScenarios = this.completeReportModel.getTotalStatistics().numFailedScenarios;
+
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "Failed Scenarios", "failed scenarios", numFailedScenarios, "scenario-" + scenarioKind,
- FEATURE_PATH, this.failedScenarioFiles
- );
+ "Failed Scenarios", "failed scenarios", numFailedScenarios);
+
+ final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
+ asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(
+ FEATURE_PATH, this.failedScenarioFeatures, SCENARIO_TAG + "failed", -1));
- writeAsciiDocBlocksToFile(targetDir, scenarioKind + "Scenarios.asciidoc",
- snippetGenerator.generateIndexSnippet());
+ writeAsciiDocBlocksToFile(targetDir, "failedScenarios", asciiDocBlocks);
}
private void writeIndexFileForPendingScenarios() {
- final String scenarioKind = "pending";
final int numPendingScenarios = this.completeReportModel.getTotalStatistics().numPendingScenarios;
- final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "Pending Scenarios", "pending scenarios", numPendingScenarios, "scenario-" + scenarioKind,
- FEATURE_PATH, this.pendingScenarioFiles
- );
- writeAsciiDocBlocksToFile(targetDir, scenarioKind + "Scenarios.asciidoc",
- snippetGenerator.generateIndexSnippet());
- }
-
- private void writeIndexFileForTaggedScenarios(final String tagId, final List files) {
- final Tag tag = allTags.get(tagId);
- final String tagName = TagMapper.toAsciiDocTagName(tag);
- final int numTaggedScenarios = taggedScenarioCounts.get(tagId);
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- tag.getName(), "tagged scenarios", numTaggedScenarios, tagName,
- "../" + FEATURE_PATH, files);
+ "Pending Scenarios", "pending scenarios", numPendingScenarios);
+
+ final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
+ asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(
+ FEATURE_PATH, this.pendingScenarioFeatures, SCENARIO_TAG + "pending", -1));
- writeAsciiDocBlocksToFile(tagsDir, tagName + ".asciidoc", snippetGenerator.generateIndexSnippet());
+ writeAsciiDocBlocksToFile(targetDir, "pendingScenarios", asciiDocBlocks);
}
private void writeTotalStatisticsFile() {
@@ -204,7 +161,7 @@ private void writeTotalStatisticsFile() {
final String statisticsBlock = blockConverter.convertStatisticsBlock(
featureStatistics, completeReportModel.getTotalStatistics());
- writeAsciiDocBlocksToFile(targetDir, "totalStatistics.asciidoc", Collections.singletonList(statisticsBlock));
+ writeAsciiDocBlocksToFile(targetDir, "totalStatistics", Collections.singletonList(statisticsBlock));
}
private void writeIndexFileForFullReport(final String reportTitle) {
@@ -222,6 +179,52 @@ private void writeIndexFileForFullReport(final String reportTitle) {
}
}
+ private boolean prepareDirectories(final File targetDir) {
+ File tagsDir;
+ this.targetDir = targetDir;
+ if (this.targetDir == null) {
+ log.error("Target directory was not configured");
+ return false;
+ }
+
+ if (!ensureDirectoryExists(this.targetDir)) {
+ return false;
+ }
+
+ tagsDir = new File(this.targetDir.getPath() + "/tags");
+ if (!ensureDirectoryExists(tagsDir)) {
+ return false;
+ }
+
+ featuresDir = new File(this.targetDir.getPath() + "/features");
+ return ensureDirectoryExists(featuresDir);
+ }
+
+ private List collectReportBlocks(
+ final String featureName,
+ final ReportStatistics statistics,
+ final ReportModel model) {
+ allTags.putAll(model.getTagMap());
+
+ allFeatures.add(featureName);
+ if (statistics.numFailedScenarios > 0) {
+ failedScenarioFeatures.add(featureName);
+ }
+ if (statistics.numPendingScenarios > 0) {
+ pendingScenarioFeatures.add(featureName);
+ }
+
+ final AsciiDocReportModelVisitor visitor = new AsciiDocReportModelVisitor(blockConverter, statistics);
+ model.accept(visitor);
+
+ visitor.getUsedTags().forEach((tagId, count) -> {
+ taggedScenarioCounts.merge(tagId, count, Integer::sum);
+ taggedScenarioFeatures.computeIfAbsent(tagId, key -> new ArrayList<>()).add(featureName);
+ });
+
+ return visitor.getAsciiDocBlocks();
+ }
+
private static boolean ensureDirectoryExists(final File directory) {
if (!directory.exists() && !directory.mkdirs()) {
log.error("Could not ensure directory exists {}", directory);
@@ -240,7 +243,7 @@ private static void writeAsciiDocBlocksToFile(
final File directory,
final String fileName,
final List asciiDocBlocks) {
- final File file = new File(directory, fileName);
+ final File file = new File(directory, fileName + ASCIIDOC_FILETYPE);
try (final PrintWriter writer = PrintWriterUtil.getPrintWriter(file)) {
for (final String block : asciiDocBlocks) {
writer.println(block);
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
index d30316a9072..ed69b4b9234 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
@@ -1,6 +1,7 @@
package com.tngtech.jgiven.report.asciidoc;
import com.google.common.base.Strings;
+import com.tngtech.jgiven.report.model.Tag;
import java.util.ArrayList;
import java.util.List;
@@ -8,60 +9,94 @@
* Generate snippets for including feature files via AsciiDoc include macro.
*/
final class AsciiDocSnippetGenerator {
+ private static final String LINE_BREAK = System.lineSeparator();
private final String title;
private final String scenarioQualifier;
- private final List featureFiles;
private final int numScenarios;
- private final String tagSelector;
- private final String featurePath;
AsciiDocSnippetGenerator(
final String title,
final String scenarioQualifier,
- final int numScenarios,
- final String tags,
- final String featurePath,
- final List featureFiles) {
+ final int numScenarios) {
this.title = title;
this.scenarioQualifier = scenarioQualifier;
this.numScenarios = numScenarios;
- this.tagSelector = Strings.isNullOrEmpty(tags) ? "" : "tag=" + tags;
- this.featurePath = featurePath;
- this.featureFiles = featureFiles;
}
- List generateIndexSnippet() {
+ List generateIntroSnippet(final String description) {
final ArrayList result = new ArrayList<>();
+
result.add("== " + this.title);
- if (featureFiles.isEmpty()) {
+ if (!description.isEmpty()) {
+ result.add("+++" + LINE_BREAK + description + LINE_BREAK + "+++");
+ }
+
+ if (numScenarios == 0) {
result.add("There are no " + scenarioQualifier + ". Keep rocking!");
} else {
- final String intro = "There are " + numScenarios + " " + scenarioQualifier + ".";
- result.addAll(generateIndexSnippet(intro, this.tagSelector));
+ result.add("There are " + numScenarios + " " + scenarioQualifier + ".");
}
return result;
}
- private List generateIndexSnippet(final String intro, final String tags) {
+ List generateIndexSnippet(final String featurePath, final List featureFiles, final String tags, final int leveloffset) {
+ final String tagSelector = Strings.isNullOrEmpty(tags) ? "" : "tag=" + tags;
final ArrayList result = new ArrayList<>();
- result.add(intro);
- if (!Strings.isNullOrEmpty(tags)) {
- result.add(":leveloffset: -1");
+ if (!featureFiles.isEmpty()) {
+ result.addAll(generateIncludeSnippet("", leveloffset, featurePath, featureFiles, tagSelector));
}
- featureFiles.forEach(fileName -> result.add(includeMacroFor(featurePath, fileName, tags)));
+ return result;
+ }
+
+ List generateTagSnippet(final Tag tag, int scenarioCount, final List features, final int leveloffset) {
+ final ArrayList result = new ArrayList<>();
- if (!Strings.isNullOrEmpty(tags)) {
- result.add(":leveloffset: +1");
+ result.add("=== " + tag.toString());
+
+ if (features.isEmpty()) {
+ result.add("There are no tagged scenarios. Keep rocking!");
+ } else {
+ final String intro = "There are " + scenarioCount + " " + scenarioQualifier + ".";
+ final String tagSelector = TagMapper.toAsciiDocTagName(tag);
+ result.addAll(generateIncludeSnippet(intro, leveloffset, "../features", features, tagSelector));
}
+
return result;
}
+ private List generateIncludeSnippet(
+ final String intro,
+ final int leveloffset,
+ final String featurePath,
+ final List featureFiles,
+ final String tags) {
+ final ArrayList result = new ArrayList<>();
+
+ if (!intro.isBlank()) {
+ result.add(intro);
+ }
+
+ if (leveloffset > 0) {
+ result.add(":leveloffset: +" + leveloffset);
+ } else if (leveloffset < 0) {
+ result.add(":leveloffset: -" + Math.abs(leveloffset));
+ }
+
+ featureFiles.forEach(fileName -> result.add(includeMacroFor(featurePath, fileName, tags)));
+
+ if (leveloffset > 0) {
+ result.add(":leveloffset: -" + leveloffset);
+ } else if (leveloffset < 0) {
+ result.add(":leveloffset: +" + Math.abs(leveloffset));
+ }
+ return result;
+ }
- private static String includeMacroFor(final String featurePath, final String fileName, final String tags) {
- return "include::" + featurePath + "/" + fileName + "[" + tags + "]";
+ private static String includeMacroFor(final String featurePath, final String featureName, final String tags) {
+ return "include::" + featurePath + "/" + featureName + ".asciidoc[" + tags + "]";
}
}
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java
index cd22db7b152..51c4d513c94 100644
--- a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGeneratorTest.java
@@ -12,18 +12,16 @@ public class AsciiDocSnippetGeneratorTest {
public void writeIndexFileForFailedScenarios() {
// given
final List featureFileNames = new ArrayList<>();
- featureFileNames.add("com.example.application.FailedScenarioOne.asciidoc");
- featureFileNames.add("com.example.application.FailedScenarioTwo.asciidoc");
+ featureFileNames.add("com.example.application.FailedScenarioOne");
+ featureFileNames.add("com.example.application.FailedScenarioTwo");
// when
AsciiDocSnippetGenerator asciiDocSnippetGenerator = new AsciiDocSnippetGenerator(
- "Failed Scenarios", "failed scenarios", 3, "scenario-failed", "features", featureFileNames);
- final List blocks = asciiDocSnippetGenerator.generateIndexSnippet();
+ "Failed Scenarios", "failed scenarios", 3);
+ final List blocks = asciiDocSnippetGenerator.generateIndexSnippet("features", featureFileNames, "scenario-failed", -1);
// then
assertThat(blocks).containsExactly(
- "== Failed Scenarios",
- "There are 3 failed scenarios.",
":leveloffset: -1",
"include::features/com.example.application.FailedScenarioOne.asciidoc[tag=scenario-failed]",
"include::features/com.example.application.FailedScenarioTwo.asciidoc[tag=scenario-failed]",
@@ -35,19 +33,17 @@ public void writeIndexFileForFailedScenarios() {
public void writeIndexFileForAllScenarios() {
// given
final List featureFileNames = new ArrayList<>();
- featureFileNames.add("com.example.application.BigFeature.asciidoc");
- featureFileNames.add("com.example.application.OtherFeature.asciidoc");
+ featureFileNames.add("com.example.application.BigFeature");
+ featureFileNames.add("com.example.application.OtherFeature");
// when
AsciiDocSnippetGenerator asciiDocSnippetGenerator = new AsciiDocSnippetGenerator(
- "All Scenarios", "scenarios in total", 40, "", "features", featureFileNames);
- final List blocks = asciiDocSnippetGenerator.generateIndexSnippet();
+ "All Scenarios", "scenarios in total", 40);
+ final List blocks = asciiDocSnippetGenerator.generateIndexSnippet("features", featureFileNames, "", 0);
// then
assertThat(blocks).containsExactly(
- "== All Scenarios",
- "There are 40 scenarios in total.",
"include::features/com.example.application.BigFeature.asciidoc[]",
"include::features/com.example.application.OtherFeature.asciidoc[]");
}
From f1a3f43a571cc6d5d7b72c7f918c093c17ad63d7 Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Sun, 11 Feb 2024 14:24:04 +0100
Subject: [PATCH 08/15] Restructure report generator
Issue: #54
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../asciidoc/AsciiDocReportGenerator.java | 83 ++++++++++++++++++-
.../report/asciidoc/HierarchyCalculator.java | 29 +++++++
.../jgiven/report/asciidoc/index.asciidoc | 2 +
3 files changed, 113 insertions(+), 1 deletion(-)
create mode 100644 jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/HierarchyCalculator.java
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
index bc0c8a61d47..a49c86d3a88 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
@@ -23,6 +23,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,6 +59,7 @@ public class AsciiDocReportGenerator extends AbstractReportGenerator {
private File targetDir;
private File featuresDir;
+ private File tagsDir;
@Override
@@ -77,6 +81,14 @@ public void generate() {
writeIndexFileForPendingScenarios();
+ final HierarchyCalculator hierarchyCalculator = new HierarchyCalculator(allTags, taggedScenarioFeatures);
+
+ final Map>> groupedTags = hierarchyCalculator.computeGroupedTag();
+
+ groupedTags.forEach(this::writeIndexFileForTaggedScenarios);
+
+ writeIndexFileForAllTags(groupedTags);
+
writeTotalStatisticsFile();
writeIndexFileForFullReport(config.getTitle());
@@ -150,6 +162,76 @@ private void writeIndexFileForPendingScenarios() {
writeAsciiDocBlocksToFile(targetDir, "pendingScenarios", asciiDocBlocks);
}
+ private void writeIndexFileForTaggedScenarios(final String tagType, final Map> taggedScenarios) {
+ final Optional firstTag = taggedScenarios.keySet().stream()
+ .findFirst()
+ .map(allTags::get);
+
+ if (firstTag.isEmpty()) {
+ return;
+ }
+
+ final int numTaggedScenarios = taggedScenarios.keySet().stream().mapToInt(taggedScenarioCounts::get).sum();
+
+ final List asciiDocBlocks = taggedScenarios.keySet().size() == 1
+ ? singleValuedTag(taggedScenarios, firstTag.get(), numTaggedScenarios)
+ : multiValuedTag(taggedScenarios, firstTag.get(), numTaggedScenarios);
+
+ writeAsciiDocBlocksToFile(tagsDir, tagType, asciiDocBlocks);
+ }
+
+ private List multiValuedTag(final Map> taggedScenarios, final Tag tag, final int numTaggedScenarios) {
+ final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
+ tag.getName(), "tagged scenarios", numTaggedScenarios);
+
+ final List asciiDocBlocks = snippetGenerator.generateIntroSnippet(tag.getDescription());
+ taggedScenarios.forEach((tagId, features) -> {
+ final List snippet = snippetGenerator.generateTagSnippet(
+ allTags.get(tagId), taggedScenarioCounts.get(tagId), features, 0);
+ asciiDocBlocks.addAll(snippet);
+ });
+ return asciiDocBlocks;
+ }
+
+ private List singleValuedTag(final Map> taggedScenarios, final Tag tag, final int numTaggedScenarios) {
+ final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
+ tag.toString(), "tagged scenarios", numTaggedScenarios);
+
+ final List asciiDocBlocks = snippetGenerator.generateIntroSnippet(tag.getDescription());
+ taggedScenarios.forEach((tagId, features) -> {
+ final Tag valueTag = allTags.get(tagId);
+ final String tagName = TagMapper.toAsciiDocTagName(valueTag);
+ asciiDocBlocks.add("=== Scenarios");
+ final List snippet = snippetGenerator.generateIndexSnippet("../" + FEATURE_PATH, features, tagName, 0);
+ asciiDocBlocks.addAll(snippet);
+ });
+ return asciiDocBlocks;
+ }
+
+
+ private void writeIndexFileForAllTags(final Map>> strings) {
+
+ final List tagFiles = strings.entrySet().stream()
+ .sorted((o1, o2) -> {
+ final Tag tag1 = allTags.get(o1.getValue().keySet().stream().findFirst().orElse(""));
+ final Tag tag2 = allTags.get(o2.getValue().keySet().stream().findFirst().orElse(""));
+ return Objects.compare(tag1, tag2, Comparator.comparing(Tag::getName));
+
+ })
+ .map(entry -> entry.getKey().replace(' ', '_'))
+ .collect(Collectors.toList());
+ final Integer total = taggedScenarioCounts.values().stream().reduce(Integer::sum).orElse(999);
+ final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
+ "Tags", "all tags are beautiful", total
+ );
+
+ final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
+ asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet("tags", tagFiles, "", 1));
+ writeAsciiDocBlocksToFile(targetDir, "allTags",
+ asciiDocBlocks);
+
+ }
+
private void writeTotalStatisticsFile() {
final ListMultimap featureStatistics = completeReportModel.getAllReportModels()
.stream()
@@ -180,7 +262,6 @@ private void writeIndexFileForFullReport(final String reportTitle) {
}
private boolean prepareDirectories(final File targetDir) {
- File tagsDir;
this.targetDir = targetDir;
if (this.targetDir == null) {
log.error("Target directory was not configured");
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/HierarchyCalculator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/HierarchyCalculator.java
new file mode 100644
index 00000000000..f3bd986423f
--- /dev/null
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/HierarchyCalculator.java
@@ -0,0 +1,29 @@
+package com.tngtech.jgiven.report.asciidoc;
+
+import com.tngtech.jgiven.report.model.Tag;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class HierarchyCalculator {
+ private final Map> taggedScenarioFiles;
+ private final Map allTags;
+
+ public HierarchyCalculator(final Map allTags, final Map> taggedScenarioFiles) {
+ this.taggedScenarioFiles = taggedScenarioFiles;
+ this.allTags = allTags;
+ }
+
+ Map>> computeGroupedTag() {
+ return taggedScenarioFiles.entrySet().stream()
+ .filter(entry -> allTags.get(entry.getKey()).getShownInNavigation())
+ .collect(Collectors.groupingBy(this::fullType, Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
+ }
+
+ private String fullType(final Map.Entry> entry) {
+ final Tag tag = allTags.get(entry.getKey());
+
+ return tag.getFullType();
+
+ }
+}
diff --git a/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc b/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc
index 75ec86dcd6a..54a5b779925 100644
--- a/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc
+++ b/jgiven-core/src/main/resources/com/tngtech/jgiven/report/asciidoc/index.asciidoc
@@ -10,3 +10,5 @@ include::allScenarios.asciidoc[]
include::failedScenarios.asciidoc[]
include::pendingScenarios.asciidoc[]
+
+include::allTags.asciidoc[]
From e1d9b698c8e847337f4ca6bede3aee2a3ccef5ba Mon Sep 17 00:00:00 2001
From: Johannes Thorn <2544827+johthor@users.noreply.github.com>
Date: Sun, 18 May 2025 20:18:26 +0200
Subject: [PATCH 09/15] Take the number of failed/pending scenarios into
account
Signed-off-by: Johannes Thorn <2544827+johthor@users.noreply.github.com>
---
.../asciidoc/AsciiDocReportGenerator.java | 40 +++++------
.../asciidoc/AsciiDocSnippetGenerator.java | 45 +++++++-----
.../AsciiDocIntroSnippetGeneratorTest.java | 70 +++++++++++++++++++
.../AsciiDocSnippetGeneratorTest.java | 13 ++--
4 files changed, 124 insertions(+), 44 deletions(-)
create mode 100644 jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocIntroSnippetGeneratorTest.java
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
index 885ced25f14..ebaf6239876 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocReportGenerator.java
@@ -25,7 +25,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -95,7 +94,6 @@ public void generate() {
writeTotalStatisticsFile();
writeIndexFileForFullReport(config.getTitle());
-
}
@@ -131,10 +129,11 @@ private void writeIndexFileForAllScenarios() {
final int numScenarios = this.completeReportModel.getTotalStatistics().numScenarios;
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "All Scenarios", "scenarios in total", numScenarios);
+ "All Scenarios", "", numScenarios);
final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
- asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(FEATURE_PATH, this.allFeatures, "", 0));
+ asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(
+ FEATURE_PATH, this.allFeatures, "", 0));
writeAsciiDocBlocksToFile(targetDir, "allScenarios", asciiDocBlocks);
}
@@ -143,7 +142,7 @@ private void writeIndexFileForFailedScenarios() {
final int numFailedScenarios = this.completeReportModel.getTotalStatistics().numFailedScenarios;
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "Failed Scenarios", "failed scenarios", numFailedScenarios);
+ "Failed Scenarios", "failed", numFailedScenarios);
final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(
@@ -156,7 +155,7 @@ private void writeIndexFileForPendingScenarios() {
final int numPendingScenarios = this.completeReportModel.getTotalStatistics().numPendingScenarios;
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- "Pending Scenarios", "pending scenarios", numPendingScenarios);
+ "Pending Scenarios", "pending", numPendingScenarios);
final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet(
@@ -167,6 +166,7 @@ private void writeIndexFileForPendingScenarios() {
private void writeIndexFileForAbortedScenarios() {
final int numAbortedScenarios = this.completeReportModel.getTotalStatistics().numAbortedScenarios;
+
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
"Aborted Scenarios", "aborted scenarios", numAbortedScenarios);
@@ -188,36 +188,37 @@ private void writeIndexFileForTaggedScenarios(final String tagType, final Map asciiDocBlocks = taggedScenarios.keySet().size() == 1
+ final List asciiDocBlocks = taggedScenarios.size() == 1
? singleValuedTag(taggedScenarios, firstTag.get(), numTaggedScenarios)
: multiValuedTag(taggedScenarios, firstTag.get(), numTaggedScenarios);
writeAsciiDocBlocksToFile(tagsDir, tagType, asciiDocBlocks);
}
- private List multiValuedTag(final Map> taggedScenarios, final Tag tag, final int numTaggedScenarios) {
+ private List singleValuedTag(final Map> taggedScenarios, final Tag tag, final int numTaggedScenarios) {
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- tag.getName(), "tagged scenarios", numTaggedScenarios);
+ tag.toString(), "tagged", numTaggedScenarios);
final List asciiDocBlocks = snippetGenerator.generateIntroSnippet(tag.getDescription());
+
taggedScenarios.forEach((tagId, features) -> {
- final List snippet = snippetGenerator.generateTagSnippet(
- allTags.get(tagId), taggedScenarioCounts.get(tagId), features, 0);
+ final Tag valueTag = allTags.get(tagId);
+ final String tagName = TagMapper.toAsciiDocTagName(valueTag);
+ asciiDocBlocks.add("=== Scenarios");
+ final List snippet = snippetGenerator.generateIndexSnippet("../" + FEATURE_PATH, features, tagName, 0);
asciiDocBlocks.addAll(snippet);
});
return asciiDocBlocks;
}
- private List singleValuedTag(final Map> taggedScenarios, final Tag tag, final int numTaggedScenarios) {
+ private List multiValuedTag(final Map> taggedScenarios, final Tag tag, final int numTaggedScenarios) {
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
- tag.toString(), "tagged scenarios", numTaggedScenarios);
+ tag.getName(), "tagged", numTaggedScenarios);
final List asciiDocBlocks = snippetGenerator.generateIntroSnippet(tag.getDescription());
taggedScenarios.forEach((tagId, features) -> {
- final Tag valueTag = allTags.get(tagId);
- final String tagName = TagMapper.toAsciiDocTagName(valueTag);
- asciiDocBlocks.add("=== Scenarios");
- final List snippet = snippetGenerator.generateIndexSnippet("../" + FEATURE_PATH, features, tagName, 0);
+ final List snippet = snippetGenerator.generateTagSnippet(
+ allTags.get(tagId), taggedScenarioCounts.get(tagId), features);
asciiDocBlocks.addAll(snippet);
});
return asciiDocBlocks;
@@ -232,7 +233,7 @@ private void writeIndexFileForAllTags(final Map
})
.map(entry -> entry.getKey().replace(' ', '_'))
- .collect(Collectors.toList());
+ .toList();
final Integer total = taggedScenarioCounts.values().stream().reduce(Integer::sum).orElse(999);
final AsciiDocSnippetGenerator snippetGenerator = new AsciiDocSnippetGenerator(
"Tags", "all tags are beautiful", total
@@ -240,8 +241,7 @@ private void writeIndexFileForAllTags(final Map
final List asciiDocBlocks = snippetGenerator.generateIntroSnippet("");
asciiDocBlocks.addAll(snippetGenerator.generateIndexSnippet("tags", tagFiles, "", 1));
- writeAsciiDocBlocksToFile(targetDir, "allTags",
- asciiDocBlocks);
+ writeAsciiDocBlocksToFile(targetDir, "allTags", asciiDocBlocks);
}
private void writeTotalStatisticsFile() {
diff --git a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
index ed69b4b9234..66c439f4ec5 100644
--- a/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
+++ b/jgiven-core/src/main/java/com/tngtech/jgiven/report/asciidoc/AsciiDocSnippetGenerator.java
@@ -10,6 +10,7 @@
*/
final class AsciiDocSnippetGenerator {
private static final String LINE_BREAK = System.lineSeparator();
+ private static final String LEVEL_OFFSET = ":leveloffset:";
private final String title;
private final String scenarioQualifier;
private final int numScenarios;
@@ -24,7 +25,7 @@ final class AsciiDocSnippetGenerator {
}
List generateIntroSnippet(final String description) {
- final ArrayList result = new ArrayList<>();
+ final List result = new ArrayList<>();
result.add("== " + this.title);
@@ -32,37 +33,47 @@ List generateIntroSnippet(final String description) {
result.add("+++" + LINE_BREAK + description + LINE_BREAK + "+++");
}
- if (numScenarios == 0) {
- result.add("There are no " + scenarioQualifier + ". Keep rocking!");
+ final String qualifiedScenario = scenarioQualifier.isBlank() ? "scenario" : scenarioQualifier + " scenario";
+
+ if (numScenarios <= 0) {
+ result.add("There are no " + qualifiedScenario + "s. Keep rocking!");
+ } else if (numScenarios == 1) {
+ result.add("There is " + numScenarios + " " + qualifiedScenario + ".");
} else {
- result.add("There are " + numScenarios + " " + scenarioQualifier + ".");
+ result.add("There are " + numScenarios + " " + qualifiedScenario + "s.");
}
return result;
}
- List generateIndexSnippet(final String featurePath, final List featureFiles, final String tags, final int leveloffset) {
+ List generateIndexSnippet(final String featurePath, final List features, final String tags, final int levelOffset) {
+ final List result = new ArrayList<>();
+
final String tagSelector = Strings.isNullOrEmpty(tags) ? "" : "tag=" + tags;
- final ArrayList result = new ArrayList<>();
- if (!featureFiles.isEmpty()) {
- result.addAll(generateIncludeSnippet("", leveloffset, featurePath, featureFiles, tagSelector));
+ if (!features.isEmpty()) {
+ result.addAll(generateIncludeSnippet("", levelOffset, featurePath, features, tagSelector));
}
return result;
}
- List generateTagSnippet(final Tag tag, int scenarioCount, final List features, final int leveloffset) {
+ List generateTagSnippet(final Tag tag, int scenarioCount, final List features) {
final ArrayList result = new ArrayList<>();
result.add("=== " + tag.toString());
- if (features.isEmpty()) {
- result.add("There are no tagged scenarios. Keep rocking!");
+ if (numScenarios <= 0) {
+ result.add("There are no " + scenarioQualifier + " scenarios. Keep rocking!");
} else {
- final String intro = "There are " + scenarioCount + " " + scenarioQualifier + ".";
+ final String intro;
+ if (numScenarios == 1) {
+ intro = "There is " + scenarioCount + " " + scenarioQualifier + " scenario.";
+ } else {
+ intro = "There are " + scenarioCount + " " + scenarioQualifier + " scenarios.";
+ }
final String tagSelector = TagMapper.toAsciiDocTagName(tag);
- result.addAll(generateIncludeSnippet(intro, leveloffset, "../features", features, tagSelector));
+ result.addAll(generateIncludeSnippet(intro, 0, "../features", features, tagSelector));
}
return result;
@@ -81,17 +92,17 @@ private List generateIncludeSnippet(
}
if (leveloffset > 0) {
- result.add(":leveloffset: +" + leveloffset);
+ result.add(LEVEL_OFFSET + " +" + leveloffset);
} else if (leveloffset < 0) {
- result.add(":leveloffset: -" + Math.abs(leveloffset));
+ result.add(LEVEL_OFFSET + " -" + Math.abs(leveloffset));
}
featureFiles.forEach(fileName -> result.add(includeMacroFor(featurePath, fileName, tags)));
if (leveloffset > 0) {
- result.add(":leveloffset: -" + leveloffset);
+ result.add(LEVEL_OFFSET + " -" + leveloffset);
} else if (leveloffset < 0) {
- result.add(":leveloffset: +" + Math.abs(leveloffset));
+ result.add(LEVEL_OFFSET + " +" + Math.abs(leveloffset));
}
return result;
}
diff --git a/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocIntroSnippetGeneratorTest.java b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocIntroSnippetGeneratorTest.java
new file mode 100644
index 00000000000..b28b43c64ae
--- /dev/null
+++ b/jgiven-core/src/test/java/com/tngtech/jgiven/report/asciidoc/AsciiDocIntroSnippetGeneratorTest.java
@@ -0,0 +1,70 @@
+package com.tngtech.jgiven.report.asciidoc;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AsciiDocIntroSnippetGeneratorTest {
+
+ @Parameterized.Parameters
+ public static Collection