diff --git a/.gitignore b/.gitignore
index 3f6fe75..1c3b157 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,6 @@ deploycmd.txt
# Intellij
.idea/
+
+#
+pom.nexus.xml
diff --git a/.travis.yml b/.travis.yml
index a165e66..9f1582c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,6 @@
language: java
jdk:
- openjdk8
-services:
- - mongodb
after_success:
- bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 908caa2..3a1d3c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,32 @@
+## [5.0.0](https://github.com/extent-framework/extentreports-java/compare/v4.1.6...v5.0.0)
+
+#### New Features
+- [#47](../issues/47) Anchors for each test
+- [#150](../issues/150) Unused status from charts will not be displayed
+- [#152](../issues/152) Spark: Author view
+- [#153](../issues/153) Spark: Device view
+- [#157](../issues/157) Spark: Add TestRunnerLogs view
+- [#168](../issues/168) Spark: Navigation from attributes pages (tags, exception) to Tests View
+- [#173](../issues/173) Spark: Allow configuration to select view order, Dashboard view as primary
+- [#176](../issues/176) Apply Status filters to report
+- [#177](../issues/177) Minified templates (> 10% reduction in file size)
+- [#184](../issues/184) Reporter::loadExternalConfiguration with json file
+- [#188](../issues/188) Display upto 4 `pre` blocks on a single row
+- [#191](../issues/191) Navigation from Tags/Exception pages to nested steps
+- [#213](../issues/213) Toggle theme from URI component using '#theme='
+- [#215](../issues/215) A test removed from the report will also be removed from MongoDB (Klov)
+
+#### Issues Resolved
+- [#131](../issues/131) Merging two html extent Reports works but Category view gets overwritten
+- [#161](../issues/161) Merging ExtentSpark 4.1.5 Reports causing the existing base64 image removed
+- [#169](../issues/169) Fix SparkReporter shortcuts
+- [#197](../issues/197) MarkupHelper::codeBlock has extra indentation on the first line
+- [#208](../issues/208) Show title text for screencaptures
+
+## [4.1.7](https://github.com/extent-framework/extentreports-java/compare/v4.1.6...v4.1.7)
+#### Fixes
+* [#193] Unable to format the JSON code String in report
+
## [4.1.6](https://github.com/extent-framework/extentreports-java/compare/v4.1.5...v4.1.6)
#### Fixes
* [#149] Skipped count is not displaying in authors tag and category tag
diff --git a/LICENSE b/LICENSE
index 261eeb9..d0de84b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
+ Copyright 2020 Anshoo Arora
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/Readme.md b/Readme.md
index 2a8f2fe..b14ed08 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,14 +1,27 @@
-## Extent Framework 4 - Community Edition
+## ExtentReports 5 [](https://gitter.im/anshooarora/extentreports?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](http://search.maven.org/#search|ga|1|g:"com.aventstack") [](https://travis-ci.com/extent-framework/extentreports-java) [](https://www.codefactor.io/repository/github/extent-framework/extentreports-java) [](https://codecov.io/gh/extent-framework/extentreports-java)
-[](https://gitter.im/anshooarora/extentreports?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[](http://search.maven.org/#search|ga|1|g:"com.aventstack")
-[](https://travis-ci.com/extent-framework/extentreports-java)
-[](https://www.codacy.com/app/anshooarora/extentreports?utm_source=github.com&utm_medium=referral&utm_content=extent-framework/extentreports&utm_campaign=Badge_Grade)
+### Documentation ###
-#### Documentation
+* Documentation for ExtentReports 5.x is hosted on GitHub at [ExtentReports Wiki](https://github.com/extent-framework/extentreports-java/wiki).
+* For versions 4 and below, visit [extentreports.com](http://extentreports.com/).
-View [extentreports.com](http://extentreports.com/docs/versions/4/java/) for complete documentation.
+### Contributing ###
-### License
+For more information on contributing to the ExtentReports project, please see [CONTRIBUTING.md](https://github.com/extent-framework/extentreports-java/blob/master/Contributing.md).
-Apache-2.0
+A complete list of contributors since ExtentReports migrated from [@anshooarora/extentreports-java](https://github.com/anshooarora/extentreports-java) can be [found here](https://github.com/extent-framework/extentreports-java/graphs/contributors).
+
+### Upcoming ###
+
+* See [v5.0.x milestones](https://github.com/extent-framework/extentreports-java/milestone/2)
+* See [v5.1.x milestones](https://github.com/extent-framework/extentreports-java/milestone/3)
+* Want to see a feature added? Let me know [here](https://github.com/extent-framework/extentreports-java/issues?q=is%3Aopen+is%3Aissue+milestone%3A5.0.x)
+
+### Versions ###
+
+* ExtentReports 4.x is available from the [4.x branch](https://github.com/extent-framework/extentreports-java/tree/4.1.x)
+* ExtentReports 3.x is no longer maintained, and is available from [@anshooarora/extentreports-java](https://github.com/anshooarora/extentreports-java)
+
+### License ###
+
+ExtentReports is Open Source software and ExtentReports 5.0 is released under Apache-2.0.
diff --git a/config/logger-config.xml b/config/logger-config.xml
deleted file mode 100644
index a0094a6..0000000
--- a/config/logger-config.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
- standard
-
-
-
- UTF-8
-
-
- true
-
-
-
- https
-
-
- Extent Framework
-
-
- Build 1
-
-
- MMM dd, yyyy HH:mm:ss
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/config/spark-config.json b/config/spark-config.json
new file mode 100644
index 0000000..26b0fb5
--- /dev/null
+++ b/config/spark-config.json
@@ -0,0 +1,12 @@
+{
+ "theme": "STANDARD",
+ "encoding": "utf-8",
+ "protocol": "HTTPS",
+ "timelineEnabled": false,
+ "offlineMode": true,
+ "documentTitle": "ExtentReports",
+ "reportName": "ExtentReports",
+ "timeStampFormat": "MMM dd, yyyy HH:mm:ss a",
+ "js": "",
+ "css": ""
+}
diff --git a/config/spark-config.xml b/config/spark-config.xml
index 0bb21fe..1c52508 100644
--- a/config/spark-config.xml
+++ b/config/spark-config.xml
@@ -1,52 +1,50 @@
-
-
-
-
- standard
-
-
-
- UTF-8
-
-
-
- https
-
-
- false
-
-
- Extent Framework
-
-
- Build 1
-
-
- MMM dd, yyyy HH:mm:ss
-
-
- true
- false
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ STANDARD
+
+
+
+ UTF-8
+
+
+
+
+ HTTPS
+
+
+ true
+
+
+ false
+
+
+ Extent Framework
+
+
+ Build 1
+
+
+ MMM dd, yyyy HH:mm:ss
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/v3html-config.xml b/config/v3html-config.xml
deleted file mode 100644
index 0bb21fe..0000000
--- a/config/v3html-config.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
- standard
-
-
-
- UTF-8
-
-
-
- https
-
-
- false
-
-
- Extent Framework
-
-
- Build 1
-
-
- MMM dd, yyyy HH:mm:ss
-
-
- true
- false
- true
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/lombok.config b/lombok.config
new file mode 100644
index 0000000..7a21e88
--- /dev/null
+++ b/lombok.config
@@ -0,0 +1 @@
+lombok.addLombokGeneratedAnnotation = true
diff --git a/pom.xml b/pom.xml
index a6d67d2..059f226 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,20 +1,20 @@
- 4.0.0
+ 4.0.0com.aventstackextentreports
- 4.1.6
+ 5.0.0
- extentreports
+ ExtentReportswww.extentreports.com
- Extent Framework
+ ExtentReports library
- scm:git:https://github.com/extent-framework/extentreports.git
- scm:git:https://github.com/extent-framework/extentreports.git
- https://github.com/extent-framework/extentreports
+ scm:git:https://github.com/extent-framework/extentreports-java.git
+ scm:git:https://github.com/extent-framework/extentreports-java.git
+ https://github.com/extent-framework/extentreports-java
@@ -37,46 +37,35 @@
UTF-8
- 3.12.0
- 4.5.2
- org.freemarker
- freemarker
- 2.3.29
+ io.reactivex.rxjava3
+ rxjava
+ 3.0.4
- org.apache.httpcomponents
- httpclient
- ${apache.httpcomponents.version}
+ org.freemarker
+ freemarker
+ 2.3.30
- org.apache.httpcomponents
- httpmime
- ${apache.httpcomponents.version}
+ org.projectlombok
+ lombok
+ 1.18.12com.google.code.gsongson2.8.6
-
- org.mongodb
- mongodb-driver
- ${mongodb.version}
-
-
- org.mongodb
- bson
- ${mongodb.version}
-
+
+
org.testngtestng
- 6.14.3
- test
+ 7.1.0
@@ -103,7 +92,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 2.3.2
+ 3.6.11.81.8
@@ -112,26 +101,12 @@
org.apache.maven.pluginsmaven-resources-plugin
- 2.7
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
+ 3.1.0org.jacocojacoco-maven-plugin
- 0.7.9
+ 0.8.5
@@ -152,27 +127,12 @@
- release-sign-artifacts
-
-
- performRelease
- true
-
-
-
- 41414BBD
-
- anshooarora
-
-
-
- org.apache.maven.pluginsmaven-source-plugin
- 2.3
+ 3.0.1attach-sources
@@ -185,7 +145,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 2.3
+ 3.0.1attach-javadocs
@@ -210,5 +170,4 @@
https://oss.sonatype.org/service/local/staging/deploy/maven2
-
-
\ No newline at end of file
+
diff --git a/pom-nexus.xml b/reporters/klov/pom.xml
similarity index 58%
rename from pom-nexus.xml
rename to reporters/klov/pom.xml
index a6d67d2..59e2842 100644
--- a/pom-nexus.xml
+++ b/reporters/klov/pom.xml
@@ -4,17 +4,18 @@
4.0.0com.aventstack
- extentreports
- 4.1.6
+ klovreporter
+ 5.0.0-SNAPSHOT
- extentreports
+ klov-reporterwww.extentreports.com
- Extent Framework
+ Reporter for Klov Server
+ jar
- scm:git:https://github.com/extent-framework/extentreports.git
- scm:git:https://github.com/extent-framework/extentreports.git
- https://github.com/extent-framework/extentreports
+ scm:git:https://github.com/extent-framework/extentreports-java.git
+ scm:git:https://github.com/extent-framework/extentreports-java.git
+ https://github.com/extent-framework/extentreports-java
@@ -28,7 +29,7 @@
Anshoo Arorahttps://github.com/anshooarora
- anshooarora
+ anshoo.aroraOwner
@@ -37,46 +38,33 @@
UTF-8
- 3.12.0
- 4.5.2
- org.freemarker
- freemarker
- 2.3.29
-
-
- org.apache.httpcomponents
- httpclient
- ${apache.httpcomponents.version}
-
-
- org.apache.httpcomponents
- httpmime
- ${apache.httpcomponents.version}
-
-
- com.google.code.gson
- gson
- 2.8.6
+ com.aventstack
+ extentreports
+ 5.0.0-SNAPSHOTorg.mongodbmongodb-driver
- ${mongodb.version}
+ 3.3.0org.mongodbbson
- ${mongodb.version}
+ 3.3.0
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.2
- org.testng
- testng
- 6.14.3
- test
+ org.apache.httpcomponents
+ httpmime
+ 4.5.2
@@ -103,7 +91,7 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 2.3.2
+ 3.6.11.81.8
@@ -112,67 +100,19 @@
org.apache.maven.pluginsmaven-resources-plugin
- 2.7
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- 1.6
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.7.9
-
-
-
- prepare-agent
-
-
-
- report
- test
-
- report
-
-
-
+ 3.1.0
- release-sign-artifacts
-
-
- performRelease
- true
-
-
-
- 41414BBD
-
- anshooarora
-
-
-
- org.apache.maven.pluginsmaven-source-plugin
- 2.3
+ 3.0.1attach-sources
@@ -185,7 +125,7 @@
org.apache.maven.pluginsmaven-javadoc-plugin
- 2.3
+ 3.0.1attach-javadocs
@@ -210,5 +150,4 @@
https://oss.sonatype.org/service/local/staging/deploy/maven2
-
\ No newline at end of file
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/ExtentKlovReporter.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/ExtentKlovReporter.java
new file mode 100644
index 0000000..82ef7ae
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/ExtentKlovReporter.java
@@ -0,0 +1,919 @@
+package com.aventstack.extentreports.reporter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import org.bson.Document;
+import org.bson.types.ObjectId;
+
+import com.aventstack.extentreports.Status;
+import com.aventstack.extentreports.model.Author;
+import com.aventstack.extentreports.model.Category;
+import com.aventstack.extentreports.model.Device;
+import com.aventstack.extentreports.model.ExceptionInfo;
+import com.aventstack.extentreports.model.Log;
+import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.MetaDataStorable;
+import com.aventstack.extentreports.model.NamedAttribute;
+import com.aventstack.extentreports.model.Report;
+import com.aventstack.extentreports.model.ReportStats;
+import com.aventstack.extentreports.model.ScreenCapture;
+import com.aventstack.extentreports.model.SystemEnvInfo;
+import com.aventstack.extentreports.model.Test;
+import com.aventstack.extentreports.model.context.NamedAttributeContext;
+import com.aventstack.extentreports.model.context.NamedAttributeContextManager;
+import com.aventstack.extentreports.observer.EntityObserver;
+import com.aventstack.extentreports.observer.entity.AttributeEntity;
+import com.aventstack.extentreports.observer.entity.LogEntity;
+import com.aventstack.extentreports.observer.entity.MediaEntity;
+import com.aventstack.extentreports.observer.entity.ObservedEntity;
+import com.aventstack.extentreports.observer.entity.ReportEntity;
+import com.aventstack.extentreports.observer.entity.TestEntity;
+import com.mongodb.BasicDBObject;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoClientURI;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+import com.mongodb.client.FindIterable;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+
+/**
+ * ExtentKlovReporter is a NoSQL database reporter (MongoDB), which updates
+ * information in the database which is then used by the ExtentX server to
+ * display in-depth analysis.
+ *
+ * ExtentKlovReporter is a port from ExtentReports version 4.
+ */
+public class ExtentKlovReporter extends AbstractReporter
+ implements
+ EntityObserver {
+ public static final String ID_KEY = "KLOV_ID";
+ public static final String REPORT_ID_KEY = "KLOV_REPORT_ID";
+ public static final String LOG_ID_KEY = "KLOV_LOG_ID";
+ public static final String TEST_ID_KEY = "KLOV_TEST_ID";
+
+ private static final String DEFAULT_PROJECT_NAME_PROP = "klov.project.name";
+ private static final String DEFAULT_REPORT_NAME_PROP = "klov.report.name";
+ private static final String DEFAULT_MONGODB_HOST_PROP = "mongodb.host";
+ private static final String DEFAULT_MONGODB_PORT_PROP = "mongodb.port";
+ private static final String DEFAULT_MONGODB_URI_PROP = "mongodb.uri";
+ private static final String DEFAULT_KLOV_HOST_PROP = "klov.host";
+ private static final String DEFAULT_KLOV_PORT_PROP = "klov.port";
+ private static final String DB_NAME = "klov";
+ private static final String DEFAULT_PROJECT_NAME = "Default";
+
+ private final AtomicBoolean initiated = new AtomicBoolean();
+
+ private String url;
+ private Boolean appendExisting = false;
+
+ private NamedAttributeContextManager categoryContext;
+ private NamedAttributeContextManager authorContext;
+ private NamedAttributeContextManager deviceContext;
+ private Map categoryNameObjectIdCollection = new HashMap<>();
+ private Map authorNameObjectIdCollection = new HashMap<>();
+ private Map deviceNameObjectIdCollection = new HashMap<>();
+ private Map exceptionNameObjectIdCollection = new HashMap<>();
+ private KlovMediaStorageHandler mediaStorageHandler;
+
+ private ObjectId reportId;
+ private String reportName;
+ private long reportSeq;
+ private ObjectId projectId;
+ private String projectName;
+
+ private MongoClient mongoClient;
+
+ private MongoCollection projectCollection;
+ private MongoCollection reportCollection;
+ private MongoCollection testCollection;
+ private MongoCollection logCollection;
+ private MongoCollection exceptionCollection;
+ private MongoCollection mediaCollection;
+ private MongoCollection categoryCollection;
+ private MongoCollection authorCollection;
+ private MongoCollection deviceCollection;
+ private MongoCollection environmentCollection;
+
+ static {
+ /* use mongodb reporting for only critical/severe events */
+ Logger mongoLogger = Logger.getLogger("org.mongodb.driver");
+ mongoLogger.setLevel(Level.SEVERE);
+ }
+
+ /**
+ * Initializes the KlovReporter
+ */
+ public ExtentKlovReporter() {
+ }
+
+ /**
+ * Initializes the KlovReporter with project and report names
+ *
+ * @param projectName
+ * Name of the project
+ * @param reportName
+ * Name of the report
+ */
+ public ExtentKlovReporter(String projectName, String reportName) {
+ this();
+ this.projectName = projectName;
+ this.reportName = reportName;
+ }
+
+ public ExtentKlovReporter(String projectName) {
+ this(projectName, null);
+ }
+
+ /**
+ * Sets the project name
+ *
+ * @param projectName
+ * Name of the project
+ */
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ /**
+ * Sets the report name
+ *
+ * @param reportName
+ * Name of the report
+ */
+ public void setReportName(String reportName) {
+ this.reportName = reportName;
+ }
+
+ /**
+ * Initialize Mongo DB connection with host and default port: 27017
+ *
+ * @param host
+ * host name
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(String host) {
+ mongoClient = new MongoClient(host, 27017);
+ return this;
+ }
+
+ /**
+ * Initialize Mongo DB connection with host and {@link MongoClientOptions}
+ *
+ * @param host
+ * host name
+ * @param options
+ * {@link MongoClientOptions} options
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(String host, MongoClientOptions options) {
+ mongoClient = new MongoClient(host, options);
+ return this;
+ }
+
+ /**
+ * Initialize Mongo DB connection with host and post
+ *
+ * @param host
+ * host name
+ * @param port
+ * port number
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(String host, int port) {
+ mongoClient = new MongoClient(host, port);
+ return this;
+ }
+
+ /**
+ * Initialize Mongo DB connection with a {@link MongoClientURI}
+ *
+ * @param uri
+ * {@link MongoClientURI} uri
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(MongoClientURI uri) {
+ mongoClient = new MongoClient(uri);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with {@link ServerAddress}
+ *
+ * @param addr
+ * {@link ServerAddress} server address
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(ServerAddress addr) {
+ mongoClient = new MongoClient(addr);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a list of {@link ServerAddress}
+ * addresses
+ *
+ * @param seeds
+ * A list of {@link ServerAddress} server addresses
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(List seeds) {
+ mongoClient = new MongoClient(seeds);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a list of {@link ServerAddress}
+ * and {@link MongoCredential}
+ *
+ * @param seeds
+ * A list of {@link ServerAddress} server addresses
+ * @param credentialsList
+ * A list of {@link MongoCredential} credentials
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(List seeds, List credentialsList) {
+ mongoClient = new MongoClient(seeds, credentialsList);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a list of {@link ServerAddress},
+ * {@link MongoCredential} and {@link MongoClientOptions}
+ *
+ * @param seeds
+ * A list of {@link ServerAddress} server addresses
+ * @param credentialsList
+ * A list of {@link MongoCredential} credentials
+ * @param options
+ * {@link MongoClientOptions} options
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(List seeds, List credentialsList,
+ MongoClientOptions options) {
+ mongoClient = new MongoClient(seeds, credentialsList, options);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a list of {@link ServerAddress}
+ * and {@link MongoClientOptions}
+ *
+ * @param seeds
+ * A list of {@link ServerAddress} server addresses
+ * @param options
+ * {@link MongoClientOptions} options
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(List seeds, MongoClientOptions options) {
+ mongoClient = new MongoClient(seeds, options);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with {@link ServerAddress} and a list
+ * of {@link MongoCredential} credentials
+ *
+ * @param addr
+ * {@link ServerAddress} server address
+ * @param credentialsList
+ * A list of {@link MongoCredential} credentials
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(ServerAddress addr, List credentialsList) {
+ mongoClient = new MongoClient(addr, credentialsList);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a list of {@link ServerAddress},
+ * {@link MongoCredential} and {@link MongoClientOptions}
+ *
+ * @param addr
+ * A list of {@link ServerAddress} server addresses
+ * @param credentialsList
+ * A list of {@link MongoCredential} credentials
+ * @param options
+ * {@link MongoClientOptions} options
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(ServerAddress addr, List credentialsList,
+ MongoClientOptions options) {
+ mongoClient = new MongoClient(addr, credentialsList, options);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a {@link ServerAddress} and
+ * {@link MongoClientOptions}
+ *
+ * @param addr
+ * A list of {@link ServerAddress} server addresses
+ * @param options
+ * {@link MongoClientOptions} options
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initMongoDbConnection(ServerAddress addr, MongoClientOptions options) {
+ mongoClient = new MongoClient(addr, options);
+ return this;
+ }
+
+ /**
+ * Initializes the Mongo DB connection with a connection url
+ *
+ * @param url
+ * Url string
+ * @return a {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initKlovServerConnection(String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Initializes KlovReporter with default Klov and MongoDB settings. This
+ * default the Klov server and MongoDB to LOCALHOST and also uses default
+ * ports 80 and 27017 respectively.
+ *
+ * @return A {@link ExtentKlovReporter} object
+ */
+ public ExtentKlovReporter initWithDefaultSettings() {
+ return initMongoDbConnection("localhost", 27017)
+ .initKlovServerConnection("http://localhost");
+ }
+
+ public void loadInitializationParams(InputStream is) {
+ try {
+ Properties props = loadProperties(is);
+ loadInitializationParams(props);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Properties loadProperties(InputStream is) throws IOException {
+ Properties properties = new Properties();
+ properties.load(is);
+ return properties;
+ }
+
+ private void loadInitializationParams(Properties props) {
+ String mongoUri = props.getProperty(DEFAULT_MONGODB_URI_PROP);
+ String mongoHost = props.getProperty(DEFAULT_MONGODB_HOST_PROP);
+ String mongoPort = props.getProperty(DEFAULT_MONGODB_PORT_PROP);
+ int mongoPortInt = IntUtil.tryParseInt(mongoPort) == true ? Integer.valueOf(mongoPort) : -1;
+ if (mongoHost != null && mongoPortInt != -1) {
+ initMongoDbConnection(mongoHost, mongoPortInt);
+ } else if (mongoHost != null) {
+ initMongoDbConnection(mongoHost);
+ } else if (mongoUri != null) {
+ initMongoDbConnection(new MongoClientURI(mongoUri));
+ } else {
+ throw new IllegalStateException("Mongo connection params have not been supplied.");
+ }
+
+ String projectName = props.getProperty(DEFAULT_PROJECT_NAME_PROP);
+ this.projectName = projectName == null || projectName.isEmpty() ? this.projectName : projectName;
+
+ String reportName = props.getProperty(DEFAULT_REPORT_NAME_PROP);
+ reportName = reportName == null || reportName.isEmpty() ? this.reportName : reportName;
+ this.reportName = reportName;
+
+ String klovHost = props.getProperty(DEFAULT_KLOV_HOST_PROP);
+ String klovPort = props.getProperty(DEFAULT_KLOV_PORT_PROP);
+ if (klovHost != null && klovPort != null) {
+ String uri = klovHost + ":" + klovPort;
+ initKlovServerConnection(uri);
+ } else if (klovHost != null) {
+ initKlovServerConnection(klovHost);
+ }
+ }
+
+ private void initCollections(MongoDatabase db) {
+ projectCollection = db.getCollection("project");
+ reportCollection = db.getCollection("report");
+ testCollection = db.getCollection("test");
+ logCollection = db.getCollection("log");
+ exceptionCollection = db.getCollection("exception");
+ mediaCollection = db.getCollection("media");
+ categoryCollection = db.getCollection("category");
+ authorCollection = db.getCollection("author");
+ deviceCollection = db.getCollection("device");
+ environmentCollection = db.getCollection("environment");
+ }
+
+ private void setupProject() {
+ String projectName = this.projectName == null || this.projectName.isEmpty()
+ ? DEFAULT_PROJECT_NAME
+ : this.projectName;
+ Document doc = new Document("name", projectName);
+ Document project = projectCollection.find(doc).first();
+ if (project != null) {
+ projectId = project.getObjectId("_id");
+ } else {
+ doc.append("createdAt", Calendar.getInstance().getTime());
+ projectCollection.insertOne(doc);
+ projectId = MongoUtil.getId(doc);
+ }
+ setupReport(projectName);
+ }
+
+ private void setupReport(String projectName) {
+ String reportName = this.reportName == null || this.reportName.isEmpty()
+ ? "Build " + Calendar.getInstance().getTimeInMillis()
+ : this.reportName;
+ this.reportName = reportName;
+ Document doc = new Document("name", reportName)
+ .append("project", projectId)
+ .append("projectName", projectName);
+ if (appendExisting) {
+ FindIterable iterable = reportCollection.find(doc);
+ Document report = iterable.first();
+ if (report != null) {
+ reportId = report.getObjectId("_id");
+ return;
+ }
+ }
+ reportSeq = reportCollection.count(new Document("project", projectId)) + 1;
+ doc.append("startTime", Calendar.getInstance().getTime())
+ .append("seq", reportSeq);
+ reportCollection.insertOne(doc);
+ reportId = MongoUtil.getId(doc);
+ }
+
+ public synchronized void flush(ReportEntity entity) {
+ Report report = entity.getReport();
+ List testList = report.getTestList();
+
+ if (testList == null || testList.isEmpty())
+ return;
+
+ ReportStats stats = report.getStats();
+ this.authorContext = report.getAuthorCtx();
+ this.categoryContext = report.getCategoryCtx();
+ this.deviceContext = report.getDeviceCtx();
+ Set authorNameList = getCollectionKeys(authorNameObjectIdCollection);
+ Set categoryNameList = getCollectionKeys(categoryNameObjectIdCollection);
+ Set deviceNameList = getCollectionKeys(deviceNameObjectIdCollection);
+ Set exceptionNameList = getCollectionKeys(exceptionNameObjectIdCollection);
+
+ Document doc = new Document("endTime", report.getEndTime())
+ .append("duration", report.timeTaken())
+ .append("status", report.getStatus().toLower())
+ .append("parentLength", stats.sumStat(stats.getParent()))
+ .append("passParentLength", stats.getParent().get(Status.PASS))
+ .append("failParentLength", stats.getParent().get(Status.FAIL))
+ .append("warningParentLength", stats.getParent().get(Status.WARNING))
+ .append("skipParentLength", stats.getParent().get(Status.SKIP))
+ .append("childLength", stats.sumStat(stats.getChild()))
+ .append("passChildLength", stats.getChild().get(Status.PASS))
+ .append("failChildLength", stats.getChild().get(Status.FAIL))
+ .append("warningChildLength", stats.getChild().get(Status.WARNING))
+ .append("skipChildLength", stats.getChild().get(Status.SKIP))
+ .append("grandChildLength", stats.sumStat(stats.getGrandchild()))
+ .append("passGrandChildLength", stats.getGrandchild().get(Status.PASS))
+ .append("failGrandChildLength", stats.getGrandchild().get(Status.FAIL))
+ .append("warningGrandChildLength", stats.getGrandchild().get(Status.WARNING))
+ .append("skipGrandChildLength", stats.getGrandchild().get(Status.SKIP))
+ .append("analysisStrategy", stats.getAnalysisStrategy().toString())
+ .append("bdd", testList.get(0).isBDD());
+
+ if (authorNameList != null && !authorNameList.isEmpty())
+ doc.append("authorNameList", authorNameList);
+ if (categoryNameList != null && !categoryNameList.isEmpty())
+ doc.append("categoryNameList", categoryNameList);
+ if (deviceNameList != null && !deviceNameList.isEmpty())
+ doc.append("deviceNameList", deviceNameList);
+ if (exceptionNameList != null && !exceptionNameList.isEmpty())
+ doc.append("exceptions", exceptionNameList);
+
+ reportCollection.updateOne(new Document("_id", reportId), new Document("$set", doc));
+ insertUpdateSystemAttribute(report);
+ }
+
+ private void insertUpdateSystemAttribute(Report report) {
+ List sysEnvInfoList = report.getSystemEnvInfo();
+ Document doc;
+ for (SystemEnvInfo attr : sysEnvInfoList) {
+ doc = new Document("project", projectId).append("report", reportId).append("name", attr.getName());
+ Document envSingle = environmentCollection.find(doc).first();
+ if (envSingle == null) {
+ doc.append("value", attr.getValue());
+ environmentCollection.insertOne(doc);
+ } else {
+ ObjectId id = envSingle.getObjectId("_id");
+ doc = new Document("_id", id).append("value", attr.getValue());
+ environmentCollection.updateOne(new Document("_id", id), new Document("$set", doc));
+ }
+ }
+ }
+
+ private Set getCollectionKeys(Map collection) {
+ if (collection == null || collection.isEmpty())
+ return null;
+ return collection.entrySet().stream().map(Map.Entry::getKey).collect(Collectors.toSet());
+ }
+
+ public Set getCollectionValues(Map collection) {
+ if (collection == null || collection.isEmpty())
+ return null;
+ return collection.entrySet().stream().map(Map.Entry::getValue).collect(Collectors.toSet());
+ }
+
+ private void updateTestDesc(Test test) {
+ Document doc = new Document("description", test.getDescription());
+ testCollection.updateOne(new Document("_id", test.getInfoMap().get(ID_KEY)), new Document("$set", doc));
+ }
+
+ private void updateTestChildrenCount(Test test) {
+ Document doc = new Document("childNodesLength", test.getChildren().size());
+ testCollection.updateOne(new Document("_id", test.getInfoMap().get(ID_KEY)), new Document("$set", doc));
+ }
+
+ public Observer getAttributesObserver() {
+ return new Observer() {
+ @Override
+ public void onSubscribe(Disposable d) {
+ }
+
+ @Override
+ public void onNext(AttributeEntity value) {
+ if (value.getAuthor() != null)
+ assignAttribute(value.getTest(), value.getAuthor(), authorNameObjectIdCollection,
+ authorCollection,
+ authorContext);
+ if (value.getCategory() != null)
+ assignAttribute(value.getTest(), value.getCategory(), categoryNameObjectIdCollection,
+ categoryCollection,
+ categoryContext);
+ if (value.getDevice() != null)
+ assignAttribute(value.getTest(), value.getDevice(), deviceNameObjectIdCollection,
+ deviceCollection,
+ deviceContext);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ }
+
+ @Override
+ public void onComplete() {
+ }
+ };
+ }
+
+ public void assignAttribute(Test test, NamedAttribute attribute,
+ Map attrObjectIdCollection, MongoCollection mongoCollection,
+ NamedAttributeContextManager attributeContext) {
+ Document doc;
+ if (!attrObjectIdCollection.containsKey(attribute.getName())) {
+ doc = new Document("report", reportId).append("project", projectId).append("name", attribute.getName());
+
+ FindIterable iterable = mongoCollection.find(doc);
+ Document docAttribute = iterable.first();
+
+ if (docAttribute != null) {
+ attrObjectIdCollection.put(attribute.getName(), docAttribute.getObjectId("_id"));
+ } else {
+ doc = new Document("testIdList", Arrays.asList(test.getInfoMap().get(ID_KEY)))
+ .append("testNameList", Arrays.asList(test.getName()))
+ .append("testLength", 1)
+ .append("project", projectId).append("report", reportId)
+ .append("name", attribute.getName())
+ .append("timeTaken", Long.valueOf(0));
+ mongoCollection.insertOne(doc);
+ ObjectId categoryId = MongoUtil.getId(doc);
+ attrObjectIdCollection.put(attribute.getName(), categoryId);
+ }
+ } else {
+ ObjectId id = attrObjectIdCollection.get(attribute.getName());
+ // default length
+ int testLength = 1;
+ if (attributeContext != null) {
+ Optional> context = attributeContext.getSet()
+ .stream().filter(x -> x.getAttr().getName().equals(attribute.getName())).findFirst();
+ if (context.isPresent())
+ testLength = context.get().size() + 1;
+ }
+ mongoCollection.updateOne(new Document("_id", id), new Document("$push",
+ new Document("testIdList", test.getInfoMap().get(ID_KEY))
+ .append("testNameList", test.getName())));
+ mongoCollection.updateOne(new Document("_id", id),
+ new Document("$set", new Document("testLength", testLength)));
+ }
+ }
+
+ public Observer getLogObserver() {
+ return new Observer() {
+ @Override
+ public void onSubscribe(Disposable d) {
+ }
+
+ @Override
+ public void onNext(LogEntity value) {
+ onLogAdded(value.getTest(), value.getLog());
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ }
+
+ @Override
+ public void onComplete() {
+ }
+ };
+ }
+
+ public synchronized void onLogAdded(Test test, Log log) {
+ Document doc = new Document("test", test.getInfoMap().get(ID_KEY))
+ .append("project", projectId).append("report", reportId)
+ .append("testName", test.getName())
+ .append("sequence", log.getSeq())
+ .append("status", log.getStatus().toLower())
+ .append("timestamp", log.getTimestamp())
+ .append("mediaCount", log.hasMedia() ? 1 : 0)
+ .append("details", log.getDetails());
+
+ if (log.hasException())
+ doc.append("exception", log.getException().getName())
+ .append("stacktrace", log.getException().getStackTrace());
+
+ if (log.hasMedia() && ((ScreenCapture) log.getMedia()).getBase64() != null)
+ doc.append("details", log.getDetails() + ((ScreenCapture) log.getMedia()).getBase64());
+
+ logCollection.insertOne(doc);
+ ObjectId logId = MongoUtil.getId(doc);
+ log.getInfoMap().put(ID_KEY, logId);
+
+ // check for exceptions..
+ for (ExceptionInfo ex : test.getExceptions()) {
+ ObjectId exceptionId;
+ doc = new Document("report", reportId)
+ .append("project", projectId)
+ .append("name", ex.getName());
+ FindIterable iterable = exceptionCollection.find(doc);
+ Document docException = iterable.first();
+
+ // check if a matching exception name is available in 'Exception'
+ // collection
+ // (MongoDB)
+ // if a matching exception name is found, associate with this
+ // exception's
+ // ObjectId
+ if (!exceptionNameObjectIdCollection.containsKey(ex.getName())) {
+ if (docException != null) {
+ exceptionNameObjectIdCollection.put(ex.getName(), docException.getObjectId("_id"));
+ } else {
+ doc = new Document("project", projectId)
+ .append("report", reportId)
+ .append("name", ex.getName())
+ .append("stacktrace", ex.getStackTrace())
+ .append("testCount", 0);
+ exceptionCollection.insertOne(doc);
+ exceptionId = MongoUtil.getId(doc);
+ docException = exceptionCollection.find(new Document("_id", exceptionId)).first();
+ exceptionNameObjectIdCollection.put(ex.getName(), exceptionId);
+ }
+ }
+ Integer testCount = ((Integer) (docException.get("testCount"))) + 1;
+ doc = new Document("testCount", testCount);
+ exceptionCollection.updateOne(new Document("_id", docException.getObjectId("_id")),
+ new Document("$set", doc));
+ doc = new Document("exception", exceptionNameObjectIdCollection.get(ex.getName()));
+ testCollection.updateOne(new Document("_id", test.getInfoMap().get(ID_KEY)), new Document("$set", doc));
+ updateTestDesc(test);
+ }
+ endTestRecursive(test);
+ }
+
+ private void endTestRecursive(Test test) {
+ Document doc = new Document("status", test.getStatus().toLower())
+ .append("endTime", test.getEndTime())
+ .append("duration", test.timeTaken())
+ .append("leaf", test.isLeaf())
+ .append("mediaCount", !test.getMedia().isEmpty() ? test.getMedia().size() : 0)
+ .append("childNodesLength", test.hasChildren() ? test.getChildren().size() : 0)
+ .append("logCount", test.hasAnyLog() ? test.getLogs().size() + test.getGeneratedLog().size() : 0)
+ .append("categorized", test.hasAttributes())
+ .append("description", test.getDescription());
+
+ if (test.hasCategory()) {
+ List categoryNameList = test.getCategorySet().stream().map(NamedAttribute::getName)
+ .collect(Collectors.toList());
+ doc.append("categoryNameList", categoryNameList);
+ }
+ if (test.hasDevice()) {
+ List deviceNameList = test.getDeviceSet().stream().map(NamedAttribute::getName)
+ .collect(Collectors.toList());
+ doc.append("deviceNameList", deviceNameList);
+ }
+ if (test.hasAuthor()) {
+ List authorNameList = test.getAuthorSet().stream().map(NamedAttribute::getName)
+ .collect(Collectors.toList());
+ doc.append("authorNameList", authorNameList);
+ }
+
+ testCollection.updateOne(new Document("_id", test.getInfoMap().get(ID_KEY)), new Document("$set", doc));
+ if (test.getLevel() > 0)
+ endTestRecursive(test.getParent());
+ }
+
+ public Observer getMediaObserver() {
+ return new Observer() {
+ @Override
+ public void onSubscribe(Disposable d) {
+ }
+
+ @Override
+ public void onNext(MediaEntity value) {
+ try {
+ if (value.getTest() != null)
+ onScreenCaptureAdded(value.getTest(), (ScreenCapture) value.getMedia());
+ if (value.getLog() != null)
+ onScreenCaptureAdded(value.getLog(), (ScreenCapture) value.getMedia());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ }
+
+ @Override
+ public void onComplete() {
+ }
+ };
+ }
+
+ public void onScreenCaptureAdded(Test test, ScreenCapture screenCapture) throws IOException {
+ screenCapture.getInfoMap().put(TEST_ID_KEY, test.getInfoMap().get(ID_KEY));
+ saveScreenCapture(test, screenCapture);
+ }
+
+ public void onScreenCaptureAdded(Log log, ScreenCapture screenCapture) throws IOException {
+ screenCapture.getInfoMap().put(LOG_ID_KEY, log.getInfoMap().get(ID_KEY));
+ saveScreenCapture(log, screenCapture);
+ }
+
+ private void saveScreenCapture(MetaDataStorable store, ScreenCapture screenCapture) throws IOException {
+ if (mediaStorageHandler == null) {
+ KlovMedia klovMedia = new KlovMedia(projectId, reportId, mediaCollection);
+ mediaStorageHandler = new KlovMediaStorageHandler(url, klovMedia);
+ }
+ mediaStorageHandler.saveScreenCapture(store, screenCapture);
+ }
+
+ public Observer getReportObserver() {
+ return new Observer() {
+ @Override
+ public void onSubscribe(Disposable d) {
+ }
+
+ @Override
+ public void onNext(ReportEntity value) {
+ flush(value);
+ }
+
+ // TODO: replace with DoFinally
+ @Override
+ public void onError(Throwable e) {
+ mongoClient.close();
+ }
+
+ @Override
+ public void onComplete() {
+ mongoClient.close();
+ }
+ };
+ }
+
+ public Observer getTestObserver() {
+ return new Observer() {
+ @Override
+ public void onSubscribe(Disposable d) {
+ }
+
+ @Override
+ public void onNext(TestEntity value) {
+ if (!initiated.get()) {
+ start();
+ initiated.compareAndSet(false, true);
+ }
+ if (!value.getRemoved())
+ onTestStarted(value.getTest());
+ else
+ onTestRemoved(value.getTest());
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ }
+
+ @Override
+ public void onComplete() {
+ }
+ };
+ }
+
+ private final void start() {
+ MongoDatabase db = mongoClient.getDatabase(DB_NAME);
+ initCollections(db);
+ setupProject();
+ }
+
+ private void onTestStarted(Test test) {
+ Document doc = new Document("project", projectId)
+ .append("report", reportId)
+ .append("reportName", reportName)
+ .append("reportSeq", reportSeq)
+ .append("level", test.getLevel())
+ .append("name", test.getName())
+ .append("status", test.getStatus().toLower())
+ .append("description", test.getDescription())
+ .append("startTime", test.getStartTime())
+ .append("endTime", test.getEndTime())
+ .append("bdd", test.isBDD())
+ .append("leaf", test.isLeaf())
+ .append("childNodesLength", test.getChildren().size())
+ .append("mediaCount", 0)
+ .append("childNodesLength", 0)
+ .append("logCount", 0);
+
+ if (test.isBDD())
+ doc.append("bddType", test.getBddType().getSimpleName());
+
+ if (test.getParent() != null) {
+ doc.append("parent", test.getParent().getInfoMap().get(ID_KEY))
+ .append("parentName", test.getParent().getName());
+ updateTestChildrenCount(test.getParent());
+ updateTestDesc(test.getParent());
+ }
+
+ testCollection.insertOne(doc);
+ ObjectId testId = MongoUtil.getId(doc);
+ test.getInfoMap().put(ID_KEY, testId);
+ }
+
+ private void onTestRemoved(Test test) {
+ Document doc = new Document("_id", test.getInfoMap().get(ID_KEY));
+ testCollection.deleteOne(doc);
+ test.getLogs().forEach(this::removeLog);
+ if (test.hasAnyLog()) {
+ doc = new Document("test", test.getInfoMap().get(ID_KEY));
+ logCollection.deleteMany(doc);
+ }
+ if (test.hasAttributes())
+ removeFromAttributes(test);
+ if (test.hasChildren())
+ test.getChildren().forEach(this::onTestRemoved);
+ }
+
+ private void removeLog(Log log) {
+ Document doc = new Document("_id", log.getInfoMap().get(ID_KEY));
+ logCollection.deleteOne(doc);
+ if (log.hasMedia())
+ removeMedia(log.getMedia());
+ }
+
+ private void removeMedia(Media m) {
+ Document doc = new Document("_id", m.getInfoMap().get(ID_KEY));
+ mediaCollection.deleteOne(doc);
+ }
+
+ private void removeFromAttributes(Test t) {
+ Document match, update;
+ for (Author x : t.getAuthorSet()) {
+ match = new Document("_id", authorNameObjectIdCollection.get(x.getName()));
+ update = new Document("testIdList", t.getInfoMap().get(ID_KEY));
+ authorCollection.updateOne(match, new BasicDBObject("$pull", update));
+ }
+ for (Category x : t.getCategorySet()) {
+ match = new Document("_id", categoryNameObjectIdCollection.get(x.getName()));
+ update = new Document("testIdList", t.getInfoMap().get(ID_KEY));
+ categoryCollection.updateOne(match, new BasicDBObject("$pull", update));
+ }
+ for (Device x : t.getDeviceSet()) {
+ match = new Document("_id", deviceNameObjectIdCollection.get(x.getName()));
+ update = new Document("testIdList", t.getInfoMap().get(ID_KEY));
+ deviceCollection.updateOne(match, new BasicDBObject("$pull", update));
+ }
+ }
+}
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/ExtentKlovReporterConfig.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/ExtentKlovReporterConfig.java
new file mode 100644
index 0000000..59157dc
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/ExtentKlovReporterConfig.java
@@ -0,0 +1,17 @@
+package com.aventstack.extentreports.reporter;
+
+import com.aventstack.extentreports.reporter.configuration.AbstractConfiguration;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * Defines configuration settings for the Klov reporter
+ */
+@Getter
+@Setter
+@SuperBuilder
+public class ExtentKlovReporterConfig extends AbstractConfiguration {
+
+}
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/FileUtil.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/FileUtil.java
new file mode 100644
index 0000000..3955831
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/FileUtil.java
@@ -0,0 +1,20 @@
+package com.aventstack.extentreports.reporter;
+
+import java.io.File;
+
+public class FileUtil {
+ private FileUtil() {
+ }
+
+ public static String getExtension(File f) {
+ String name = f.getName();
+ int i = name.lastIndexOf('.');
+ if (i > 0)
+ return name.substring(i + 1);
+ return "";
+ }
+
+ public static String getExtension(String filePath) {
+ return getExtension(new File(filePath));
+ }
+}
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/HttpMediaManagerImplKlov.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/HttpMediaManagerImplKlov.java
new file mode 100644
index 0000000..1425ff8
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/HttpMediaManagerImplKlov.java
@@ -0,0 +1,109 @@
+package com.aventstack.extentreports.reporter;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.logging.Logger;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.mime.HttpMultipartMode;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.entity.mime.content.StringBody;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+
+import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.ScreenCapture;
+
+public class HttpMediaManagerImplKlov
+ implements
+ MediaStorage {
+
+ private static final Logger logger = Logger.getLogger(HttpMediaManagerImplKlov.class.getName());
+ private static final String ROUTE = "api/files";
+
+ private String host;
+
+ @Override
+ public void init(String host) throws IOException {
+ this.host = host;
+ if (host.lastIndexOf('/') != host.length() - 1) {
+ this.host = host + "/";
+ }
+ }
+
+ @Override
+ public void storeMedia(Media m) throws IOException {
+ if (m.getPath() == null)
+ return;
+ if (m instanceof ScreenCapture && ((ScreenCapture) m).getBase64() != null) {
+ return;
+ }
+ File f = new File(m.getResolvedPath());
+ if (!f.exists()) {
+ throw new IOException("The system cannot find the file specified " + m.getPath());
+ }
+
+ HttpPost post = new HttpPost(host + ROUTE);
+ post.addHeader("Connection", "keep-alive");
+ post.addHeader("User-Agent", "Mozilla/5.0");
+ post.addHeader("Accept", "application/json");
+
+ String ext = FileUtil.getExtension(m.getPath());
+
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
+ builder.addPart("name",
+ new StringBody(Calendar.getInstance().getTime().getTime() + "." + ext, ContentType.TEXT_PLAIN));
+ builder.addPart("id",
+ new StringBody(m.getInfoMap().get(ExtentKlovReporter.ID_KEY).toString(), ContentType.TEXT_PLAIN));
+ builder.addPart("reportId",
+ new StringBody(m.getInfoMap().get(ExtentKlovReporter.REPORT_ID_KEY).toString(),
+ ContentType.TEXT_PLAIN));
+ builder.addPart("testId",
+ new StringBody(m.getInfoMap().get(ExtentKlovReporter.TEST_ID_KEY).toString(), ContentType.TEXT_PLAIN));
+ builder.addPart("f", new FileBody(new File(m.getResolvedPath())));
+ post.setEntity(builder.build());
+
+ String logId = m.getInfoMap().get(ExtentKlovReporter.LOG_ID_KEY) == null
+ ? null
+ : m.getInfoMap().get(ExtentKlovReporter.LOG_ID_KEY).toString();
+ builder.addPart("logId", new StringBody(logId, ContentType.TEXT_PLAIN));
+
+ HttpClient client = null;
+ if (host.toLowerCase().startsWith("https")) {
+ SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
+ try {
+ sslContextBuilder.loadTrustMaterial(new TrustSelfSignedStrategy());
+ @SuppressWarnings("deprecation")
+ SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+ sslContextBuilder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
+ client = HttpClients.custom().setSSLSocketFactory(
+ sslsf).build();
+ } catch (Exception e) {
+
+ }
+ } else {
+ client = HttpClientBuilder.create().build();
+ }
+
+ HttpResponse response = client.execute(post);
+ int responseCode = response.getStatusLine().getStatusCode();
+ boolean isValid = isResponseValid(responseCode);
+
+ if (!isValid)
+ logger.warning("Unable to upload file to server " + m.getPath());
+ }
+
+ private boolean isResponseValid(int responseCode) {
+ return 200 <= responseCode && responseCode <= 399;
+ }
+
+}
diff --git a/src/main/java/com/aventstack/extentreports/utils/IntUtil.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/IntUtil.java
similarity index 85%
rename from src/main/java/com/aventstack/extentreports/utils/IntUtil.java
rename to reporters/klov/src/main/java/com/aventstack/extentreports/reporter/IntUtil.java
index 4ec796b..ed32eca 100644
--- a/src/main/java/com/aventstack/extentreports/utils/IntUtil.java
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/IntUtil.java
@@ -1,7 +1,6 @@
-package com.aventstack.extentreports.utils;
+package com.aventstack.extentreports.reporter;
public class IntUtil {
-
private IntUtil() {
}
@@ -13,5 +12,4 @@ public static boolean tryParseInt(String value) {
return false;
}
}
-
}
\ No newline at end of file
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/KlovMedia.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/KlovMedia.java
new file mode 100644
index 0000000..5421417
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/KlovMedia.java
@@ -0,0 +1,21 @@
+package com.aventstack.extentreports.reporter;
+
+import org.bson.Document;
+import org.bson.types.ObjectId;
+
+import com.mongodb.client.MongoCollection;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class KlovMedia {
+ private ObjectId reportId;
+ private ObjectId projectId;
+ private MongoCollection mediaCollection;
+}
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/KlovMediaStorageHandler.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/KlovMediaStorageHandler.java
new file mode 100644
index 0000000..66c07c1
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/KlovMediaStorageHandler.java
@@ -0,0 +1,49 @@
+package com.aventstack.extentreports.reporter;
+
+import java.io.IOException;
+
+import org.bson.Document;
+import org.bson.types.ObjectId;
+
+import com.aventstack.extentreports.model.MetaDataStorable;
+import com.aventstack.extentreports.model.ScreenCapture;
+import com.aventstack.extentreports.model.Test;
+
+public class KlovMediaStorageHandler {
+ private MediaStorage mediaStorage;
+ private KlovMedia klovMedia;
+
+ public KlovMediaStorageHandler(String url, KlovMedia klovMedia) throws IOException {
+ if (url == null || url.isEmpty())
+ throw new IllegalArgumentException("Invalid URL or resource not found");
+ this.klovMedia = klovMedia;
+ this.mediaStorage = new HttpMediaManagerImplKlov();
+ mediaStorage.init(url);
+ }
+
+ public void saveScreenCapture(MetaDataStorable el, ScreenCapture media) throws IOException {
+ Document doc = new Document("project", klovMedia.getProjectId())
+ .append("report", klovMedia.getReportId())
+ .append("test", media.getInfoMap().get(ExtentKlovReporter.TEST_ID_KEY));
+
+ if (el.getClass() != Test.class) {
+ doc.append("log", media.getInfoMap().get(ExtentKlovReporter.LOG_ID_KEY));
+ } else {
+ doc.append("testName", ((Test) el).getName());
+ }
+
+ if (media.getBase64() != null)
+ doc.append("base64String", media.getBase64());
+
+ klovMedia.getMediaCollection().insertOne(doc);
+ ObjectId mediaId = MongoUtil.getId(doc);
+ media.getInfoMap().put(ExtentKlovReporter.ID_KEY, mediaId);
+ media.getInfoMap().put(ExtentKlovReporter.REPORT_ID_KEY, klovMedia.getReportId());
+
+ if (media.getBase64() != null)
+ return;
+
+ mediaStorage.storeMedia((ScreenCapture) media);
+ }
+
+}
diff --git a/src/main/java/com/aventstack/extentreports/mediastorage/MediaStorage.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/MediaStorage.java
similarity index 61%
rename from src/main/java/com/aventstack/extentreports/mediastorage/MediaStorage.java
rename to reporters/klov/src/main/java/com/aventstack/extentreports/reporter/MediaStorage.java
index 835efe5..87c8639 100644
--- a/src/main/java/com/aventstack/extentreports/mediastorage/MediaStorage.java
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/MediaStorage.java
@@ -1,13 +1,10 @@
-package com.aventstack.extentreports.mediastorage;
+package com.aventstack.extentreports.reporter;
import java.io.IOException;
import com.aventstack.extentreports.model.Media;
public interface MediaStorage {
-
- void init(String v) throws IOException;
-
+ void init(String v) throws IOException;
void storeMedia(Media m) throws IOException;
-
}
diff --git a/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/MongoUtil.java b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/MongoUtil.java
new file mode 100644
index 0000000..f34afa1
--- /dev/null
+++ b/reporters/klov/src/main/java/com/aventstack/extentreports/reporter/MongoUtil.java
@@ -0,0 +1,13 @@
+package com.aventstack.extentreports.reporter;
+
+import org.bson.Document;
+import org.bson.types.ObjectId;
+
+public class MongoUtil {
+ private MongoUtil() {
+ }
+
+ public static ObjectId getId(Document doc) {
+ return (ObjectId) doc.get("_id");
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/AbstractProcessor.java b/src/main/java/com/aventstack/extentreports/AbstractProcessor.java
new file mode 100644
index 0000000..36a0d3d
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/AbstractProcessor.java
@@ -0,0 +1,100 @@
+package com.aventstack.extentreports;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.aventstack.extentreports.append.RawEntityConverter;
+import com.aventstack.extentreports.model.Author;
+import com.aventstack.extentreports.model.Category;
+import com.aventstack.extentreports.model.Device;
+import com.aventstack.extentreports.model.Log;
+import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.SystemEnvInfo;
+import com.aventstack.extentreports.model.Test;
+import com.aventstack.extentreports.model.service.MediaService;
+import com.aventstack.extentreports.model.service.TestService;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public abstract class AbstractProcessor extends ReactiveSubject {
+ private String[] mediaResolverPath;
+ private boolean usingNaturalConf = true;
+
+ @Override
+ protected void onTestCreated(Test test) {
+ getReport().getTestList().add(test);
+ super.onTestCreated(test);
+ }
+
+ @Override
+ protected void onTestRemoved(Test test) {
+ TestService.deleteTest(getReport().getTestList(), test);
+ super.onTestRemoved(test);
+ }
+
+ protected void onNodeCreated(Test node) {
+ super.onTestCreated(node);
+ }
+
+ @Override
+ protected void onLogCreated(Log log, Test test) {
+ super.onLogCreated(log, test);
+ if (log.hasException())
+ getReport().getExceptionInfoCtx().addContext(log.getException(), test);
+ }
+
+ @Override
+ protected void onMediaAdded(Media m, Test test) {
+ tryResolvePath(m);
+ super.onMediaAdded(m, test);
+ }
+
+ @Override
+ protected void onMediaAdded(Media m, Log log) {
+ tryResolvePath(m);
+ super.onMediaAdded(m, log);
+ }
+
+ private void tryResolvePath(Media m) {
+ MediaService.tryResolveMediaPath(m, mediaResolverPath);
+ }
+
+ protected void onAuthorAdded(Author x, Test test) {
+ getReport().getAuthorCtx().addContext(x, test);
+ super.onAuthorAssigned(x, test);
+ }
+
+ protected void onCategoryAdded(Category x, Test test) {
+ getReport().getCategoryCtx().addContext(x, test);
+ super.onCategoryAssigned(x, test);
+ }
+
+ protected void onDeviceAdded(Device x, Test test) {
+ getReport().getDeviceCtx().addContext(x, test);
+ super.onDeviceAssigned(x, test);
+ }
+
+ @Override
+ protected void onFlush() {
+ getReport().refresh();
+ if (!usingNaturalConf)
+ getReport().applyOverrideConf();
+ super.onFlush();
+ }
+
+ protected void onReportLogAdded(String log) {
+ getReport().getLogs().add(log);
+ }
+
+ protected void onSystemInfoAdded(SystemEnvInfo env) {
+ getReport().getSystemEnvInfo().add(env);
+ }
+
+ protected void convertRawEntities(ExtentReports extent, File f) throws IOException {
+ RawEntityConverter converter = new RawEntityConverter(extent);
+ converter.convertAndApply(f);
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/AnalysisStrategy.java b/src/main/java/com/aventstack/extentreports/AnalysisStrategy.java
index fdfca02..e305a8c 100644
--- a/src/main/java/com/aventstack/extentreports/AnalysisStrategy.java
+++ b/src/main/java/com/aventstack/extentreports/AnalysisStrategy.java
@@ -1,16 +1,36 @@
package com.aventstack.extentreports;
+import com.aventstack.extentreports.gherkin.model.IGherkinFormatterModel;
+import com.aventstack.extentreports.model.ReportStats;
+
/**
- * Strategy used to generate statistics for the current run
- *
- * Available strategies are:
- *
- *
BDD: Strategy for BDD-style (Gherkin) tests
- *
CLASS: Used for 2 levels: Class, Test
- *
SUITE: Used for 3 levels: Suite, Class, Test
- *
TEST: Used for 1 level only: Test
- *
+ * Enumeration for various kinds of analysis strategies used by
+ * {@link ReportStats}
*/
public enum AnalysisStrategy {
- BDD, CLASS, SUITE, TEST
-}
\ No newline at end of file
+ /**
+ * This is the efault strategy used by BDD tests. If any created test uses a
+ * {@link IGherkinFormatterModel} type, the strategy would default to BDD
+ * and {@link AnalysisStrategy} would be ignored.
+ */
+ BDD,
+
+ /**
+ * This strategy is useful when all tests in a suite are grouped together by
+ * their containing Class. This strategy would account for the number of
+ * Classes that passed or failed, and repeat the process for tests.
+ */
+ CLASS,
+
+ /**
+ * This strategy is useful if there are 3 levels in the hierarchy: Suite,
+ * Class and Test.
+ */
+ SUITE,
+
+ /**
+ * TEST is the default strategy and only tracks the leaf nodes to create
+ * {@link ReportStats}
+ */
+ TEST
+}
diff --git a/src/main/java/com/aventstack/extentreports/AnalysisTypeConfigurable.java b/src/main/java/com/aventstack/extentreports/AnalysisTypeConfigurable.java
new file mode 100644
index 0000000..58cc9c2
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/AnalysisTypeConfigurable.java
@@ -0,0 +1,6 @@
+package com.aventstack.extentreports;
+
+@FunctionalInterface
+public interface AnalysisTypeConfigurable {
+ void setAnalysisStrategy(AnalysisStrategy strategy);
+}
diff --git a/src/main/java/com/aventstack/extentreports/ExtentReports.java b/src/main/java/com/aventstack/extentreports/ExtentReports.java
index e5f7a77..1e92e89 100644
--- a/src/main/java/com/aventstack/extentreports/ExtentReports.java
+++ b/src/main/java/com/aventstack/extentreports/ExtentReports.java
@@ -6,14 +6,13 @@
import java.util.Arrays;
import java.util.List;
-import com.aventstack.extentreports.convert.TestModelReportBuilder;
-import com.aventstack.extentreports.gherkin.GherkinDialectProvider;
+import com.aventstack.extentreports.gherkin.GherkinDialectManager;
import com.aventstack.extentreports.gherkin.model.IGherkinFormatterModel;
import com.aventstack.extentreports.model.Media;
-import com.aventstack.extentreports.model.ScreenCapture;
-import com.aventstack.extentreports.model.SystemAttribute;
-import com.aventstack.extentreports.reporter.ExtentKlovReporter;
-import com.aventstack.extentreports.reporter.ExtentReporter;
+import com.aventstack.extentreports.model.ReportStats;
+import com.aventstack.extentreports.model.SystemEnvInfo;
+import com.aventstack.extentreports.model.service.TestService;
+import com.aventstack.extentreports.observer.ExtentObserver;
/**
*
@@ -56,433 +55,411 @@
* @see IGherkinFormatterModel
* @see Status
*/
-public class ExtentReports extends ReportObservable {
- private static final String[] IMAGE_PATH_RESOLVER_DIR = new String[] { "target/", "test-output/" };
-
- /**
- * Attach a {@link ExtentReporter} reporter, allowing it to access all started
- * tests, nodes and logs
- *
- *
- * Available reporter types are:
- *
- *
- *
- *
ExtentHtmlReporter provided by artifactId "extent-html-formatter"
- *
ExtentEmailReporter (pro-only) provided by artifactId
- * "extent-email-formatter"
- *
KlovReporter provided by artifactId "extent-klov-reporter"
- *
ConsoleLogger
- *
- *
- * @param reporter {@link ExtentReporter} reporter
- */
- public void attachReporter(ExtentReporter... reporter) {
- Arrays.stream(reporter).forEach(this::register);
- }
+public class ExtentReports extends AbstractProcessor implements Writable, AnalysisTypeConfigurable {
- /**
- * Returns a list of started reporters
- *
- * @return A list of {@link ExtentReporter}
- */
- public List getStartedReporters() {
- return getReporterCollection();
- }
+ /**
+ * Attach a {@link ExtentObserver} reporter, allowing it to access all
+ * started tests, nodes and logs
+ *
+ * @param observer
+ * {@link ExtentObserver} reporter
+ */
+ @SuppressWarnings("rawtypes")
+ public void attachReporter(ExtentObserver... observer) {
+ attachReporter(Arrays.asList(observer));
+ }
- /**
- * Creates a BDD-style test with description representing one of the
- * {@link IGherkinFormatterModel} classes such as:
- *
- *
- *
- * @param type A {@link IGherkinFormatterModel} type
- * @param testName Name of test
- * @param description A short description of the test
- *
- * @return {@link ExtentTest} object
- */
- public ExtentTest createTest(Class extends IGherkinFormatterModel> type, String testName,
- String description) {
- ExtentTest t = new ExtentTest(this, type, testName, description);
- applyCommonTestSettings(t);
- saveTest(t.getModel());
- return t;
- }
+ /**
+ * Creates a BDD-style test with description representing one of the
+ * {@link IGherkinFormatterModel} classes such as:
+ *
+ *
+ *
+ * @param type
+ * A {@link IGherkinFormatterModel} type
+ * @param name
+ * Name of test
+ * @param description
+ * A short description of the test
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest createTest(Class extends IGherkinFormatterModel> type, String name,
+ String description) {
+ ExtentTest t = new ExtentTest(this, type, name, description);
+ onTestCreated(t.getModel());
+ return t;
+ }
- /**
- * Creates a BDD-style test representing one of the
- * {@link IGherkinFormatterModel} classes such as:
- *
- *
+ *
+ * @param type
+ * A {@link IGherkinFormatterModel} type
+ * @param name
+ * Name of test
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest createTest(Class extends IGherkinFormatterModel> type, String name) {
+ return createTest(type, name, null);
+ }
- /**
- * Creates a BDD-style test with description using name of the Gherkin model
- * such as:
- *
- *
- *
- * @param gherkinKeyword Name of the {@link GherkinKeyword}
- * @param testName Name of test
- * @param description A short description of the test
- *
- * @return {@link ExtentTest} object
- */
- public ExtentTest createTest(GherkinKeyword gherkinKeyword, String testName, String description) {
- Class extends IGherkinFormatterModel> clazz = gherkinKeyword.getKeyword().getClass();
- return createTest(clazz, testName, description);
- }
+ /**
+ * Creates a BDD-style test with description using name of the Gherkin model
+ * such as:
+ *
+ *
+ *
+ * @param gherkinKeyword
+ * Name of the {@link GherkinKeyword}
+ * @param name
+ * Name of test
+ * @param description
+ * A short description of the test
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest createTest(GherkinKeyword gherkinKeyword, String name, String description) {
+ Class extends IGherkinFormatterModel> clazz = gherkinKeyword.getKeyword().getClass();
+ return createTest(clazz, name, description);
+ }
- /**
- * Creates a BDD-style test using name of the Gherkin model such as:
- *
- *
- *
- * @param gherkinKeyword Name of the {@link GherkinKeyword}
- * @param testName Name of test
- *
- * @return {@link ExtentTest} object
- */
- public ExtentTest createTest(GherkinKeyword gherkinKeyword, String testName) {
- return createTest(gherkinKeyword, testName, null);
- }
+ /**
+ * Creates a BDD-style test using name of the Gherkin model such as:
+ *
+ *
+ *
+ * @param k
+ * Name of system variable
+ * @param v
+ * Value of system variable
+ */
+ public void setSystemInfo(String k, String v) {
+ onSystemInfoAdded(new SystemEnvInfo(k, v));
+ }
- /**
- * Writes test information from the started reporters to their output view
- *
- *
- *
extent-html-formatter: flush output to HTML file
- *
extent-klov-reporter: updates MongoDB collections
- *
extent-email-formatter (pro-only): creates or appends to an HTML
- * file
- *
ConsoleLogger: no action taken
- *
- */
- @Override
- public void flush() {
- super.flush();
- }
+ /**
+ * Adds logs from test framework tools to the test-runner logs view (if
+ * available in the reporter)
+ *
+ *
+ *
+ * @param log
+ * Log string from the test runner frameworks such as TestNG or
+ * JUnit
+ */
+ public void addTestRunnerOutput(List log) {
+ log.forEach(this::addTestRunnerOutput);
+ }
- /**
- * Adds any applicable system information to all started reporters
- *
- *
- *
- * @param k Name of system variable
- * @param v Value of system variable
- */
- public void setSystemInfo(String k, String v) {
- SystemAttribute sa = new SystemAttribute(k, v);
- super.setSystemInfo(sa);
- }
+ /**
+ * Adds logs from test framework tools to the test-runner logs view (if
+ * available in the reporter)
+ *
+ *
+ * TestNG usage example:
+ *
+ *
+ *
+ * for (String s : Reporter.getOutput()) {
+ * extent.setTestRunnerOutput(s);
+ * }
+ *
+ *
+ * @param log
+ * Log string from the test runner frameworks such as TestNG or
+ * JUnit
+ */
+ public void addTestRunnerOutput(String log) {
+ onReportLogAdded(log);
+ }
- /**
- * Adds logs from test framework tools to the test-runner logs view (if
- * available in the reporter)
- *
- *
- *
- * @param log Log string from the test runner frameworks such as TestNG or JUnit
- */
- public void setTestRunnerOutput(List log) {
- log.forEach(this::setTestRunnerLogs);
- }
+ /**
+ * Tries to resolve a {@link Media} location if the supplied path is not
+ * found using supplied locations. This can resolve cases where the default
+ * path was supplied to be relative for a FileReporter. If the absolute path
+ * is not determined, the supplied will be used.
+ *
+ * @param path
+ * Dirs used to create absolute path of the {@link Media} object
+ *
+ * @return {@link ExtentReports}
+ */
+ public ExtentReports tryResolveMediaPath(String[] path) {
+ setMediaResolverPath(path);
+ return this;
+ }
- /**
- * Adds logs from test framework tools to the test-runner logs view (if
- * available in the reporter)
- *
- *
- * TestNG usage example:
- *
- *
- *
- * for (String s : Reporter.getOutput()) {
- * extent.setTestRunnerOutput(s);
- * }
- *
- *
- * @param log Log string from the test runner frameworks such as TestNG or JUnit
- */
- public void setTestRunnerOutput(String log) {
- setTestRunnerLogs(log);
- }
-
- /**
- * Tries to resolve a {@link ScreenCapture} location if the supplied path is not found using
- * default locations. This can resolve cases where the default path was supplied to be relative
- * for a FileReporter. If the absolute path is not determined, the supplied will be used.
- * below paths are used to locate the image:
- *
- *
- *
target/
- *
test-output//
- *
- *
- * @return {@link ExtentKlovReporter}
- */
- public ExtentReports tryResolveMediaPath() {
- setMediaPathResolveDir(IMAGE_PATH_RESOLVER_DIR);
- return this;
- }
-
- /**
- * Tries to resolve a {@link ScreenCapture} location if the supplied path is not found using
- * supplied locations. This can resolve cases where the default path was supplied to be relative
- * for a FileReporter. If the absolute path is not determined, the supplied will be used.
- *
- * @param paths Dirs used to create absolute path of the {@link Media} object
- *
- * @return {@link ExtentKlovReporter}
- */
- public ExtentReports tryResolveMediaPath(String[] paths) {
- setMediaPathResolveDir(paths);
- return this;
- }
+ /**
+ * Creates the internal models by consuming a JSON archive from a previous
+ * run session. This provides the same functionality as available in earlier
+ * versions via appendExisting, with the exception of being
+ * accessible by all reporters instead of just one.
+ *
+ * @param jsonFile
+ * The JSON archive file
+ * @throws IOException
+ * Exception thrown if the jsonFile is not found
+ */
+ public void createDomainFromJsonArchive(File jsonFile) throws IOException {
+ convertRawEntities(this, jsonFile);
+ }
- /**
- * Creates the internal models by consuming a JSON archive from a previous run
- * session. This provides the same functionality as available in earlier versions
- * via appendExisting, with the exception of being accessible by
- * all reporters instead of just one.
- *
- * @param jsonFile The JSON archive file
- * @throws IOException Exception thrown if the jsonFile is not found
- */
- public void createDomainFromJsonArchive(File jsonFile) throws IOException {
- TestModelReportBuilder modelBuilder = new TestModelReportBuilder();
- modelBuilder.createDomainFromJsonArchive(this, jsonFile);
- }
-
- /**
- * Creates the internal models by consuming a JSON archive from a previous run
- * session. This provides the same functionality as available in earlier versions
- * via appendExisting, with the exception of being accessible by
- * all reporters instead of just one.
- *
- * @param jsonFilePath The JSON archive file
- * @throws IOException Exception thrown if the jsonFilePath is not found
- */
- public void createDomainFromJsonArchive(String jsonFilePath) throws IOException {
- createDomainFromJsonArchive(new File(jsonFilePath));
- }
-
- /**
- * Use this setting when building post-execution reports, such as from TestNG
- * IReporter. This setting allows setting test with your own time-stamps. With
- * this enabled, Extent does not use time-stamps for tests at the time they were
- * created.
- *
- *
- * If this setting is enabled and time-stamps are not specified explicitly, the
- * time-stamps of test creation are used.
- *
- * @param useManualConfig Set to true if building reports at the end of
- * execution with manual configuration
- */
- public void setReportUsesManualConfiguration(boolean useManualConfig) {
- setAllowManualConfig(useManualConfig);
- }
-
- public Boolean getReportUsesManualConfiguration() {
- return getAllowManualConfig();
- }
+ /**
+ * Creates the internal models by consuming a JSON archive from a previous
+ * run session. This provides the same functionality as available in earlier
+ * versions via appendExisting, with the exception of being
+ * accessible by all reporters instead of just one.
+ *
+ * @param jsonFilePath
+ * The JSON archive file
+ * @throws IOException
+ * Exception thrown if the jsonFilePath is not found
+ */
+ public void createDomainFromJsonArchive(String jsonFilePath) throws IOException {
+ createDomainFromJsonArchive(new File(jsonFilePath));
+ }
- /**
- * Type of AnalysisStrategy for the reporter. Not all reporters support this
- * setting.
- *
- *
- * There are 2 types of strategies available:
- *
- *
- *
TEST: Shows analysis at the test and step level
- *
SUITE: Shows analysis at the suite, test and step level
- *
- *
- * @param strategy {@link AnalysisStrategy} determines the type of analysis
- * (dashboard) created for the reporter. Not all reporters will
- * support this setting.
- */
- @Override
- public void setAnalysisStrategy(AnalysisStrategy strategy) {
- super.setAnalysisStrategy(strategy);
- }
+ /**
+ * Use this setting when building post-execution reports, such as from
+ * TestNG IReporter. This setting allows setting test with your own
+ * time-stamps. With this enabled, Extent does not use time-stamps for tests
+ * at the time they were created.
+ *
+ *
+ * If this setting is enabled and time-stamps are not specified explicitly,
+ * the time-stamps of test creation are used.
+ *
+ * @param useManualConfig
+ * Set to true if building reports at the end of execution with
+ * manual configuration
+ */
+ public void setReportUsesManualConfiguration(boolean useManualConfig) {
+ setUsingNaturalConf(!useManualConfig);
+ }
- /**
- * Provides common report configurations
- *
- * @return an instance of {@link ReportConfigurator}
- */
- public ReportConfigurator config() {
- return ReportConfigurator.getInstance();
- }
+ /**
+ * Type of AnalysisStrategy for the reporter. Not all reporters support this
+ * setting.
+ *
+ *
+ * There are 2 types of strategies available:
+ *
+ *
+ *
TEST: Shows analysis at the test and step level
+ *
SUITE: Shows analysis at the suite, test and step level
+ *
+ *
+ * @param strategy
+ * {@link AnalysisStrategy} determines the type of analysis
+ * (dashboard) created for the reporter. Not all reporters will
+ * support this setting.
+ */
+ @Override
+ public void setAnalysisStrategy(AnalysisStrategy strategy) {
+ getReport().getStats().setAnalysisStrategy(strategy);
+ }
- /**
- * Allows setting the target language for Gherkin keywords.
- *
- *
- * Default setting is "en"
- *
- * @param language A valid dialect from gherkin-languages.json
- *
- * @throws UnsupportedEncodingException Thrown if the language is one of the
- * supported language from gherkin-languages.json
- */
- public void setGherkinDialect(String language) throws UnsupportedEncodingException {
- GherkinDialectProvider.setLanguage(language);
- }
-
- /**
- * Returns an instance of {@link ReportStatusStats} with counts of tests
- * executed by their status (pass, fail, skip etc)
- *
- * @return an instance of {@link ReportStatusStats}
- */
- @Override
- public ReportStatusStats getStats() {
- return super.getStats();
- }
+ /**
+ * Allows setting the target language for Gherkin keywords.
+ *
+ *
+ * Default setting is "en"
+ *
+ * @param language
+ * A valid dialect from gherkin-languages.json
+ *
+ * @throws UnsupportedEncodingException
+ * Thrown if the language is one of the supported language from
+ * gherkin-languages.json
+ */
+ public void setGherkinDialect(String language) throws UnsupportedEncodingException {
+ GherkinDialectManager.setLanguage(language);
+ }
+ /**
+ * Returns an instance of {@link ReportStats} with counts of tests executed
+ * by their status (pass, fail, skip etc)
+ *
+ * @return an instance of {@link ReportStats}
+ */
+ public ReportStats getStats() {
+ return getReport().getStats();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ExtentTest.java b/src/main/java/com/aventstack/extentreports/ExtentTest.java
index 0eff914..1d8a392 100644
--- a/src/main/java/com/aventstack/extentreports/ExtentTest.java
+++ b/src/main/java/com/aventstack/extentreports/ExtentTest.java
@@ -1,21 +1,23 @@
package com.aventstack.extentreports;
-import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
import com.aventstack.extentreports.gherkin.model.IGherkinFormatterModel;
import com.aventstack.extentreports.markuputils.Markup;
+import com.aventstack.extentreports.markuputils.MarkupHelper;
import com.aventstack.extentreports.model.Author;
import com.aventstack.extentreports.model.Category;
import com.aventstack.extentreports.model.Device;
import com.aventstack.extentreports.model.ExceptionInfo;
import com.aventstack.extentreports.model.Log;
import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.RunResult;
import com.aventstack.extentreports.model.ScreenCapture;
import com.aventstack.extentreports.model.Test;
-import com.aventstack.extentreports.utils.ExceptionUtil;
-import com.aventstack.extentreports.utils.StringUtil;
+import com.aventstack.extentreports.model.service.ExceptionInfoService;
+
+import lombok.Getter;
/**
* Defines a test. You can add logs, snapshots, assign author and categories to
@@ -42,1183 +44,1052 @@
* always level 1 and greater
*
*/
-public class ExtentTest implements IAddsMedia, RunResult, Serializable {
-
- private static final long serialVersionUID = 9199820968410788862L;
-
- /**
- * An instance of {@link ExtentReports} to which this {@link ExtentTest} belongs
- */
- private transient ExtentReports extent;
-
- /**
- * Internal model
- */
- private Test test;
-
- /**
- * Creates a BDD style parent test representing one of the
- * {@link IGherkinFormatterModel} classes. This method would ideally be used for
- * creating the parent, ie {@link Feature).
- *
- *
- *
- * @param type A {@link IGherkinFormatterModel} type
- * @param name Name of node
- *
- * @return {@link ExtentTest} object
- */
- public ExtentTest createNode(Class extends IGherkinFormatterModel> type, String name) {
- return createNode(type, name, null);
- }
-
- /**
- * Creates a BDD-style node with description using name of the Gherkin model
- * such as:
- *
- *
- *
- * @param gherkinKeyword Name of the {@link GherkinKeyword}
- * @param name Name of node
- * @param description A short description
- *
- * @return {@link ExtentTest}
- */
- public ExtentTest createNode(GherkinKeyword gherkinKeyword, String name, String description) {
- Class extends IGherkinFormatterModel> clazz = gherkinKeyword.getKeyword().getClass();
- return createNode(clazz, name, description);
- }
-
- /**
- * Creates a BDD-style node using name of the Gherkin model such as:
- *
- *
+ *
+ * @param type
+ * A {@link IGherkinFormatterModel} type
+ * @param name
+ * Name of node
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest createNode(Class extends IGherkinFormatterModel> type, String name) {
+ return createNode(type, name, null);
+ }
+
+ /**
+ * Creates a BDD-style node with description using name of the Gherkin model
+ * such as:
+ *
+ *
+ *
+ * @param gherkinKeyword
+ * Name of the {@link GherkinKeyword}
+ * @param name
+ * Name of node
+ * @param description
+ * A short description
+ *
+ * @return {@link ExtentTest}
+ */
+ public ExtentTest createNode(GherkinKeyword gherkinKeyword, String name, String description) {
+ return createNode(gherkinKeyword.getKeyword().getClass(), name, description);
+ }
+
+ /**
+ * Creates a BDD-style node using name of the Gherkin model such as:
+ *
+ *
+ *
+ * @param gherkinKeyword
+ * Name of the {@link GherkinKeyword}
+ * @param name
+ * Name of node
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest createNode(GherkinKeyword gherkinKeyword, String name) {
+ return createNode(gherkinKeyword, name, null);
+ }
+
+ /**
+ * Creates a node
+ *
+ * @param name
+ * Name of node
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest createNode(String name) {
+ return createNode(name, null);
+ }
+
+ /**
+ * Create a non-standard log with details. This is unlike the
+ * log method, which creates a fixed table layout with the
+ * following columns:
+ *
+ *
+ *
Timestamp
+ *
Status
+ *
Details
+ *
+ *
+ * @param status
+ * {@link Status}
+ * @param details
+ * Text details of the step
+ * @return an {@link ExtentTest} object
+ */
+ public ExtentTest generateLog(Status status, String details) {
+ Log log = Log.builder().status(status).details(details).build();
+ model.addGeneratedLog(log);
+ return this;
+ }
+
+ /**
+ * Create a non-standard log with details. This is unlike the
+ * log method, which creates a fixed table layout with the
+ * following columns:
+ *
+ *
+ *
Timestamp
+ *
Status
+ *
Details
+ *
+ *
+ * generateLog with {@link Markup} allows for a user-defined
+ * log with any type of markup supported by {@link MarkupHelper}.
+ *
+ * @param status
+ * {@link Status}
+ * @param markup
+ * A {@link Markup} created by {@link MarkupHelper}
+ * @return an {@link ExtentTest} object
+ */
+ public ExtentTest generateLog(Status status, Markup markup) {
+ return generateLog(status, markup.getMarkup());
+ }
+
+ /**
+ * Logs an event with {@link Status}, details and a media object:
+ * {@link ScreenCapture}
+ *
+ *
+ *
+ * @param t
+ * A {@link Throwable} exception to be logged, enabling the
+ * Exception view of certain HTML reporters
+ * @param media
+ * A {@link Media} object provided by {@link MediaEntityBuilder}
+ *
+ * @return An {@link ExtentTest} object
+ */
+ public ExtentTest info(Throwable t, Media media) {
+ log(Status.INFO, t, media);
+ return this;
+ }
+
+ /**
+ * Logs an event with Status.INFO and exception
+ *
+ * @param t
+ * A {@link Throwable} exception to be logged, enabling the
+ * Exception view of certain HTML reporters
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest info(Throwable t) {
+ return info(t, null);
+ }
+
+ /**
+ * Logs an event with Status.INFO and custom {@link Markup}
+ * such as:
+ *
+ *
+ *
Code block
+ *
Label
+ *
Table
+ *
+ *
+ * @param m
+ * {@link Markup}
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest info(Markup m) {
+ log(Status.INFO, m);
+ return this;
+ }
+
+ /**
+ * Logs an event with Status.INFO and {@link ScreenCapture}
+ *
+ * @param media
+ * A {@link Media} object provided by {@link MediaEntityBuilder}
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest info(Media media) {
+ log(Status.INFO, media);
+ return this;
+ }
+
+ /**
+ * Logs an Status.PASS event with details and a media object:
+ * {@link ScreenCapture}
+ *
+ *
+ *
+ * @param t
+ * {@link Throwable}
+ * @param media
+ * A {@link Media} object provided by {@link MediaEntityBuilder}
+ *
+ * @return An {@link ExtentTest} object
+ */
+ public ExtentTest skip(Throwable t, Media media) {
+ log(Status.SKIP, t, media);
+ return this;
+ }
+
+ /**
+ * Logs an event with Status.SKIP and exception
+ *
+ * @param t
+ * {@link Throwable}
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest skip(Throwable t) {
+ return skip(t, null);
+ }
+
+ /**
+ * Logs an event with Status.SKIP and custom {@link Markup}
+ * such as:
+ *
+ *
+ *
Code block
+ *
Label
+ *
Table
+ *
+ *
+ * @param m
+ * {@link Markup}
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest skip(Markup m) {
+ log(Status.SKIP, m);
+ return this;
+ }
+
+ /**
+ * Logs an event with Status.SKIP and {@link ScreenCapture}
+ *
+ * @param media
+ * A {@link Media} object provided by {@link MediaEntityBuilder}
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest skip(Media media) {
+ log(Status.SKIP, media);
+ return this;
+ }
+
+ /**
+ * Assigns a category or group
+ *
+ * @param category
+ * Category name
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest assignCategory(String... category) {
+ if (category == null || category.length == 0)
+ return this;
+ Arrays.stream(category)
+ .forEach(x -> {
+ Category c = new Category(x.replaceAll("\\s+", ""));
+ model.getCategorySet().add(c);
+ extent.onCategoryAdded(c, model);
+ });
+ return this;
+ }
+
+ /**
+ * Assigns an author
+ *
+ * @param author
+ * Author name
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest assignAuthor(String... author) {
+ if (author == null || author.length == 0)
+ return this;
+ Arrays.stream(author)
+ .forEach(x -> {
+ Author a = new Author(x.replaceAll("\\s+", ""));
+ model.getAuthorSet().add(a);
+ extent.onAuthorAdded(a, model);
+ });
+ return this;
+ }
+
+ /**
+ * Assign a device
+ *
+ * @param device
+ * Device name
+ *
+ * @return {@link ExtentTest} object
+ */
+ public ExtentTest assignDevice(String... device) {
+ if (device == null || device.length == 0)
+ return this;
+ Arrays.stream(device)
+ .forEach(x -> {
+ Device d = new Device(x.replaceAll("\\s+", ""));
+ model.getDeviceSet().add(d);
+ extent.onDeviceAdded(d, model);
+ });
+ return this;
+ }
+
+ @Override
+ public Status getStatus() {
+ return model.getStatus();
+ }
+
+ public ExtentTest addScreenCaptureFromPath(String path, String title) {
+ if (path == null || path.isEmpty())
+ throw new IllegalArgumentException("ScreenCapture path cannot be null or empty");
+ Media m = ScreenCapture.builder().path(path).title(title).build();
+ model.addMedia(m);
+ extent.onMediaAdded(m, model);
+ return this;
+ }
+
+ public ExtentTest addScreenCaptureFromPath(String path) {
+ return addScreenCaptureFromPath(path, null);
+ }
+
+ public ExtentTest addScreenCaptureFromBase64String(String base64, String title) {
+ if (base64 == null || base64.isEmpty())
+ throw new IllegalArgumentException("Base64 string cannot be null or empty");
+ if (!base64.startsWith("data:"))
+ base64 = "data:image/png;base64," + base64;
+ Media m = ScreenCapture.builder().base64(base64).title(title).build();
+ model.addMedia(m);
+ extent.onMediaAdded(m, model);
+ return this;
+ }
+
+ public ExtentTest addScreenCaptureFromBase64String(String base64) {
+ return addScreenCaptureFromBase64String(base64, null);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/GherkinKeyword.java b/src/main/java/com/aventstack/extentreports/GherkinKeyword.java
index f61ca5e..b1dd4d0 100644
--- a/src/main/java/com/aventstack/extentreports/GherkinKeyword.java
+++ b/src/main/java/com/aventstack/extentreports/GherkinKeyword.java
@@ -7,11 +7,12 @@
import java.util.logging.Logger;
import com.aventstack.extentreports.gherkin.GherkinDialect;
-import com.aventstack.extentreports.gherkin.GherkinDialectProvider;
+import com.aventstack.extentreports.gherkin.GherkinDialectManager;
import com.aventstack.extentreports.gherkin.model.Asterisk;
import com.aventstack.extentreports.gherkin.model.IGherkinFormatterModel;
import freemarker.template.utility.StringUtil;
+import lombok.Getter;
/**
* Allows {@link IGherkinFormatterModel} to be returned by using a name, from
@@ -39,49 +40,47 @@
*
* @see IGherkinFormatterModel
*/
+@Getter
public class GherkinKeyword {
+ private static final Logger logger = Logger.getLogger(GherkinKeyword.class.getName());
- private static final Logger logger = Logger.getLogger(GherkinKeyword.class.getName());
+ private Class clazz = IGherkinFormatterModel.class;
+ private IGherkinFormatterModel keyword;
- private Class clazz = IGherkinFormatterModel.class;
- private IGherkinFormatterModel keywordClazz;
+ public GherkinKeyword(String gk) throws ClassNotFoundException {
+ GherkinDialect dialect = null;
+ String apiKeyword = StringUtil.capitalize(gk.trim());
+ String refPath = clazz.getPackage().getName();
- public GherkinKeyword(String keyword) throws ClassNotFoundException {
- GherkinDialect dialect = null;
- String apiKeyword = StringUtil.capitalize(keyword.trim());
- String refPath = clazz.getPackage().getName();
-
- try {
- apiKeyword = apiKeyword.equals("*") ? Asterisk.class.getSimpleName() : apiKeyword;
- dialect = GherkinDialectProvider.getDialect();
- if (dialect != null
- && !dialect.getLanguage().equalsIgnoreCase(GherkinDialectProvider.getDefaultLanguage())) {
- apiKeyword = null;
- Map> keywords = dialect.getKeywords();
-
- for (Entry> key : keywords.entrySet()) {
- boolean keywordLocated = key.getValue().stream()
- .anyMatch(x -> x.trim().equalsIgnoreCase(keyword.trim()));
- if (keywordLocated) {
- apiKeyword = StringUtil.capitalize(key.getKey());
- break;
- }
- }
- }
-
- if (apiKeyword == null) {
- throw new GherkinKeywordNotFoundException("Keyword " + apiKeyword + " cannot be null");
- }
-
- String clazzName = refPath + "." + apiKeyword.replace(" ", "");
- Class> c = Class.forName(clazzName);
- keywordClazz = (IGherkinFormatterModel) c.newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- logger.log(Level.SEVERE, "", e);
- }
- }
-
- IGherkinFormatterModel getKeyword() {
- return keywordClazz;
- }
+ try {
+ apiKeyword = apiKeyword.equals("*") ? Asterisk.class.getSimpleName() : apiKeyword;
+ dialect = GherkinDialectManager.getDialect();
+ if (dialect != null
+ && !dialect.getLanguage().equalsIgnoreCase(GherkinDialectManager.getDefaultLanguage())) {
+ apiKeyword = null;
+ Map> keywords = dialect.getKeywords();
+ for (Entry> key : keywords.entrySet()) {
+ apiKeyword = key.getValue().stream()
+ .filter(x -> x.trim().equalsIgnoreCase(gk.trim()))
+ .findAny()
+ .map(x -> StringUtil.capitalize(x))
+ .orElse(null);
+ if (apiKeyword != null) {
+ apiKeyword = StringUtil.capitalize(key.getKey());
+ break;
+ }
+ }
+ }
+ if (apiKeyword == null)
+ throw new GherkinKeywordNotFoundException("Keyword cannot be found. " +
+ "You supplied: " + gk + " for dialect " + dialect + " which couldn't be mapped.");
+ if (apiKeyword.toLowerCase().contains("scenario") && apiKeyword.toLowerCase().contains("outline"))
+ apiKeyword = "ScenarioOutline";
+ String clazzName = refPath + "." + apiKeyword.replace(" ", "");
+ Class> c = Class.forName(clazzName);
+ keyword = (IGherkinFormatterModel) c.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ logger.log(Level.SEVERE, "", e);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/GherkinKeywordNotFoundException.java b/src/main/java/com/aventstack/extentreports/GherkinKeywordNotFoundException.java
index 198412f..85166e4 100644
--- a/src/main/java/com/aventstack/extentreports/GherkinKeywordNotFoundException.java
+++ b/src/main/java/com/aventstack/extentreports/GherkinKeywordNotFoundException.java
@@ -1,11 +1,9 @@
package com.aventstack.extentreports;
public class GherkinKeywordNotFoundException extends ClassNotFoundException {
-
private static final long serialVersionUID = 3140022717738862603L;
public GherkinKeywordNotFoundException(String message) {
super(message);
}
-
}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/IAddsMedia.java b/src/main/java/com/aventstack/extentreports/IAddsMedia.java
deleted file mode 100644
index daff630..0000000
--- a/src/main/java/com/aventstack/extentreports/IAddsMedia.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.io.IOException;
-
-public interface IAddsMedia {
-
- /**
- * Adds a snapshot to the test or log with title
- *
- * @param mediaPath Image path
- * @param title Image title
- *
- * @return Object this method is called from, generally
- * {@link com.aventstack.extentreports.ExtentTest} or {@link com.aventstack.extentreports.model.Log}
- *
- * @throws IOException thrown if the imagePath of image is not
- * found
- */
- T addScreenCaptureFromPath(String mediaPath, String title) throws IOException;
-
- /**
- * Adds a snapshot to test or log
- *
- * @param mediaPath Image path
- *
- * @return Object this method is called from, generally
- * {@link com.aventstack.extentreports.ExtentTest} or {@link com.aventstack.extentreports.model.Log}
- *
- * @throws IOException thrown if the imagePath of image is not
- * found
- */
- T addScreenCaptureFromPath(String mediaPath) throws IOException;
-
- /**
- * Adds a base64 screenshot
- *
- * @param base64 base64 string
- * @param title Image title
- *
- * @return Object this method is called from, generally
- * {@link com.aventstack.extentreports.ExtentTest} or {@link com.aventstack.extentreports.model.Log}
- */
- T addScreenCaptureFromBase64String(String base64, String title);
-
- /**
- * Adds a base64 screenshot
- *
- * @param base64 base64 string
- *
- * @return Object this method is called from, generally
- * {@link com.aventstack.extentreports.ExtentTest} or {@link com.aventstack.extentreports.model.Log}
- */
- T addScreenCaptureFromBase64String(String base64);
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/IAnalysisStrategyMethod.java b/src/main/java/com/aventstack/extentreports/IAnalysisStrategyMethod.java
deleted file mode 100644
index 4a92a99..0000000
--- a/src/main/java/com/aventstack/extentreports/IAnalysisStrategyMethod.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.aventstack.extentreports;
-
-/**
- * Marker interface for a class that uses an {@link AnalysisStrategy}
- *
- */
-public interface IAnalysisStrategyMethod {
-
- void setAnalysisStrategy(AnalysisStrategy strategy);
-
- AnalysisStrategy getAnalysisStrategy();
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/InvalidAnalysisStrategyException.java b/src/main/java/com/aventstack/extentreports/InvalidAnalysisStrategyException.java
deleted file mode 100644
index 4ab1c81..0000000
--- a/src/main/java/com/aventstack/extentreports/InvalidAnalysisStrategyException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.aventstack.extentreports;
-
-public class InvalidAnalysisStrategyException extends IllegalArgumentException {
-
- private static final long serialVersionUID = 5955632288593334683L;
-
- public InvalidAnalysisStrategyException(String message) {
- super(message);
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/MediaEntityBuilder.java b/src/main/java/com/aventstack/extentreports/MediaEntityBuilder.java
index e008747..04c6b05 100644
--- a/src/main/java/com/aventstack/extentreports/MediaEntityBuilder.java
+++ b/src/main/java/com/aventstack/extentreports/MediaEntityBuilder.java
@@ -1,7 +1,5 @@
package com.aventstack.extentreports;
-import java.io.IOException;
-
import com.aventstack.extentreports.model.Media;
import com.aventstack.extentreports.model.ScreenCapture;
@@ -10,61 +8,48 @@
*
*/
public class MediaEntityBuilder {
-
- private static ThreadLocal media = new ThreadLocal<>();
-
- private static class MediaBuilderInstance {
- static final MediaEntityBuilder INSTANCE = new MediaEntityBuilder();
-
- private MediaBuilderInstance() {
- }
- }
-
- private MediaEntityBuilder() {
- }
-
- private static synchronized MediaEntityBuilder getInstance() {
- return MediaBuilderInstance.INSTANCE;
- }
-
- public MediaEntityModelProvider build() {
- return new MediaEntityModelProvider(media.get());
- }
-
- public static synchronized MediaEntityBuilder createScreenCaptureFromPath(String path, String title)
- throws IOException {
- if (path == null || path.isEmpty())
- throw new IOException("ScreenCapture path cannot be null or empty.");
-
- return createScreenCapture(path, title, false);
- }
-
- public static synchronized MediaEntityBuilder createScreenCaptureFromPath(String path) throws IOException {
- return createScreenCaptureFromPath(path, null);
- }
-
- public static synchronized MediaEntityBuilder createScreenCaptureFromBase64String(String base64String)
- throws IOException {
- if (base64String == null || base64String.trim().equals(""))
- throw new IOException("Base64 string cannot be null or empty.");
-
- return createScreenCapture(base64String, null, true);
- }
-
- private static synchronized MediaEntityBuilder createScreenCapture(String pathOrBase64String, String title,
- boolean isBase64String) {
- ScreenCapture sc = new ScreenCapture();
- if (isBase64String)
- sc.setBase64String(pathOrBase64String);
- else
- sc.setPath(pathOrBase64String);
-
- if (title != null)
- sc.setName(title);
-
- media.set(sc);
-
- return getInstance();
- }
-
+ private static final String BASE64_ENCODED = "data:image/png;base64,";
+ private static ThreadLocal media = new ThreadLocal<>();
+
+ private static class MediaBuilderInstance {
+ static final MediaEntityBuilder INSTANCE = new MediaEntityBuilder();
+
+ private MediaBuilderInstance() {
+ }
+ }
+
+ private MediaEntityBuilder() {
+ }
+
+ private static synchronized MediaEntityBuilder getInstance() {
+ return MediaBuilderInstance.INSTANCE;
+ }
+
+ public Media build() {
+ return media.get();
+ }
+
+ public static MediaEntityBuilder createScreenCaptureFromPath(String path, String title) {
+ if (path == null || path.isEmpty())
+ throw new IllegalArgumentException("ScreenCapture path cannot be null or empty");
+ media.set(ScreenCapture.builder().path(path).title(title).build());
+ return getInstance();
+ }
+
+ public static MediaEntityBuilder createScreenCaptureFromPath(String path) {
+ return createScreenCaptureFromPath(path, null);
+ }
+
+ public static MediaEntityBuilder createScreenCaptureFromBase64String(String base64, String title) {
+ if (base64 == null || base64.trim().equals(""))
+ throw new IllegalArgumentException("Base64 string cannot be null or empty");
+ if (!base64.startsWith("data:"))
+ base64 = BASE64_ENCODED + base64;
+ media.set(ScreenCapture.builder().base64(base64).title(title).build());
+ return getInstance();
+ }
+
+ public static MediaEntityBuilder createScreenCaptureFromBase64String(String base64) {
+ return createScreenCaptureFromBase64String(base64, null);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/MediaEntityModelProvider.java b/src/main/java/com/aventstack/extentreports/MediaEntityModelProvider.java
deleted file mode 100644
index ef13f46..0000000
--- a/src/main/java/com/aventstack/extentreports/MediaEntityModelProvider.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.aventstack.extentreports;
-
-import com.aventstack.extentreports.model.Media;
-
-public class MediaEntityModelProvider {
-
- private Media m;
-
- public MediaEntityModelProvider(Media m) {
- this.m = m;
- }
-
- public Media getMedia() {
- return m;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/NullStatusException.java b/src/main/java/com/aventstack/extentreports/NullStatusException.java
new file mode 100644
index 0000000..87c6081
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/NullStatusException.java
@@ -0,0 +1,5 @@
+package com.aventstack.extentreports;
+
+public class NullStatusException extends NullPointerException {
+ private static final long serialVersionUID = 3331364668564936293L;
+}
diff --git a/src/main/java/com/aventstack/extentreports/NullTestException.java b/src/main/java/com/aventstack/extentreports/NullTestException.java
new file mode 100644
index 0000000..6ad9fa4
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/NullTestException.java
@@ -0,0 +1,5 @@
+package com.aventstack.extentreports;
+
+public class NullTestException extends NullPointerException {
+ private static final long serialVersionUID = -2987170430890267069L;
+}
diff --git a/src/main/java/com/aventstack/extentreports/ProvidesSource.java b/src/main/java/com/aventstack/extentreports/ProvidesSource.java
new file mode 100644
index 0000000..f53ead7
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/ProvidesSource.java
@@ -0,0 +1,5 @@
+package com.aventstack.extentreports;
+
+public interface ProvidesSource {
+ String getSource();
+}
diff --git a/src/main/java/com/aventstack/extentreports/ReactiveSubject.java b/src/main/java/com/aventstack/extentreports/ReactiveSubject.java
new file mode 100644
index 0000000..de53bae
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/ReactiveSubject.java
@@ -0,0 +1,90 @@
+package com.aventstack.extentreports;
+
+import java.util.List;
+
+import com.aventstack.extentreports.model.Author;
+import com.aventstack.extentreports.model.Category;
+import com.aventstack.extentreports.model.Device;
+import com.aventstack.extentreports.model.Log;
+import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.Report;
+import com.aventstack.extentreports.model.Test;
+import com.aventstack.extentreports.observer.AttributesObserver;
+import com.aventstack.extentreports.observer.ExtentObserver;
+import com.aventstack.extentreports.observer.LogObserver;
+import com.aventstack.extentreports.observer.MediaObserver;
+import com.aventstack.extentreports.observer.ReportObserver;
+import com.aventstack.extentreports.observer.TestObserver;
+import com.aventstack.extentreports.observer.entity.AttributeEntity;
+import com.aventstack.extentreports.observer.entity.LogEntity;
+import com.aventstack.extentreports.observer.entity.MediaEntity;
+import com.aventstack.extentreports.observer.entity.ReportEntity;
+import com.aventstack.extentreports.observer.entity.TestEntity;
+
+import io.reactivex.rxjava3.subjects.PublishSubject;
+import lombok.Getter;
+
+@Getter
+abstract class ReactiveSubject {
+ private final Report report = Report.builder().build();
+ private final PublishSubject reportSubject = PublishSubject.create();
+ private final PublishSubject testSubject = PublishSubject.create();
+ private final PublishSubject logSubject = PublishSubject.create();
+ private final PublishSubject mediaSubject = PublishSubject.create();
+ private final PublishSubject attribSubject = PublishSubject.create();
+
+ protected ReactiveSubject() {
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected final void attachReporter(List observerList) {
+ for (ExtentObserver o : observerList) {
+ if (o instanceof ReportObserver)
+ reportSubject.subscribe(((ReportObserver) o).getReportObserver());
+ if (o instanceof TestObserver)
+ testSubject.subscribe(((TestObserver) o).getTestObserver());
+ if (o instanceof LogObserver)
+ logSubject.subscribe(((LogObserver) o).getLogObserver());
+ if (o instanceof MediaObserver)
+ mediaSubject.subscribe(((MediaObserver) o).getMediaObserver());
+ if (o instanceof AttributesObserver)
+ attribSubject.subscribe(((AttributesObserver) o).getAttributesObserver());
+ }
+ }
+
+ protected void onTestCreated(Test test) {
+ testSubject.onNext(TestEntity.builder().test(test).build());
+ }
+
+ protected void onTestRemoved(Test test) {
+ testSubject.onNext(TestEntity.builder().test(test).removed(true).build());
+ }
+
+ protected void onLogCreated(Log log, Test test) {
+ logSubject.onNext(LogEntity.builder().log(log).test(test).build());
+ }
+
+ protected void onAuthorAssigned(Author x, Test test) {
+ attribSubject.onNext(AttributeEntity.builder().author(x).test(test).build());
+ }
+
+ protected void onCategoryAssigned(Category x, Test test) {
+ attribSubject.onNext(AttributeEntity.builder().category(x).test(test).build());
+ }
+
+ protected void onDeviceAssigned(Device x, Test test) {
+ attribSubject.onNext(AttributeEntity.builder().device(x).test(test).build());
+ }
+
+ protected void onMediaAdded(Media m, Test test) {
+ mediaSubject.onNext(MediaEntity.builder().media(m).test(test).build());
+ }
+
+ protected void onMediaAdded(Media m, Log log) {
+ mediaSubject.onNext(MediaEntity.builder().media(m).log(log).build());
+ }
+
+ protected void onFlush() {
+ reportSubject.onNext(ReportEntity.builder().report(report).build());
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/ReportAggregates.java b/src/main/java/com/aventstack/extentreports/ReportAggregates.java
deleted file mode 100644
index e1558f2..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportAggregates.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-
-import com.aventstack.extentreports.model.Author;
-import com.aventstack.extentreports.model.Category;
-import com.aventstack.extentreports.model.Device;
-import com.aventstack.extentreports.model.Test;
-import com.aventstack.extentreports.model.context.ExceptionTestContextStore;
-import com.aventstack.extentreports.model.context.SystemAttributeContext;
-import com.aventstack.extentreports.model.context.TestAttributeTestContextStore;
-
-/**
- * Aggregator for report elements and collections
- *
- */
-public class ReportAggregates {
-
- private List testList;
- private List testRunnerLogs;
- private TestAttributeTestContextStore categoryContext;
- private TestAttributeTestContextStore authorContext;
- private TestAttributeTestContextStore deviceContext;
- private ExceptionTestContextStore exceptionContext;
- private SystemAttributeContext systemAttributeContext;
- private ReportStatusStats reportStatusStats;
- private Collection statusCollection;
- private Date startTime;
- private Date endTime;
-
- public Date getStartTime() {
- return startTime;
- }
-
- public void setStartTime(Date startTime) {
- this.startTime = startTime;
- }
-
- public Date getEndTime() {
- return endTime;
- }
-
- public void setEndTime(Date endTime) {
- this.endTime = endTime;
- }
-
- public Status getStatus() {
- return Status.getHighestStatus(getStatusCollection());
- }
-
- public List getTestList() {
- return testList;
- }
-
- public void setTestList(List testList) {
- this.testList = testList;
- }
-
- public List getTestRunnerLogs() {
- return testRunnerLogs;
- }
-
- public void setTestRunnerLogs(List testRunnerLogs) {
- this.testRunnerLogs = testRunnerLogs;
- }
-
- public TestAttributeTestContextStore getCategoryContext() {
- return categoryContext;
- }
-
- public void setCategoryContext(TestAttributeTestContextStore categoryContext) {
- this.categoryContext = categoryContext;
- }
-
- public TestAttributeTestContextStore getAuthorContext() {
- return authorContext;
- }
-
- public void setAuthorContext(TestAttributeTestContextStore authorContext) {
- this.authorContext = authorContext;
- }
-
- public TestAttributeTestContextStore getDeviceContext() {
- return deviceContext;
- }
-
- public void setDeviceContext(TestAttributeTestContextStore deviceContext) {
- this.deviceContext = deviceContext;
- }
-
- public ExceptionTestContextStore getExceptionContext() {
- return exceptionContext;
- }
-
- public void setExceptionContext(ExceptionTestContextStore exceptionContext) {
- this.exceptionContext = exceptionContext;
- }
-
- public SystemAttributeContext getSystemAttributeContext() {
- return systemAttributeContext;
- }
-
- public void setSystemAttributeContext(SystemAttributeContext systemAttributeContext) {
- this.systemAttributeContext = systemAttributeContext;
- }
-
- public ReportStatusStats getReportStatusStats() {
- return reportStatusStats;
- }
-
- public void setReportStatusStats(ReportStatusStats reportStatusStats) {
- this.reportStatusStats = reportStatusStats;
- }
-
- public Collection getStatusCollection() {
- return statusCollection;
- }
-
- public void setStatusCollection(Collection statusCollection) {
- this.statusCollection = statusCollection;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ReportAggregatesBuilder.java b/src/main/java/com/aventstack/extentreports/ReportAggregatesBuilder.java
deleted file mode 100644
index bea527a..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportAggregatesBuilder.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-
-import com.aventstack.extentreports.model.Author;
-import com.aventstack.extentreports.model.Category;
-import com.aventstack.extentreports.model.Device;
-import com.aventstack.extentreports.model.Test;
-import com.aventstack.extentreports.model.context.ExceptionTestContextStore;
-import com.aventstack.extentreports.model.context.SystemAttributeContext;
-import com.aventstack.extentreports.model.context.TestAttributeTestContextStore;
-
-/**
- * Builds {@link ReportAggregates}
- *
- */
-public class ReportAggregatesBuilder {
-
- private List testList;
- private List testRunnerLogs;
- private TestAttributeTestContextStore categoryContext;
- private TestAttributeTestContextStore authorContext;
- private TestAttributeTestContextStore deviceContext;
- private ExceptionTestContextStore exceptionContext;
- private SystemAttributeContext systemAttributeContext;
- private ReportStatusStats reportStatusStats;
- private Collection statusCollection;
- private Date startTime;
- private Date endTime;
-
- public ReportAggregates build() {
- ReportAggregates aggregates = new ReportAggregates();
- aggregates.setTestList(testList);
- aggregates.setTestRunnerLogs(testRunnerLogs);
- aggregates.setCategoryContext(categoryContext);
- aggregates.setAuthorContext(authorContext);
- aggregates.setDeviceContext(deviceContext);
- aggregates.setExceptionContext(exceptionContext);
- aggregates.setSystemAttributeContext(systemAttributeContext);
- aggregates.setReportStatusStats(reportStatusStats);
- aggregates.setStatusCollection(statusCollection);
- aggregates.setStartTime(startTime);
- aggregates.setEndTime(endTime);
- return aggregates;
- }
-
- public ReportAggregatesBuilder setTestList(List testList) {
- this.testList = testList;
- return this;
- }
-
- public ReportAggregatesBuilder setTestRunnerLogs(List testRunnerLogs) {
- this.testRunnerLogs = testRunnerLogs;
- return this;
- }
-
- public ReportAggregatesBuilder setCategoryContext(TestAttributeTestContextStore categoryContext) {
- this.categoryContext = categoryContext;
- return this;
- }
-
- public ReportAggregatesBuilder setAuthorContext(TestAttributeTestContextStore authorContext) {
- this.authorContext = authorContext;
- return this;
- }
-
- public ReportAggregatesBuilder setDeviceContext(TestAttributeTestContextStore deviceContext) {
- this.deviceContext = deviceContext;
- return this;
- }
-
- public ReportAggregatesBuilder setExceptionContext(ExceptionTestContextStore exceptionContext) {
- this.exceptionContext = exceptionContext;
- return this;
- }
-
- public ReportAggregatesBuilder setSystemAttributeContext(SystemAttributeContext systemAttributeContext) {
- this.systemAttributeContext = systemAttributeContext;
- return this;
- }
-
- public ReportAggregatesBuilder setReportStatusStats(ReportStatusStats reportStatusStats) {
- this.reportStatusStats = reportStatusStats;
- return this;
- }
-
- public ReportAggregatesBuilder setStatusCollection(Collection statusCollection) {
- this.statusCollection = statusCollection;
- return this;
- }
-
- public ReportAggregatesBuilder setStartTime(Date startTime) {
- this.startTime = startTime;
- return this;
- }
-
- public ReportAggregatesBuilder setEndTime(Date endTime) {
- this.endTime = endTime;
- return this;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ReportAggregatesListener.java b/src/main/java/com/aventstack/extentreports/ReportAggregatesListener.java
deleted file mode 100644
index a49863c..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportAggregatesListener.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.util.List;
-
-import com.aventstack.extentreports.model.Author;
-import com.aventstack.extentreports.model.Category;
-import com.aventstack.extentreports.model.Device;
-import com.aventstack.extentreports.model.Test;
-import com.aventstack.extentreports.model.context.ExceptionTestContextStore;
-import com.aventstack.extentreports.model.context.SystemAttributeContext;
-import com.aventstack.extentreports.model.context.TestAttributeTestContextStore;
-
-/**
- * Allows sharing of aggregates/collections to be shared with the started
- * reporter. A few examples of these collections include:
- *
- *
- *
Category context - list of all categories and associated tests
- *
Exception context - list of all exceptions and associated tests
- *
Complete list of started tests
- *
System information
- *
- */
-public interface ReportAggregatesListener {
-
- /**
- * Allows sharing the complete list of tests with the reporter
- *
- * @param testList list of all tests created by {@link ExtentReports}
- */
- void setTestList(List testList);
-
- /**
- * Passes the complete list of logs to the reporter
- *
- * @param logs testrunner logs
- */
- void setTestRunnerLogs(List logs);
-
- /**
- * Allows sharing the complete list of category and associated tests with the
- * reporter
- *
- * @param categoryContext collection containing categories and all associated
- * tests
- */
- void setCategoryContextInfo(TestAttributeTestContextStore categoryContext);
-
- /**
- * Allows sharing the complete list of author and associated tests with the
- * reporter
- *
- * @param authorContext collection containing author and all associated tests
- */
- void setAuthorContextInfo(TestAttributeTestContextStore authorContext);
-
- /**
- * Allows sharing the complete list of device and associated tests with the
- * reporter
- *
- * @param deviceContext collection containing devices and all associated tests
- */
- void setDeviceContextInfo(TestAttributeTestContextStore deviceContext);
-
- /**
- * Allows sharing the complete list of exceptions and associated tests with the
- * reporter
- *
- * @param exceptionContext collection containing exception and all associated
- * tests
- */
- void setExceptionContextInfo(ExceptionTestContextStore exceptionContext);
-
- /**
- * Passes all system information to the reporter
- *
- * @param systemAttributeContext system information
- */
- void setSystemAttributeContext(SystemAttributeContext systemAttributeContext);
-
- /**
- * Report Status stats of the run session for all possible levels:
- *
- *
- *
Features/Tests
- *
Scenarios/Logs
- *
Steps
- *
- *
- * @param sc {@link ReportStatusStats} represents stats of each hierarchical
- * level of test/event
- */
- void setReportStatusStats(ReportStatusStats sc);
-
- /**
- * A distinct list of status assigned to tests
- *
- * @param statusList a distinct list of {@link Status}
- */
- void setStatusList(List statusList);
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ReportConfigurator.java b/src/main/java/com/aventstack/extentreports/ReportConfigurator.java
deleted file mode 100644
index 3469eaf..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportConfigurator.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.aventstack.extentreports;
-
-public class ReportConfigurator {
-
- private static class ReportConfiguratorInstance {
- static final ReportConfigurator INSTANCE = new ReportConfigurator();
-
- private ReportConfiguratorInstance() {
- }
- }
-
- private ReportConfigurator() {
- }
-
- public static ReportConfigurator getInstance() {
- return ReportConfiguratorInstance.INSTANCE;
- }
-
- public StatusConfigurator statusConfigurator() {
- return StatusConfigurator.getInstance();
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ReportObservable.java b/src/main/java/com/aventstack/extentreports/ReportObservable.java
deleted file mode 100644
index 7e4ff66..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportObservable.java
+++ /dev/null
@@ -1,597 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import com.aventstack.extentreports.model.Author;
-import com.aventstack.extentreports.model.Category;
-import com.aventstack.extentreports.model.Device;
-import com.aventstack.extentreports.model.Log;
-import com.aventstack.extentreports.model.ScreenCapture;
-import com.aventstack.extentreports.model.SystemAttribute;
-import com.aventstack.extentreports.model.Test;
-import com.aventstack.extentreports.model.context.ExceptionTestContextStore;
-import com.aventstack.extentreports.model.context.SystemAttributeContext;
-import com.aventstack.extentreports.model.context.TestAttributeTestContextStore;
-import com.aventstack.extentreports.model.context.helpers.TestRemover;
-import com.aventstack.extentreports.model.service.ScreenCaptureService;
-import com.aventstack.extentreports.model.service.TestService;
-import com.aventstack.extentreports.reporter.BasicFileReporter;
-import com.aventstack.extentreports.reporter.ExtentReporter;
-
-abstract class ReportObservable implements ReportService {
-
- /**
- * The current AnalysisStrategy for the run session. This decides the technique
- * used to count the test status at differing levels. For example, for a BDD
- * style report, the levels to be calculated are Feature, Scenario and Step (3
- * levels). For a generic, non-BDD style report, levels can be dynamic. For a
- * non-BDD style report, levels typically consist of:
- *
- *
- * 2 levels: Test, the leaf-node
- * Test
- * - Node
- * -- Leaf Node
- * --- Event
- *
- */
- private AnalysisStrategy strategy = AnalysisStrategy.TEST;
-
- /**
- * Use this setting when building post-execution reports, such as from TestNG
- * IReporter. This setting allows setting test with your own variables and
- * prevent update by Extent. As of today, with this enabled, Extent does not use
- * time-stamps for tests at the time they were created
- */
- private boolean usesManualConfiguration = false;
-
- /**
- * The status of the entire report or build
- */
- private Status reportStatus = Status.PASS;
-
- /**
- * Time the report or build was started
- */
- private Date reportStartDate = Calendar.getInstance().getTime();
-
- /**
- * Time the report or build ended. This value is updated everytime
- * flush is called
- */
- private Date reportEndDate;
-
- /**
- * A collection of tests arranged by category
- */
- private TestAttributeTestContextStore categoryContext = new TestAttributeTestContextStore<>();
-
- /**
- * A collection of tests arranged by author
- */
- private TestAttributeTestContextStore authorContext = new TestAttributeTestContextStore<>();
-
- /**
- * A collection of tests arranged by author
- */
- private TestAttributeTestContextStore deviceContext = new TestAttributeTestContextStore<>();
-
- /**
- * A collection of tests arranged by exception
- */
- private ExceptionTestContextStore exceptionContext = new ExceptionTestContextStore();
-
- /**
- * A context of all system or environment variables
- */
- private SystemAttributeContext systemAttributeContext = new SystemAttributeContext();
-
- /**
- * A list of all {@link ExtentReporter} reporters started by the
- * attachReporter method
- */
- private List reporterList = new ArrayList<>();
-
- /**
- * Any logs added by to the test runner can be added to Extent
- *
- *
- * TestNG Example:
- *
- *
- * Setting logs with TestNG:
- *
- *
- * Reporter.log("hello world")
- *
- *
- *
- * Informing Extent of any logs added:
- *
- *
- * for (String s : Reporter.getOutput()) {
- * extent.setTestRunnerOutput(s);
- * }
- *
- */
- private List testRunnerLogs = new ArrayList<>();
-
- /**
- * A list of all tests created
- */
- private List testList = new ArrayList<>();
-
- /**
- * Instance of {@link ReportStatusStats}
- */
- private ReportStatusStats stats = new ReportStatusStats(strategy);
-
- /**
- * A unique collection of statuses tests are marked with
- *
- *
- * Consider a report having 10 tests:
- *
- *
- *
Test1: PASS
- *
Test2: PASS
- *
Test3: PASS
- *
Test4: SKIP
- *
Test5: SKIP
- *
Test6: FAIL
- *
Test7: PASS
- *
Test8: PASS
- *
Test9: FAIL
- *
Test10: PASS
- *
- *
- *
- * Distinct list of contained status:
- *
- *
- *
PASS
- *
SKIP
- *
FAIL
- *
- */
- private Set statusSet = new HashSet();
-
- /**
- * Path of dirs to resolve relative image path so there is a direct access
- */
- private String[] imagePathResolveDir;
-
- protected ReportObservable() {
- }
-
- /**
- * Subscribe the reporter to receive updates when making calls to the API
- *
- * @param reporter {@link ExtentReporter} reporter
- */
- protected void register(ExtentReporter reporter) {
- reporterList.add(reporter);
- reporter.start();
- }
-
- /**
- * Unsubscribe the reporter. Calling unsubscribe will call the stop
- * method and also remove the reporter from the list of started reporters
- *
- * @param reporter {@link ExtentReporter} reporter to unsubscribe
- */
- protected void deregister(ExtentReporter reporter) {
-
- if (reporterList.contains((Object) reporter)) {
- reporter.stop();
- reporterList.remove(reporter);
- }
- }
-
- /**
- * Retrieve a list of all started reporters
- *
- * @return a list of {@link ExtentReporter} objects
- */
- protected List getReporterCollection() {
- return reporterList;
- }
-
- /**
- * Saves the started test and notifies all started reporters
- *
- * @param test a {@link Test} object
- */
- protected synchronized void saveTest(Test test) {
- testList.add(test);
- reporterList.forEach(x -> x.onTestStarted(test));
- }
-
- /**
- * Removes the test and notifies all started reporters
- *
- * @param test a {@link Test} object
- */
- protected void removeTest(Test test) {
- removeTestTestList(test);
- removeTestTestAttributeContext(test);
- reporterList.forEach(x -> x.onTestRemoved(test));
- }
-
- /**
- * Removes a test from test list
- *
- * @param test a {@link Test} object
- */
- private void removeTestTestList(Test test) {
- TestRemover.remove(testList, test);
- refreshReportEntities();
- }
-
- /**
- * Removes test from test list of each context
- *
- * @param test a {@link Test} object
- */
- private void removeTestTestAttributeContext(Test test) {
- if (TestService.testHasCategory(test)) {
- categoryContext.removeTest(test);
- }
- if (TestService.testHasAuthor(test)) {
- authorContext.removeTest(test);
- }
- if (TestService.testHasDevice(test)) {
- deviceContext.removeTest(test);
- }
- }
-
- /**
- * Refreshes report entities such as {@link ReportStatusStats} and list of
- * distinct {@link Status}
- */
- private void refreshReportEntities() {
- refreshReportStats();
- refreshStatusList();
- }
-
- /**
- * Refresh and notify all reports of {@link ReportStatusStats}
- */
- private synchronized void refreshReportStats() {
- stats.refresh(testList);
- }
-
- /**
- * Refresh and notify all reports of distinct status assigned to tests
- */
- private synchronized void refreshStatusList() {
- statusSet.clear();
- refreshStatusList(testList);
- }
-
- /**
- * Refreshes distinct status list
- *
- * @param list a list of started {@link Test}
- */
- private synchronized void refreshStatusList(List list) {
- if (list == null || list.isEmpty()) {
- return;
- }
- list.forEach(x -> statusSet.add(x.getStatus()));
- list.forEach(x -> refreshStatusList(x.getNodeContext().getAll()));
- }
-
- /**
- * Notify reporters of the added node
- *
- * @param node a {@link Test} node
- */
- void addNode(Test node) {
- reporterList.forEach(x -> x.onNodeStarted(node));
- }
-
- /**
- * Notifies reporters with information of added {@link Log}
- *
- * @param test {@link Test} to which the event is added
- * @param log {@link Log}
- */
- void addLog(Test test, Log log) {
- reporterList.forEach(x -> x.onLogAdded(test, log));
- }
-
- /**
- * Notifies reporters with information of added {@link Category}
- *
- * @param test {@link Test} to which the Category is added
- * @param category {@link Category}
- */
- void assignCategory(Test test, Category category) {
- reporterList.forEach(x -> x.onCategoryAssigned(test, category));
- }
-
- /**
- * Notifies reporters with information of added {@link Author}
- *
- * @param test {@link Test} to which the Author is added
- * @param author {@link Author}
- */
- void assignAuthor(Test test, Author author) {
- reporterList.forEach(x -> x.onAuthorAssigned(test, author));
- }
-
- /**
- * Notifies reporters with information of added {@link Author}
- *
- * @param test {@link Test} to which the Device is added
- * @param device {@link Device}
- */
- void assignDevice(Test test, Device device) {
- reporterList.forEach(x -> x.onDeviceAssigned(test, device));
- }
-
- /**
- * Notifies reporters with information of added {@link ScreenCapture}
- *
- * @param test {@link Test} to which the ScreenCapture is added
- * @param screenCapture {@link ScreenCapture}
- *
- * @throws IOException thrown if the {@link ScreenCapture} is not found
- */
- void addScreenCapture(Test test, ScreenCapture screenCapture) throws IOException {
- ScreenCaptureService.resolvePath(screenCapture, imagePathResolveDir);
- for (ExtentReporter r : reporterList) {
- r.onScreenCaptureAdded(test, screenCapture);
- }
- }
-
- /**
- * Notifies reporters with information of added {@link ScreenCapture}
- *
- * @param test {@link Log} to which the ScreenCapture is added
- * @param screenCapture {@link ScreenCapture}
- *
- * @throws IOException thrown if the {@link ScreenCapture} is not found
- */
- void addScreenCapture(Log log, ScreenCapture screenCapture) throws IOException {
- ScreenCaptureService.resolvePath(screenCapture, imagePathResolveDir);
- for (ExtentReporter r : reporterList) {
- r.onScreenCaptureAdded(log, screenCapture);
- }
- }
-
- /**
- * Returns the context of author with the list of tests for each
- *
- * @return a {@link TestAttributeTestContextStore} object
- */
- protected TestAttributeTestContextStore getAuthorContextInfo() {
- return authorContext;
- }
-
- /**
- * Updates the status of the report or build
- *
- * @param status a {@link Status}
- */
- private void updateReportStatus(Status status) {
- int statusIndex = Status.getStatusHierarchy().indexOf(status);
- int reportStatusIndex = Status.getStatusHierarchy().indexOf(reportStatus);
-
- reportStatus = statusIndex < reportStatusIndex ? status : reportStatus;
- }
-
- /**
- * Ends the test
- *
- * @param test a {@link Test} object
- */
- private void endTest(Test test) {
- test.end();
- updateReportStatus(test.getStatus());
- }
-
- /**
- * Collects and updates all run information and notifies all reporters.
- * Depending upon the type of reporter, additional events can occur such as:
- *
- *
- *
A file written to disk (eg. case of {@link BasicFileReporter}
- *
A database being updated (eg. case of KlovReporter)
- *
- */
- protected synchronized void flush() {
- generateRecentStatus();
- notifyReporters();
- }
-
- /**
- * Collects run information from all tests for assigned {@link Category},
- * {@link Author}, Exception, Nodes. This also ends and updates all internal
- * test information and refreshes {@link ReportStatusStats} and the distinct
- * list of {@link Status}
- */
- public synchronized void generateRecentStatus() {
- if (testList == null || testList.isEmpty())
- return;
-
- reportEndDate = Calendar.getInstance().getTime();
-
- for (Test test : testList) {
- endTest(test);
- test.setUsesManualConfiguration(getAllowManualConfig());
- if (TestService.testHasCategory(test)) {
- test.getCategoryContext().getAll().forEach(x -> categoryContext.setAttributeContext((Category) x, test));
- }
- if (TestService.testHasAuthor(test)) {
- test.getAuthorContext().getAll().forEach(x -> authorContext.setAttributeContext((Author) x, test));
- }
- if (TestService.testHasDevice(test)) {
- test.getDeviceContext().getAll().forEach(x -> deviceContext.setAttributeContext((Device) x, test));
- }
- if (TestService.testHasException(test)) {
- test.getExceptionInfoContext().getAll().forEach(x -> exceptionContext.setExceptionContext(x, test));
- }
- if (TestService.testHasChildren(test)) {
- Iterator iter = test.getNodeContext().getIterator();
- while (iter.hasNext()) {
- Test node = iter.next();
- copyNodeAttributeAndRunTimeInfoToAttributeContexts(node);
- node.setUsesManualConfiguration(getAllowManualConfig());
- }
- }
- }
-
- refreshReportEntities();
- updateReportStartTimeForManualConfigurationSetting();
- }
-
- /**
- * In case where manual configuration is used, calculate the correct timestamps
- * based upon the timestamps assigned to tests
- */
- private void updateReportStartTimeForManualConfigurationSetting() {
- if (getAllowManualConfig() && !testList.isEmpty()) {
- Date minDate = testList.stream().map(t -> t.getStartTime()).min(Date::compareTo).get();
- Date maxDate = testList.stream().map(t -> t.getEndTime()).max(Date::compareTo).get();
- reportStartDate = reportStartDate.getTime() > minDate.getTime() ? minDate : reportStartDate;
- reportEndDate = reportEndDate.getTime() < maxDate.getTime() ? maxDate : reportEndDate;
- }
- }
-
- /**
- * Traverse all nodes and refresh {@link Category}, {@link Author}, Exception
- * and Node context information
- *
- * @param node a {@link Test} node
- */
- private void copyNodeAttributeAndRunTimeInfoToAttributeContexts(Test node) {
- if (TestService.testHasCategory(node)) {
- node.getCategoryContext().getAll().forEach(x -> categoryContext.setAttributeContext((Category) x, node));
- }
- if (TestService.testHasAuthor(node)) {
- node.getAuthorContext().getAll().forEach(x -> authorContext.setAttributeContext((Author) x, node));
- }
- if (TestService.testHasDevice(node)) {
- node.getDeviceContext().getAll().forEach(x -> deviceContext.setAttributeContext((Device) x, node));
- }
- if (TestService.testHasException(node)) {
- node.getExceptionInfoContext().getAll().forEach(x -> exceptionContext.setExceptionContext(x, node));
- }
- if (TestService.testHasChildren(node)) {
- node.getNodeContext().getAll().forEach(this::copyNodeAttributeAndRunTimeInfoToAttributeContexts);
- }
- }
-
- /**
- * Notify all reporters with complete run information
- */
- private synchronized void notifyReporters() {
- if (!testList.isEmpty() && TestService.isTestBehaviorDriven(testList.get(0))) {
- strategy = AnalysisStrategy.BDD;
- }
- ReportAggregates reportAggregates = new ReportAggregatesBuilder()
- .setAuthorContext(authorContext)
- .setCategoryContext(categoryContext)
- .setDeviceContext(deviceContext)
- .setExceptionContext(exceptionContext)
- .setReportStatusStats(stats)
- .setStatusCollection(statusSet)
- .setSystemAttributeContext(systemAttributeContext)
- .setTestList(testList)
- .setTestRunnerLogs(testRunnerLogs)
- .setStartTime(reportStartDate)
- .setEndTime(reportEndDate)
- .build();
- reporterList.forEach(x -> x.setAnalysisStrategy(strategy));
- reporterList.forEach(x -> x.flush(reportAggregates));
- }
-
- /**
- * Ends logging, stops and clears the list of reporters
- */
- protected void end() {
- flush();
- reporterList.forEach(ExtentReporter::stop);
- reporterList.clear();
- }
-
- /**
- * Add a system attribute
- *
- * @param sa a {@link SystemAttribute} object
- */
- protected void setSystemInfo(SystemAttribute sa) {
- systemAttributeContext.setSystemAttribute(sa);
- }
-
- /**
- * Add a test runner log
- *
- * @param log a log event
- */
- protected void setTestRunnerLogs(String log) {
- testRunnerLogs.add(log);
- }
-
- /**
- * Updates the {@link AnalysisStrategy}
- *
- * @param strategy a {@link AnalysisStrategy} object
- */
- protected void setAnalysisStrategy(AnalysisStrategy strategy) {
- this.strategy = strategy;
- stats = new ReportStatusStats(strategy);
- }
-
- protected void setMediaPathResolveDir(String[] imagePathResolveDir) {
- this.imagePathResolveDir = imagePathResolveDir;
- }
-
- /**
- * Setting to allow user driven configuration for test time-stamps
- *
- * @param useManualConfig setting for manual configuration
- */
- protected void setAllowManualConfig(boolean useManualConfig) {
- this.usesManualConfiguration = useManualConfig;
- }
-
- /**
- * Returns the current value of using manual configuration for test time-stamps
- *
- * @return setting for manual configuration
- */
- protected boolean getAllowManualConfig() {
- return usesManualConfiguration;
- }
-
- /**
- * Return the {@link ReportStatusStats} object
- *
- * @return a {@link ReportStatusStats} object
- */
- protected ReportStatusStats getStats() {
- generateRecentStatus();
- return stats;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ReportService.java b/src/main/java/com/aventstack/extentreports/ReportService.java
deleted file mode 100644
index 2932d67..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportService.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.aventstack.extentreports;
-
-import com.aventstack.extentreports.reporter.AbstractReporter;
-import com.aventstack.extentreports.reporter.ExtentReporter;
-
-/**
- * The main service for {@link ExtentReports} which allows attaching a reporter
- * of type {@link AbstractReporter}
- */
-public interface ReportService {
-
- void attachReporter(ExtentReporter... reporter);
-
- void generateRecentStatus();
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/ReportStatusStats.java b/src/main/java/com/aventstack/extentreports/ReportStatusStats.java
deleted file mode 100644
index 3de73be..0000000
--- a/src/main/java/com/aventstack/extentreports/ReportStatusStats.java
+++ /dev/null
@@ -1,602 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.util.Iterator;
-import java.util.List;
-
-import com.aventstack.extentreports.gherkin.model.Scenario;
-import com.aventstack.extentreports.model.Log;
-import com.aventstack.extentreports.model.Test;
-
-/**
- *
- * This class maintains the total count of tests and its nodes along with their
- * statuses for a given run session
- */
-public class ReportStatusStats {
-
- private List testList;
- private AnalysisStrategy strategy = AnalysisStrategy.TEST;
-
- private int parentPass = 0;
- private int parentFail = 0;
- private int parentFatal = 0;
- private int parentError = 0;
- private int parentWarning = 0;
- private int parentSkip = 0;
- private int parentExceptions = 0;
-
- private int childPass = 0;
- private int childFail = 0;
- private int childFatal = 0;
- private int childError = 0;
- private int childWarning = 0;
- private int childSkip = 0;
- private int childInfo = 0;
- private int childDebug = 0;
- private int childExceptions = 0;
-
- private int grandChildPass = 0;
- private int grandChildFail = 0;
- private int grandChildFatal = 0;
- private int grandChildError = 0;
- private int grandChildWarning = 0;
- private int grandChildSkip = 0;
- private int grandChildInfo = 0;
- private int grandChildDebug = 0;
- private int grandChildExceptions = 0;
-
- private int eventsPass = 0;
- private int eventsFail = 0;
- private int eventsFatal = 0;
- private int eventsError = 0;
- private int eventsWarning = 0;
- private int eventsSkip = 0;
- private int eventsInfo = 0;
- private int eventsDebug = 0;
- private int eventsExceptions = 0;
-
- public ReportStatusStats(AnalysisStrategy strategy) {
- this.strategy = strategy;
- }
-
- public void refresh(List testList) {
- reset();
- this.testList = testList;
- refreshStats();
- }
-
- private void reset() {
- parentPass = 0;
- parentFail = 0;
- parentFatal = 0;
- parentError = 0;
- parentWarning = 0;
- parentSkip = 0;
- parentExceptions = 0;
-
- childPass = 0;
- childFail = 0;
- childFatal = 0;
- childError = 0;
- childWarning = 0;
- childSkip = 0;
- childInfo = 0;
- childExceptions = 0;
-
- grandChildPass = 0;
- grandChildFail = 0;
- grandChildFatal = 0;
- grandChildError = 0;
- grandChildWarning = 0;
- grandChildSkip = 0;
- grandChildInfo = 0;
- grandChildExceptions = 0;
-
- eventsPass = 0;
- eventsFail = 0;
- eventsFatal = 0;
- eventsError = 0;
- eventsWarning = 0;
- eventsSkip = 0;
- eventsInfo = 0;
- eventsDebug = 0;
- eventsExceptions = 0;
- }
-
- public int getParentCount() {
- return getParentCountPass() + getParentCountFail() + getParentCountFatal() + getParentCountError()
- + getParentCountWarning() + getParentCountSkip();
- }
-
- public int getParentCountPass() {
- return parentPass;
- }
-
- public int getParentCountFail() {
- return parentFail;
- }
-
- public int getParentCountFatal() {
- return parentFatal;
- }
-
- public int getParentCountError() {
- return parentError;
- }
-
- public int getParentCountWarning() {
- return parentWarning;
- }
-
- public int getParentCountSkip() {
- return parentSkip;
- }
-
- public int getParentCountExceptions() {
- return parentExceptions;
- }
-
- public float getParentPercentagePass() {
- float p = getParentCount() > 0 ? (float) getParentCountPass() / (float) getParentCount() : 0;
- return p * 100;
- }
-
- public float getParentPercentageFail() {
- float p = getParentCount() > 0
- ? ((float) getParentCountFail() + (float) getParentCountFatal()) / (float) getParentCount()
- : 0;
- return p * 100;
- }
-
- public float getParentPercentageOthers() {
- float p = getParentCount() > 0
- ? ((float) getParentCountWarning() + (float) getParentCountError()) / (float) getParentCount()
- : 0;
- return p * 100;
- }
-
- public float getParentPercentageSkip() {
- float p = getParentCount() > 0 ? (float) getParentCountSkip() / (float) getParentCount() : 0;
- return p * 100;
- }
-
- public int getChildCount() {
- return getChildCountPass() + getChildCountFail() + getChildCountFatal() + getChildCountError()
- + getChildCountWarning() + getChildCountSkip() + getChildCountInfo();
- }
-
- public int getChildCountPass() {
- return childPass;
- }
-
- public int getChildCountFail() {
- return childFail;
- }
-
- public int getChildCountFatal() {
- return childFatal;
- }
-
- public int getChildCountError() {
- return childError;
- }
-
- public int getChildCountWarning() {
- return childWarning;
- }
-
- public int getChildCountSkip() {
- return childSkip;
- }
-
- public int getChildCountInfo() {
- return childInfo;
- }
-
- public int getChildCountDebug() {
- return childDebug;
- }
-
- public int getChildCountExceptions() {
- return childExceptions;
- }
-
- public float getChildPercentagePass() {
- float p = getChildCount() > 0 ? (float) getChildCountPass() / (float) getChildCount() : 0;
- return p * 100;
- }
-
- public float getChildPercentageFail() {
- float p = getChildCount() > 0
- ? ((float) getChildCountFail() + (float) getChildCountFatal()) / (float) getChildCount()
- : 0;
- return p * 100;
- }
-
- public float getChildPercentageOthers() {
- float p = getChildCount() > 0
- ? (((float) getChildCountWarning() + (float) getChildCountError() + (float) getChildCountSkip()
- + (float) getChildCountInfo()) / (float) getChildCount())
- : 0;
- return p * 100;
- }
-
- public float getChildPercentageSkip() {
- float p = getChildCount() > 0 ? (float) getChildCountSkip() / (float) getChildCount() : 0;
- return p * 100;
- }
-
- public int getGrandChildCount() {
- return getGrandChildCountPass() + getGrandChildCountFail() + getGrandChildCountFatal()
- + getGrandChildCountError() + getGrandChildCountWarning() + getGrandChildCountSkip()
- + getGrandChildCountInfo();
- }
-
- public int getGrandChildCountPass() {
- return grandChildPass;
- }
-
- public int getGrandChildCountFail() {
- return grandChildFail;
- }
-
- public int getGrandChildCountFatal() {
- return grandChildFatal;
- }
-
- public int getGrandChildCountError() {
- return grandChildError;
- }
-
- public int getGrandChildCountWarning() {
- return grandChildWarning;
- }
-
- public int getGrandChildCountSkip() {
- return grandChildSkip;
- }
-
- public int getGrandChildCountInfo() {
- return grandChildInfo;
- }
-
- public int getGrandChildCountDebug() {
- return grandChildDebug;
- }
-
- public int getGrandChildCountExceptions() {
- return grandChildExceptions;
- }
-
- public float getGrandChildPercentagePass() {
- float p = getGrandChildCount() > 0 ? (float) getGrandChildCountPass() / (float) getGrandChildCount() : 0;
- return p * 100;
- }
-
- public float getGrandChildPercentageFail() {
- float p = getGrandChildCount() > 0
- ? ((float) getGrandChildCountFail() + (float) getGrandChildCountFatal()) / (float) getGrandChildCount()
- : 0;
- return p * 100;
- }
-
- public float getGrandChildPercentageOthers() {
- float p = getGrandChildCount() > 0
- ? (((float) getGrandChildCountWarning() + (float) getGrandChildCountError()
- + (float) getGrandChildCountSkip() + (float) getGrandChildCountInfo())
- / (float) getGrandChildCount())
- : 0;
- return p * 100;
- }
-
- public float getGrandChildPercentageSkip() {
- float p = getGrandChildCount() > 0 ? (float) getGrandChildCountSkip() / (float) getGrandChildCount() : 0;
- return p * 100;
- }
-
- public int getEventsCount() {
- return getEventsCountPass() + getEventsCountFail() + getEventsCountFatal()
- + getEventsCountError() + getEventsCountWarning() + getEventsCountSkip()
- + getEventsCountInfo();
- }
-
- public int getEventsCountPass() {
- return eventsPass;
- }
-
- public int getEventsCountFail() {
- return eventsFail;
- }
-
- public int getEventsCountFatal() {
- return eventsFatal;
- }
-
- public int getEventsCountError() {
- return eventsError;
- }
-
- public int getEventsCountWarning() {
- return eventsWarning;
- }
-
- public int getEventsCountSkip() {
- return eventsSkip;
- }
-
- public int getEventsCountInfo() {
- return eventsInfo;
- }
-
- public int getEventsCountDebug() {
- return eventsDebug;
- }
-
- public int getEventsCountExceptions() {
- return eventsExceptions;
- }
-
- public float getEventsPercentagePass() {
- float p = getEventsCount() > 0 ? (float) getEventsCountPass() / (float) getEventsCount() : 0;
- return p * 100;
- }
-
- public float getEventsPercentageFail() {
- float p = getEventsCount() > 0
- ? ((float) getEventsCountFail() + (float) getEventsCountFatal()) / (float) getEventsCount()
- : 0;
- return p * 100;
- }
-
- public float getEventsPercentageOthers() {
- float p = getEventsCount() > 0
- ? (((float) getEventsCountWarning() + (float) getEventsCountError()
- + (float) getEventsCountSkip() + (float) getEventsCountInfo())
- / (float) getEventsCount())
- : 0;
- return p * 100;
- }
-
- public float getEventsPercentageSkip() {
- float p = getEventsCount() > 0 ? (float) getEventsCountSkip() / (float) getEventsCount() : 0;
- return p * 100;
- }
-
- private void refreshStats() {
- testList.forEach(this::addTestForStatusStatsUpdate);
- }
-
- private void addTestForStatusStatsUpdate(Test test) {
- updateEventsCount(test);
-
- if (test.getBddType() != null
- || (!test.getNodeContext().isEmpty() && test.getNodeContext().get(0).getBddType() != null)) {
- updateGroupCountsBDD(test);
- return;
- }
-
- if (strategy == AnalysisStrategy.TEST || strategy == AnalysisStrategy.CLASS) {
- updateGroupCountsTestStrategy(test);
- return;
- }
-
- if (strategy == AnalysisStrategy.SUITE) {
- updateGroupCountsSuiteStrategy(test);
- return;
- }
-
- throw new InvalidAnalysisStrategyException("No such strategy found: " + strategy);
- }
-
- private synchronized void updateEventsCount(Test test) {
- test.getLogContext().getAll().stream()
- .map(Log::getStatus)
- .forEach(this::incrementEvent);
- Iterator iter = test.getNodeContext().getIterator();
- while (iter.hasNext()) {
- Test node = iter.next();
- updateEventsCount(node);
- }
- }
-
- private void updateGroupCountsSuiteStrategy(Test test) {
- incrementItemCountByStatus(ItemLevel.PARENT, test.getStatus());
-
- if (!test.getNodeContext().isEmpty()) {
- for (Test x : test.getNodeContext().getAll()) {
- incrementItemCountByStatus(ItemLevel.CHILD, x.getStatus());
- if (!x.getNodeContext().isEmpty()) {
- x.getNodeContext().getAll()
- .forEach(n -> incrementItemCountByStatus(ItemLevel.GRANDCHILD, n.getStatus()));
- }
- }
- }
- }
-
- private void updateGroupCountsBDD(Test test) {
- incrementItemCountByStatus(ItemLevel.PARENT, test.getStatus());
-
- if (!test.getNodeContext().isEmpty()) {
- for (Test x : test.getNodeContext().getAll()) {
- if (x.getBddType() == Scenario.class) {
- incrementItemCountByStatus(ItemLevel.CHILD, x.getStatus());
- }
- if (!x.getNodeContext().isEmpty()) {
- for (Test n : x.getNodeContext().getAll()) {
- if (n.getBddType() == Scenario.class) {
- incrementItemCountByStatus(ItemLevel.CHILD, n.getStatus());
- n.getNodeContext().getAll()
- .forEach(z -> incrementItemCountByStatus(ItemLevel.GRANDCHILD, z.getStatus()));
- } else {
- incrementItemCountByStatus(ItemLevel.GRANDCHILD, n.getStatus());
- }
- }
- }
- }
- }
- }
-
- private void updateGroupCountsTestStrategy(Test test) {
- incrementItemCountByStatus(ItemLevel.PARENT, test.getStatus());
-
- if (!test.getNodeContext().isEmpty()) {
- updateGroupCountsTestStrategyChildren(test);
- }
- }
-
- private void updateGroupCountsTestStrategyChildren(Test test) {
- test.getNodeContext().getAll().forEach(x -> {
- if (x.getNodeContext().isEmpty()) {
- incrementItemCountByStatus(ItemLevel.CHILD, x.getStatus());
- } else {
- updateGroupCountsTestStrategyChildren(x);
- }
- });
- }
-
- enum ItemLevel {
- PARENT, CHILD, GRANDCHILD
- }
-
- private void incrementItemCountByStatus(ItemLevel item, Status status) {
- switch (item) {
- case PARENT:
- incrementParent(status);
- break;
- case CHILD:
- incrementChild(status);
- break;
- case GRANDCHILD:
- incrementGrandChild(status);
- break;
- default:
- break;
- }
- }
-
- private void incrementParent(Status status) {
- switch (status) {
- case PASS:
- parentPass++;
- return;
- case FAIL:
- parentFail++;
- break;
- case FATAL:
- parentFatal++;
- break;
- case ERROR:
- parentError++;
- break;
- case WARNING:
- parentWarning++;
- break;
- case SKIP:
- parentSkip++;
- break;
- default:
- break;
- }
-
- parentExceptions++;
- }
-
- private void incrementChild(Status status) {
- switch (status) {
- case PASS:
- childPass++;
- break;
- case FAIL:
- childFail++;
- break;
- case FATAL:
- childFatal++;
- break;
- case ERROR:
- childError++;
- break;
- case WARNING:
- childWarning++;
- break;
- case SKIP:
- childSkip++;
- break;
- case INFO:
- childInfo++;
- break;
- case DEBUG:
- childDebug++;
- break;
- default:
- break;
- }
-
- if (status != Status.PASS && status != Status.INFO)
- childExceptions++;
- }
-
- private void incrementGrandChild(Status status) {
- switch (status) {
- case PASS:
- grandChildPass++;
- break;
- case FAIL:
- grandChildFail++;
- break;
- case FATAL:
- grandChildFatal++;
- break;
- case ERROR:
- grandChildError++;
- break;
- case WARNING:
- grandChildWarning++;
- break;
- case SKIP:
- grandChildSkip++;
- break;
- case INFO:
- grandChildInfo++;
- break;
- case DEBUG:
- grandChildDebug++;
- break;
- default:
- break;
- }
-
- if (status != Status.PASS && status != Status.INFO)
- grandChildExceptions++;
- }
-
- private void incrementEvent(Status status) {
- switch (status) {
- case PASS:
- eventsPass++;
- break;
- case FAIL:
- eventsFail++;
- break;
- case FATAL:
- eventsFatal++;
- break;
- case ERROR:
- eventsError++;
- break;
- case WARNING:
- eventsWarning++;
- break;
- case SKIP:
- eventsSkip++;
- break;
- case INFO:
- eventsInfo++;
- break;
- case DEBUG:
- eventsDebug++;
- break;
- default:
- break;
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/RunResult.java b/src/main/java/com/aventstack/extentreports/RunResult.java
deleted file mode 100644
index 426197f..0000000
--- a/src/main/java/com/aventstack/extentreports/RunResult.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.aventstack.extentreports;
-/**
- * Marker interface for the run result with a single getStatus() method
- *
- */
-public interface RunResult {
-
- Status getStatus();
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/SourceProvider.java b/src/main/java/com/aventstack/extentreports/SourceProvider.java
deleted file mode 100644
index eccc815..0000000
--- a/src/main/java/com/aventstack/extentreports/SourceProvider.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.aventstack.extentreports;
-
-/**
- * Some formatters can provide the source which is also written to file,
- * for example, Email formatters or reporters. Along with writing the contents
- * to a file, you can retrieve the source using which an email can be sent.
- */
-public interface SourceProvider {
-
- String getSource();
-
-}
diff --git a/src/main/java/com/aventstack/extentreports/Status.java b/src/main/java/com/aventstack/extentreports/Status.java
index f4ca613..cf3e389 100644
--- a/src/main/java/com/aventstack/extentreports/Status.java
+++ b/src/main/java/com/aventstack/extentreports/Status.java
@@ -1,97 +1,62 @@
package com.aventstack.extentreports;
-import java.io.Serializable;
-import java.util.Arrays;
+
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.List;
-/**
- * List of allowed status for {@link com.aventstack.extentreports.model.Log}
- */
-public enum Status implements Serializable {
- PASS,
- FAIL,
- FATAL,
- ERROR,
- WARNING,
- INFO,
- DEBUG,
- SKIP;
+import com.aventstack.extentreports.view.Ico;
+
+import lombok.Getter;
+
+@Getter
+public enum Status {
+ INFO("Info", 10), PASS("Pass", 20), SKIP("Skip", 30), WARNING("Warning", 40), FAIL("Fail", 50);
+
+ private final Integer level;
+ private final String name;
+
+ Status(String name, Integer level) {
+ this.name = name;
+ this.level = level;
+ }
+
+ private static void resolveHierarchy(List status) {
+ status.sort((Status s1, Status s2) -> s1.getLevel().compareTo(s2.getLevel()));
+ }
+
+ public static List getResolvedHierarchy(List status) {
+ List list = new ArrayList<>(status);
+ resolveHierarchy(list);
+ return list;
+ }
- private static List statusHierarchy = Arrays.asList(
- Status.FATAL,
- Status.FAIL,
- Status.ERROR,
- Status.WARNING,
- Status.SKIP,
- Status.PASS,
- Status.INFO,
- Status.DEBUG
- );
-
- /**
- * Returns the hierarchical list of status, in the below order:
- *
- *
- *
FATAL
- *
FAIL
- *
ERROR
- *
WARNING
- *
SKIP
- *
PASS
- *
DEBUG
- *
INFO
- *
- *
- * @return Hierarchical list of status
- */
- public static List getStatusHierarchy() {
- return statusHierarchy;
+ public static Status max(Collection status) {
+ return status.stream().max(Comparator.comparing(Status::getLevel)).orElse(PASS);
}
-
- public static Status getHighestStatus(Collection statusCollection) {
- Status highestStatus = Status.PASS;
- if (statusCollection == null || statusCollection.isEmpty()) {
- return highestStatus;
- }
- for (Status status : statusCollection) {
- highestStatus = Status.getStatusHierarchy().indexOf(status) < Status.getStatusHierarchy().indexOf(highestStatus)
- ? status
- : highestStatus;
- }
- return highestStatus;
+
+ public static Status max(Status s1, Status s2) {
+ return s1.getLevel() > s2.getLevel() ? s1 : s2;
}
-
- static void setStatusHierarchy(List statusHierarchy) {
- Status.statusHierarchy = statusHierarchy;
+
+ public static Status min(Collection status) {
+ return status.stream().min(Comparator.comparing(Status::getLevel)).orElse(PASS);
}
-
- static void resetStatusHierarchy() {
- List statusHierarchy = Arrays.asList(
- Status.FATAL,
- Status.FAIL,
- Status.ERROR,
- Status.WARNING,
- Status.SKIP,
- Status.PASS,
- Status.INFO,
- Status.DEBUG
- );
-
- setStatusHierarchy(statusHierarchy);
+
+ public static String i(String status) {
+ return Ico.ico(status).toString();
}
-
+
+ public static String i(Status status) {
+ return Ico.ico(status.toString()).toString();
+ }
+
+ public String toLower() {
+ return name.toLowerCase();
+ }
+
@Override
public String toString() {
- switch (this) {
- case PASS: return "pass";
- case FAIL: return "fail";
- case FATAL: return "fatal";
- case ERROR: return "error";
- case WARNING: return "warning";
- case INFO: return "info";
- case DEBUG: return "debug";
- case SKIP: return "skip";
- default: return "unknown";
- }
+ return name;
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/aventstack/extentreports/StatusConfigurator.java b/src/main/java/com/aventstack/extentreports/StatusConfigurator.java
deleted file mode 100644
index 9e5cc6a..0000000
--- a/src/main/java/com/aventstack/extentreports/StatusConfigurator.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.util.List;
-
-public class StatusConfigurator {
-
- private static class StatusConfiguratorInstance {
- static final StatusConfigurator INSTANCE = new StatusConfigurator();
-
- private StatusConfiguratorInstance() {
- }
- }
-
- private StatusConfigurator() {
- }
-
- public static StatusConfigurator getInstance() {
- return StatusConfiguratorInstance.INSTANCE;
- }
-
- public List getStatusHierarchy() {
- return Status.getStatusHierarchy();
- }
-
- public void setStatusHierarchy(List statusHierarchy) {
- Status.setStatusHierarchy(statusHierarchy);
- }
-
- public void resetStatusHierarchy() {
- Status.resetStatusHierarchy();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/TestListener.java b/src/main/java/com/aventstack/extentreports/TestListener.java
deleted file mode 100644
index 5201df4..0000000
--- a/src/main/java/com/aventstack/extentreports/TestListener.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.aventstack.extentreports;
-
-import java.io.IOException;
-
-import com.aventstack.extentreports.model.Author;
-import com.aventstack.extentreports.model.Category;
-import com.aventstack.extentreports.model.Device;
-import com.aventstack.extentreports.model.Log;
-import com.aventstack.extentreports.model.ScreenCapture;
-import com.aventstack.extentreports.model.Test;
-
-/**
- * Listener methods invoked when an event occurs in the report:
- *
- *
- *
A test or node is started
- *
A category or author is added
- *
A media object is added etc.
- *
- */
-public interface TestListener {
-
- /**
- * Invoked when a test is started using createTest()
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- */
- void onTestStarted(Test test);
-
- /**
- * Invoked when a test is removed using removeTest()
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- */
- void onTestRemoved(Test test);
-
- /**
- * Invoked when a node is started using createNode()
- *
- * @param node {@link com.aventstack.extentreports.model.Test} object
- */
- void onNodeStarted(Test node);
-
- /**
- * Invoked each time a log is added to any test/node
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- * @param log {@link com.aventstack.extentreports.model.Log} object
- */
- void onLogAdded(Test test, Log log);
-
- /**
- * Invoked each time a category is assigned to any test/node
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- * @param category {@link com.aventstack.extentreports.model.Category} object
- */
- void onCategoryAssigned(Test test, Category category);
-
- /**
- * Invoked each time an author is assigned to any test/node
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- * @param author {@link com.aventstack.extentreports.model.Author} object
- */
- void onAuthorAssigned(Test test, Author author);
-
- /**
- * Invoked each time a device is assigned to any test/node
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- * @param device {@link com.aventstack.extentreports.model.Device} object
- */
- void onDeviceAssigned(Test test, Device device);
-
- /**
- * Invoked each time a screencapture is added to test
- *
- * @param test {@link com.aventstack.extentreports.model.Test} object
- * @param screenCapture {@link com.aventstack.extentreports.model.ScreenCapture}
- * object
- * @throws IOException Exception thrown if the media object is not found
- */
- void onScreenCaptureAdded(Test test, ScreenCapture screenCapture) throws IOException;
-
- /**
- * Invoked each time a screencapture is added to log
- *
- * @param log {@link com.aventstack.extentreports.model.Log} object
- * @param screenCapture {@link com.aventstack.extentreports.model.ScreenCapture}
- * object
- * @throws IOException Exception thrown if the media object is not found
- */
- void onScreenCaptureAdded(Log log, ScreenCapture screenCapture) throws IOException;
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/Writable.java b/src/main/java/com/aventstack/extentreports/Writable.java
new file mode 100644
index 0000000..22e187a
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/Writable.java
@@ -0,0 +1,6 @@
+package com.aventstack.extentreports;
+
+@FunctionalInterface
+public interface Writable {
+ void flush();
+}
diff --git a/src/main/java/com/aventstack/extentreports/annotations/MarkupIgnore.java b/src/main/java/com/aventstack/extentreports/annotations/MarkupIgnore.java
new file mode 100644
index 0000000..1ead655
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/annotations/MarkupIgnore.java
@@ -0,0 +1,11 @@
+package com.aventstack.extentreports.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface MarkupIgnore {
+}
diff --git a/src/main/java/com/aventstack/extentreports/annotations/MarkupInclude.java b/src/main/java/com/aventstack/extentreports/annotations/MarkupInclude.java
new file mode 100644
index 0000000..ee510a0
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/annotations/MarkupInclude.java
@@ -0,0 +1,11 @@
+package com.aventstack.extentreports.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface MarkupInclude {
+}
diff --git a/src/main/java/com/aventstack/extentreports/append/JsonDeserializer.java b/src/main/java/com/aventstack/extentreports/append/JsonDeserializer.java
new file mode 100644
index 0000000..71e1c4d
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/append/JsonDeserializer.java
@@ -0,0 +1,33 @@
+package com.aventstack.extentreports.append;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.aventstack.extentreports.gson.GsonExtentTypeAdapterBuilder;
+import com.aventstack.extentreports.model.Test;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+public class JsonDeserializer {
+ private File f;
+
+ public JsonDeserializer(File f) {
+ this.f = f;
+ }
+
+ public List deserialize() throws IOException {
+ Gson gson = GsonExtentTypeAdapterBuilder.builder()
+ .withBddTypeAdapterFactory()
+ .withScreenCaptureTypeAdapter()
+ .build();
+ String json = new String(Files.readAllBytes(f.toPath()));
+ Type t = new TypeToken>() {
+ }.getType();
+ List tests = gson.fromJson(json, t);
+ return tests;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/append/RawEntityConverter.java b/src/main/java/com/aventstack/extentreports/append/RawEntityConverter.java
new file mode 100644
index 0000000..7598b90
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/append/RawEntityConverter.java
@@ -0,0 +1,101 @@
+package com.aventstack.extentreports.append;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import com.aventstack.extentreports.ExtentReports;
+import com.aventstack.extentreports.ExtentTest;
+import com.aventstack.extentreports.GherkinKeyword;
+import com.aventstack.extentreports.MediaEntityBuilder;
+import com.aventstack.extentreports.model.Log;
+import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.ScreenCapture;
+import com.aventstack.extentreports.model.Test;
+
+public class RawEntityConverter {
+ private final ExtentReports extent;
+
+ public RawEntityConverter(ExtentReports extent) {
+ this.extent = extent;
+ }
+
+ public void convertAndApply(File jsonFile) throws IOException {
+ if (!jsonFile.exists())
+ return;
+ extent.setReportUsesManualConfiguration(true);
+ List tests = new JsonDeserializer(jsonFile).deserialize();
+ for (Test test : tests) {
+ try {
+ if (test.getBddType() == null) {
+ createDomain(test, extent.createTest(test.getName(), test.getDescription()));
+ } else {
+ ExtentTest extentTest = extent.createTest(new GherkinKeyword(test.getBddType().getSimpleName()),
+ test.getName(), test.getDescription());
+ createDomain(test, extentTest);
+ }
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void createDomain(Test test, ExtentTest extentTest) throws ClassNotFoundException {
+ extentTest.getModel().setStartTime(test.getStartTime());
+ extentTest.getModel().setEndTime(test.getEndTime());
+ addMedia(test, extentTest);
+
+ // create events
+ for (Log log : test.getLogs()) {
+ if (log.hasException() && log.hasMedia())
+ addMedia(log, extentTest, log.getException().getException());
+ else if (log.hasException())
+ extentTest.log(log.getStatus(), log.getException().getException());
+ else if (log.hasMedia())
+ addMedia(log, extentTest, null);
+ else
+ extentTest.log(log.getStatus(), log.getDetails());
+ }
+
+ // assign attributes
+ test.getAuthorSet().stream().map(x -> x.getName()).forEach(extentTest::assignAuthor);
+ test.getCategorySet().stream().map(x -> x.getName()).forEach(extentTest::assignCategory);
+ test.getDeviceSet().stream().map(x -> x.getName()).forEach(extentTest::assignDevice);
+
+ // handle nodes
+ for (Test node : test.getChildren()) {
+ ExtentTest extentNode = null;
+ if (node.getBddType() == null)
+ extentNode = extentTest.createNode(node.getName(), node.getDescription());
+ else
+ extentNode = extentTest.createNode(new GherkinKeyword(node.getBddType().getSimpleName()), node.getName(),
+ node.getDescription());
+ addMedia(node, extentNode);
+ createDomain(node, extentNode);
+ }
+ }
+
+ private void addMedia(Log log, ExtentTest extentTest, Throwable ex) {
+ Media m = log.getMedia();
+ if (m.getPath() != null) {
+ extentTest.log(log.getStatus(), ex,
+ MediaEntityBuilder.createScreenCaptureFromPath(m.getPath()).build());
+ } else if (((ScreenCapture) m).getBase64() != null) {
+ extentTest.log(log.getStatus(), ex,
+ MediaEntityBuilder.createScreenCaptureFromBase64String(((ScreenCapture) m).getBase64())
+ .build());
+ }
+ }
+
+ private void addMedia(Test test, ExtentTest extentTest) {
+ if (test.getMedia() != null) {
+ for (Media m : test.getMedia()) {
+ if (m.getPath() != null) {
+ extentTest.addScreenCaptureFromPath(m.getPath());
+ } else if (m instanceof ScreenCapture) {
+ extentTest.addScreenCaptureFromBase64String(((ScreenCapture) m).getBase64());
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/append/ScreenCaptureTypeAdapter.java b/src/main/java/com/aventstack/extentreports/append/ScreenCaptureTypeAdapter.java
new file mode 100644
index 0000000..55e6c1e
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/append/ScreenCaptureTypeAdapter.java
@@ -0,0 +1,48 @@
+package com.aventstack.extentreports.append;
+
+import java.io.IOException;
+
+import com.aventstack.extentreports.model.Media;
+import com.aventstack.extentreports.model.ScreenCapture;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+public class ScreenCaptureTypeAdapter extends TypeAdapter {
+
+ @Override
+ public void write(JsonWriter out, Media value) throws IOException {
+ }
+
+ @Override
+ public Media read(JsonReader reader) throws IOException {
+ ScreenCapture sc = ScreenCapture.builder().build();
+ reader.beginObject();
+ String fieldName = null;
+ int cycle = 0;
+ while (reader.hasNext()) {
+ JsonToken token = reader.peek();
+ if (token.equals(JsonToken.NAME)) {
+ fieldName = reader.nextName();
+ }
+ if ("string".equalsIgnoreCase(token.name()) && fieldName.equalsIgnoreCase("path")) {
+ token = reader.peek();
+ sc.setPath(reader.nextString());
+ }
+ if ("string".equalsIgnoreCase(token.name()) && fieldName.equalsIgnoreCase("resolvedPath")) {
+ token = reader.peek();
+ sc.setResolvedPath(reader.nextString());
+ }
+ if ("string".equalsIgnoreCase(token.name()) && fieldName.equalsIgnoreCase("base64")) {
+ token = reader.peek();
+ sc.setBase64(reader.nextString());
+ }
+ if (cycle++ > 10)
+ return sc;
+ }
+ reader.endObject();
+ return sc;
+ }
+
+}
diff --git a/src/main/java/com/aventstack/extentreports/config/ConfigStore.java b/src/main/java/com/aventstack/extentreports/config/ConfigStore.java
new file mode 100644
index 0000000..61f7614
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/config/ConfigStore.java
@@ -0,0 +1,41 @@
+package com.aventstack.extentreports.config;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ConfigStore {
+ private Map store = new HashMap();
+
+ public void addConfig(String key, Object value) {
+ store.put(key, value);
+ }
+
+ public boolean containsConfig(String k) {
+ return store.containsKey(k);
+ }
+
+ public void removeConfig(String k) {
+ store.remove(k);
+ }
+
+ public Object getConfig(String k) {
+ return store.get(k);
+ }
+
+ public void extendConfig(Map map) {
+ map.forEach((key, value) -> this.addConfig(key, value));
+ }
+
+ public void extendConfig(ConfigStore configMap) {
+ this.extendConfig(configMap.getStore());
+ }
+
+ public boolean isEmpty() {
+ return store.isEmpty();
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/config/external/CaseInsensitiveEnumTypeAdapterFactory.java b/src/main/java/com/aventstack/extentreports/config/external/CaseInsensitiveEnumTypeAdapterFactory.java
new file mode 100644
index 0000000..bc27424
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/config/external/CaseInsensitiveEnumTypeAdapterFactory.java
@@ -0,0 +1,52 @@
+package com.aventstack.extentreports.config.external;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+public class CaseInsensitiveEnumTypeAdapterFactory implements TypeAdapterFactory {
+ @SuppressWarnings("unchecked")
+ public TypeAdapter create(Gson gson, TypeToken type) {
+ Class rawType = (Class) type.getRawType();
+ if (!rawType.isEnum()) {
+ return null;
+ }
+
+ final Map lowercaseToConstant = new HashMap();
+ for (T constant : rawType.getEnumConstants()) {
+ lowercaseToConstant.put(toLowercase(constant), constant);
+ }
+
+ return new TypeAdapter() {
+ public void write(JsonWriter out, T value) throws IOException {
+ if (value == null) {
+ out.nullValue();
+ } else {
+ out.value(toLowercase(value));
+ }
+ }
+
+ public T read(JsonReader reader) throws IOException {
+ if (reader.peek() == JsonToken.NULL) {
+ reader.nextNull();
+ return null;
+ } else {
+ return lowercaseToConstant.get(toLowercase(reader.nextString()));
+ }
+ }
+ };
+ }
+
+ private String toLowercase(Object o) {
+ return o.toString().toLowerCase(Locale.US);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/config/external/ConfigLoadable.java b/src/main/java/com/aventstack/extentreports/config/external/ConfigLoadable.java
new file mode 100644
index 0000000..9c88256
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/config/external/ConfigLoadable.java
@@ -0,0 +1,6 @@
+package com.aventstack.extentreports.config.external;
+
+@FunctionalInterface
+public interface ConfigLoadable {
+ void apply();
+}
diff --git a/src/main/java/com/aventstack/extentreports/config/external/JsonConfigLoader.java b/src/main/java/com/aventstack/extentreports/config/external/JsonConfigLoader.java
new file mode 100644
index 0000000..1275934
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/config/external/JsonConfigLoader.java
@@ -0,0 +1,58 @@
+package com.aventstack.extentreports.config.external;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import org.testng.reporters.Files;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.InstanceCreator;
+
+public class JsonConfigLoader implements ConfigLoadable {
+ private File f;
+ private String json;
+ private T instance;
+ private InstanceCreator creator;
+
+ public JsonConfigLoader(T instance, File f) throws FileNotFoundException {
+ if (f == null)
+ throw new IllegalArgumentException("File cannot be null");
+ if (!f.exists())
+ throw new FileNotFoundException("File " + f.getAbsolutePath() + " could not be found");
+ init(instance);
+ this.f = f;
+ }
+
+ public JsonConfigLoader(T instance, String json) {
+ if (json == null || json.isEmpty())
+ throw new IllegalArgumentException("Json input cannot be null or empty");
+ init(instance);
+ this.json = json;
+ }
+
+ private void init(T instance) {
+ this.instance = instance;
+ creator = new InstanceCreator() {
+ @Override
+ public T createInstance(Type type) {
+ return instance;
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ public void apply() {
+ final Gson gson = new GsonBuilder()
+ .registerTypeAdapter(instance.getClass(), creator)
+ .create();
+ try {
+ String json = f != null ? Files.readFile(f) : this.json;
+ instance = (T) gson.fromJson(json, instance.getClass());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/config/external/XmlConfigLoader.java b/src/main/java/com/aventstack/extentreports/config/external/XmlConfigLoader.java
new file mode 100644
index 0000000..60ec21d
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/config/external/XmlConfigLoader.java
@@ -0,0 +1,86 @@
+package com.aventstack.extentreports.config.external;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.CharacterData;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.aventstack.extentreports.config.ConfigStore;
+import com.google.gson.Gson;
+
+public class XmlConfigLoader implements ConfigLoadable {
+ private static final Logger LOG = Logger.getLogger(XmlConfigLoader.class.getName());
+
+ private ConfigStore store = new ConfigStore();
+ private InputStream stream;
+ private T instance;
+
+ public XmlConfigLoader(T instance, File f) throws FileNotFoundException {
+ createStream(f);
+ this.instance = instance;
+ }
+
+ private void createStream(File file) {
+ try {
+ stream = new FileInputStream(file);
+ } catch (FileNotFoundException e) {
+ LOG.log(Level.SEVERE, file.getPath(), e);
+ }
+ }
+
+ public void apply() {
+ ConfigStore store = getConfigStore();
+ Gson gson = new Gson();
+ String json = gson.toJson(store.getStore());
+ JsonConfigLoader jsonLoader = new JsonConfigLoader(instance, json);
+ jsonLoader.apply();
+ }
+
+ public ConfigStore getConfigStore() {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder;
+ String value;
+
+ try {
+ documentBuilder = documentBuilderFactory.newDocumentBuilder();
+
+ Document doc = documentBuilder.parse(stream);
+ doc.getDocumentElement().normalize();
+
+ NodeList nodeList = doc.getElementsByTagName("configuration").item(0).getChildNodes();
+
+ for (int ix = 0; ix < nodeList.getLength(); ix++) {
+ Node node = nodeList.item(ix);
+
+ Element el = node.getNodeType() == Node.ELEMENT_NODE ? (Element) node : null;
+
+ if (el != null) {
+ value = el.getTextContent();
+
+ value = el instanceof CharacterData ? ((CharacterData) el).getData() : value;
+ store.addConfig(el.getNodeName(), value);
+ }
+ }
+
+ return store;
+ } catch (IOException | SAXException | ParserConfigurationException e) {
+ LOG.log(Level.SEVERE, "Failed to load external configuration", e);
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/configuration/ConfigurationBuilder.java b/src/main/java/com/aventstack/extentreports/configuration/ConfigurationBuilder.java
deleted file mode 100644
index c66ec17..0000000
--- a/src/main/java/com/aventstack/extentreports/configuration/ConfigurationBuilder.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.aventstack.extentreports.configuration;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.w3c.dom.CharacterData;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
-public class ConfigurationBuilder {
-
- private static final Logger logger = Logger.getLogger(ConfigurationBuilder.class.getName());
-
- private ConfigurationStore store = new ConfigurationStore();
- private InputStream stream;
-
- public ConfigurationBuilder(URL url) {
- try {
- stream = url.openStream();
- } catch (IOException e) {
- logger.log(Level.SEVERE, url.toString(), e);
- }
- }
-
- public ConfigurationBuilder(File file, Boolean silent) {
- try {
- if (silent && !file.exists())
- return;
- stream = new FileInputStream(file);
- } catch (FileNotFoundException e) {
- logger.log(Level.SEVERE, file.getPath(), e);
- }
- }
-
- public ConfigurationStore getConfigurationStore() {
- if (stream == null)
- return null;
-
- DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder documentBuilder;
- String value;
-
- try {
- documentBuilder = documentBuilderFactory.newDocumentBuilder();
-
- Document doc = documentBuilder.parse(stream);
- doc.getDocumentElement().normalize();
-
- NodeList nodeList = doc.getElementsByTagName("configuration").item(0).getChildNodes();
-
- for (int ix = 0; ix < nodeList.getLength(); ix++) {
- Node node = nodeList.item(ix);
-
- Element el = node.getNodeType() == Node.ELEMENT_NODE ? (Element) node : null;
-
- if (el != null) {
- value = el.getTextContent();
-
- value = el instanceof CharacterData ? ((CharacterData) el).getData() : value;
- store.storeConfig(el.getNodeName(), value);
- }
- }
-
- return store;
- } catch (IOException | SAXException | ParserConfigurationException e) {
- logger.log(Level.SEVERE, "Failed to load external configuration", e);
- }
-
- return null;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/configuration/ConfigurationStore.java b/src/main/java/com/aventstack/extentreports/configuration/ConfigurationStore.java
deleted file mode 100644
index 51e2acb..0000000
--- a/src/main/java/com/aventstack/extentreports/configuration/ConfigurationStore.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.aventstack.extentreports.configuration;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class ConfigurationStore {
-
- private Map store = new HashMap();
-
- public Map getStore() {
- return store;
- }
-
- public void storeConfig(String key, Object value) {
- store.put(key, value);
- }
-
- public boolean containsConfig(String k) {
- return store.containsKey(k);
- }
-
- public void removeConfig(String k) {
- store.remove(k);
- }
-
- public Object getConfig(String k) {
- return store.containsKey(k) ? store.get(k) : null;
- }
-
- public void extendConfig(Map map) {
- map.forEach((key, value) -> this.storeConfig(key, value));
- }
-
- public void extendConfig(ConfigurationStore config) {
- Map map = config.store;
- this.extendConfig(map);
- }
-
- public boolean isEmpty() {
- return store.isEmpty();
- }
-
-}
diff --git a/src/main/java/com/aventstack/extentreports/convert/JsonDeserializer.java b/src/main/java/com/aventstack/extentreports/convert/JsonDeserializer.java
deleted file mode 100644
index 669f6d0..0000000
--- a/src/main/java/com/aventstack/extentreports/convert/JsonDeserializer.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.aventstack.extentreports.convert;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Type;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.aventstack.extentreports.model.Test;
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
-
-class JsonDeserializer {
-
- public static List deserialize(File f) throws IOException {
- Gson gson = new Gson();
- String json = new String(Files.readAllBytes(f.toPath()));
- Type t = new TypeToken>() {}.getType();
- List tests = gson.fromJson(json, t);
- return tests;
- }
-
-}
diff --git a/src/main/java/com/aventstack/extentreports/convert/TestModelReportBuilder.java b/src/main/java/com/aventstack/extentreports/convert/TestModelReportBuilder.java
deleted file mode 100644
index eeef8a9..0000000
--- a/src/main/java/com/aventstack/extentreports/convert/TestModelReportBuilder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.aventstack.extentreports.convert;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-import com.aventstack.extentreports.ExtentReports;
-import com.aventstack.extentreports.ExtentTest;
-import com.aventstack.extentreports.GherkinKeyword;
-import com.aventstack.extentreports.model.Log;
-import com.aventstack.extentreports.model.Test;
-
-public class TestModelReportBuilder {
-
- public void createDomainFromJsonArchive(ExtentReports extent, File jsonFile) throws IOException {
- if (!jsonFile.exists()) {
- return;
- }
- Boolean configChanged = extent.getReportUsesManualConfiguration() ? false : true;
- extent.setReportUsesManualConfiguration(true);
- List tests = JsonDeserializer.deserialize(jsonFile);
- for (Test test : tests) {
- try {
- if (test.getBehaviorDrivenTypeName() == null) {
- createDomain(test, extent.createTest(test.getName(), test.getDescription()));
- } else {
- createDomain(test, extent.createTest(new GherkinKeyword(test.getBehaviorDrivenTypeName()),
- test.getName(), test.getDescription()));
-
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- if (configChanged) {
- extent.setReportUsesManualConfiguration(false);
- }
- }
-
- public void createDomain(Test test, ExtentTest extentTest) throws ClassNotFoundException {
- extentTest.getModel().setStartTime(test.getStartTime());
- extentTest.getModel().setEndTime(test.getEndTime());
- extentTest.getModel().computeEndTimeFromChildren();
-
- // create events
- for (Log log : test.getLogContext().getAll()) {
- if (log.getDetails() != null)
- extentTest.log(log.getStatus(), log.getDetails());
- if (log.getExceptionInfo() != null)
- extentTest.log(log.getStatus(), log.getExceptionInfo());
- }
-
- // assign attributes
- test.getAuthorContext().getAll().forEach(x -> extentTest.assignAuthor(x.getName()));
- test.getCategoryContext().getAll().forEach(x -> extentTest.assignCategory(x.getName()));
- test.getDeviceContext().getAll().forEach(x -> extentTest.assignDevice(x.getName()));
-
- // handle nodes
- for (Test node : test.getNodeContext().getAll()) {
- ExtentTest extentNode = null;
- if (node.getBehaviorDrivenTypeName() == null) {
- extentNode = extentTest.createNode(node.getName(), node.getDescription());
- } else {
- extentNode = extentTest.createNode(new GherkinKeyword(node.getBehaviorDrivenTypeName()), node.getName(),
- node.getDescription());
- }
- createDomain(node, extentNode);
- }
- }
-
-}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialect.java b/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialect.java
index 37b51a5..d209f79 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialect.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialect.java
@@ -3,6 +3,9 @@
import java.util.List;
import java.util.Map;
+import lombok.Getter;
+import lombok.ToString;
+
/**
*
* Modified version of GherkinKeyword.java from cucumber/gherkin. Source url:
@@ -12,24 +15,16 @@
* Gherkin source is licensed under the MIT License
*
*/
+@Getter
+@ToString
public class GherkinDialect {
-
- private final Map> keywords;
- private String language;
-
- public GherkinDialect(String language, Map> keywords) {
- keywords.remove("name");
- keywords.remove("native");
- this.language = language;
- this.keywords = keywords;
- }
-
- public Map> getKeywords() {
- return keywords;
- }
+ private final Map> keywords;
+ private String language;
- public String getLanguage() {
- return language;
- }
-
-}
\ No newline at end of file
+ public GherkinDialect(String language, Map> keywords) {
+ keywords.remove("name");
+ keywords.remove("native");
+ this.language = language;
+ this.keywords = keywords;
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialectManager.java b/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialectManager.java
new file mode 100644
index 0000000..6ca1efb
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialectManager.java
@@ -0,0 +1,78 @@
+package com.aventstack.extentreports.gherkin;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.gson.Gson;
+
+/**
+ *
+ * Modified version of GherkinDialectProvider.java from cucumber/gherkin. Source
+ * url:
+ * https://github.com/cucumber/cucumber/blob/master/gherkin/java/src/main/java/gherkin/GherkinDialectProvider.java.
+ *
+ *
+ * Gherkin source is licensed under the MIT License
+ *
+ */
+@SuppressWarnings("unchecked")
+public class GherkinDialectManager {
+ private static final Logger LOG = Logger.getLogger(GherkinDialectManager.class.getName());
+ private static final String DEFAULT_LANGUAGE = "en";
+ private static final String GHERKIN_LANGUAGES_JSON_URL = "https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json";
+ private static final String GHERKIN_LANGUAGES_PATH = "gherkin-languages.json";
+
+ private static GherkinDialect currentDialect;
+ private static Map>> dialects;
+ private static String language;
+
+ static {
+ Gson gson = new Gson();
+ try {
+ Reader d = new InputStreamReader(GherkinDialectManager.class.getResourceAsStream(GHERKIN_LANGUAGES_PATH),
+ "UTF-8");
+ dialects = gson.fromJson(d, Map.class);
+ } catch (UnsupportedEncodingException e) {
+ LOG.log(Level.SEVERE, "Unable to parse Gherkin languages file. Either the file is missing or corrupted.", e);
+ }
+ }
+
+ public static GherkinDialect getDialect() {
+ return currentDialect;
+ }
+
+ public static String getDefaultLanguage() {
+ return DEFAULT_LANGUAGE;
+ }
+
+ public static String getLanguage() {
+ if (language == null || language.isEmpty())
+ language = DEFAULT_LANGUAGE;
+ return language;
+ }
+
+ /**
+ * Sets/changes the default language
+ *
+ * @param lang
+ * A valid dialect from
+ * https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json
+ *
+ * @throws UnsupportedEncodingException
+ * Thrown if the language is one of the supported language from
+ * https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json
+ */
+ public static void setLanguage(String lang) throws UnsupportedEncodingException {
+ language = lang;
+ Map> map = dialects.get(GherkinDialectManager.language);
+ if (map == null)
+ throw new UnsupportedEncodingException("Invalid language [" + language
+ + "]. See list of supported languages: " + GHERKIN_LANGUAGES_JSON_URL);
+ currentDialect = new GherkinDialect(language, map);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialectProvider.java b/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialectProvider.java
deleted file mode 100644
index 4cc01cc..0000000
--- a/src/main/java/com/aventstack/extentreports/gherkin/GherkinDialectProvider.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.aventstack.extentreports.gherkin;
-
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
-import java.util.List;
-import java.util.Map;
-
-import com.google.gson.Gson;
-
-/**
- *
- * Modified version of GherkinDialectProvider.java from cucumber/gherkin. Source
- * url:
- * https://github.com/cucumber/cucumber/blob/master/gherkin/java/src/main/java/gherkin/GherkinDialectProvider.java.
- *
- *
- * Gherkin source is licensed under the MIT License
- *
- */
-@SuppressWarnings("unchecked")
-public class GherkinDialectProvider {
-
- private static final String DEFAULT_LANGUAGE = "en";
- private static final String GHERKIN_LANGUAGES_JSON_URL = "https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json";
- private static final String GHERKIN_LANGUAGES_PATH = "gherkin-languages.json";
-
- private static GherkinDialect currentDialect;
- private static Map>> dialects;
- private static String language;
-
- static {
- Gson gson = new Gson();
- try {
- Reader d = new InputStreamReader(GherkinDialectProvider.class.getResourceAsStream(GHERKIN_LANGUAGES_PATH),
- "UTF-8");
- dialects = gson.fromJson(d, Map.class);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static GherkinDialect getDialect() {
- return currentDialect;
- }
-
- public static String getDefaultLanguage() {
- return DEFAULT_LANGUAGE;
- }
-
- public static String getLanguage() {
- if (language == null || language.isEmpty()) {
- language = DEFAULT_LANGUAGE;
- }
- return language;
- }
-
- /**
- * Sets/changes the default language
- *
- * @param lang A valid dialect from
- * https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json
- *
- * @throws UnsupportedEncodingException Thrown if the language is one of the
- * supported language from
- * https://github.com/cucumber/cucumber/blob/master/gherkin/gherkin-languages.json
- */
- public static void setLanguage(String lang) throws UnsupportedEncodingException {
- language = lang;
- Map> map = dialects.get(GherkinDialectProvider.language);
- if (map == null) {
- throw new UnsupportedEncodingException("Invalid language [" + language + "]. See list of supported languages: " + GHERKIN_LANGUAGES_JSON_URL);
- }
- currentDialect = new GherkinDialect(language, map);
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/And.java b/src/main/java/com/aventstack/extentreports/gherkin/model/And.java
index c2a2bff..c81d896 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/And.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/And.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class And implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 8543289653944756660L;
+ private static final String VALUE = "And";
- private static final long serialVersionUID = 8543289653944756660L;
- private static final String VALUE = "And";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/Asterisk.java b/src/main/java/com/aventstack/extentreports/gherkin/model/Asterisk.java
index c6e10d7..24402b4 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/Asterisk.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/Asterisk.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class Asterisk implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 7251419811428200133L;
+ private static final String VALUE = "*";
- private static final long serialVersionUID = 7251419811428200133L;
- private static final String VALUE = "*";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/Background.java b/src/main/java/com/aventstack/extentreports/gherkin/model/Background.java
index 1e42713..7d83736 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/Background.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/Background.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class Background implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = -955371501488725151L;
+ private static final String VALUE = "Background";
- private static final long serialVersionUID = -955371501488725151L;
- private static final String VALUE = "Background";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/But.java b/src/main/java/com/aventstack/extentreports/gherkin/model/But.java
index 6d88158..acf42e5 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/But.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/But.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class But implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 3420514631996827220L;
+ private static final String VALUE = "But";
- private static final long serialVersionUID = 3420514631996827220L;
- private static final String VALUE = "But";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/Feature.java b/src/main/java/com/aventstack/extentreports/gherkin/model/Feature.java
index 6475396..63e435e 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/Feature.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/Feature.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class Feature implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = -4719215211721789414L;
+ private static final String VALUE = "Feature";
- private static final long serialVersionUID = -4719215211721789414L;
- private static final String VALUE = "Feature";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/GherkinEntity.java b/src/main/java/com/aventstack/extentreports/gherkin/model/GherkinEntity.java
new file mode 100644
index 0000000..ef59e65
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/GherkinEntity.java
@@ -0,0 +1,7 @@
+package com.aventstack.extentreports.gherkin.model;
+
+import java.io.Serializable;
+
+public abstract class GherkinEntity implements Serializable {
+ private static final long serialVersionUID = 725203884274843593L;
+}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/GherkinModelSerializer.java b/src/main/java/com/aventstack/extentreports/gherkin/model/GherkinModelSerializer.java
deleted file mode 100644
index 242fefa..0000000
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/GherkinModelSerializer.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.aventstack.extentreports.gherkin.model;
-
-import java.io.IOException;
-
-import com.google.gson.TypeAdapter;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
-
-public class GherkinModelSerializer extends TypeAdapter {
-
- @Override
- public void write(JsonWriter out, IGherkinFormatterModel value) throws IOException {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public IGherkinFormatterModel read(JsonReader in) throws IOException {
- // TODO Auto-generated method stub
- return null;
- }
-
-}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/Given.java b/src/main/java/com/aventstack/extentreports/gherkin/model/Given.java
index 483b0d2..521f016 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/Given.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/Given.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class Given implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 939197985263690070L;
+ private static final String VALUE = "Given";
- private static final long serialVersionUID = 939197985263690070L;
- private static final String VALUE = "Given";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/Scenario.java b/src/main/java/com/aventstack/extentreports/gherkin/model/Scenario.java
index 683f4d1..6404ad3 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/Scenario.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/Scenario.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class Scenario implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 7401124129196617280L;
+ private static final String VALUE = "Scenario";
- private static final long serialVersionUID = 7401124129196617280L;
- private static final String VALUE = "Scenario";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/ScenarioOutline.java b/src/main/java/com/aventstack/extentreports/gherkin/model/ScenarioOutline.java
index 6b84733..9e7bca3 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/ScenarioOutline.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/ScenarioOutline.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class ScenarioOutline implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = -2058398543903906031L;
+ private static final String VALUE = "Scenario Outline";
- private static final long serialVersionUID = -2058398543903906031L;
- private static final String VALUE = "Scenario Outline";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/Then.java b/src/main/java/com/aventstack/extentreports/gherkin/model/Then.java
index 2ad2bc7..5297125 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/Then.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/Then.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class Then implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 2493591502473169772L;
+ private static final String VALUE = "Then";
- private static final long serialVersionUID = 2493591502473169772L;
- private static final String VALUE = "Then";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gherkin/model/When.java b/src/main/java/com/aventstack/extentreports/gherkin/model/When.java
index d9460db..28934e4 100644
--- a/src/main/java/com/aventstack/extentreports/gherkin/model/When.java
+++ b/src/main/java/com/aventstack/extentreports/gherkin/model/When.java
@@ -3,17 +3,15 @@
import java.io.Serializable;
public class When implements IGherkinFormatterModel, Serializable {
+ private static final long serialVersionUID = 8337259741948416898L;
+ private static final String VALUE = "When";
- private static final long serialVersionUID = 8337259741948416898L;
- private static final String VALUE = "When";
-
- public static String getGherkinName() {
- return VALUE;
- }
-
- @Override
- public String toString() {
- return getGherkinName();
- }
+ public static String getGherkinName() {
+ return VALUE;
+ }
+ @Override
+ public String toString() {
+ return getGherkinName();
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/gson/BddTypeAdapter.java b/src/main/java/com/aventstack/extentreports/gson/BddTypeAdapter.java
new file mode 100644
index 0000000..78e161f
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/gson/BddTypeAdapter.java
@@ -0,0 +1,48 @@
+package com.aventstack.extentreports.gson;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.aventstack.extentreports.gherkin.model.IGherkinFormatterModel;
+import com.google.gson.TypeAdapter;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+public class BddTypeAdapter extends TypeAdapter> {
+ private static final Logger LOG = Logger.getLogger(BddTypeAdapter.class.getName());
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class extends IGherkinFormatterModel> read(final JsonReader reader) throws IOException {
+ int cycle = 0;
+ while (reader.hasNext()) {
+ JsonToken token = reader.peek();
+ if ("string".equalsIgnoreCase(token.name())) {
+ token = reader.peek();
+ String s = reader.nextString();
+ if (s != null && !s.isEmpty()) {
+ try {
+ return (Class extends IGherkinFormatterModel>) Class.forName(s);
+ } catch (ClassNotFoundException e) {
+ LOG.log(Level.SEVERE, "Failed to convert Gherkin type", e);
+ }
+ }
+ }
+ if (cycle++ > 10)
+ return null;
+ }
+ return null;
+ }
+
+ @Override
+ public void write(final JsonWriter out, final Class extends IGherkinFormatterModel> value) throws IOException {
+ if (value == null) {
+ out.nullValue();
+ return;
+ }
+ out.value(value.getName());
+ }
+
+}
diff --git a/src/main/java/com/aventstack/extentreports/gson/BddTypeAdapterFactory.java b/src/main/java/com/aventstack/extentreports/gson/BddTypeAdapterFactory.java
new file mode 100644
index 0000000..441ccc2
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/gson/BddTypeAdapterFactory.java
@@ -0,0 +1,16 @@
+package com.aventstack.extentreports.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+
+public class BddTypeAdapterFactory implements TypeAdapterFactory {
+ @SuppressWarnings("unchecked")
+ @Override
+ public TypeAdapter create(Gson gson, TypeToken type) {
+ if (!Class.class.isAssignableFrom(type.getRawType()))
+ return null;
+ return (TypeAdapter) new BddTypeAdapter();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/gson/GsonExtentTypeAdapterBuilder.java b/src/main/java/com/aventstack/extentreports/gson/GsonExtentTypeAdapterBuilder.java
new file mode 100644
index 0000000..5c873d3
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/gson/GsonExtentTypeAdapterBuilder.java
@@ -0,0 +1,30 @@
+package com.aventstack.extentreports.gson;
+
+import com.aventstack.extentreports.append.ScreenCaptureTypeAdapter;
+import com.aventstack.extentreports.model.Media;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class GsonExtentTypeAdapterBuilder {
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ GsonBuilder builder = new GsonBuilder();
+
+ public Builder withBddTypeAdapterFactory() {
+ builder.registerTypeAdapterFactory(new BddTypeAdapterFactory());
+ return this;
+ }
+
+ public Builder withScreenCaptureTypeAdapter() {
+ builder.registerTypeAdapter(Media.class, new ScreenCaptureTypeAdapter());
+ return this;
+ }
+
+ public Gson build() {
+ return builder.create();
+ }
+ }
+}
diff --git a/src/main/java/com/aventstack/extentreports/io/BufferedWriterWriter.java b/src/main/java/com/aventstack/extentreports/io/BufferedWriterWriter.java
new file mode 100644
index 0000000..5661df3
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/io/BufferedWriterWriter.java
@@ -0,0 +1,35 @@
+package com.aventstack.extentreports.io;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class BufferedWriterWriter {
+
+ private static class WriterInstance {
+ static final BufferedWriterWriter INSTANCE = new BufferedWriterWriter();
+
+ private WriterInstance() {
+ }
+ }
+
+ static final Logger logger = Logger.getLogger(BufferedWriterWriter.class.getName());
+
+ private BufferedWriterWriter() {
+ }
+
+ public synchronized void write(final File f, String text) {
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
+ writer.write(text);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, f.getPath(), e);
+ }
+ }
+
+ public static BufferedWriterWriter getInstance() {
+ return WriterInstance.INSTANCE;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/io/FileWriterBuffered.java b/src/main/java/com/aventstack/extentreports/io/FileWriterBuffered.java
deleted file mode 100644
index 76b1bcb..0000000
--- a/src/main/java/com/aventstack/extentreports/io/FileWriterBuffered.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.aventstack.extentreports.io;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class FileWriterBuffered {
-
- private static class WriterInstance {
- static final FileWriterBuffered INSTANCE = new FileWriterBuffered();
-
- private WriterInstance() {
- }
- }
-
- static final Logger logger = Logger.getLogger(FileWriterBuffered.class.getName());
-
- private FileWriterBuffered() {
- }
-
- public synchronized void write(final File f, String text) {
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) {
- writer.write(text);
- } catch (Exception e) {
- logger.log(Level.SEVERE, f.getPath(), e);
- }
- }
-
- public static FileWriterBuffered getInstance() {
- return WriterInstance.INSTANCE;
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/io/ResourceUtil.java b/src/main/java/com/aventstack/extentreports/io/ResourceUtil.java
deleted file mode 100644
index 6069081..0000000
--- a/src/main/java/com/aventstack/extentreports/io/ResourceUtil.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.aventstack.extentreports.io;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-
-public class ResourceUtil {
-
- public static void moveResource(String resourcePath, String copyPath) {
- if (copyPath != null)
- copyPath = copyPath.replace("\\", "/");
- if (resourcePath != null)
- resourcePath = resourcePath.replace("\\", "/");
-
- try {
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- InputStream in = classLoader.getResourceAsStream(resourcePath);
-
- FileOutputStream out = new FileOutputStream(copyPath);
-
- byte[] b = new byte[1024];
- int noOfBytes = 0;
-
- while ((noOfBytes = in.read(b)) != -1) {
- out.write(b, 0, noOfBytes);
- }
-
- in.close();
- out.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public static void moveBinaryFile(String resourcePath, String copyPath) {
- URI uri = new File(resourcePath).toURI();
- Path path = Paths.get(uri);
- try {
- Files.copy(path, new File(copyPath).toPath(), StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/aventstack/extentreports/markuputils/CodeBlock.java b/src/main/java/com/aventstack/extentreports/markuputils/CodeBlock.java
index d378816..c7d0787 100644
--- a/src/main/java/com/aventstack/extentreports/markuputils/CodeBlock.java
+++ b/src/main/java/com/aventstack/extentreports/markuputils/CodeBlock.java
@@ -5,61 +5,64 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import com.google.gson.Gson;
+
import freemarker.template.Template;
import freemarker.template.TemplateException;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+@Getter
+@Builder
+@AllArgsConstructor
+@ToString
class CodeBlock extends MarkupTemplate implements Markup {
- private static final long serialVersionUID = -5532095355983830164L;
- private static final AtomicInteger id = new AtomicInteger(0);
- private static final String CODEBLOCK_TEMPLATE = "codeblock.ftl";
- private static final String CODEBLOCK_JSON_TEMPLATE = "codeblock.json.ftl";
- private static Template codeblock;
- private static Template codeblockJson;
- private String code;
- private CodeLanguage lang;
-
- static {
- try {
- codeblock = ft.createTemplate(CODEBLOCK_TEMPLATE);
- codeblockJson = ft.createTemplate(CODEBLOCK_JSON_TEMPLATE);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public void setCodeBlock(String code) {
- this.code = code;
- }
-
- public String getCodeBlock() {
- return code;
- }
+ private static final long serialVersionUID = -5532095355983830164L;
+ private static final AtomicInteger id = new AtomicInteger(0);
+ private static final String CODEBLOCK_TEMPLATE = "codeblock.ftl";
+ private static final String CODEBLOCK_JSON_TEMPLATE = "codeblock.json.ftl";
+ private static Template codeblock;
+ private static Template codeblockJson;
+ private String[] codeArray;
+ private CodeLanguage lang;
+ private Object jsonObject;
- public void setCodeBlock(String code, CodeLanguage lang) {
- this.code = code;
- this.lang = lang;
- }
+ static {
+ try {
+ codeblock = ft.createTemplate(CODEBLOCK_TEMPLATE);
+ codeblockJson = ft.createTemplate(CODEBLOCK_JSON_TEMPLATE);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
- @Override
- public String getMarkup() {
- int index = 0;
- Template t = codeblock;
- if (lang == CodeLanguage.JSON) {
- index = id.getAndIncrement();
- t = codeblockJson;
- }
- Map map = new HashMap<>();
- map.put("code", code);
- map.put("index", index);
- try {
- return ft.getSource(t, map);
- } catch (TemplateException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
+ @Override
+ public String getMarkup() {
+ if (jsonObject == null && codeArray == null)
+ return "";
+ if (jsonObject != null)
+ codeArray = new String[]{new Gson().toJson(jsonObject)};
+
+ int index = 0;
+ Template t = codeblock;
+ if (lang == CodeLanguage.JSON) {
+ index = id.getAndIncrement();
+ t = codeblockJson;
+ }
+ Map map = new HashMap<>();
+ map.put("code", codeArray);
+ map.put("index", index);
+ try {
+ return ft.getSource(t, map);
+ } catch (TemplateException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
}
diff --git a/src/main/java/com/aventstack/extentreports/markuputils/CodeLanguage.java b/src/main/java/com/aventstack/extentreports/markuputils/CodeLanguage.java
index 67f07ca..eb74eea 100644
--- a/src/main/java/com/aventstack/extentreports/markuputils/CodeLanguage.java
+++ b/src/main/java/com/aventstack/extentreports/markuputils/CodeLanguage.java
@@ -4,6 +4,5 @@
* List of supported languages that will be prettified on output
*/
public enum CodeLanguage {
- XML,
- JSON
+ XML, JSON
}
diff --git a/src/main/java/com/aventstack/extentreports/markuputils/HtmlList.java b/src/main/java/com/aventstack/extentreports/markuputils/HtmlList.java
new file mode 100644
index 0000000..1a0dfd3
--- /dev/null
+++ b/src/main/java/com/aventstack/extentreports/markuputils/HtmlList.java
@@ -0,0 +1,38 @@
+package com.aventstack.extentreports.markuputils;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+abstract class HtmlList {
+ @SuppressWarnings("unchecked")
+ protected String getList(Object object, ListType listType) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("<" + listType.toString().toLowerCase() + ">");
+ if (object instanceof Map) {
+ for (Map.Entry