diff --git a/core/pom.xml b/core/pom.xml
index 7d5f3aac..68d0353b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -6,7 +6,7 @@
org.jsmart
zerocode-tdd-parent
- 1.4.2
+ 1.4.3
zerocode-tdd
diff --git a/core/src/main/java/org/jsmart/zerocode/core/constants/ZeroCodeReportConstants.java b/core/src/main/java/org/jsmart/zerocode/core/constants/ZeroCodeReportConstants.java
index 4442dd52..23d49f09 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/constants/ZeroCodeReportConstants.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/constants/ZeroCodeReportConstants.java
@@ -7,6 +7,7 @@ public interface ZeroCodeReportConstants {
String TARGET_FULL_REPORT_DIR = "target/";
String TARGET_REPORT_DIR = "target/zerocode-test-reports/";
String TARGET_FULL_REPORT_CSV_FILE_NAME = "zerocode-junit-granular-report.csv";
+ String TARGET_FULL_REPORT_TXT_FILE_NAME = "zerocode-junit-granular-report.txt";
String TARGET_FILE_NAME = "target/zerocode-junit-interactive-fuzzy-search.html";
String HIGH_CHART_HTML_FILE_NAME = "zerocode_results_chart";
String AUTHOR_MARKER_OLD = "@@"; //Deprecated
diff --git a/core/src/main/java/org/jsmart/zerocode/core/engine/listener/TestUtilityListener.java b/core/src/main/java/org/jsmart/zerocode/core/engine/listener/TestUtilityListener.java
index 96142ae7..49fe477f 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/engine/listener/TestUtilityListener.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/engine/listener/TestUtilityListener.java
@@ -58,6 +58,7 @@ public void runPostFinished() {
private void generateChartsAndReports() {
reportGenerator.generateCsvReport();
+ reportGenerator.generateTableReport();
/**
* Not compatible with open source license i.e. why not activated. But if it has to be used inside intranet,
diff --git a/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java b/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java
index 6e71fbfb..9210ce96 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java
@@ -10,4 +10,6 @@ public interface ZeroCodeReportGenerator {
void generateHighChartReport();
void generateExtentReport();
+
+ void generateTableReport();
}
diff --git a/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java b/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java
index 122796fa..32bf4817 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java
@@ -24,6 +24,7 @@
import java.io.File;
import java.io.IOException;
+import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
@@ -119,9 +120,11 @@ public void generateExtentReport() {
thisReport.getResults().forEach(thisScenario -> {
ExtentTest test = extentReports.createTest(thisScenario.getScenarioName());
- /**This code checks if the scenario has meta data.
- If it does, it iterates through each meta data entry and adds it to
- the Extent report as an info label.**/
+ /**
+ * This code checks if the scenario has meta data.
+ * If it does, it iterates through each meta data entry and adds it to
+ * the Extent report as an info label.
+ */
if (thisScenario.getMeta() != null) {
for (Map.Entry> entry : thisScenario.getMeta().entrySet()) {
String key = entry.getKey();
@@ -133,14 +136,14 @@ public void generateExtentReport() {
// Assign Category
test.assignCategory(DEFAULT_REGRESSION_CATEGORY); //Super set
String[] hashTagsArray = optionalCategories(thisScenario.getScenarioName()).toArray(new String[0]);
- if(hashTagsArray.length > 0) {
+ if (hashTagsArray.length > 0) {
test.assignCategory(hashTagsArray); //Sub categories
}
// Assign Authors
test.assignAuthor(DEFAULT_REGRESSION_AUTHOR); //Super set
String[] authorsArray = optionalAuthors(thisScenario.getScenarioName()).toArray(new String[0]);
- if(authorsArray.length > 0) {
+ if (authorsArray.length > 0) {
test.assignAuthor(authorsArray); //Sub authors
}
@@ -207,12 +210,12 @@ protected List optionalCategories(String scenarioName) {
private List deriveNames(String scenarioName, String marker) {
List nameList = new ArrayList<>();
- for(String thisName : scenarioName.trim().split(" ")){
- if(thisName.startsWith(marker) && !thisName.startsWith(AUTHOR_MARKER_OLD)){
+ for (String thisName : scenarioName.trim().split(" ")) {
+ if (thisName.startsWith(marker) && !thisName.startsWith(AUTHOR_MARKER_OLD)) {
nameList.add(thisName);
}
// Depreciated, but still supports. Remove this via a new ticket
- if(thisName.startsWith(AUTHOR_MARKER_OLD)){
+ if (thisName.startsWith(AUTHOR_MARKER_OLD)) {
nameList.add(thisName);
}
}
@@ -448,6 +451,129 @@ protected void validateReportsFolderAndTheFilesExists(String reportsFolder) {
}
+ @Override
+ public void generateTableReport() {
+ if (zeroCodeCsvFlattenedRows == null || zeroCodeCsvFlattenedRows.isEmpty()) {
+ LOGGER.warn("No CSV rows available — skipping table report generation.");
+ return;
+ }
+
+ String table = buildTableReportContent(zeroCodeCsvFlattenedRows);
+
+ // --------------------------------------------------------------------------------------
+ // This is intentionally commented out here.
+ // Let the end-users control/print/log via runPostFinished() lifecycle method [OPTIONAL].
+ // Also, at this point, end-users can download or view the .txt report file in "target/"
+ // --------------------------------------------------------------------------------------
+ // LOGGER.info("\n{}", table);
+
+ String txtFileName = resolveCsvReportName().replace(".csv", ".txt");
+ File txtFile = new File(TARGET_FULL_REPORT_DIR + txtFileName);
+ try (PrintWriter pw = new PrintWriter(txtFile)) {
+ pw.print(table);
+ } catch (IOException e) {
+ LOGGER.error("Failed to write table report to {}: {}", txtFile.getPath(), e.getMessage());
+ }
+
+ LOGGER.info("Tabular .txt report written to: {}", txtFile.getPath());
+ }
+
+ String buildTableReportContent(List rows) {
+ final int FIELDS_COUNT = 5;
+ final int PADDING = 2;
+ final int SCEN_WIDTH = 48,
+ STEP_WIDTH = 25,
+ METH_WIDTH = 22,
+ RES_WIDTH = 8,
+ DELAY_WIDTH = 10;
+
+ String colSepr = "+" + repeat('-', SCEN_WIDTH + PADDING)
+ + "+" + repeat('-', STEP_WIDTH + PADDING)
+ + "+" + repeat('-', METH_WIDTH + PADDING)
+ + "+" + repeat('-', RES_WIDTH + PADDING)
+ + "+" + repeat('-', DELAY_WIDTH + PADDING)
+ + "+";
+
+ int allFieldWidth = SCEN_WIDTH + STEP_WIDTH + METH_WIDTH + RES_WIDTH + DELAY_WIDTH;
+ int innerPlusCount = FIELDS_COUNT - 1;
+ int footRepeat = (FIELDS_COUNT * PADDING) + allFieldWidth + innerPlusCount;
+
+ String footSep = "+" + repeat('-', footRepeat) + "+";
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(colSepr).append('\n');
+ sb.append("| ").append(pad("SCENARIO", SCEN_WIDTH)).append(" | ")
+ .append(pad("STEP", STEP_WIDTH)).append(" | ")
+ .append(pad("METHOD", METH_WIDTH)).append(" | ")
+ .append(pad("RESULT ", RES_WIDTH)).append(" | ")
+ .append(pad("DELAY (ms)", DELAY_WIDTH)).append(" |\n");
+ sb.append(colSepr).append('\n');
+
+ int passed = 0, failed = 0;
+ double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
+
+ for (ZeroCodeCsvReport row : rows) {
+ String scen = trunc(row.getScenarioName(), SCEN_WIDTH);
+ String step = pad(row.getStepName(), STEP_WIDTH);
+ String method = pad(row.getMethod(), METH_WIDTH);
+ boolean isPass = RESULT_PASS.equals(row.getResult());
+ String resCell = isPass ? "PASSED ✅" : "FAILED ❌";
+ double delay = row.getResponseDelayMilliSec() != null ? row.getResponseDelayMilliSec() : 0.0;
+
+ if (isPass) passed++;
+ else failed++;
+ if (delay < min) min = delay;
+ if (delay > max) max = delay;
+
+ sb.append("| ").append(scen).append(" | ")
+ .append(step).append(" | ")
+ .append(method).append(" | ")
+ .append(resCell).append("| ")
+ .append(rpad(delay, DELAY_WIDTH)).append(" |\n");
+ }
+
+ String summary = String.format(
+ "Total: %d | PASSED: %d | FAILED: %d | Min delay: %s ms | Max delay: %s ms",
+ rows.size(), passed, failed, fmt(min), fmt(max));
+
+ sb.append(footSep).append('\n');
+ sb.append("| ").append(pad(summary, footRepeat - PADDING)).append(" |\n");
+ sb.append(footSep).append('\n');
+
+ return sb.toString();
+ }
+
+ private static String repeat(char fillChar, int count) {
+ char[] arr = new char[count];
+ Arrays.fill(arr, fillChar);
+ return new String(arr);
+ }
+
+ private static String pad(String text, int width) {
+ if (text == null) text = "";
+ if (text.length() >= width) return text.substring(0, width);
+ return text + repeat(' ', width - text.length());
+ }
+
+ private static String trunc(String text, int width) {
+ if (text == null) text = "";
+ text = text.trim();
+ if (text.length() <= width) return pad(text, width);
+ return text.substring(0, width - 2) + "..";
+ }
+
+ private static String rpad(double value, int width) {
+ String text = fmt(value);
+ if (text.length() >= width) return text;
+ return repeat(' ', width - text.length()) + text;
+ }
+
+ private static String fmt(double value) {
+ return (value == Math.floor(value) && !Double.isInfinite(value))
+ ? String.valueOf((long) value) + ".0"
+ : String.valueOf(value);
+ }
+
private static Date utilDateOf(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
index b2221ce4..270dfb97 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/utils/SmartUtils.java
@@ -133,8 +133,15 @@ public static List getAllEndPointFiles(String packagePath) {
);
}
- endpointFiles.sort(null);
- return endpointFiles;
+// endpointFiles.sort(null);
+// return endpointFiles;
+
+ List deduplicatedFiles = endpointFiles.stream()
+ .distinct()
+ .sorted()
+ .collect(Collectors.toList());
+ return deduplicatedFiles;
+
}
private static List collectJsonFilesFromDirectory(URL resourceUrl,
diff --git a/core/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java b/core/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java
index 8f8813ae..3a89f156 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jsmart.zerocode.core.di.provider.ObjectMapperProvider;
import org.jsmart.zerocode.core.domain.reports.ZeroCodeReportStep;
+import org.jsmart.zerocode.core.domain.reports.csv.ZeroCodeCsvReport;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -10,11 +11,13 @@
import org.junit.rules.ExpectedException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.core.Is.is;
import static org.jsmart.zerocode.core.constants.ZeroCodeReportConstants.RESULT_FAIL;
import static org.jsmart.zerocode.core.constants.ZeroCodeReportConstants.RESULT_PASS;
@@ -199,4 +202,100 @@ public void resolveCsvReportName_returnsDefault_whenZerocodePropertiesIsEmpty()
assertThat(zeroCodeReportGenerator.resolveCsvReportName(), is(TARGET_FULL_REPORT_CSV_FILE_NAME));
}
+ // -------------------------------------------------------------------------
+ // buildTableReportContent() unit tests
+ // -------------------------------------------------------------------------
+
+ private static ZeroCodeCsvReport csvRow(String scenario, String step, String method,
+ String result, double delay) {
+ return new ZeroCodeCsvReport(scenario, 0, step, 0, "corr-id",
+ result, method, "2026-01-01T00:00:00", "2026-01-01T00:00:01", delay);
+ }
+
+ @Test
+ public void buildTableReport_allRowsHaveSameLineLength() {
+ List rows = Arrays.asList(
+ csvRow("Scenario One", "step_one", "GET", RESULT_PASS, 100.0),
+ csvRow("Scenario Two", "step_two", "POST", RESULT_FAIL, 200.0)
+ );
+
+ String table = zeroCodeReportGenerator.buildTableReportContent(rows);
+ String[] lines = table.split("\n");
+
+ // Every line must be the same display-string length
+ // (emoji rows are 1 string-char shorter but emoji is 2-display-wide — check string length)
+ int separatorLen = lines[0].length();
+ for (String line : lines) {
+ assertThat("Line not same width as separator: [" + line + "]",
+ line.length() == separatorLen || line.length() == separatorLen - 1, is(true));
+ }
+ }
+
+ @Test
+ public void buildTableReport_truncatesLongScenarioAt48Chars() {
+ String longScenario = "GIVEN the very long scenario name that exceeds the column width limit set for the table";
+ List rows = Arrays.asList(
+ csvRow(longScenario, "step", "GET", RESULT_PASS, 50.0)
+ );
+
+ String table = zeroCodeReportGenerator.buildTableReportContent(rows);
+ // truncated text ends with ".." and is exactly 48 chars (46 base + "..")
+ assertThat(table, containsString("GIVEN the very long scenario name that exceeds.."));
+ }
+
+ @Test
+ public void buildTableReport_passedRowContainsCheckEmoji() {
+ List rows = Arrays.asList(
+ csvRow("My Scenario", "my_step", "GET", RESULT_PASS, 75.0)
+ );
+ assertThat(zeroCodeReportGenerator.buildTableReportContent(rows), containsString("PASSED ✅"));
+ }
+
+ @Test
+ public void buildTableReport_failedRowContainsCrossEmoji() {
+ List rows = Arrays.asList(
+ csvRow("My Scenario", "my_step", "POST", RESULT_FAIL, 0.0)
+ );
+ assertThat(zeroCodeReportGenerator.buildTableReportContent(rows), containsString("FAILED ❌"));
+ }
+
+ @Test
+ public void buildTableReport_footerContainsCorrectCounts() {
+ List rows = Arrays.asList(
+ csvRow("S1", "step1", "GET", RESULT_PASS, 10.0),
+ csvRow("S2", "step2", "POST", RESULT_PASS, 20.0),
+ csvRow("S3", "step3", "PUT", RESULT_FAIL, 5.0)
+ );
+
+ String table = zeroCodeReportGenerator.buildTableReportContent(rows);
+ assertThat(table, containsString("Total: 3"));
+ assertThat(table, containsString("PASSED: 2"));
+ assertThat(table, containsString("FAILED: 1"));
+ }
+
+ @Test
+ public void buildTableReport_footerContainsMinMaxDelayWithPipeSeparator() {
+ List rows = Arrays.asList(
+ csvRow("S1", "step1", "GET", RESULT_PASS, 410.0),
+ csvRow("S2", "step2", "POST", RESULT_PASS, 1.0),
+ csvRow("S3", "step3", "DELETE", RESULT_FAIL, 0.0)
+ );
+
+ String table = zeroCodeReportGenerator.buildTableReportContent(rows);
+ assertThat(table, containsString("Min delay: 0.0 ms | Max delay: 410.0 ms"));
+ }
+
+ @Test
+ public void buildTableReport_delayValuesAreRightAligned() {
+ List rows = Arrays.asList(
+ csvRow("S1", "step1", "GET", RESULT_PASS, 1000.0),
+ csvRow("S2", "step2", "GET", RESULT_PASS, 1.0)
+ );
+
+ String table = zeroCodeReportGenerator.buildTableReportContent(rows);
+ // Both delay values right-padded to same field width: " 1000.0" and " 1.0"
+ assertThat(table, containsString(" 1000.0 |"));
+ assertThat(table, containsString(" 1.0 |"));
+ }
+
}
\ No newline at end of file
diff --git a/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java b/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java
index fad2ee2a..450e6480 100644
--- a/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/core/utils/SmartUtilsTest.java
@@ -363,6 +363,135 @@ public void getAllEndPointFiles_skipsCorruptJar() throws Exception {
}
+ @Test
+ public void getAllEndPointFiles_deduplicates_sameFilePath_fromMultipleClasspathEntries() throws Exception {
+ // Reproduce the bug: same package exists in two classpath roots (e.g. file: + jar:),
+ // causing the same relative file path to be collected twice before the distinct() fix.
+ String packagePath = "unit_test_files/engine_unit_test_jsons";
+
+ Path tempDir = Files.createTempDirectory("dedup-test");
+ File jarFile = new File(tempDir.toFile(), "duplicate-scenarios.jar");
+
+ // Mirror the real test-resources files into a JAR so the same paths appear twice
+ File resourceDir = new File(getClass().getClassLoader().getResource(packagePath).getFile());
+ try (java.util.jar.JarOutputStream jarOut =
+ new java.util.jar.JarOutputStream(Files.newOutputStream(jarFile.toPath()))) {
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/"));
+ jarOut.closeEntry();
+ for (File f : resourceDir.listFiles((d, n) -> n.endsWith(".json"))) {
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/" + f.getName()));
+ jarOut.write(Files.readAllBytes(f.toPath()));
+ jarOut.closeEntry();
+ }
+ }
+
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}, original);
+ Thread.currentThread().setContextClassLoader(urlClassLoader);
+
+ try {
+ List files = SmartUtils.getAllEndPointFiles(packagePath);
+
+ // Must be 25, not 50 — distinct() collapses duplicate paths from two classpath roots
+ assertThat(files.size(), is(25));
+
+ // No duplicate entries
+ assertThat(new java.util.HashSet<>(files).size(), is(files.size()));
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ @Test
+ public void getAllEndPointFiles_returnsSortedList() {
+ List files = SmartUtils.getAllEndPointFiles("unit_test_files/engine_unit_test_jsons");
+
+ for (int i = 0; i < files.size() - 1; i++) {
+ assertThat(files.get(i).compareTo(files.get(i + 1)) <= 0, is(true));
+ }
+ }
+
+ @Test
+ public void checkDuplicateScenarios_stillThrows_whenTwoDifferentFiles_shareSameName_afterFileDedup() throws Exception {
+ // Two distinct file paths each declare scenarioName "Given_When_Then_1".
+ // File-path dedup must NOT collapse them — they are different files.
+ // checkDuplicateScenarios must still detect the duplicate scenario name.
+ String packagePath = "unit_test_files/test_scenario_cases";
+
+ Path tempDir = Files.createTempDirectory("scenario-dedup-test");
+ File jarFile = new File(tempDir.toFile(), "scenario-cases.jar");
+
+ File resourceDir = new File(getClass().getClassLoader().getResource(packagePath).getFile());
+ try (java.util.jar.JarOutputStream jarOut =
+ new java.util.jar.JarOutputStream(Files.newOutputStream(jarFile.toPath()))) {
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/"));
+ jarOut.closeEntry();
+ for (File f : resourceDir.listFiles((d, n) -> n.endsWith(".json"))) {
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/" + f.getName()));
+ jarOut.write(Files.readAllBytes(f.toPath()));
+ jarOut.closeEntry();
+ }
+ }
+
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{jarFile.toURI().toURL()}, original);
+ Thread.currentThread().setContextClassLoader(urlClassLoader);
+
+ try {
+ // File-path dedup collapses the jar duplicates back to 3 distinct paths.
+ // The two files with the same scenarioName are still two distinct paths — must still throw.
+ expectedException.expect(RuntimeException.class);
+ expectedException.expectMessage("Oops! Duplicate scenario found, either rename or remove extra ones");
+ smartUtils.checkDuplicateScenarios(packagePath);
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
+ @Test
+ public void checkDuplicateScenarios_doesNotThrow_whenSameFileAppearsInTwoClasspathRoots() throws Exception {
+ // Before the distinct() fix, the same file appearing twice from two classpath roots caused
+ // a false-positive duplicate scenario name error. After the fix it must not throw.
+ String packagePath = "unit_test_files/no_dup_scenario_cases";
+
+ String scenario1 = "{\"scenarioName\":\"UniqueScenario_A\",\"steps\":[]}";
+ String scenario2 = "{\"scenarioName\":\"UniqueScenario_B\",\"steps\":[]}";
+
+ // Build a real file: classpath root (temp dir with package structure)
+ Path fsRoot = Files.createTempDirectory("no-false-positive-fs");
+ Path packageDir = fsRoot.resolve(packagePath);
+ Files.createDirectories(packageDir);
+ Files.write(packageDir.resolve("scenario_a.json"), scenario1.getBytes());
+ Files.write(packageDir.resolve("scenario_b.json"), scenario2.getBytes());
+
+ // Build a JAR with the identical files — same relative paths will appear twice
+ File jarFile = new File(fsRoot.toFile(), "mirrored-scenarios.jar");
+ try (java.util.jar.JarOutputStream jarOut =
+ new java.util.jar.JarOutputStream(Files.newOutputStream(jarFile.toPath()))) {
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/"));
+ jarOut.closeEntry();
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/scenario_a.json"));
+ jarOut.write(scenario1.getBytes());
+ jarOut.closeEntry();
+ jarOut.putNextEntry(new java.util.jar.JarEntry(packagePath + "/scenario_b.json"));
+ jarOut.write(scenario2.getBytes());
+ jarOut.closeEntry();
+ }
+
+ ClassLoader original = Thread.currentThread().getContextClassLoader();
+ // Both classpath roots contain the same two files — paths appear twice before dedup
+ URLClassLoader urlClassLoader = new URLClassLoader(
+ new URL[]{fsRoot.toUri().toURL(), jarFile.toURI().toURL()}, original);
+ Thread.currentThread().setContextClassLoader(urlClassLoader);
+
+ try {
+ // Should not throw — distinct() deduplicates the paths; no true duplicate scenario names
+ smartUtils.checkDuplicateScenarios(packagePath);
+ } finally {
+ Thread.currentThread().setContextClassLoader(original);
+ }
+ }
+
// Move this to File Util class
private static File createCascadeIfNotExisting(String fileName) {
try {
diff --git a/http-testing-examples/pom.xml b/http-testing-examples/pom.xml
index 99357867..287279cf 100644
--- a/http-testing-examples/pom.xml
+++ b/http-testing-examples/pom.xml
@@ -4,7 +4,7 @@
zerocode-tdd-parent
org.jsmart
- 1.4.2
+ 1.4.3
org.jsmart
diff --git a/junit5-testing-examples/pom.xml b/junit5-testing-examples/pom.xml
index e2a8b30a..c587263b 100644
--- a/junit5-testing-examples/pom.xml
+++ b/junit5-testing-examples/pom.xml
@@ -4,7 +4,7 @@
zerocode-tdd-parent
org.jsmart
- 1.4.2
+ 1.4.3
zerocode-tdd-jupiter
diff --git a/kafka-testing-examples/pom.xml b/kafka-testing-examples/pom.xml
index a36abf52..f90bd892 100644
--- a/kafka-testing-examples/pom.xml
+++ b/kafka-testing-examples/pom.xml
@@ -4,7 +4,7 @@
zerocode-tdd-parent
org.jsmart
- 1.4.2
+ 1.4.3
kafka-testing-examples
diff --git a/pom.xml b/pom.xml
index ef90f742..d8d18c26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
zerocode-tdd-parent
org.jsmart
- 1.4.2
+ 1.4.3
pom
ZeroCode TDD Parent
diff --git a/zerocode-maven-archetype/pom.xml b/zerocode-maven-archetype/pom.xml
index 356cf309..ff54f4d9 100644
--- a/zerocode-maven-archetype/pom.xml
+++ b/zerocode-maven-archetype/pom.xml
@@ -4,7 +4,7 @@
org.jsmart
zerocode-tdd-parent
- 1.4.2
+ 1.4.3
zerocode-maven-archetype