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 c296ddc21..4442dd523 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
@@ -21,6 +21,10 @@ public interface ZeroCodeReportConstants {
String ZEROCODE_JUNIT = "zerocode.junit";
String CHARTS_AND_CSV = "gen-smart-charts-csv-reports";
+ String ZEROCODE_PROPERTIES_FILE = "zerocode.properties";
+ String REPORT_HTML_FILE_NAME_KEY = "zerocode.report.html.name";
+ String REPORT_CSV_FILE_NAME_KEY = "zerocode.report.csv.name";
+
// Custom js and css for extent report
String EXTENT_ADDITIONAL_JS = "document.querySelector('.vheader').insertAdjacentHTML('afterbegin'," +
"'
" +
diff --git a/core/src/main/java/org/jsmart/zerocode/core/logbuilder/RequestLogBuilder.java b/core/src/main/java/org/jsmart/zerocode/core/logbuilder/RequestLogBuilder.java
index 83d248d2b..8c0140ad9 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/logbuilder/RequestLogBuilder.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/logbuilder/RequestLogBuilder.java
@@ -104,7 +104,7 @@ public String toString() {
return relationshipId +
"\n*requestTimeStamp:" + requestTimeStamp +
"\nstep:" + stepName +
- "\nid:" + (id != null? id : "None") +
+ (id != null ? "\nid:" + id : "") +
"\nurl:" + url +
"\nmethod:" + method +
"\nrequest:\n" + request;
diff --git a/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ResponseLogBuilder.java b/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ResponseLogBuilder.java
index 15269136c..9a919254d 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ResponseLogBuilder.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ResponseLogBuilder.java
@@ -48,7 +48,6 @@ public String toString() {
return relationshipId +
"\nResponse:\n" + response +
"\n*responseTimeStamp:" + responseTimeStamp;
- //"\n\n---------> Assertion: <----------\n" + assertion;
}
public ResponseLogBuilder assertionSection(String assertionJson) {
diff --git a/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ZerocodeCorrelationshipLogger.java b/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ZerocodeCorrelationshipLogger.java
index 53c807b7c..d6f563701 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ZerocodeCorrelationshipLogger.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/logbuilder/ZerocodeCorrelationshipLogger.java
@@ -1,8 +1,5 @@
package org.jsmart.zerocode.core.logbuilder;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import org.jsmart.zerocode.core.domain.builders.ZeroCodeReportStepBuilder;
import org.jsmart.zerocode.core.domain.reports.ZeroCodeReportStep;
import org.slf4j.Logger;
@@ -25,19 +22,10 @@ public class ZerocodeCorrelationshipLogger {
private String correlationId;
private RequestLogBuilder requestLogBuilder = new RequestLogBuilder();
private ResponseLogBuilder responseLogBuilder = new ResponseLogBuilder();
- private ScenarioLogBuilder scenarioLogBuilder = new ScenarioLogBuilder();
private Integer stepLoop;
private Boolean result;
private Double responseDelay;
- private List steps = Collections.synchronizedList(new ArrayList());
-
- public ZerocodeCorrelationshipLogger step(ZeroCodeReportStep step) {
- this.steps.add(step);
- return this;
- }
-
-
public ZerocodeCorrelationshipLogger(Logger logger) {
this.logger = logger;
}
@@ -80,7 +68,7 @@ public ZeroCodeReportStep buildReportSingleStep() {
.loop(stepLoop)
.name(requestLogBuilder.getStepName())
.correlationId(getCorrelationId())
- .result(result == true? RESULT_PASS : RESULT_FAIL)
+ .result(result ? RESULT_PASS : RESULT_FAIL)
.url(requestLogBuilder.getUrl())
.operation(requestLogBuilder.getMethod())
.assertions(responseLogBuilder.getAssertion())
@@ -88,12 +76,10 @@ public ZeroCodeReportStep buildReportSingleStep() {
.responseTimeStamp(responseLogBuilder.responseTimeStamp)
.responseDelay(responseDelay)
.id(requestLogBuilder.getId());
- if (this.result) {
- zeroCodeReportStep.result(RESULT_PASS);
- }else{
- zeroCodeReportStep.response(responseLogBuilder.getResponse());
- zeroCodeReportStep.request(requestLogBuilder.getRequest());
- }
+ if (!result) {
+ zeroCodeReportStep.response(responseLogBuilder.getResponse());
+ zeroCodeReportStep.request(requestLogBuilder.getRequest());
+ }
if(null != responseLogBuilder.customLog){
zeroCodeReportStep.customLog(responseLogBuilder.customLog);
}
@@ -105,10 +91,6 @@ public ResponseLogBuilder aResponseBuilder() {
return responseLogBuilder;
}
- public ScenarioLogBuilder aScenarioBuilder() {
- return scenarioLogBuilder;
- }
-
public void buildResponseDelay() {
responseDelay = durationMilliSecBetween(
requestLogBuilder.getRequestTimeStamp(),
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 56398b1aa..122796fa9 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
@@ -33,12 +33,32 @@
import static java.util.Optional.ofNullable;
import static org.jsmart.zerocode.core.constants.ZeroCodeReportConstants.*;
import static org.jsmart.zerocode.core.domain.builders.ExtentReportsFactory.getReportName;
+import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadCustomZerocodeProperties;
public class ZeroCodeReportGeneratorImpl implements ZeroCodeReportGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(ZeroCodeReportGeneratorImpl.class);
private static String spikeChartFileName;
+ java.util.Properties zerocodeProperties = null;
+
+ java.util.Properties getZerocodeProperties() {
+ if (zerocodeProperties == null) {
+ zerocodeProperties = loadCustomZerocodeProperties();
+ }
+ return zerocodeProperties;
+ }
+
+ String resolveHtmlReportName() {
+ String customFileName = getZerocodeProperties().getProperty(REPORT_HTML_FILE_NAME_KEY);
+ return (customFileName != null && !customFileName.trim().isEmpty()) ? customFileName : TARGET_FILE_NAME;
+ }
+
+ String resolveCsvReportName() {
+ String customFileName = getZerocodeProperties().getProperty(REPORT_CSV_FILE_NAME_KEY);
+ return (customFileName != null && !customFileName.trim().isEmpty()) ? customFileName : TARGET_FULL_REPORT_CSV_FILE_NAME;
+ }
+
/**
* Spike chat is disabled by default
*/
@@ -90,7 +110,7 @@ public void generateExtentReport() {
return;
}
- ExtentReports extentReports = ExtentReportsFactory.createReportTheme(TARGET_FILE_NAME);
+ ExtentReports extentReports = ExtentReportsFactory.createReportTheme(resolveHtmlReportName());
linkToSpikeChartIfEnabled();
@@ -301,12 +321,7 @@ public void generateCsvReport(List zeroCodeCsvReportRows) {
ObjectWriter writer = csvMapper.writer(schema.withLineSeparator("\n"));
try {
writer.writeValue(
- new File(TARGET_FULL_REPORT_DIR +
- TARGET_FULL_REPORT_CSV_FILE_NAME
- //"_" +
- //LocalDateTime.now().toString().replace(":", "-") +
- //".csv"
- ),
+ new File(TARGET_FULL_REPORT_DIR + resolveCsvReportName()),
zeroCodeCsvReportRows);
} catch (IOException e) {
diff --git a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodePackageRunner.java b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodePackageRunner.java
index 8ef9f6df0..a6cec85ff 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodePackageRunner.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodePackageRunner.java
@@ -144,15 +144,26 @@ protected Description describeChild(ScenarioSpec child) {
@Override
public void run(RunNotifier notifier) {
RunListener reportListener = createTestUtilityListener();
- notifier.addListener(reportListener);
+ // ------------------------------------------------------------------------
+ // Commented this to prevent duplicate JSON report generation : 16/05/2026
+ // Uncomment if it breaks anything for Gradle. Maven should be fine.
+ // ------------------------------------------------------------------------
+ // notifier.addListener(reportListener);
LOGGER.debug("System property " + ZEROCODE_JUNIT + "=" + getProperty(ZEROCODE_JUNIT));
+ // Gradle doesn't fire JUnit RunListener events (known Gradle bug).
+ // When the flag is set, skip normal listener registration — Gradle would ignore it anyway.
+ // Maven/IDE: flag absent, so listener registers normally via JUnit lifecycle.
if (!CHARTS_AND_CSV.equals(getProperty(ZEROCODE_JUNIT))) {
+ // This is for usual Maven flow
notifier.addListener(reportListener);
}
super.run(notifier);
+ // Gradle Flow starts here: manually fire testRunFinished() to generate reports,
+ // since Gradle never triggers the JUnit RunListener that would do it.
+ // See inside, it checks the Gradle flags(if supplied by user)
handleNoRunListenerReport(reportListener);
}
diff --git a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java
index 76037cd33..9c29d953c 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java
@@ -100,12 +100,17 @@ public void run(RunNotifier notifier) {
RunListener reportListener = createTestUtilityListener();
LOGGER.debug("System property " + ZEROCODE_JUNIT + "=" + getProperty(ZEROCODE_JUNIT));
+ // Gradle doesn't fire JUnit RunListener events (known Gradle bug).
+ // When the flag is set, skip normal listener registration — Gradle would ignore it anyway.
+ // Maven/IDE: flag absent, so listener registers normally via JUnit lifecycle.
if (!CHARTS_AND_CSV.equals(getProperty(ZEROCODE_JUNIT))) {
notifier.addListener(reportListener);
}
super.run(notifier);
+ // Gradle bypass: manually fire testRunFinished() to generate reports,
+ // since Gradle never triggers the JUnit RunListener that would do it.
handleNoRunListenerReport(reportListener);
}
diff --git a/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java b/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java
index d5ce4e817..33b2047b6 100644
--- a/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java
+++ b/core/src/main/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtils.java
@@ -5,6 +5,10 @@
import java.io.InputStream;
import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.jsmart.zerocode.core.constants.ZeroCodeReportConstants.ZEROCODE_PROPERTIES_FILE;
import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_CONTEXT;
import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_HOST;
import static org.jsmart.zerocode.core.di.PropertyKeys.RESTFUL_APPLICATION_ENDPOINT_PORT;
@@ -16,6 +20,7 @@
public class PropertiesProviderUtils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesProviderUtils.class);
private static Properties properties = new Properties();
@@ -54,6 +59,22 @@ public static Properties loadAbsoluteProperties(String host, Properties properti
}
}
+ public static Properties loadCustomZerocodeProperties() {
+ Properties props = new Properties();
+ InputStream inputStream = PropertiesProviderUtils.class
+ .getClassLoader()
+ .getResourceAsStream(ZEROCODE_PROPERTIES_FILE);
+ if (inputStream == null) {
+ return props;
+ }
+ try {
+ props.load(inputStream);
+ } catch (IOException e) {
+ LOGGER.debug("Could not load {}. Using defaults. Details: {}", ZEROCODE_PROPERTIES_FILE, e.getMessage());
+ }
+ return props;
+ }
+
public static void checkAndLoadOldProperties(Properties properties) {
if (properties.get(WEB_APPLICATION_ENDPOINT_HOST) == null && properties.get(RESTFUL_APPLICATION_ENDPOINT_HOST) != null) {
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 60dfa5ea3..8f8813ae4 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
@@ -12,10 +12,14 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Properties;
+
import static org.hamcrest.MatcherAssert.assertThat;
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;
+import static org.jsmart.zerocode.core.constants.ZeroCodeReportConstants.TARGET_FILE_NAME;
+import static org.jsmart.zerocode.core.constants.ZeroCodeReportConstants.TARGET_FULL_REPORT_CSV_FILE_NAME;
import static org.junit.Assert.assertEquals;
public class ZeroCodeReportGeneratorImplTest {
@@ -173,4 +177,26 @@ public void testGettingUniqueStepsForNoRetries(){
}
+ @Test
+ public void resolveHtmlReportName_returnsCustomName_whenKeyPresentInZerocodeProperties() {
+ assertThat(zeroCodeReportGenerator.resolveHtmlReportName(), is("target/my-custom-report.html"));
+ }
+
+ @Test
+ public void resolveCsvReportName_returnsCustomName_whenKeyPresentInZerocodeProperties() {
+ assertThat(zeroCodeReportGenerator.resolveCsvReportName(), is("my-custom-granular.csv"));
+ }
+
+ @Test
+ public void resolveHtmlReportName_returnsDefault_whenZerocodePropertiesIsEmpty() {
+ zeroCodeReportGenerator.zerocodeProperties = new Properties(); //setting to empty
+ assertThat(zeroCodeReportGenerator.resolveHtmlReportName(), is(TARGET_FILE_NAME));
+ }
+
+ @Test
+ public void resolveCsvReportName_returnsDefault_whenZerocodePropertiesIsEmpty() {
+ zeroCodeReportGenerator.zerocodeProperties = new Properties();
+ assertThat(zeroCodeReportGenerator.resolveCsvReportName(), is(TARGET_FULL_REPORT_CSV_FILE_NAME));
+ }
+
}
\ No newline at end of file
diff --git a/core/src/test/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtilsTest.java b/core/src/test/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtilsTest.java
new file mode 100644
index 000000000..f101dd8c4
--- /dev/null
+++ b/core/src/test/java/org/jsmart/zerocode/core/utils/PropertiesProviderUtilsTest.java
@@ -0,0 +1,30 @@
+package org.jsmart.zerocode.core.utils;
+
+import org.junit.Test;
+
+import java.util.Properties;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.jsmart.zerocode.core.utils.PropertiesProviderUtils.loadCustomZerocodeProperties;
+
+public class PropertiesProviderUtilsTest {
+
+ @Test
+ public void loadCustomZerocodeProperties_returnsProperties_whenFileOnClasspath() {
+ Properties props = loadCustomZerocodeProperties();
+ assertNotNull(props);
+ assertThat(props.getProperty("zerocode.report.html.name"), is("target/my-custom-report.html"));
+ assertThat(props.getProperty("zerocode.report.csv.name"), is("my-custom-granular.csv"));
+ }
+
+ @Test
+ public void loadCustomZerocodeProperties_returnsEmptyProperties_forAbsentKey() {
+ Properties props = loadCustomZerocodeProperties();
+ assertNotNull(props);
+ assertNull(props.getProperty("zerocode.report.nonexistent.key"));
+ }
+
+}
diff --git a/core/src/test/java/org/jsmart/zerocode/integrationtests/db/DbSqlExecutorScenarioPostgresTest.java b/core/src/test/java/org/jsmart/zerocode/integrationtests/db/DbSqlExecutorScenarioPostgresTest.java
index a3a1f6e54..675a7cb76 100644
--- a/core/src/test/java/org/jsmart/zerocode/integrationtests/db/DbSqlExecutorScenarioPostgresTest.java
+++ b/core/src/test/java/org/jsmart/zerocode/integrationtests/db/DbSqlExecutorScenarioPostgresTest.java
@@ -10,6 +10,8 @@
public class DbSqlExecutorScenarioPostgresTest {
// Note: Spin up the DB container before running this test: docker/compose/pg_compose.yml
+ // or, you can ignore these two tests failures locally(laptop) if you're running only unit tests.
+ // Note: in CI, these must pass as PG docker is auto spun up(do not ignore failures in CI)
@Test
@Scenario("integration_test_files/db/db_csv_load_with_headers_postgres.json")
diff --git a/core/src/test/resources/zerocode.properties b/core/src/test/resources/zerocode.properties
new file mode 100644
index 000000000..3b611123f
--- /dev/null
+++ b/core/src/test/resources/zerocode.properties
@@ -0,0 +1,2 @@
+zerocode.report.html.name=target/my-custom-report.html
+zerocode.report.csv.name=my-custom-granular.csv
diff --git a/http-testing-examples/src/test/resources/zerocode.properties b/http-testing-examples/src/test/resources/zerocode.properties
new file mode 100644
index 000000000..4920e46bc
--- /dev/null
+++ b/http-testing-examples/src/test/resources/zerocode.properties
@@ -0,0 +1,2 @@
+zerocode.report.html.name=target/zerocode-tdd-report.html
+zerocode.report.csv.name=zerocode-tdd-report.csv