Repository: authorjapps/zerocode
Pull request #50 (2018-03-11 15:04:40)

Zcode sonatype release branch


Patch:
diff --git a/pom.xml b/pom.xml
index dab88995..e5ab0f12 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
 
     <artifactId>zerocode-rest-bdd</artifactId>
     <groupId>org.jsmart</groupId>
-    <version>1.1.28-SNAPSHOT</version>
+    <version>1.1.34-SNAPSHOT</version>
 
     <packaging>jar</packaging>
     <name>ZeroCode REST BDD</name>
@@ -70,6 +70,7 @@
         <micro-simulator.version>1.1.10</micro-simulator.version>
         <httpclient.version>4.5</httpclient.version>
         <h2.db.version>1.4.191</h2.db.version>
+        <extentreports.version>3.1.3</extentreports.version>
 
         <!-- maven plugin build related  -->
         <maven-compiler-plugin.version>3.2</maven-compiler-plugin.version>
@@ -198,6 +199,11 @@
             <version>${h2.db.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.aventstack</groupId>
+            <artifactId>extentreports</artifactId>
+            <version>${extentreports.version}</version>
+        </dependency>
     </dependencies>
 
     <profiles>
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/MockStep.java b/src/main/java/org/jsmart/zerocode/core/domain/MockStep.java
index 37382f45..988637ea 100644
--- a/src/main/java/org/jsmart/zerocode/core/domain/MockStep.java
+++ b/src/main/java/org/jsmart/zerocode/core/domain/MockStep.java
@@ -3,7 +3,13 @@ package org.jsmart.zerocode.core.domain;
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.jsmart.zerocode.core.di.ObjectMapperProvider;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
 
 @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
 /**
@@ -19,7 +25,14 @@ public class MockStep {
     private final JsonNode response;
     private final JsonNode assertions; //<-- In case the wiremock or simulator mock throws a status code etc
 
+    // derived value i.e. body as JSON string
+    private String body;
+
+    // derived value i.e. headers as JSON string
+    private String headers;
 
+    // derived value ie headers as Map
+    private Map<String, Object> headersMap;
 
     public String getName() {
         return name;
@@ -45,6 +58,31 @@ public class MockStep {
         return assertions;
     }
 
+    public String getBody() {
+        final JsonNode bodyNode = request.get("body");
+        return bodyNode != null ? request.get("body").toString() : null;
+    }
+
+    public String getHeaders() {
+        return request.get("headers").toString();
+    }
+
+    public Map<String, Object> getHeadersMap() {
+        ObjectMapper objectMapper = new ObjectMapperProvider().get();
+        HashMap<String, Object> headersMap = new HashMap<>();
+        try {
+            final JsonNode headersNode = request.get("headers");
+            if (null != headersNode) {
+                headersMap = (HashMap<String, Object>) objectMapper.readValue(headersNode.toString(), HashMap.class);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+
+        return headersMap;
+    }
+
     @JsonCreator
     public MockStep(
             @JsonProperty("name") String name,
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/ReportProperties.java b/src/main/java/org/jsmart/zerocode/core/domain/ReportProperties.java
new file mode 100644
index 00000000..7b3d3bc9
--- /dev/null
+++ b/src/main/java/org/jsmart/zerocode/core/domain/ReportProperties.java
@@ -0,0 +1,20 @@
+//package org.jsmart.zerocode.core.domain;
+//
+//import java.lang.annotation.Documented;
+//import java.lang.annotation.ElementType;
+//import java.lang.annotation.Inherited;
+//import java.lang.annotation.Retention;
+//import java.lang.annotation.RetentionPolicy;
+//import java.lang.annotation.Target;
+//
+//@Documented
+//@Target(ElementType.TYPE)
+//@Retention(RetentionPolicy.RUNTIME)
+//@Inherited
+//public @interface ReportProperties {
+//
+//    boolean searchableReport() default true;
+//    boolean filterByAuthor() default true;
+//    boolean spikeReport() default false;
+//
+//}
\ No newline at end of file
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/builders/ExtentReportsFactory.java b/src/main/java/org/jsmart/zerocode/core/domain/builders/ExtentReportsFactory.java
new file mode 100644
index 00000000..2b1d341e
--- /dev/null
+++ b/src/main/java/org/jsmart/zerocode/core/domain/builders/ExtentReportsFactory.java
@@ -0,0 +1,106 @@
+package org.jsmart.zerocode.core.domain.builders;
+
+import com.aventstack.extentreports.ExtentReports;
+import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
+import com.aventstack.extentreports.reporter.configuration.ChartLocation;
+import com.aventstack.extentreports.reporter.configuration.Theme;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.REPORT_DISPLAY_NAME_DEFAULT;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.REPORT_TITLE_DEFAULT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class ExtentReportsFactory {
+    private static final org.slf4j.Logger LOGGER = getLogger(ExtentReportsFactory.class);
+
+    private static ExtentHtmlReporter extentHtmlReporter;
+
+    private static ExtentReports extentReports;
+
+    private static Map<Object, String> systemProperties = new HashMap<>();
+
+    public static ExtentReports createReportTheme(String reportFileName) {
+        ExtentHtmlReporter extentHtmlReporter = createExtentHtmlReporter(reportFileName);
+
+        extentReports = new ExtentReports();
+
+        attachSystemInfo();
+
+        extentReports.attachReporter(extentHtmlReporter);
+        extentReports.setReportUsesManualConfiguration(true);
+
+        return extentReports;
+    }
+
+    public static void attachSystemInfo() {
+        systemProperties = getSystemProperties();
+        final String osName = systemProperties.get("os.name");
+        final String osArchitecture = systemProperties.get("os.arch");
+        final String javaVersion = systemProperties.get("java.version");
+
+        LOGGER.info("Where were the tests fired? Ans: OS:{}, Architecture:{}, Java:{}",
+                osName, osArchitecture, javaVersion);
+
+        extentReports.setSystemInfo("OS : ", osName);
+        extentReports.setSystemInfo("OS Architecture : ", osArchitecture);
+        extentReports.setSystemInfo("Java Version : ", javaVersion);
+    }
+
+    public static ExtentHtmlReporter createExtentHtmlReporter(String reportFileName) {
+        extentHtmlReporter = new ExtentHtmlReporter(reportFileName);
+
+        extentHtmlReporter.config().setChartVisibilityOnOpen(false);
+        extentHtmlReporter.config().setTheme(Theme.STANDARD);
+
+        extentHtmlReporter.config().setDocumentTitle(REPORT_TITLE_DEFAULT);
+        extentHtmlReporter.config().setReportName(REPORT_DISPLAY_NAME_DEFAULT);
+        extentHtmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
+
+        return extentHtmlReporter;
+    }
+
+
+    public static Map<Object, String> getSystemProperties() {
+        Map<Object, String> map = new HashMap<>();
+        try {
+            Properties properties = System.getProperties();
+
+            Set sysPropertiesKeys = properties.keySet();
+            for (Object key : sysPropertiesKeys) {
+                map.put(key, properties.getProperty((String) key));
+            }
+
+            // Ask in the community forum who needs and why this Ip information ?
+            /*
+            String ipAddress = InetAddress.getLocalHost().toString();
+            map.put("machine.name", ipAddress.split("/")[0]);
+            map.put("machine.address", ipAddress.split("/")[1]);
+            */
+        } catch (Exception e) {
+            e.printStackTrace();
+            LOGGER.error("Extent reporting error. You can safely ignore this. But to fix this see:" + e);
+        }
+
+        return map;
+    }
+
+    public static void reportName(String reportName) {
+        extentHtmlReporter.config().setReportName(reportName);
+    }
+
+    public static String getReportName() {
+        return extentHtmlReporter.config().getReportName();
+    }
+
+    public static void chartVisibilityOnOpen(boolean chartVisibilityOnOpen) {
+        extentHtmlReporter.config().setChartVisibilityOnOpen(chartVisibilityOnOpen);
+    }
+
+    public static void getChartVisibilityOnOpen(boolean chartVisibilityOnOpen) {
+        extentHtmlReporter.config().getChartVisibilityOnOpen();
+    }
+}
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueArrayBuilder.java b/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueArrayBuilder.java
index e4cf4124..34a87373 100644
--- a/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueArrayBuilder.java
+++ b/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueArrayBuilder.java
@@ -8,15 +8,36 @@ import java.util.stream.Collectors;
 
 public class ZeroCodeChartKeyValueArrayBuilder {
 
+    public static final String TEST_FAILED = "FAILED";
+
     List<ZeroCodeChartKeyValue> kvs = new ArrayList<>();
 
     public static ZeroCodeChartKeyValueArrayBuilder newInstance() {
         return new ZeroCodeChartKeyValueArrayBuilder();
     }
 
+    /*
+        var processed_test_result = [{
+            name: 'Will Mock an End Point and Test the end point using Zerocode->setup_mocks',
+            y: 429.0
+        }, {
+            name: 'Will Mock an End Point and Test the end point using Zerocode->actual_test_verify_get_customer',
+            color: '#FF0000',
+            y: 92.0
+        }];
+    */
     public String build() {
         final String dataRowsCommaSeparated = kvs.stream()
-                .map(thisRow -> String.format("['%s',%s]", thisRow.getKey(), thisRow.getValue()))
+                .map(thisRow -> {
+                            if (TEST_FAILED.equals(thisRow.getResult())) {
+                                return String.format("{name: '%s', y: %s, color: '#FF0000'}", thisRow.getKey(), thisRow.getValue());
+                            } else {
+                                return String.format("{name: '%s', y: %s}", thisRow.getKey(), thisRow.getValue());
+                            }
+
+                        }
+
+                )
                 .collect(Collectors.joining(", "));
 
         String dataArray = "[" + dataRowsCommaSeparated + "]";
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueBuilder.java b/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueBuilder.java
index 2e4eb8a4..81500361 100644
--- a/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueBuilder.java
+++ b/src/main/java/org/jsmart/zerocode/core/domain/builders/ZeroCodeChartKeyValueBuilder.java
@@ -6,13 +6,14 @@ public class ZeroCodeChartKeyValueBuilder {
 
     String key;
     Double value;
+    String result;
 
     public static ZeroCodeChartKeyValueBuilder newInstance() {
         return new ZeroCodeChartKeyValueBuilder();
     }
 
     public ZeroCodeChartKeyValue build() {
-        ZeroCodeChartKeyValue built = new ZeroCodeChartKeyValue(key, value);
+        ZeroCodeChartKeyValue built = new ZeroCodeChartKeyValue(key, value, result);
         return built;
     }
 
@@ -26,4 +27,9 @@ public class ZeroCodeChartKeyValueBuilder {
         this.value = value;
         return  this;
     }
+
+    public ZeroCodeChartKeyValueBuilder result(String result) {
+        this.result = result;
+        return  this;
+    }
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/reports/ZeroCodeReportProperties.java b/src/main/java/org/jsmart/zerocode/core/domain/reports/ZeroCodeReportProperties.java
index 813f1536..878c2bd0 100644
--- a/src/main/java/org/jsmart/zerocode/core/domain/reports/ZeroCodeReportProperties.java
+++ b/src/main/java/org/jsmart/zerocode/core/domain/reports/ZeroCodeReportProperties.java
@@ -1,11 +1,19 @@
 package org.jsmart.zerocode.core.domain.reports;
 
 public interface ZeroCodeReportProperties {
+    String TEST_STEP_CORRELATION_ID = "TEST-STEP-CORRELATION-ID:";
+    String TARGET_FILE_NAME = "target/zerocode-interactive.html";
     String TARGET_REPORT_DIR = "target/zerocode-test-reports/";
     String TARGET_FULL_REPORT_DIR = "target/";
     String TARGET_FULL_REPORT_CSV_FILE_NAME = "zerocode_full_report";
     String RESULT_PASS = "PASSED";
     String RESULT_FAIL = "FAILED";
     String HIGH_CHART_HTML_FILE_NAME = "zerocode_results_chart";
+    String AUTHOR_MARKER = "@@";
+    String ANONYMOUS_AUTHOR = "Anonymous";
+    String REPORT_TITLE_DEFAULT = "Zerocode Test Report";
+    String REPORT_DISPLAY_NAME_DEFAULT = "Zerocode Interactive Report";
+    String DEFAULT_REGRESSION_CATEGORY = "Regression";
+    String LINK_LABEL_NAME = "Spike Chart(Click here)";
 
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValue.java b/src/main/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValue.java
index 6365f8db..54bf5324 100644
--- a/src/main/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValue.java
+++ b/src/main/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValue.java
@@ -6,12 +6,15 @@ public class ZeroCodeChartKeyValue {
 
     String key;
     Double value;
+    private String result;
 
     public ZeroCodeChartKeyValue(
             @JsonProperty("key")String key,
-            @JsonProperty("value")Double value) {
+            @JsonProperty("value")Double value,
+            @JsonProperty("result")String result) {
         this.key = key;
         this.value = value;
+        this.result = result;
     }
 
 
@@ -30,4 +33,8 @@ public class ZeroCodeChartKeyValue {
                 ", value=" + value +
                 '}';
     }
+
+    public String getResult() {
+        return result;
+    }
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/engine/assertion/FieldHasSubStringIgnoreCaseValueAsserter.java b/src/main/java/org/jsmart/zerocode/core/engine/assertion/FieldHasSubStringIgnoreCaseValueAsserter.java
new file mode 100644
index 00000000..25cdfc73
--- /dev/null
+++ b/src/main/java/org/jsmart/zerocode/core/engine/assertion/FieldHasSubStringIgnoreCaseValueAsserter.java
@@ -0,0 +1,32 @@
+package org.jsmart.zerocode.core.engine.assertion;
+
+public class FieldHasSubStringIgnoreCaseValueAsserter implements JsonAsserter {
+    private final String path;
+    private final String expected;
+
+    public FieldHasSubStringIgnoreCaseValueAsserter(String path, String expected) {
+        this.path = path;
+        this.expected = expected;
+    }
+
+    @Override
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public AssertionReport actualEqualsToExpected(Object result) {
+        boolean areEqual;
+        if (result instanceof String && expected instanceof String) {
+            String s1 = (String) result;
+            String s2 = expected;
+            areEqual = s1.toUpperCase().contains(s2.toUpperCase());
+        } else {
+            areEqual = false;
+        }
+
+        return areEqual ?
+                AssertionReport.createFieldMatchesReport() :
+                AssertionReport.createFieldDoesNotMatchReport(path, "containing sub-string with ignoring case:" + expected, result);
+    }
+}
diff --git a/src/main/java/org/jsmart/zerocode/core/engine/executor/JsonServiceExecutorImpl.java b/src/main/java/org/jsmart/zerocode/core/engine/executor/JsonServiceExecutorImpl.java
index 6ead1e9f..2b168d98 100644
--- a/src/main/java/org/jsmart/zerocode/core/engine/executor/JsonServiceExecutorImpl.java
+++ b/src/main/java/org/jsmart/zerocode/core/engine/executor/JsonServiceExecutorImpl.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.inject.Inject;
+import com.google.inject.name.Named;
 import com.jayway.jsonpath.JsonPath;
 import com.jayway.jsonpath.PathNotFoundException;
 
@@ -32,12 +33,20 @@ public class JsonServiceExecutorImpl implements JsonServiceExecutor {
     //guice
     @Inject
     private JavaExecutor javaExecutor;
+
     @Inject
     private ObjectMapper objectMapper;
+
     @Inject
     SmartUtils smartUtils;
+
     @Inject
     BasicHttpClient httpClient;
+
+    @Inject(optional = true)
+    @Named("mock.api.port")
+    private int mockPort;
+
     //guice
 
     public JsonServiceExecutorImpl() {
@@ -90,8 +99,6 @@ public class JsonServiceExecutorImpl implements JsonServiceExecutor {
         HashMap headers = (HashMap) readJsonPathOrElseNull(requestJson, "$.headers");
         Object bodyContent = readJsonPathOrElseNull(requestJson, "$.body");
 
-        final javax.ws.rs.core.Response serverResponse = httpClient.execute(httpUrl, methodName, headers, queryParams, bodyContent);
-
         /*
          * $MOCK: Create mock endpoints supplied for this scenario
          */
@@ -102,6 +109,8 @@ public class JsonServiceExecutorImpl implements JsonServiceExecutor {
             return "{\"status\": 200}";
         }
 
+        final javax.ws.rs.core.Response serverResponse = httpClient.execute(httpUrl, methodName, headers, queryParams, bodyContent);
+
         /*
          * now read the response for :
          * - headers
@@ -151,11 +160,18 @@ public class JsonServiceExecutorImpl implements JsonServiceExecutor {
 
             MockSteps mockSteps = smartUtils.getMapper().readValue(requestJson, MockSteps.class);
 
-            createWithWireMock(mockSteps);
+            if(mockPort > 0){
+                createWithWireMock(mockSteps, mockPort);
 
-            LOGGER.info("#SUCCESS: End points simulated via wiremock.");
+                LOGGER.info("#SUCCESS: End points simulated via wiremock.");
 
-            return true;
+                return true;
+            }
+
+            LOGGER.error("\n\n#DISABLED: Mocking was not activated as there was no port configured in the properties file. \n\n " +
+                    "Usage: e.g. in your <env host config .properties> file provide- \n " +
+                    "mock.api.port=8888\n\n");
+            return false;
         }
 
         else if(httpUrl.contains("/$MOCK") && methodName.equals("$USE.VIRTUOSO")){
diff --git a/src/main/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMocker.java b/src/main/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMocker.java
index fa7db919..3d094210 100644
--- a/src/main/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMocker.java
+++ b/src/main/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMocker.java
@@ -1,51 +1,119 @@
 package org.jsmart.zerocode.core.engine.mocker;
 
 import com.github.tomakehurst.wiremock.WireMockServer;
+import com.github.tomakehurst.wiremock.client.MappingBuilder;
+import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
 import com.github.tomakehurst.wiremock.client.WireMock;
 import org.apache.commons.lang.StringUtils;
+import org.jsmart.zerocode.core.domain.MockStep;
 import org.jsmart.zerocode.core.domain.MockSteps;
+import org.jsmart.zerocode.core.engine.executor.JsonServiceExecutorImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
 
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.givenThat;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.put;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
 import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 public class RestEndPointMocker {
+    private static final Logger LOGGER = LoggerFactory.getLogger(JsonServiceExecutorImpl.class);
 
-    public static void createWithWireMock(MockSteps mockSteps) {
-        WireMockServer wireMockServer = new WireMockServer(wireMockConfig().port(8888)); //<-- Strange
-        wireMockServer.start();
-        WireMock.configureFor("localhost", 8888); //<-- Repetition of PORT
+    public static WireMockServer wireMockServer;
+
+    public static void createWithWireMock(MockSteps mockSteps, int mockPort) {
+
+        restartWireMock(mockPort);
 
-        // TODO - Check wiremock APIs to whether it supports method as a paramater or direct JSON
         mockSteps.getMocks().forEach(mockStep -> {
             String jsonBodyRequest = mockStep.getResponse().get("body").toString();
 
             if("GET".equals(mockStep.getOperation())){
-                givenThat(WireMock.get(urlEqualTo(mockStep.getUrl()))
-                        .willReturn(aResponse()
-                                .withStatus(mockStep.getResponse().get("status").asInt())
-                                .withHeader("Content-Type", APPLICATION_JSON)
-                                .withBody(jsonBodyRequest)));
+                LOGGER.info("*****WireMock- Mocking the GET endpoint");
+                givenThat(createGetRequestBuilder(mockStep)
+                        .willReturn(responseBuilder(mockStep, jsonBodyRequest)));
+                LOGGER.info("WireMock- Mocking the GET endpoint -done- *****");
             }
             else if("POST".equals(mockStep.getOperation())){
-                givenThat(WireMock.post(urlEqualTo(mockStep.getUrl()))
-                        .willReturn(aResponse()
-                                .withStatus(mockStep.getResponse().get("status").asInt())
-                                .withHeader("Content-Type", APPLICATION_JSON)
-                                .withBody(jsonBodyRequest)));
+                LOGGER.info("*****WireMock- Mocking the POST endpoint");
+                givenThat(createPostRequestBuilder(mockStep)
+                        .willReturn(responseBuilder(mockStep, jsonBodyRequest)));
+                LOGGER.info("WireMock- Mocking the POST endpoint -done-*****");
             }
             else if("PUT".equals(mockStep.getOperation())){
-                givenThat(WireMock.put(urlEqualTo(mockStep.getUrl()))
-                        .willReturn(aResponse()
-                                .withStatus(mockStep.getResponse().get("status").asInt())
-                                .withHeader("Content-Type", APPLICATION_JSON)
-                                .withBody(jsonBodyRequest)));
+                LOGGER.info("*****WireMock- Mocking the PUT endpoint");
+                givenThat(createPutRequestBuilder(mockStep)
+                        .willReturn(responseBuilder(mockStep, jsonBodyRequest)));
+                LOGGER.info("WireMock- Mocking the PUT endpoint -done-*****");
             }
         });
     }
 
+
+    public static void restartWireMock(int dynamicPort) {
+        wireMockServer = new WireMockServer(wireMockConfig().port(dynamicPort)); // <-- Strange
+        /*
+         * Stop the wireMock server if it is running previously due to any other tests.
+         */
+        wireMockServer.stop();
+        wireMockServer.start();
+        WireMock.configureFor("localhost", dynamicPort); // <-- Repetition of PORT was needed, this is a wireMock bug
+    }
+
+    private static MappingBuilder createPutRequestBuilder(MockStep mockStep) {
+        final MappingBuilder requestBuilder = put(urlEqualTo(mockStep.getUrl()));
+        return createRequestBuilderWithHeaders(mockStep, requestBuilder);
+    }
+
+    private static MappingBuilder createPostRequestBuilder(MockStep mockStep) {
+        final MappingBuilder requestBuilder = post(urlEqualTo(mockStep.getUrl()));
+        return createRequestBuilderWithHeaders(mockStep, requestBuilder);
+    }
+
+    private static MappingBuilder createGetRequestBuilder(MockStep mockStep) {
+        final MappingBuilder requestBuilder = get(urlEqualTo(mockStep.getUrl()));
+        return createRequestBuilderWithHeaders(mockStep, requestBuilder);
+    }
+
+    private static MappingBuilder createRequestBuilderWithHeaders(MockStep mockStep, MappingBuilder requestBuilder) {
+
+        final String bodyJson = mockStep.getBody();
+        // -----------------------------------------------
+        // read request body and set to request builder
+        // -----------------------------------------------
+        if(StringUtils.isNotEmpty(bodyJson)){
+            requestBuilder.withRequestBody(equalToJson(bodyJson));
+        }
+
+        final Map<String, Object> headersMap = mockStep.getHeadersMap();
+        // -----------------------------------------------
+        // read request headers and set to request builder
+        // -----------------------------------------------
+        if (headersMap.size() > 0) {
+            for (Object key : headersMap.keySet()) {
+                requestBuilder.withHeader((String) key, equalTo((String) headersMap.get(key)));
+            }
+        }
+        return requestBuilder;
+    }
+
+    private static ResponseDefinitionBuilder responseBuilder(MockStep mockStep, String jsonBodyRequest) {
+        final ResponseDefinitionBuilder responseBuilder = aResponse()
+                .withStatus(mockStep.getResponse().get("status").asInt())
+                //.withHeader("Content-Type", APPLICATION_JSON) //Can be XMl also for SOAP end points
+                .withBody(jsonBodyRequest);
+
+        return responseBuilder;
+    }
+
+
     /*
      * This is working code, whenever you put the virtuoso dependency here, you can uncomment this block.
      */
diff --git a/src/main/java/org/jsmart/zerocode/core/logbuilder/LogCorrelationshipPrinter.java b/src/main/java/org/jsmart/zerocode/core/logbuilder/LogCorrelationshipPrinter.java
index 722a9695..eec727df 100644
--- a/src/main/java/org/jsmart/zerocode/core/logbuilder/LogCorrelationshipPrinter.java
+++ b/src/main/java/org/jsmart/zerocode/core/logbuilder/LogCorrelationshipPrinter.java
@@ -11,9 +11,10 @@ import java.util.UUID;
 import static java.lang.String.format;
 import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.RESULT_FAIL;
 import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.RESULT_PASS;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TEST_STEP_CORRELATION_ID;
 
 public class LogCorrelationshipPrinter {
-    private static final String DISPLAY_DEMARCATION_ = "\n--------- CORRELATION-ID: %s ---------";
+    private static final String DISPLAY_DEMARCATION_ = "\n--------- " + TEST_STEP_CORRELATION_ID + " %s ---------";
 
     Logger logger;
     static String correlationId;
@@ -46,8 +47,8 @@ public class LogCorrelationshipPrinter {
         return this;
     }
 
-    public LogCorrelationshipPrinter result(Boolean passed) {
-        this.result = passed;
+    public LogCorrelationshipPrinter result(Boolean result) {
+        this.result = result;
         return this;
     }
 
@@ -56,6 +57,7 @@ public class LogCorrelationshipPrinter {
         ZeroCodeReportStep zeroCodeReportStep = ZeroCodeReportStepBuilder.newInstance()
                 //.request(requestLogBuilder.request) //TODO
                 //.response(responseLogBuilder.response) //TODO
+                //.assertions()
                 .loop(stepLoop)
                 .name(requestLogBuilder.stepName)
                 .correlationId(correlationId)
diff --git a/src/main/java/org/jsmart/zerocode/core/report/HighChartColumnHtmlWriter.java b/src/main/java/org/jsmart/zerocode/core/report/HighChartColumnHtmlWriter.java
index f0f9a884..bc6ff025 100644
--- a/src/main/java/org/jsmart/zerocode/core/report/HighChartColumnHtmlWriter.java
+++ b/src/main/java/org/jsmart/zerocode/core/report/HighChartColumnHtmlWriter.java
@@ -10,9 +10,7 @@ import org.jsmart.zerocode.core.domain.reports.chart.HighChartColumnHtml;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.StringWriter;
-import java.time.LocalDateTime;
 
-import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.HIGH_CHART_HTML_FILE_NAME;
 import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TARGET_FULL_REPORT_DIR;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -32,7 +30,7 @@ public class HighChartColumnHtmlWriter {
         this.templateFile = templateFile;
     }
 
-    public String generateHighChart(HighChartColumnHtml highChartColumnHtml){
+    public String generateHighChart(HighChartColumnHtml highChartColumnHtml, String spikeChartFileName){
         vEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
         vEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
 
@@ -49,12 +47,7 @@ public class HighChartColumnHtmlWriter {
         /*  now render the template into a Writer  */
         FileWriter fileWriter = null;
         try {
-            fileWriter = new FileWriter(
-                    TARGET_FULL_REPORT_DIR +
-                    HIGH_CHART_HTML_FILE_NAME +
-                    LocalDateTime.now().toString().replace(":", "-") +
-                    ".html"
-            );
+            fileWriter = new FileWriter(TARGET_FULL_REPORT_DIR + spikeChartFileName);
 
             t.merge(context, fileWriter);
             fileWriter.close();
diff --git a/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java b/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java
index 8dc71f5b..6e71fbfb 100644
--- a/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java
+++ b/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGenerator.java
@@ -1,7 +1,13 @@
 package org.jsmart.zerocode.core.report;
 
 public interface ZeroCodeReportGenerator {
-    public void generateCsvReport();
+    void generateCsvReport();
 
-    public void generateHighChartReport();
+    /**
+     * Spike chat is disabled by default
+     *
+     */
+    void generateHighChartReport();
+
+    void generateExtentReport();
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java b/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java
index f1b3557d..e83e48c6 100644
--- a/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java
+++ b/src/main/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImpl.java
@@ -1,11 +1,17 @@
 package org.jsmart.zerocode.core.report;
 
+import com.aventstack.extentreports.ExtentReports;
+import com.aventstack.extentreports.ExtentTest;
+import com.aventstack.extentreports.Status;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
 import com.fasterxml.jackson.dataformat.csv.CsvMapper;
 import com.fasterxml.jackson.dataformat.csv.CsvParser;
 import com.fasterxml.jackson.dataformat.csv.CsvSchema;
 import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import org.apache.commons.lang.StringUtils;
+import org.jsmart.zerocode.core.domain.builders.ExtentReportsFactory;
 import org.jsmart.zerocode.core.domain.builders.HighChartColumnHtmlBuilder;
 import org.jsmart.zerocode.core.domain.builders.ZeroCodeChartKeyValueArrayBuilder;
 import org.jsmart.zerocode.core.domain.builders.ZeroCodeChartKeyValueBuilder;
@@ -13,61 +19,167 @@ import org.jsmart.zerocode.core.domain.builders.ZeroCodeCsvReportBuilder;
 import org.jsmart.zerocode.core.domain.reports.ZeroCodeReport;
 import org.jsmart.zerocode.core.domain.reports.chart.HighChartColumnHtml;
 import org.jsmart.zerocode.core.domain.reports.csv.ZeroCodeCsvReport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
 import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
 
 import static java.util.Collections.emptyList;
 import static java.util.Optional.ofNullable;
+import static org.apache.commons.lang.StringUtils.substringBetween;
+import static org.jsmart.zerocode.core.domain.builders.ExtentReportsFactory.getReportName;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.ANONYMOUS_AUTHOR;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.AUTHOR_MARKER;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.DEFAULT_REGRESSION_CATEGORY;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.HIGH_CHART_HTML_FILE_NAME;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.LINK_LABEL_NAME;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.RESULT_PASS;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TARGET_FILE_NAME;
 import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TARGET_FULL_REPORT_CSV_FILE_NAME;
 import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TARGET_FULL_REPORT_DIR;
 import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TARGET_REPORT_DIR;
+import static org.jsmart.zerocode.core.domain.reports.ZeroCodeReportProperties.TEST_STEP_CORRELATION_ID;
 
 public class ZeroCodeReportGeneratorImpl implements ZeroCodeReportGenerator {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ZeroCodeReportGeneratorImpl.class);
+
+    private static String spikeChartFileName;
+
+    /**
+     * Spike chat is disabled by default
+     */
+    @Inject(optional = true)
+    @Named("report.spike.chart.enabled")
+    private boolean spikeChartReportEnabled;
 
     @Inject
     private ObjectMapper mapper;
 
-    private List<ZeroCodeReport> individualReports;
+    private List<ZeroCodeReport> treeReports;
 
-    private List<ZeroCodeCsvReport> csvRows = new ArrayList<>();
+    private List<ZeroCodeCsvReport> zeroCodeCsvFlattenedRows;
 
-    private List<ZeroCodeCsvReport> zeroCodeCsvReportRows;
+    private List<ZeroCodeCsvReport> csvRows = new ArrayList<>();
 
     public ZeroCodeReportGeneratorImpl() {
     }
 
+    @Override
+    public void generateExtentReport() {
+
+        ExtentReports extentReports = ExtentReportsFactory.createReportTheme(TARGET_FILE_NAME);
+
+        linkToSpikeChartIfEnabled();
+
+        treeReports.forEach(thisReport -> {
+
+            thisReport.getResults().forEach(thisScenario -> {
+                ExtentTest test = extentReports.createTest(thisScenario.getScenarioName());
+                test.assignCategory(DEFAULT_REGRESSION_CATEGORY);
+
+                test.assignAuthor(optionalAuthor(thisScenario.getScenarioName()));
+
+                thisScenario.getSteps().forEach(thisStep -> {
+                    test.getModel().setStartTime(utilDateOf(thisStep.getRequestTimeStamp()));
+                    test.getModel().setEndTime(utilDateOf(thisStep.getResponseTimeStamp()));
+
+                    final Status testStatus = thisStep.getResult().equals(RESULT_PASS) ? Status.PASS : Status.FAIL;
+                    test.createNode(thisStep.getName(), TEST_STEP_CORRELATION_ID + " "
+                            + thisStep.getCorrelationId()).log(testStatus, thisStep.getName()
+                            + " has " + thisStep.getResult()
+                            + ". \n Search in the log file for-  " + TEST_STEP_CORRELATION_ID + "  \n"
+                            + thisStep.getCorrelationId() + "\n"
+                            + ", url:" + thisStep.getUrl() + "\n"
+                    );
+
+                    extentReports.flush();
+                });
+
+            });
+
+        });
+    }
+
+    public void linkToSpikeChartIfEnabled() {
+
+        // ------------------------------------------------
+        // If spikeChartFileName is not null,
+        // that means it was enabled by one of the runner.
+        // (might be disabled by current runner)
+        // Then it's good to link it to that spike report.
+        // ------------------------------------------------
+        if(spikeChartReportEnabled || spikeChartFileName != null){
+            final String reportName = getReportName();
+
+            String linkCodeToTargetSpikeChartHtml =
+                    String.format("<code>&nbsp;&nbsp;<a href='%s' style=\"color: #006; background: #ff6;\"> %s </a></code>",
+                    spikeChartFileName,
+                    LINK_LABEL_NAME);;
+
+            ExtentReportsFactory.reportName(reportName + linkCodeToTargetSpikeChartHtml);
+        }
+    }
+
+    protected String optionalAuthor(String scenarioName) {
+        String authorName = substringBetween(scenarioName, AUTHOR_MARKER, AUTHOR_MARKER);
+
+        if(authorName == null){
+            authorName = substringBetween(scenarioName, AUTHOR_MARKER, ",");
+        }
+
+        if(authorName == null){
+            authorName = substringBetween(scenarioName, AUTHOR_MARKER, " ");
+        }
+
+        if(authorName == null){
+            authorName = scenarioName.substring(scenarioName.lastIndexOf(AUTHOR_MARKER) + AUTHOR_MARKER.length());
+        }
+
+        if(scenarioName.lastIndexOf(AUTHOR_MARKER) == -1 || StringUtils.isEmpty(authorName)){
+            authorName = ANONYMOUS_AUTHOR;
+        }
+
+        return authorName;
+    }
+
     @Override
     public void generateCsvReport() {
         /*
          * Read individual reports for aggregation
          */
-        individualReports = readZeroCodeReportsByPath(TARGET_REPORT_DIR);
+        treeReports = readZeroCodeReportsByPath(TARGET_REPORT_DIR);
 
         /*
          * Generate: CSV report
          */
-        zeroCodeCsvReportRows = buildCsvRows();
-        generateCsvReport(zeroCodeCsvReportRows);
+        zeroCodeCsvFlattenedRows = buildCsvRows();
+        generateCsvReport(zeroCodeCsvFlattenedRows);
     }
 
     @Override
     public void generateHighChartReport() {
+        LOGGER.info("####spikeChartReportEnabled: " + spikeChartReportEnabled);
+
         /*
-         * Generate: Chart using HighChart
+         * Generate: Spike Chart using HighChart
          */
-        HighChartColumnHtml highChartColumnHtml = convertCsvRowsToHighChartData(zeroCodeCsvReportRows);
-        generateHighChartReport(highChartColumnHtml);
+        if(spikeChartReportEnabled){
+            HighChartColumnHtml highChartColumnHtml = convertCsvRowsToHighChartData(zeroCodeCsvFlattenedRows);
+            generateHighChartReport(highChartColumnHtml);
+        }
     }
 
+
     private HighChartColumnHtml convertCsvRowsToHighChartData(List<ZeroCodeCsvReport> zeroCodeCsvReportRows) {
 
-        //TODO: read from the property file. Inject as fields.
         HighChartColumnHtmlBuilder highChartColumnHtmlBuilder = HighChartColumnHtmlBuilder.newInstance()
                 .chartSeriesName("Test Results")
                 .chartTitleTop("Request Vs Response Delay Chart")
@@ -80,6 +192,7 @@ public class ZeroCodeReportGeneratorImpl implements ZeroCodeReportGenerator {
                 dataArrayBuilder.kv(ZeroCodeChartKeyValueBuilder.newInstance()
                         .key(thisRow.getScenarioName() + "->" + thisRow.getStepName())
                         .value(thisRow.getResponseDelayMilliSec())
+                        .result(thisRow.getResult())
                         .build())
         );
 
@@ -93,7 +206,9 @@ public class ZeroCodeReportGeneratorImpl implements ZeroCodeReportGenerator {
 
         HighChartColumnHtmlWriter highChartColumnHtmlWriter = new HighChartColumnHtmlWriter();
 
-        highChartColumnHtmlWriter.generateHighChart(highChartColumnHtml);
+        spikeChartFileName = createTimeStampedFileName();
+
+        highChartColumnHtmlWriter.generateHighChart(highChartColumnHtml, spikeChartFileName);
     }
 
     public void generateCsvReport(List<ZeroCodeCsvReport> zeroCodeCsvReportRows) {
@@ -139,7 +254,7 @@ public class ZeroCodeReportGeneratorImpl implements ZeroCodeReportGenerator {
          */
         ZeroCodeCsvReportBuilder csvFileBuilder = ZeroCodeCsvReportBuilder.newInstance();
 
-        individualReports.forEach(thisReport ->
+        treeReports.forEach(thisReport ->
                 thisReport.getResults().forEach(thisResult -> {
 
                     csvFileBuilder.scenarioLoop(thisResult.getLoop());
@@ -227,4 +342,14 @@ public class ZeroCodeReportGeneratorImpl implements ZeroCodeReportGenerator {
 
     }
 
+    private static Date utilDateOf(LocalDateTime localDateTime) {
+        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    private String createTimeStampedFileName() {
+        return HIGH_CHART_HTML_FILE_NAME +
+                LocalDateTime.now().toString().replace(":", "-") +
+                ".html";
+    }
+
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java b/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java
index ee6335f4..7a3699b2 100644
--- a/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java
+++ b/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeMultiStepsScenarioRunnerImpl.java
@@ -27,6 +27,7 @@ import java.util.function.BiConsumer;
 
 import static org.apache.commons.lang.StringUtils.isEmpty;
 import static org.jsmart.zerocode.core.domain.builders.ZeroCodeExecResultBuilder.newInstance;
+import static org.jsmart.zerocode.core.engine.mocker.RestEndPointMocker.wireMockServer;
 import static org.jsmart.zerocode.core.utils.SmartUtils.prettyPrintJson;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -290,6 +291,12 @@ public class ZeroCodeMultiStepsScenarioRunnerImpl implements ZeroCodeMultiStepsS
 
         } //<-- Scenario Loop
 
+        /*
+         * Completed executing all steps?
+         * Then stop the wiremock server, so that its ready for next scenario.
+         */
+        stopWireMockServer();
+
         reportBuilder.printToFile(scenario.getScenarioName() + ".json");
 
         /*
@@ -346,4 +353,12 @@ public class ZeroCodeMultiStepsScenarioRunnerImpl implements ZeroCodeMultiStepsS
         
         return serviceType;
     }
+
+    private void stopWireMockServer() {
+        if (null != wireMockServer) {
+            wireMockServer.stop();
+            wireMockServer = null;
+            LOGGER.info("Scenario: All mockings done via WireMock server. Dependant end points executed. Stopped WireMock.");
+        }
+    }
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java b/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java
index 94419a59..20486223 100644
--- a/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java
+++ b/src/main/java/org/jsmart/zerocode/core/runner/ZeroCodeUnitRunner.java
@@ -3,17 +3,6 @@ package org.jsmart.zerocode.core.runner;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.util.Modules;
-
-import org.jsmart.zerocode.core.domain.EnvProperty;
-import org.jsmart.zerocode.core.engine.listener.ZeroCodeTestReportListener;
-import org.junit.runner.Description;
-import org.junit.runner.notification.Failure;
-import org.junit.runner.notification.RunNotifier;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.jsmart.zerocode.core.di.ApplicationMainModule;
 import org.jsmart.zerocode.core.di.RuntimeHttpClientModule;
 import org.jsmart.zerocode.core.domain.HostProperties;
@@ -21,20 +10,25 @@ import org.jsmart.zerocode.core.domain.JsonTestCase;
 import org.jsmart.zerocode.core.domain.ScenarioSpec;
 import org.jsmart.zerocode.core.domain.TargetEnv;
 import org.jsmart.zerocode.core.domain.UseHttpClient;
+import org.jsmart.zerocode.core.engine.listener.ZeroCodeTestReportListener;
 import org.jsmart.zerocode.core.httpclient.BasicHttpClient;
 import org.jsmart.zerocode.core.httpclient.RestEasyDefaultHttpClient;
 import org.jsmart.zerocode.core.report.ZeroCodeReportGenerator;
 import org.jsmart.zerocode.core.utils.SmartUtils;
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Collectors;
 
 import static org.jsmart.zerocode.core.utils.RunnerUtils.getEnvSpecificConfigFile;
-import static org.jsmart.zerocode.core.utils.SmartUtils.getAllTokens;
-import static org.jsmart.zerocode.core.utils.SmartUtils.getEnvPropertyValue;
 
 public class ZeroCodeUnitRunner extends BlockJUnit4ClassRunner {
     private static final Logger LOGGER = LoggerFactory.getLogger(ZeroCodeUnitRunner.class);
@@ -174,7 +168,7 @@ public class ZeroCodeUnitRunner extends BlockJUnit4ClassRunner {
     }
 
     public Injector getMainModuleInjector() {
-        // TODO: Synchronise this with an object lock e.g. synchronized (IptSmartRunner.class) {}
+        // TODO: Synchronise this with an object lock e.g. synchronized (ZeroCodeUnitRunner.class) {}
         final TargetEnv envAnnotation = testClass.getAnnotation(TargetEnv.class);
         String serverEnv = envAnnotation != null ? envAnnotation.value() : "config_hosts.properties";
 
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index ee183043..960b6c78 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -9,9 +9,12 @@
 	</appender>
 
 	<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
-		<encoder>
+		<!--<encoder>
 			<pattern>%d [%thread] %-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
-		</encoder>
+		</encoder>-->
+		<layout class="ch.qos.logback.classic.PatternLayout">
+			<pattern>%d %green([%thread]) %highlight(%level) %logger{50} - %blue(%msg%n)</pattern>
+		</layout>
 	</appender>
 
 	<root level="INFO">
diff --git a/src/main/resources/reports/01_high_chart_column.vm b/src/main/resources/reports/01_high_chart_column.vm
index a90a1352..01f32a89 100644
--- a/src/main/resources/reports/01_high_chart_column.vm
+++ b/src/main/resources/reports/01_high_chart_column.vm
@@ -16,12 +16,26 @@
     <script type="text/javascript">
         $(function () {
 
+## OLD - Only 1 color-
 ##            var processed_test_result = [
 ##                ['Test Case -> Step1',1],
 ##                ['Test Case -> Step2',10],
 ##                ['Test Case -> Step3', 5]
 ##            ];
 
+## NEW - Red color for failed tests-
+## See example: https://www.highcharts.com/docs/chart-concepts/series
+## and test in JSFiddle: http://jsfiddle.net/awsLL5sx/1/
+##
+##           var processed_test_result = [{
+##               name: 'Will Mock an End Point and Test the end point using Zerocode->setup_mocks',
+##               y: 429.0
+##           }, {
+##               name: 'Will Mock an End Point and Test the end point using Zerocode->actual_test_verify_get_customer',
+##               color: '#FF0000',
+##               y: 92.0
+##           }];
+
             var processed_test_result = $highChartColumnHtml.testResult;
             $(function () {
 


Test patch:
diff --git a/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java b/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java
index c2460c59..22fd2530 100644
--- a/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java
+++ b/src/main/java/org/jsmart/zerocode/core/engine/listener/ZeroCodeTestReportListener.java
@@ -40,15 +40,24 @@ public class ZeroCodeTestReportListener extends RunListener {
          */
         LOGGER.info("### ZeroCode: all testRunFinished. Generating test reports and charts");
 
-        generateCharts();
+        generateChartsAndReports();
 
     }
 
-    private void generateCharts() {
+    private void generateChartsAndReports() {
 
         reportGenerator.generateCsvReport();
 
-        reportGenerator.generateHighChartReport();
+        /**
+         * Not compatible with open source license i.e. why not activated But if it has to be used inside intranet,
+         * then a single Developer's license should do. But visit www.highcharts.com for details.
 
+         * https://shop.highsoft.com/faq
+         * If I am using the Software on a commercial company´s intranet, does it require a license?
+           Yes. The Developer License allows you to install and use the software on a commercial company's intranet.
+         */
+        //reportGenerator.generateHighChartReport();
+
+        reportGenerator.generateExtentReport();
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImpl.java b/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImpl.java
index 0d73f309..be90c405 100644
--- a/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImpl.java
+++ b/src/main/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImpl.java
@@ -14,6 +14,7 @@ import org.jsmart.zerocode.core.engine.assertion.FieldHasExactValueAsserter;
 import org.jsmart.zerocode.core.engine.assertion.FieldHasGreaterThanValueAsserter;
 import org.jsmart.zerocode.core.engine.assertion.FieldHasInEqualNumberValueAsserter;
 import org.jsmart.zerocode.core.engine.assertion.FieldHasLesserThanValueAsserter;
+import org.jsmart.zerocode.core.engine.assertion.FieldHasSubStringIgnoreCaseValueAsserter;
 import org.jsmart.zerocode.core.engine.assertion.FieldHasSubStringValueAsserter;
 import org.jsmart.zerocode.core.engine.assertion.FieldIsNotNullAsserter;
 import org.jsmart.zerocode.core.engine.assertion.FieldIsNullAsserter;
@@ -33,10 +34,12 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
+import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import static java.lang.String.format;
+import static java.util.UUID.randomUUID;
 import static org.apache.commons.lang.StringEscapeUtils.escapeJava;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -54,6 +57,7 @@ public class ZeroCodeJsonTestProcesorImpl implements ZeroCodeJsonTestProcesor {
     private static final String LOCALDATE_TODAY = "LOCAL.DATE.TODAY:";
     private static final String LOCALDATETIME_NOW = "LOCAL.DATETIME.NOW:";
     private static final String XML_FILE = "XML.FILE:";
+    private static final String RANDOM_UU_ID = "RANDOM.UUID";
 
     private static final List<String> availableTokens = Arrays.asList(
             PREFIX_ASU,
@@ -62,7 +66,8 @@ public class ZeroCodeJsonTestProcesorImpl implements ZeroCodeJsonTestProcesor {
             STATIC_ALPHABET,
             LOCALDATE_TODAY,
             LOCALDATETIME_NOW,
-            XML_FILE
+            XML_FILE,
+            RANDOM_UU_ID
     );
 
     /*
@@ -73,6 +78,7 @@ public class ZeroCodeJsonTestProcesorImpl implements ZeroCodeJsonTestProcesor {
     public static final String ASSERT_VALUE_EMPTY_ARRAY = "$[]";
     public static final String ASSERT_PATH_SIZE = ".SIZE";
     public static final String ASSERT_VALUE_CONTAINS_STRING = "$CONTAINS.STRING:";
+    public static final String ASSERT_VALUE_CONTAINS_STRING_IGNORE_CASE = "$CONTAINS.STRING.IGNORECASE:";
     public static final String ASSERT_VALUE_EQUAL_TO_NUMBER = "$EQ.";
     public static final String ASSERT_VALUE_NOT_EQUAL_TO_NUMBER = "$NOT.EQ.";
     public static final String ASSERT_VALUE_GREATER_THAN = "$GT.";
@@ -122,6 +128,9 @@ public class ZeroCodeJsonTestProcesorImpl implements ZeroCodeJsonTestProcesor {
                         // Used escapeJava, do not use escapeXml as it replaces
                         // with GT LT etc ie what exactly you don't want
                         parammap.put(runTimeToken, escapeJava(xmlString));
+
+                    } else if (runTimeToken.startsWith(RANDOM_UU_ID)) {
+                        parammap.put(runTimeToken, randomUUID().toString());
                     }
                 }
             });
@@ -239,6 +248,10 @@ public class ZeroCodeJsonTestProcesorImpl implements ZeroCodeJsonTestProcesor {
                     String expected = ((String) value).substring(ASSERT_VALUE_CONTAINS_STRING.length());
                     asserter = new FieldHasSubStringValueAsserter(path, expected);
                 }
+                else if (value instanceof String && ((String) value).startsWith(ASSERT_VALUE_CONTAINS_STRING_IGNORE_CASE)) {
+                    String expected = ((String) value).substring(ASSERT_VALUE_CONTAINS_STRING_IGNORE_CASE.length());
+                    asserter = new FieldHasSubStringIgnoreCaseValueAsserter(path, expected);
+                }
                 else if (value instanceof String && (value.toString()).startsWith(ASSERT_VALUE_EQUAL_TO_NUMBER)) {
                     String expected = ((String) value).substring(ASSERT_VALUE_EQUAL_TO_NUMBER.length());
                     asserter = new FieldHasEqualNumberValueAsserter(path, new BigDecimal(expected));
diff --git a/src/test/java/org/jsmart/zerocode/core/domain/MockStepTest.java b/src/test/java/org/jsmart/zerocode/core/domain/MockStepTest.java
new file mode 100644
index 00000000..525e73fc
--- /dev/null
+++ b/src/test/java/org/jsmart/zerocode/core/domain/MockStepTest.java
@@ -0,0 +1,66 @@
+package org.jsmart.zerocode.core.domain;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.jsmart.zerocode.core.di.ObjectMapperProvider;
+import org.jsmart.zerocode.core.utils.SmartUtils;
+import org.junit.Test;
+
+import java.util.HashMap;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * See also:
+ * RestEndPointMockerTest.java - for integration tests.
+ *
+ */
+public class MockStepTest {
+
+    private ObjectMapper objectMapper = new ObjectMapperProvider().get();
+
+    @Test
+    public void testSerDeSer() throws Exception {
+        final String aMock = SmartUtils.readJsonAsString("wiremock/test_mock_step.json");
+
+        final MockStep mockStep = objectMapper.readValue(aMock, MockStep.class);
+
+        assertThat(mockStep.getOperation(), is("GET"));
+        assertThat(mockStep.getResponse().get("status").asInt(), is(200));
+        assertThat(mockStep.getResponse().get("status").intValue(), is(200));
+        assertThat(mockStep.getResponse().get("status").toString(), is("200"));
+
+    }
+
+    @Test
+    public void testSerDeSer_headers() throws Exception {
+        final String aMock = SmartUtils.readJsonAsString("wiremock/test_mock_step_request_headers.json");
+
+        final MockStep mockStep = objectMapper.readValue(aMock, MockStep.class);
+
+        assertThat(mockStep.getOperation(), is("GET"));
+        assertThat(mockStep.getRequest().get("headers").toString(), is("{\"key\":\"key-001\",\"secret\":\"secret-001\"}"));
+
+        HashMap<String,Object> headersMap = (HashMap<String,Object>)objectMapper.readValue(mockStep.getRequest().get("headers").toString(), HashMap.class);
+        assertThat(headersMap.get("key"), is("key-001"));
+        assertThat(headersMap.get("secret"), is("secret-001"));
+
+    }
+
+    @Test
+    public void testSerDeSer_body() throws Exception {
+        final String aMock = SmartUtils.readJsonAsString("wiremock/test_mock_step_request_body.json");
+
+        final MockStep mockStep = objectMapper.readValue(aMock, MockStep.class);
+
+        assertThat(mockStep.getOperation(), is("GET"));
+        assertThat(mockStep.getRequest().get("body").toString(),
+                is("{\"name\":\"Emma\",\"age\":33,\"address\":{\"line1\":\"address line 1\",\"line2\":\"address line 2\"}}"));
+
+        // The followings are not needed as WireMock will accept the full JSON body as it is
+        HashMap<String,Object> bodyAsMap = (HashMap<String,Object>)objectMapper.readValue(mockStep.getRequest().get("body").toString(), HashMap.class);
+        assertThat(bodyAsMap.get("name"), is("Emma"));
+        assertThat(((HashMap)bodyAsMap.get("address")).get("line1"), is("address line 1"));
+
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/HighChartColumnHtmlWriterTest.java b/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/HighChartColumnHtmlWriterTest.java
index 3963f582..4eb75154 100644
--- a/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/HighChartColumnHtmlWriterTest.java
+++ b/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/HighChartColumnHtmlWriterTest.java
@@ -24,7 +24,7 @@ public class HighChartColumnHtmlWriterTest {
                 "chart Series Name",
                 "Chart title Top in a Box");
 
-        final String htmlOut = highChartColumnHtmlWriter.generateHighChart(highChartColumnHtml);
+        final String htmlOut = highChartColumnHtmlWriter.generateHighChart(highChartColumnHtml, "someFileName.html");
 
         /* assert resolved strings */
         assertThat(htmlOut, containsString("[['apple', 50], ['windows', 25]]"));
diff --git a/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValueTest.java b/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValueTest.java
index 296eb70a..187a4741 100644
--- a/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValueTest.java
+++ b/src/test/java/org/jsmart/zerocode/core/domain/reports/chart/ZeroCodeChartKeyValueTest.java
@@ -10,6 +10,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static javax.management.remote.JMXConnectionNotification.FAILED;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -21,24 +22,27 @@ public class ZeroCodeChartKeyValueTest {
     public void serialize() throws Exception {
 
         ZeroCodeChartKeyValue kv1 = ZeroCodeChartKeyValueBuilder.newInstance().key("Scenario 1: Step1").value(100D).build();
-        ZeroCodeChartKeyValue kv2 = ZeroCodeChartKeyValueBuilder.newInstance().key("Scenario 2: Step2").value(500D).build();
+        ZeroCodeChartKeyValue kv2 = ZeroCodeChartKeyValueBuilder.newInstance().key("Scenario 2: Step2").value(500D).result(ZeroCodeChartKeyValueArrayBuilder.TEST_FAILED).build();
 
         List<ZeroCodeChartKeyValue> kvs = Arrays.asList(kv1, kv2);
 
         final String jsonString = mapper.writeValueAsString(kvs);
 
-        assertThat(jsonString, containsString("{\"key\":\"Scenario 2: Step2\",\"value\":500.0}"));
+        assertThat(jsonString, containsString("[{\"key\":\"Scenario 1: Step1\",\"value\":100.0,\"result\":null},{\"key\":\"Scenario 2: Step2\",\"value\":500.0,\"result\":\"FAILED\"}]"));
 
     }
 
     @Test
     public void willGenertaeCommaSeparatedKV() throws Exception {
         /*
-                var myArray = [
-                ['Test Scenario1 -> Step1',1],
-                ['Test Scenario2 -> Step2',10],
-                ['Test Scenario3 -> Step3', 5]
-                ];
+           var processed_result_array = [{
+               name: 'Scenario 1 -> Step no.1',
+               y: 429.0
+           }, {
+               name: name: 'Scenario 2 -> Step no.2',
+               color: '#FF0000',
+               y: 92.0
+           }];
         */
 
         ZeroCodeChartKeyValue kv1 = ZeroCodeChartKeyValueBuilder.newInstance().key("Scenario 1: Step1").value(100D).build();
@@ -55,13 +59,13 @@ public class ZeroCodeChartKeyValueTest {
         assertThat(dataArray, is("[['Scenario 1: Step1',100.0], ['Scenario 2: Step2',500.0]]"));
 
         String kvArray = ZeroCodeChartKeyValueArrayBuilder.newInstance().kvs(kvs).build();
-        assertThat(kvArray, is("[['Scenario 1: Step1',100.0], ['Scenario 2: Step2',500.0]]"));
+        assertThat(kvArray, is("[{name: 'Scenario 1: Step1', y: 100.0}, {name: 'Scenario 2: Step2', y: 500.0}]"));
 
         String kvArrayAgain = ZeroCodeChartKeyValueArrayBuilder.newInstance()
                 .kv(kv1)
                 .kv(kv2)
                 .build();
-        assertThat(kvArrayAgain, is("[['Scenario 1: Step1',100.0], ['Scenario 2: Step2',500.0]]"));
+        assertThat(kvArrayAgain, is("[{name: 'Scenario 1: Step1', y: 100.0}, {name: 'Scenario 2: Step2', y: 500.0}]"));
 
     }
 }
\ No newline at end of file
diff --git a/src/test/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMockerTest.java b/src/test/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMockerTest.java
index 3cd841bf..acde9320 100644
--- a/src/test/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMockerTest.java
+++ b/src/test/java/org/jsmart/zerocode/core/engine/mocker/RestEndPointMockerTest.java
@@ -2,8 +2,16 @@ package org.jsmart.zerocode.core.engine.mocker;
 
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.tomakehurst.wiremock.client.MappingBuilder;
 import com.github.tomakehurst.wiremock.client.WireMock;
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
 import org.jboss.resteasy.client.ClientRequest;
 import org.jboss.resteasy.client.ClientResponse;
 import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
@@ -22,8 +30,15 @@ import org.skyscreamer.jsonassert.JSONAssert;
 
 import javax.inject.Inject;
 
+import java.util.Map;
+
 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
 import static com.github.tomakehurst.wiremock.client.WireMock.givenThat;
+import static com.github.tomakehurst.wiremock.client.WireMock.post;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static org.hamcrest.CoreMatchers.containsString;
@@ -33,6 +48,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 
 @RunWith(JukitoRunner.class)
 public class RestEndPointMockerTest {
+
     public static class JukitoModule extends TestModule {
         @Override
         protected void configureTest() {
@@ -47,7 +63,7 @@ public class RestEndPointMockerTest {
     SmartUtils smartUtils;
 
     @Inject
-    private ObjectMapper mapper;
+    private ObjectMapper objectMapper;
 
     RestEndPointMocker restEndPointMocker;
 
@@ -60,8 +76,8 @@ public class RestEndPointMockerTest {
 
     @Test
     public void willDeserializeA_VanilaFlow() throws Exception {
-        String jsonDocumentAsString = smartUtils.getJsonDocumentAsString("10_mocking_endpoints/01_wire_mock_end_point_will_respond_to_get_call.json");
-        ScenarioSpec flowDeserialized = mapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
+        String jsonDocumentAsString = smartUtils.getJsonDocumentAsString("wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json");
+        ScenarioSpec flowDeserialized = objectMapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
 
         assertThat(flowDeserialized, notNullValue());
         assertThat(flowDeserialized.getSteps().size(), is(1));
@@ -96,15 +112,15 @@ public class RestEndPointMockerTest {
         // WireMock wireMock = new WireMock(9073);
         // WireMock.configureFor(9073);
 
-        String jsonDocumentAsString = smartUtils.getJsonDocumentAsString("10_mocking_endpoints/01_wire_mock_end_point_will_respond_to_get_call.json");
-        ScenarioSpec flowDeserialized = mapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
+        String jsonDocumentAsString = smartUtils.getJsonDocumentAsString("wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json");
+        ScenarioSpec flowDeserialized = objectMapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
         MockSteps mockSteps = smartUtils.getMapper().readValue(flowDeserialized.getSteps().get(0).getRequest().toString(), MockSteps.class);
 
         final MockStep mockStep = mockSteps.getMocks().get(0);
         String jsonBodyRequest = mockStep.getResponse().get("body").toString();
 
         WireMock.configureFor(9073);
-        givenThat(WireMock.get(urlEqualTo(mockStep.getUrl()))
+        givenThat(get(urlEqualTo(mockStep.getUrl()))
                 .willReturn(aResponse()
                         .withStatus(mockStep.getResponse().get("status").asInt())
                         .withHeader("Content-Type", APPLICATION_JSON)
@@ -119,6 +135,84 @@ public class RestEndPointMockerTest {
         JSONAssert.assertEquals(jsonBodyRequest, respBodyAsString, true);
 
         System.out.println("### zerocode: \n" + respBodyAsString);
+
+    }
+
+    @Test
+    public void willMockAPostRequest() throws Exception{
+
+        WireMock.configureFor(9073);
+
+        String jsonDocumentAsString = smartUtils.getJsonDocumentAsString("wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json");
+        ScenarioSpec flowDeserialized = objectMapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
+        MockSteps mockSteps = smartUtils.getMapper().readValue(flowDeserialized.getSteps().get(0).getRequest().toString(), MockSteps.class);
+
+        final MockStep mockPost = mockSteps.getMocks().get(1);
+        String jsonBodyResponse = mockPost.getResponse().get("body").toString();
+
+        final String bodyJson = mockPost.getRequest().get("body").toString(); //"{ \"id\" : \"p002\" }";
+        stubFor(post(urlEqualTo(mockPost.getUrl()))
+                .withRequestBody(equalToJson(bodyJson))
+                .willReturn(aResponse()
+                        .withStatus(mockPost.getResponse().get("status").asInt())
+                        .withHeader("Content-Type", APPLICATION_JSON)
+                        .withBody(jsonBodyResponse)));
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpPost request = new HttpPost("http://localhost:9073" + mockPost.getUrl());
+        request.addHeader("Content-Type", "application/json");
+        StringEntity entity = new StringEntity(bodyJson);
+        request.setEntity(entity);
+        HttpResponse response = httpClient.execute(request);
+
+        final String responseBodyActual = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
+        System.out.println("### response: \n" + responseBodyActual);
+
+        assertThat(response.getStatusLine().getStatusCode(), is(201));
+        JSONAssert.assertEquals(jsonBodyResponse, responseBodyActual, true);
+
     }
 
+    @Test
+    public void willMockAGetRequestWith_headers() throws Exception{
+
+        WireMock.configureFor(9073);
+
+        String jsonDocumentAsString = smartUtils.getJsonDocumentAsString("wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json");
+        ScenarioSpec flowDeserialized = objectMapper.readValue(jsonDocumentAsString, ScenarioSpec.class);
+        MockSteps mockSteps = smartUtils.getMapper().readValue(flowDeserialized.getSteps().get(0).getRequest().toString(), MockSteps.class);
+
+        final MockStep mockGetStep = mockSteps.getMocks().get(0);
+        final Map<String, Object> headersMap = mockGetStep.getHeadersMap();
+
+        final MappingBuilder requestBuilder = get(urlEqualTo(mockGetStep.getUrl()));
+
+        // read request headers and set to request builder
+        if (headersMap.size() > 0) {
+            for (Object key : headersMap.keySet()) {
+                requestBuilder.withHeader((String) key, equalTo((String) headersMap.get(key)));
+            }
+        }
+
+        String jsonBodyResponse = mockGetStep.getResponse().get("body").toString();
+
+        stubFor(requestBuilder
+                .willReturn(aResponse()
+                        .withStatus(mockGetStep.getResponse().get("status").asInt())
+                        //.withHeader("Content-Type", APPLICATION_JSON)
+                        .withBody(jsonBodyResponse)));
+
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        HttpGet request = new HttpGet("http://localhost:9073" + mockGetStep.getUrl());
+        request.addHeader("key", "key-007");
+        request.addHeader("secret", "secret-007");
+
+        HttpResponse response = httpClient.execute(request);
+        final String responseBodyActual = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
+        System.out.println("### response: \n" + responseBodyActual);
+
+        assertThat(response.getStatusLine().getStatusCode(), is(200));
+        JSONAssert.assertEquals(jsonBodyResponse, responseBodyActual, true);
+
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImplTest.java b/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImplTest.java
index 9bf8588c..a313c74d 100644
--- a/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImplTest.java
+++ b/src/test/java/org/jsmart/zerocode/core/engine/preprocessor/ZeroCodeJsonTestProcesorImplTest.java
@@ -13,6 +13,7 @@ import org.jsmart.zerocode.core.utils.SmartUtils;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.HashMap;
 import java.util.List;
 
 import static org.hamcrest.CoreMatchers.containsString;
@@ -294,4 +295,71 @@ public class ZeroCodeJsonTestProcesorImplTest {
         final String resolvedRequestJson = jsonPreProcessor.resolveStringJson(requestJsonAsString, requestJsonAsString);
         assertThat(resolvedRequestJson.indexOf("LOCAL.DATETIME.NOW:"), is(-1));
     }
+
+    @Test
+    public void testRandom_UUID() throws Exception {
+
+        final String requestJsonAsString = "{\n" +
+                "\t\"onlineOrderId\": \"${RANDOM.UUID}\"\n" +
+                "}";
+
+        final List<String> placeHolders = jsonPreProcessor.getAllTokens(requestJsonAsString);
+        assertThat(placeHolders.size(), is(1));
+
+        final String resolvedRequestJson = jsonPreProcessor.resolveStringJson(requestJsonAsString, requestJsonAsString);
+        assertThat(resolvedRequestJson.indexOf("RANDOM.UUID"), is(-1));
+
+        final HashMap<String, String> hashMap = smartUtils.getMapper().readValue(resolvedRequestJson, HashMap.class);
+
+        assertThat(hashMap.get("onlineOrderId").length(), is(36)); //"onlineOrderId": "48c3b4ff-5078-40bb-8d62-11abcbdef5b3"
+    }
+
+    @Test
+    public void testIgnoreCaseWith_containsNoMatch() throws Exception {
+        ScenarioSpec scenarioSpec = smartUtils.jsonFileToJava("18_ignore_case/test_string_match_withIgnoring_case.json", ScenarioSpec.class);
+
+        final String assertionsSectionAsString = scenarioSpec.getSteps().get(0).getAssertions().toString();
+        String mockScenarioState = "{}";
+
+        final String resolvedAssertions = jsonPreProcessor.resolveStringJson(assertionsSectionAsString, mockScenarioState);
+        assertThat(resolvedAssertions, containsString("{\"name\":\"$CONTAINS.STRING.IGNORECASE:CReASY\"}}"));
+
+        List<JsonAsserter> asserters = jsonPreProcessor.createAssertersFrom(resolvedAssertions);
+        assertThat(asserters.size(), is(2));
+
+        String mockTestResponse = "{\n" +
+                "  \"status\": 201,\n" +
+                "  \"body\": {\n" +
+                "    \"name\": \"Hello CreXasy\"\n" +
+                "  }\n" +
+                "}";
+        List<AssertionReport> failedReports = jsonPreProcessor.assertAllAndReturnFailed(asserters, mockTestResponse);
+
+        assertThat(failedReports.size(), is(1));
+        assertThat(failedReports.toString(), containsString("did not match the expected value 'containing sub-string with ignoring case:"));
+    }
+
+    @Test
+    public void testIgnoreCaseWith_containsMatch() throws Exception {
+        ScenarioSpec scenarioSpec = smartUtils.jsonFileToJava("18_ignore_case/test_string_match_withIgnoring_case.json", ScenarioSpec.class);
+
+        final String assertionsSectionAsString = scenarioSpec.getSteps().get(0).getAssertions().toString();
+        String mockScenarioState = "{}";
+
+        final String resolvedAssertions = jsonPreProcessor.resolveStringJson(assertionsSectionAsString, mockScenarioState);
+        assertThat(resolvedAssertions, containsString("{\"name\":\"$CONTAINS.STRING.IGNORECASE:CReASY\"}}"));
+
+        List<JsonAsserter> asserters = jsonPreProcessor.createAssertersFrom(resolvedAssertions);
+        assertThat(asserters.size(), is(2));
+
+        String mockTestResponse = "{\n" +
+                "  \"status\": 201,\n" +
+                "  \"body\": {\n" +
+                "    \"name\": \"Hello Creasy\"\n" +
+                "  }\n" +
+                "}";
+        List<AssertionReport> failedReports = jsonPreProcessor.assertAllAndReturnFailed(asserters, mockTestResponse);
+
+        assertThat(failedReports.size(), is(0));
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java b/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java
index 1699708a..5d84b411 100644
--- a/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java
+++ b/src/test/java/org/jsmart/zerocode/core/report/ZeroCodeReportGeneratorImplTest.java
@@ -6,6 +6,9 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
 public class ZeroCodeReportGeneratorImplTest {
 
     @Rule
@@ -42,4 +45,32 @@ public class ZeroCodeReportGeneratorImplTest {
         zeroCodeReportGenerator.validateReportsFolderAndTheFilesExists(reportsFolder);
 
     }
+
+    @Test
+    public void testAuthorJiraStyle() throws Exception {
+        String author;
+
+        author = zeroCodeReportGenerator.optionalAuthor("PayPal One touch payment @@Peter@@");
+        assertThat(author, is("Peter"));
+
+        author = zeroCodeReportGenerator.optionalAuthor("PayPal One touch @@payment @@Peter@@");
+        assertThat(author, is("payment "));
+
+        author = zeroCodeReportGenerator.optionalAuthor("PayPal One touch payment @@Peter Gibson@@");
+        assertThat(author, is("Peter Gibson"));
+
+        author = zeroCodeReportGenerator.optionalAuthor("PayPal One touch payment @@Peter Gibson");
+        assertThat(author, is("Peter"));
+
+        author = zeroCodeReportGenerator.optionalAuthor("PayPal One touch payment @@Peter");
+        assertThat(author, is("Peter"));
+
+        author = zeroCodeReportGenerator.optionalAuthor("@@Peter, PayPal One touch payment ");
+        assertThat(author, is("Peter"));
+
+        author = zeroCodeReportGenerator.optionalAuthor("PayPal One touch payment");
+        assertThat(author, is("Anonymous"));
+
+    }
+
 }
\ No newline at end of file
diff --git a/src/test/java/org/jsmart/zerocode/wiremock/WireMockIntegrationTest.java b/src/test/java/org/jsmart/zerocode/wiremock/WireMockIntegrationTest.java
new file mode 100644
index 00000000..e11712fc
--- /dev/null
+++ b/src/test/java/org/jsmart/zerocode/wiremock/WireMockIntegrationTest.java
@@ -0,0 +1,19 @@
+package org.jsmart.zerocode.wiremock;
+
+import org.jsmart.zerocode.core.domain.JsonTestCase;
+import org.jsmart.zerocode.core.domain.TargetEnv;
+import org.jsmart.zerocode.core.runner.ZeroCodeUnitRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@TargetEnv("web_app.properties")
+@RunWith(ZeroCodeUnitRunner.class)
+public class WireMockIntegrationTest {
+
+    @Test
+    @JsonTestCase("wiremock_integration/mock_via_wiremock_then_test_the_end_point.json")
+    public void testWireMock() throws Exception {
+
+    }
+}
+
diff --git a/src/test/resources/10_mocking_endpoints/01_wire_mock_end_point_will_respond_to_get_call.json b/src/test/resources/10_mocking_endpoints/01_wire_mock_end_point_will_respond_to_get_call.json
deleted file mode 100644
index 41acd4c7..00000000
--- a/src/test/resources/10_mocking_endpoints/01_wire_mock_end_point_will_respond_to_get_call.json
+++ /dev/null
@@ -1,45 +0,0 @@
-{
-  "scenarioName": "create_mocks", //<-- comments allowed
-  "steps": [
-    {
-      "name": "GetBathRoomDetails",
-      "url": "/$MOCK",
-      "operation": "$USE.WIREMOCK",
-      "request": {
-        "mocks":[
-          {
-            "name": "Mock the Get Person",
-            "operation": "GET",
-            "url": "/google-guys/persons/p001",
-            "response": {
-              "status": 200,
-              "body": {
-                "id": "p001",
-                "source": {
-                  "code": "GOOGLE.UK"
-                }
-              }
-            }
-          },
-          {
-            "name": "Mock the POST Person",
-            "operation": "POST",
-            "url": "/google-guys/persons/p002",
-            "response": {
-              "status": 201,
-              "body": {
-                "id": "p002",
-                "source": {
-                  "code": "GOOGLE.IN"
-                }
-              }
-            }
-          }
-        ]
-      },
-      "assertions": {
-        "status": 200
-      }
-    }
-  ]
-}
diff --git a/src/test/resources/15_localdatetime/00_local_date_time_place_holders_unit_test.json b/src/test/resources/15_localdatetime/00_local_date_time_place_holders_unit_test.json
index 3cbcba83..d4d8a6ba 100644
--- a/src/test/resources/15_localdatetime/00_local_date_time_place_holders_unit_test.json
+++ b/src/test/resources/15_localdatetime/00_local_date_time_place_holders_unit_test.json
@@ -1,31 +1,30 @@
 {
-  "scenarioName": "Given_When_Then-Flow name",
-  "steps": [
-    {
-      "name": "StepNameWithoutSpaceEgCREATESD",
-      "url": "/persons",
-      "operation": "POST",
-      "request": {
-        "headers": {
-          "Content-Type": "application/json;charset=UTF-8"
-        },
-        "queryParams": {
-          "param1": "value1"
-        },
-        "body": {
-          "persons": {
-            "dateOfBirth": "${LOCAL.DATE.TODAY:yyyy-MM-dd}",
-            "birthDateOnly": "${LOCAL.DATETIME.NOW:yyyy}"
-          }
+    "scenarioName": "Given_When_Then-Flow name",
+    "steps": [
+        {
+            "name": "StepNameWithoutSpaceEgCREATESD",
+            "url": "/persons",
+            "operation": "POST",
+            "request": {
+                "headers": {
+                    "Content-Type": "application/json;charset=UTF-8"
+                },
+                "queryParams": {
+                    "param1": "value1"
+                },
+                "body": {
+                    "persons": {
+                        "dateOfBirth": "${LOCAL.DATE.TODAY:yyyy-MM-dd}",
+                        "birthDateOnly": "${LOCAL.DATETIME.NOW:yyyy}"
+                    }
+                }
+            },
+            "assertions": {
+                "status": 201,
+                "body": {
+                    "id": 1001
+                }
+            }
         }
-      },
-      "assertions": {
-        "status": 201,
-        "body": {
-          "id": 1001
-        }
-      }
-    }
-
-  ]
+    ]
 }
diff --git a/src/test/resources/18_ignore_case/test_string_match_withIgnoring_case.json b/src/test/resources/18_ignore_case/test_string_match_withIgnoring_case.json
new file mode 100644
index 00000000..27fcfcd6
--- /dev/null
+++ b/src/test/resources/18_ignore_case/test_string_match_withIgnoring_case.json
@@ -0,0 +1,23 @@
+{
+    "scenarioName": "Testing string comparable with ignoring case",
+    "steps": [
+        {
+            "name": "StepNameWithoutSpaceEgCREATESD",
+            "url": "/persons",
+            "operation": "POST",
+            "request": {
+                "body": {
+                    "persons": {
+                        "firstName": "Oliver Creasy"
+                    }
+                }
+            },
+            "assertions": {
+                "status": 201,
+                "body": {
+                    "name": "$CONTAINS.STRING.IGNORECASE:CReASY"
+                }
+            }
+        }
+    ]
+}
diff --git a/src/test/resources/soap_host.properties b/src/test/resources/soap_host.properties
index 1f96a583..f33b6de7 100755
--- a/src/test/resources/soap_host.properties
+++ b/src/test/resources/soap_host.properties
@@ -4,3 +4,6 @@ restful.application.endpoint.port=80
 
 # Google Map Service Context
 restful.application.endpoint.context=
+
+# This is false by default, it runs even if this property is not present here
+report.spike.chart.enabled=true
\ No newline at end of file
diff --git a/src/test/resources/web_app.properties b/src/test/resources/web_app.properties
new file mode 100644
index 00000000..0230e83d
--- /dev/null
+++ b/src/test/resources/web_app.properties
@@ -0,0 +1,9 @@
+# Web Server host and port
+restful.application.endpoint.host=http://localhost
+restful.application.endpoint.port=8888
+
+# Web Service context; Leave it blank in case you do not have a common context
+restful.application.endpoint.context=
+
+# WireMock will use this port for mocking dependent end points
+mock.api.port=8888
\ No newline at end of file
diff --git a/src/test/resources/wiremock/test_mock_step.json b/src/test/resources/wiremock/test_mock_step.json
new file mode 100644
index 00000000..83462fd5
--- /dev/null
+++ b/src/test/resources/wiremock/test_mock_step.json
@@ -0,0 +1,14 @@
+{
+    "name": "Mock the Get Person",
+    "operation": "GET",
+    "url": "/google-guys/persons/p001",
+    "response": {
+        "status": 200,
+        "body": {
+            "id": "p001",
+            "source": {
+                "code": "GOOGLE.UK"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/wiremock/test_mock_step_request_body.json b/src/test/resources/wiremock/test_mock_step_request_body.json
new file mode 100644
index 00000000..24ff05d6
--- /dev/null
+++ b/src/test/resources/wiremock/test_mock_step_request_body.json
@@ -0,0 +1,24 @@
+{
+    "name": "Mock the Get Person With Request Body",
+    "operation": "GET",
+    "url": "/google-guys/persons/p001",
+    "request": {
+        "body": {
+            "name": "Emma",
+            "age": 33,
+            "address": {
+                "line1": "address line 1",
+                "line2": "address line 2"
+            }
+        }
+    },
+    "response": {
+        "status": 200,
+        "body": {
+            "id": "p001",
+            "source": {
+                "code": "GOOGLE.UK"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/wiremock/test_mock_step_request_headers.json b/src/test/resources/wiremock/test_mock_step_request_headers.json
new file mode 100644
index 00000000..06f6d175
--- /dev/null
+++ b/src/test/resources/wiremock/test_mock_step_request_headers.json
@@ -0,0 +1,20 @@
+{
+    "name": "Mock the Get Person With Request Headers",
+    "operation": "GET",
+    "url": "/google-guys/persons/p001",
+    "request" : {
+        "headers":{
+            "key": "key-001",
+            "secret": "secret-001"
+        }
+    },
+    "response": {
+        "status": 200,
+        "body": {
+            "id": "p001",
+            "source": {
+                "code": "GOOGLE.UK"
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json b/src/test/resources/wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json
new file mode 100644
index 00000000..04e95b2a
--- /dev/null
+++ b/src/test/resources/wiremock_integration/01_wire_mock_end_point_will_respond_to_get_call.json
@@ -0,0 +1,56 @@
+{
+    "scenarioName": "create_mocks",
+    "steps": [
+        {
+            "name": "GetBathRoomDetails",
+            "url": "/$MOCK",
+            "operation": "$USE.WIREMOCK",
+            "request": {
+                "mocks": [
+                    {
+                        "name": "Mock the Get Person",
+                        "operation": "GET",
+                        "url": "/google-guys/persons/p001",
+                        "request": {
+                            "headers": {
+                                "key": "key-007",
+                                "secret": "secret-007"
+                            }
+                        },
+                        "response": {
+                            "status": 200,
+                            "body": {
+                                "id": "p001",
+                                "source": {
+                                    "code": "GOOGLE.UK"
+                                }
+                            }
+                        }
+                    },
+                    {
+                        "name": "Mock the POST Person",
+                        "operation": "POST",
+                        "url": "/google-guys/persons/p002",
+                        "request": {
+                            "body":{
+                                "id": "p002"
+                            }
+                        },
+                        "response": {
+                            "status": 201,
+                            "body": {
+                                "id": "p002",
+                                "source": {
+                                    "code": "GOOGLE.IN"
+                                }
+                            }
+                        }
+                    }
+                ]
+            },
+            "assertions": {
+                "status": 200
+            }
+        }
+    ]
+}
diff --git a/src/test/resources/wiremock_integration/mock_via_wiremock_then_test_the_end_point.json b/src/test/resources/wiremock_integration/mock_via_wiremock_then_test_the_end_point.json
new file mode 100644
index 00000000..4c13ca6b
--- /dev/null
+++ b/src/test/resources/wiremock_integration/mock_via_wiremock_then_test_the_end_point.json
@@ -0,0 +1,72 @@
+{
+    "scenarioName": "Will Mock some End Points via WireMock and Test the end points using Zerocode",
+    "steps": [
+        {
+            "name": "setup_mocks",
+            "url": "/$MOCK",
+            "operation": "$USE.WIREMOCK",
+            "request": {
+                "mocks": [
+                    {
+                        "name": "mocking_a_GET_endpoint_with_headers",
+                        "operation": "GET",
+                        "url": "/api/v1/amazon/customers/cust-007",
+                        "request": {
+                            "headers": {
+                                "api_key": "key-01-01",
+                                "api_secret": "secret-01-01"
+                            }
+                        },
+                        "response": {
+                            "status": 200,
+                            "body": {
+                                "id": "cust-007",
+                                "type": "Premium"
+                            }
+                        }
+                    }
+                ]
+            },
+            "assertions": {
+                "status": 200
+            }
+        },
+        //------------- Mocking done at this point via WireMock ---------------
+
+        //------------- Let's verify the end points by writing the following small tests ------------
+        {
+            "name": "verify_get_customer_with_headers",
+            "url": "/api/v1/amazon/customers/cust-007",
+            "operation": "GET",
+            "request": {
+                "headers": {
+                    "api_key": "key-01-01", //<--- Please try with a wrong key. The test should fail.
+                    "api_secret": "secret-01-01"
+                }
+            },
+            "assertions": {
+                "status": 200,
+                "body": {
+                    "id": "cust-007",
+                    "type": "Premium"
+                }
+            }
+        },
+        {
+            "name": "verify_get_customer_without_headers",
+            "url": "/api/v1/amazon/customers/cust-007",
+            "operation": "GET",
+            "request": {
+                "headers": {
+                    //"api_key": "key-01-01", //<--- Please try with a wrong key. The test should fail.
+                    "api_secret": "secret-01-01"
+                }
+            },
+            "assertions": {
+                "status" : 404,
+                "rawBody" : "$CONTAINS.STRING:HTTP ERROR 404"
+            }
+        }
+
+    ]
+}