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