Javadoc

Click here to view javadocs.

Klov

Klov is supported by version 3.1.0 of ExtentReports API. See API usage instructions here.

Pro vs Community Version

Version 3 comes with 2 versios: pro and community. Differences below:

Features Community Pro
ExtentHtmlReporter - Interactive HTML report
Support for Standard and BDD Tests
Add screenshots to tests
Add screenshots to logs
Add Base64 screenshots to tests
Add Base64 screenshots to logs
Append to existing report
Offline report
Configure View Visibility
Timeline
Auto-save screenshots relative to report
Auto-link screenshots to report
Create Custom Dashboard Sections
TestDisplayOrder setting
Markup Helpers
Code Block
Label
Table
Card
External Link
Modal
AnalysisStrategy
Customizable
KlovReporter
Views - Dashboard
Views - Builds (list)
Views - Build (single)
Views - Test
Views - Tags
Views - Search
ExtentXReporter - ExtentX Server (Deprecated, use Klov)
Support for ExtentX-Community Server
ExtentEmailReporter - Emailable reports
ExtentLogger - Realtime logger
Premium Email Support
CDN Github, ExtentReports Amazon

Contributors

View this link for all active contributors.

Maven


<!-- pom.xml -->
<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>3.1.5</version>
</dependency>

Basic Usage

This section demonstrates basic methods that you will be using with this library.

Initializing Report

It is required to start and attach reporters to ExtentReports class in order to successfully generate test information. Failure to start reporters or attaching them will result in IllegalStateException when creating tests or flushing out run content to the report.


// initialize the HtmlReporter
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");

// initialize ExtentXReporter (deprecated)
ExtentXReporter extentxReporter = new ExtentXReporter("mongodb-host", mongodb-port);

// initialize KlovReporter
KlovReporter klov = new KlovReporter();

// initialize EmailReporter (pro-only)
ExtentEmailReporter emailReporter = new ExtentEmailReporter("email.html");

// intiailize the realtime logger (pro-only)
ExtentLogger logger = new ExtentLogger();

// initialize ExtentReports and attach the HtmlReporter
ExtentReports extent = new ExtentReports();

// attach only HtmlReporter
extent.attachReporter(htmlReporter);

// attach all reporters
extent.attachReporter(htmlReporter, klovReporter, emailReporter, logger);

Writing to Report

Once your session is complete and you are ready to write all logs to the report, simply call the flush() method.

Simply call the flush() method to write or update test information to your reporter. Below is what each reporter does to write/update results:

  • ExtentHtmlReporter: builds a new report using the html template each time upon flush with the cumulative test information
  • ExtentEmailReporter pro-only: builds a new report using the email template each time upon flush with the cumulative test information
  • ExtentXReporter: updates database at each listener event
  • ExtentLogger pro-only: logs information at each listener event

HtmlReporter: Offline Report pro-only

The HtmlReporter allows creating offline report using:


ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
htmlReporter.config().setCreateOfflineReport(true);

Appending to Report

ExtentHtmlReporter and ExtentXReporter allow appending test information to an existing report.

Appending to HtmlReporter


htmlReporter.setAppendExisting(true);

Appending to ExtentXReporter

If you want to append to an existing report based on its ObjectId, use:


extentXReporter.setAppendExisting(true, reportId);

// or:
extentxReporter.config().setReportObjectId(ObjectId id);
extentxReporter.config().setReportObjectId(String id);

If you want to append to an existing report based on reportName, use:


extentxReporter.setAppendExisting(true);

Note: If you have multiple reports with the same name, the append will be applied to the last-run report that matches the report-name.

Creating Tests

To create tests, use the createTest or createNode methods:

Note: it is only possible to create tests and nodes if atleast 1 repoter is attached to ExtentReports.


// creating tests
ExtentTest test = extent.createTest("MyFirstTest");
test.pass("details");

// short-hand for above
extent
    .createTest("MyFirstTest")
    .pass("details");
	
// test with description
extent
    .createTest("MyFirstTest", "Test Description")
    .pass("details");

Adding Child Tests

To add a test node as a child of another test, use the createNode method.


// creating nodes	
ExtentTest parentTest = extent.createTest("MyFirstTest");
ExtentTest childTest = parentTest.createNode("MyFirstChildTest");
childTest.pass("details");

// short-hand for above
extent
    .createTest("MyFirstTest")
    .createNode("MyFirstChildTest")
	.pass("details");
	
// node description
ExtentTest childTest = parentTest.createNode("MyFirstChildTest", "Node Description");

Create BDD-style Tests

To create cucumber/gherkin style tests, use the gherkin model or model names:

Note: When creating BDD-style tests, all your tests must be created in the same fashion. Do not mix standard tests (as shown above) with BDD. A report can have either style of tests only.


// gherkin classes

// feature
ExtentTest feature = extent.createTest(Feature.class, "Refund item");

// scenario
ExtentTest scenario = feature.createNode(Scenario.class, "Jeff returns a faulty microwave");
scenario.createNode(Given.class, "Jeff has bought a microwave for $100").pass("pass");
scenario.createNode(And.class, "he has a receipt").pass("pass");
scenario.createNode(When.class, "he returns the microwave").pass("pass");
scenario.createNode(Then.class, "Jeff should be refunded $100").fail("fail");

// using keyword names

// feature
ExtentTest feature = extent.createTest(new GherkinKeyword("Feature"), "Refund item");

// scenario
ExtentTest scenario = feature.createNode(new GherkinKeyword("Scenario") , "Jeff returns a faulty microwave");
scenario.createNode(new GherkinKeyword("Given"), "Jeff has bought a microwave for $100").pass("pass");
scenario.createNode(new GherkinKeyword("And"), "he has a receipt").pass("pass");
scenario.createNode(new GherkinKeyword("When"), "he returns the microwave").pass("pass");
scenario.createNode(new GherkinKeyword("Then"), "Jeff should be refunded $100").fail("fail");

Gherkin Dialect setting

If you are using a dialect other than "en" or English keywords, you can now specify a gherkin dialect (3.0.7+). The API uses the same source for dialects as used by Cucumber. To specify your dialect, simply add this before any of your tests start:


// German
extent.setGherkinDialect("de");

// Norwegian
extent.setGherkinDialect("no");

Removing Tests

To remove any started test, simply call the removeTest (3.0.4+) method:


ExtentTest test = extent.createTest("Test");
extent.removeTest(test);

Setting Test Display Order pro-only

To set the order in which tests are displayed in the report, use:


// The 1st executed test appears at the top of the report
extent.setTestDisplayOrder(TestDisplayOrder.OLDEST_FIRST);

// The last executed test appears at the top of the report
extent.setTestDisplayOrder(TestDisplayOrder.NEWEST_FIRST);

Creating Log Events


ExtentTest test = extent.createTest("TestName");
test.log(Status.PASS, "pass");
// or:
test.pass("pass");

test.log(Status.FAIL, "fail");
// or:
test.fail("fail");

Logging Exceptions

To log exceptions, simply pass the exception.

Note: doing this will also enable the defect/bug tab in the report.


Exception e;
test.fail(e);

Assign Categories to Tests

You can assign categories to tests using assignCategory method:

test.assignCategory("Regression");
test.assignCategory("Regression", "ExtentAPI");
test.assignCategory("Regression", "ExtentAPI", "category-3", "cagegory-4", ..);

// while creating test
extent
    .createTest("MyFirstTest")
	.assignCategory("Regression")
    .pass("details");

Assign Authors to Tests

You can assign categories to tests using assignAuthor method:


test.assignAuthor("aventstack");
test.assignAuthor("name1", "name2");

// while creating test
extent
    .createTest("MyFirstTest")
	.assignAuthor("aventstack")
    .pass("details");

Screenshots

It is possible to attach screenshots to test and logs:


// adding screenshots to log
test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build());

// or:
MediaModelProvider mediaModel = MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build();
test.fail("details", mediaModel);	

// adding screenshots to test
test.fail("details").addScreenCaptureFromPath("screenshot.png");

Base64 Screenshots pro-only

To add base64 screenshots to tests and logs, use createScreenCaptureFromBase64String or addScreenCaptureFromBase64String methods:


// adding base64 strings to log:
test.warning("details", MediaEntityBuilder.createScreenCaptureFromBase64String("base64String").build());
test.log(Status.WARNING, "details", MediaEntityBuilder.createScreenCaptureFromBase64String("base64String").build());

// adding base64 strings to tests:
test.addScreenCaptureFromBase64String("base64String");

Inserting custom HTML

Simply insert any custom HTML in the logs by using an HTML tag:


extent.log(LogStatus.INFO, "HTML", "Usage: BOLD TEXT");

Adding System Info

It is possible to add system or environment info for your using using the setSystemInfo method.

Note: This method automatically adds system information to all started reporters.


extent.setSystemInfo("os", "win7");

ExtentHtmlReporter Features

Extent-Config.xml Professional


<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>standard</theme>
    
        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>
        
        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>
        
        <!-- title of the document -->
        <documentTitle>Extent</documentTitle>
        
        <!-- report name - displayed at top-nav -->
        <reportName>Automation Report</reportName>
        
        <!-- location of charts in the test view -->
        <!-- top, bottom -->
        <testViewChartLocation>bottom</testViewChartLocation>
        
		<!-- settings to enable/disable views -->
		<!-- professional version only -->
		<enableCategoryView>true</enableCategoryView>
		<enableAuthorView>false</enableAuthorView>
		<enableExceptionView>true</enableExceptionView>
		<enableTestRunnerLogsView>true</enableTestRunnerLogsView>
		
        <!-- custom javascript -->
        <scripts>
            <![CDATA[
                $(document).ready(function() {
                    
                });
            ]]>
        </scripts>
        
        <!-- custom styles -->
        <styles>
            <![CDATA[
                
            ]]>
        </styles>
    </configuration>
</extentreports>

Extent-Config.xml Community


<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>standard</theme>
    
        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>
        
        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>
        
        <!-- title of the document -->
        <documentTitle>Extent</documentTitle>
        
        <!-- report name - displayed at top-nav -->
        <reportName>Automation Report</reportName>
        
        <!-- location of charts in the test view -->
        <!-- top, bottom -->
        <testViewChartLocation>bottom</testViewChartLocation>
		
        <!-- custom javascript -->
        <scripts>
            <![CDATA[
                $(document).ready(function() {
                    
                });
            ]]>
        </scripts>
        
        <!-- custom styles -->
        <styles>
            <![CDATA[
                
            ]]>
        </styles>
    </configuration>
</extentreports>

Automatic Screenshot Management pro-only

To automatically create relative paths from the report, use configuration:


htmlReporter.config().setAutoCreateRelativePathMedia(true);

This configuration will copy the media-file to a directory relative to the file without you having to manually provide path that works relative to the report.


ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
htmlReporter.config().setAutoCreateRelativePathMedia(true);

test1.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("1.png").build());
test2.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("2.png").build());

The above 2 screenshots will automatically be saved as and added to the report. This will allow you to move the screenshots along with the report without any side-affects, such breaking the report or failure to load screenshots on load.


extent.html
/ extent.0
    1.png
    2.png

Offline Report pro-only


htmlReporter.config().setCreateOfflineReport(true);

Append Tests to Existing Report


ExtentHtmlReporter htmlreporter = new ExtentHtmlReporter("Extent.html");
htmlreporter.setAppendExisting(true);

Configuring Analysis Strategy

It is possible to configure the analysis strategy for the report (only available in HTMLReporter). To view the differences between the different types of analysis strategies, view the dashboard/analysis section from the links below:

  • Test: (Default) 4 Tests (Child02, Child03, Child04, GrandChild1), each having 1 log (4 logs/steps)
  • Suite: 2 Suites (ParentTest01, ParentTest02), 4 Tests (Child01, Child02, Child03, Child04), 1 Test Method (GrandChild1)
  • Class: 2 Classes (ParentTest01, ParentTest02), 4 Tests (Child02, Child03, Child04, GrandChild1), Each having 1 log (4 logs/steps)

// test view (default):
extent.setAnalysisStrategy(AnalysisStrategy.TEST);

// suite view:
extent.setAnalysisStrategy(AnalysisStrategy.SUITE);

// class view:
extent.setAnalysisStrategy(AnalysisStrategy.CLASS);

Configuring View Visibility pro-only

It is possible to configure the visibility of views using config.xml or code:


// enable category view
htmlReporter.config().setCategoryViewVisibility(true);

// disable Author view
htmlReporter.config().setAuthorViewVisibility(false);

// disable TestRunnerLogs view
htmlReporter.config().setTestRunnerLogsViewVisibility(false);

// enable Exception view
htmlReporter.config().setExceptionViewVisibility(true);

The same can also be done from config.xml:


<!-- settings to enable/disable views -->
<enableCategoryView>true</enableCategoryView>
<enableAuthorView>false</enableAuthorView>
<enableExceptionView>true</enableExceptionView>
<enableTestRunnerLogsView>true</enableTestRunnerLogsView>

htmlReporter.loadXMLConfig("extent-config.xml");

Timeline pro-only

Dashboard view displays the timeline of your tests and can be enabled/disabled using:

  • This section can be disabled from extent-config.xml by setting <enableTimeline>false</enableTimeline>
  • This section can be disabled from htmlReporter.config().setShowTimeline(false);

Test names are disabled when you hover over the bar.

Creating Custom Dashboard Sections pro-only

To add custom sections in the report, use htmlReporter.views().dashboard().setSection(...):


// setSection(String name, SectionSize size, String[] header, List data)
String[] header = new String[] { "HeaderA", "HeaderB", "HeaderC" };
        
List list = new ArrayList();
list.add(new String[] { "cell1", "cell2", "cell3" });
list.add(new String[] { "cell4", "cell5", "cell6" });

htmlReporter.views().dashboard().setSection("Sample", SectionSize.S4, header, list);

Status Hierarchy Override Configuration

(#680) It is possible to setup your own hierarchy by using ExtentReports config():


List statusHierarchy = Arrays.asList(
                    Status.FATAL,
                    Status.FAIL,
                    Status.ERROR,
                    Status.WARNING,
                    Status.SKIP,
                    Status.PASS,
                    Status.DEBUG,
                    Status.INFO
);

extent.config().statusConfigurator().setStatusHierarchy(statusHierarchy);

The ExtentHtmlReporter supports several short-cuts for quick viewing and navigation:

Views


t - test-view
c - category-view
x - exception-view
d - dashboard

Filters


p - show passed tests
e - show error tests
f - show failed tests
s - show skipped tests
w - show warning tests
esc - clear filters

Scroll


down-arrow - scroll down
up-arrow - scroll up

ExtentHtmlReporter Configuration

Each reporter supports several configuration items to change the look and feel, add content, manage tests etc.


// make the charts visible on report open
htmlReporter.config().setChartVisibilityOnOpen(true);

// create offline report (pro-only)
htmlReporter.config().setCreateOfflineReport(true);

// automatic screenshot management (pro-only)
htmlReporter.config().setAutoCreateRelativePathMedia(true);

// report title
htmlReporter.config().setDocumentTitle("aventstack - ExtentReports");

// encoding, default = UTF-8
htmlReporter.config().setEncoding("UTF-8");

// protocol (http, https)
htmlReporter.config().setProtocol(Protocol.HTTPS);

// report or build name
htmlReporter.config().setReportName("Build-1224");

// chart location - top, bottom
htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);

// theme - standard, dark
htmlReporter.config().setTheme(Theme.STANDARD);

// set timeStamp format
htmlReporter.config().setTimeStampFormat("mm/dd/yyyy hh:mm:ss a");

// add custom css
htmlreporter.config().setCSS("css-string");

// add custom javascript
htmlreporter.config().setJS("js-string");

KlovReporter

Klov is the report server for the Extent API. Demo: here.

To use KlovReporter with the API, simply attach the reporter:


extent.attachReporter(klov);

Settings


KlovReporter klov = new KlovReporter();

// specify mongoDb connection
klov.initMongoDbConnection("localhost", 27017);

// specify project
// ! you must specify a project, other a "Default project will be used"
klov.setProjectName("Java");

// you must specify a reportName otherwise a default timestamp will be used
klov.setReportName("AppBuild");

// URL of the KLOV server
// you must specify the served URL to ensure all your runtime media is uploaded
// to the server
klov.setKlovUrl("http://localhost");

Views

  • Dashboard
  • Builds (list)
  • Build
  • Test
  • Tags pro-only
  • Search

Append to an Existing Report pro-only

To append to an existing Klov build (with the same name), simply enable the below property to true:


klov.setAppendExisting(true); 

ExtentEmailReporter pro-only

The ExtentEmailReporter is available in the professional version only. To use this reporter:


ExtentEmailReporter emailReporter = new ExtentEmailReporter("Email.html");

// optional, select a template
emailReporter.setTemplate(EmailTemplate.NEWSLETTER_COMPLETE);

extent.attachReporter(emailReporter);

Configuration


// report or build name
emailReporter.config().setReportName("Today's Report");

// report title
emailReporter.config().setDocumentTitle("aventstack - ExtentReports");

// encoding, default = UTF-8
emailReporter.config().setEncoding("UTF-8");

Templates

To select different templates, use the setTemplate method of the reporter:


ExtentEmailReporter emailReporter = new ExtentEmailReporter("Email.html");
emailReporter.setTemplate(EmailTemplate.NEWSLETTER_COMPLETE);

ExtentLogger pro-only

ExtentLogger outputs logs immediately to console and can be further appended to text files and all other sources supported by slf4j. To use this reporter:


ExtentLogger logger = new ExtentLogger();
extent.attachReporter(logger);

ExtentXReporter


// project name
extentx.config().setProjectName("ProjectName");

// report or build name
extentx.config().setReportName("Build-1224");

// server URL
// ! must provide this to be able to upload snapshots
// Note: this is the address to the ExtentX server, not the Mongo database
extentx.config().setServerUrl("http://localhost:1337");

ExtentXReporter is deprecated as of version 3.1.0 professional and community.

Getting ReportID


extentXReporter.getReportId();

Getting ProjectID


extentXReporter.getProjectId();

Append to Existing Report


extentXReporter.setAppendExisting(true, reportId);

// or:
extentXReporter.config().setReportObjectId(id);

Append to Last Run Report pro-only

(pro version only, 3.0.2+) It is possible to automatically append your results from the current session, to the last run session in ExtentX (mongodb).


extentXReporter.appendToLastRunReport();

// or:
ObjectId reportId = extentXReporter.getLastRunReportId();
extentXReporter.setAppendExisting(true, reportId);

Be careful when using this setting when you have multiple projects. If you fail to set the project, or set the project after this setting, ExtentXReporter will by default append to the last-run report, which may belong to another project. Always remember to set the project (if you have multiple projects) before using this setting.


extentXReporter.config().setProjectName(projectName);

Markup Helpers

A few helpers are provided to allow:

  • Code block
  • Table
  • Label
  • Card pro-only
  • External Link pro-only
  • Modal pro-only

Code block


String code = "\n\t\n\t\tText\n\t\n";
Markup m = MarkupHelper.createCodeBlock(code);

test.pass(m);
// or
test.log(Status.PASS, m);

Label


String text = "extent";
Markup m = MarkupHelper.createLabel(text, ExtentColor.BLUE);

test.pass(m);
// or
test.log(Status.PASS, m);

Table


String[][] data = {
    { "Header1", "Header2", "Header3" },
    { "Content.1.1", "Content.2.1", "Content.3.1" },
    { "Content.1.2", "Content.2.2", "Content.3.2" },
    { "Content.1.3", "Content.2.3", "Content.3.3" },
    { "Content.1.4", "Content.2.4", "Content.3.4" }
};
Markup m = MarkupHelper.createTable(data);

test.pass(m);
// or
test.log(Status.PASS, m);

Card pro-only


String text = "This text will become part of a material card.";
Markup m = MarkupHelper.createCard(text, ExtentColor.CYAN);

test.pass(m);
// or
test.log(Status.PASS, m);

String url = "http://extentreports.com";
String name = "extent";
Markup m = MarkupHelper.createExternalLink(url, name);

test.pass(m);
// or
test.log(Status.PASS, m);

Modal pro-only


test.info(MarkupHelper.createModal("Modal text"));

Examples

This section shows basic examples to get started with ExtentReports 3.x.

Basic Usage Example

A simple example with a static entry-point.


public class Main {
    public static void main(String[] args) {
        // start reporters
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
    
        // create ExtentReports and attach reporter(s)
        ExtentReports extent = new ExtentReports();
        extent.attachReporter(htmlReporter);

        // creates a toggle for the given test, adds all log events under it    
        ExtentTest test = extent.createTest("MyFirstTest", "Sample description");

        // log(Status, details)
        test.log(Status.INFO, "This step shows usage of log(status, details)");

        // info(details)
        test.info("This step shows usage of info(details)");
        
        // log with snapshot
        test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build());
        
        // test with snapshot
        test.addScreenCaptureFromPath("screenshot.png");
        
        // calling flush writes everything to the log file
        extent.flush();
    }
}

TestNG Examples

Using ExtentTestNGReportBuilder


public class ExtentTestNGReportBuilder {

	private static ExtentReports extent;
    private static ThreadLocal parentTest = new ThreadLocal();
    private static ThreadLocal test = new ThreadLocal();

	@BeforeSuite
	public void beforeSuite() {
		extent = ExtentManager.createInstance("extent.html");
		ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
		extent.attachReporter(htmlReporter);
	}
	
    @BeforeClass
    public synchronized void beforeClass() {
        ExtentTest parent = extent.createTest(getClass().getName());
        parentTest.set(parent);
    }

    @BeforeMethod
    public synchronized void beforeMethod(Method method) {
        ExtentTest child = parentTest.get().createNode(method.getName());
        test.set(child);
    }

    @AfterMethod
    public synchronized void afterMethod(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE)
            test.get().fail(result.getThrowable());
        else if (result.getStatus() == ITestResult.SKIP)
            test.get().skip(result.getThrowable());
        else
            test.get().pass("Test passed");

        extent.flush();
    }
}

public class ExtentManager {
    
    private static ExtentReports extent;
    
    public static ExtentReports getInstance() {
    	if (extent == null)
    		createInstance("test-output/extent.html");
    	
        return extent;
    }
    
    public static ExtentReports createInstance(String fileName) {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        
        return extent;
    }
}

TestNG IReporter


public class ExtentTestNGIReporterListener implements IReporter {
    
    private static final String OUTPUT_FOLDER = "test-output/";
    private static final String FILE_NAME = "Extent.html";
    
    private ExtentReports extent;

    @Override
    public void generateReport(List xmlSuites, List suites, String outputDirectory) {
        init();
        
        for (ISuite suite : suites) {
            Map result = suite.getResults();
            
            for (ISuiteResult r : result.values()) {
                ITestContext context = r.getTestContext();
                
                buildTestNodes(context.getFailedTests(), Status.FAIL);
                buildTestNodes(context.getSkippedTests(), Status.SKIP);
                buildTestNodes(context.getPassedTests(), Status.PASS);
                
            }
        }
        
        for (String s : Reporter.getOutput()) {
            extent.setTestRunnerOutput(s);
        }
        
        extent.flush();
    }
    
    private void init() {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
        htmlReporter.config().setDocumentTitle("ExtentReports - Created by TestNG Listener");
        htmlReporter.config().setReportName("ExtentReports - Created by TestNG Listener");
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setTheme(Theme.STANDARD);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        extent.setReportUsesManualConfiguration(true);
    }
    
    private void buildTestNodes(IResultMap tests, Status status) {
        ExtentTest test;
        
        if (tests.size() > 0) {
            for (ITestResult result : tests.getAllResults()) {
                test = extent.createTest(result.getMethod().getMethodName());
                
                for (String group : result.getMethod().getGroups())
                    test.assignCategory(group);

                if (result.getThrowable() != null) {
                    test.log(status, result.getThrowable());
                }
                else {
                    test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                }
                
                test.getModel().setStartTime(getTime(result.getStartMillis()));
                test.getModel().setEndTime(getTime(result.getEndMillis()));
            }
        }
    }
    
    private Date getTime(long millis) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        return calendar.getTime();      
    }
}

TestNG ITestListener


public class ExtentTestNGITestListener implements ITestListener {

	private static ExtentReports extent = ExtentManager.createInstance("extent.html");
	private static ThreadLocal parentTest = new ThreadLocal();
    private static ThreadLocal test = new ThreadLocal();
	
    @Override
	public synchronized void onStart(ITestContext context) {
    	ExtentTest parent = extent.createTest(getClass().getName());
        parentTest.set(parent);
	}

	@Override
	public synchronized void onFinish(ITestContext context) {
		extent.flush();
	}
	
	@Override
	public synchronized void onTestStart(ITestResult result) {
		ExtentTest child = parentTest.get().createNode(result.getMethod().getMethodName());
        test.set(child);
	}

	@Override
	public synchronized void onTestSuccess(ITestResult result) {
		test.get().pass("Test passed");
	}

	@Override
	public synchronized void onTestFailure(ITestResult result) {
		test.get().fail(result.getThrowable());
	}

	@Override
	public synchronized void onTestSkipped(ITestResult result) {
		test.get().skip(result.getThrowable());
	}

	@Override
	public synchronized void onTestFailedButWithinSuccessPercentage(ITestResult result) {
		
	}
}

public class ExtentManager {
    
    private static ExtentReports extent;
    
    public static ExtentReports getInstance() {
    	if (extent == null)
    		createInstance("test-output/extent.html");
    	
        return extent;
    }
    
    public static ExtentReports createInstance(String fileName) {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        
        return extent;
    }
}

Sending Email using JavaMail API

Below is a quick example showing a possible way to email source to recipients using JavaMail API.

Note: this may not work in your environment. If SSL authentication is required, use "Java SMTP Example with SSL Authentication" approach as shown here.


import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Email {
	
	// supply your SMTP host name
	private static String host = "host";
	
	// supply a static sendTo list
	private static String to = "recipient@host.com";
	
	// supply the sender email
	private static String from = "sender@host.com";
	
	/ default cc list
	private static String cc = "";
	
	// default bcc list
	private static String bcc = "";
	
	public static void send(String from, String to, String subject, String contents) throws MessagingException {		
		Properties prop = System.getProperties();
		prop.setProperty("mail.smtp.host", host);
		
		Session session = Session.getDefaultInstance(prop);
		
		MimeMessage message = new MimeMessage(session);
		message.setFrom(new InternetAddress(from));
		message.setSubject(subject);
		message.setContent(contents, "text/html");

		List toList = getAddress(to);
		for (String address : toList) {
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
		}
		
		List ccList = getAddress(cc);
		for (String address : ccList) {
			message.addRecipient(Message.RecipientType.CC, new InternetAddress(address));
		}
		
		List bccList = getAddress(bcc);
		for (String address : bccList) {
			message.addRecipient(Message.RecipientType.BCC, new InternetAddress(address));
		}
		
		Transport.send(message);
	}
	
	public static void send(String to, String subject, String contents) throws MessagingException {
		send(from, to, subject, contents);
	}
	
	public static void send(String subject, String contents) throws MessagingException {
		send(from, to, subject, contents);
	}
	
	private static List getAddress(String address) {
		List addressList = new ArrayList();
		
		if (address.isEmpty())
			return addressList;
		
		if (address.indexOf(";") > 0) {
			String[] addresses = address.split(";");
			
			for (String a : addresses) {
				addressList.add(a);
			}
		} else {
			addressList.add(address);
		}
		
		return addressList;
	}

}

// usage
Email.send("me@host.com", "recip1@host.com;recip2@host.com", "New Subject", "<h1>header</h2>")

What's new in 3.1.6 Professional

Version 3.1.6 launched on June 18, 2018. Here is what's new:

  • new JSON strings will be automatically formatted when using:
    
    test.pass(MarkupHelper.createCodeBlock(json, CodeLanguage.JSON));
    
  • fix Issue where filters are not removed when jumping to a hidden test in TestView
  • fix Issue where chart labels were hidden in dark theme
  • improvement Change run date/time style to long format (Html and Email reporters)

What's new in 3.1.5 Professional

Version 3.1.5 launched on June 08, 2018. Here is what's new:

  • new Dashboard view displays complete run duration instead of milliseconds
  • new Dashboard view now displays the timeline for all executed tests
    • This section can be disabled from extent-config.xml by setting <enableTimeline>false</enableTimeline>
    • This section can be disabled from htmlReporter.config().setShowTimeline(false);
  • new Append to an existing Klov build (with the same name) by using: klov.setAppendExisting(true);

What's new in 3.1.2 Professional

Version 3.1.2 launched on April 03, 2018. Here is what's new:

  • new Enable showing description for Feature.class
  • improvement Adds test name to media/images
  • fix Fixes issue where appendExisting for BDD fails due to log being null
  • fix Fixes issues where large base64 image took the entire screen

What's new in 3.1.1 Professional

Version 3.1.1 launched on Dec 22, 2017. Here is what's new:

  • new KlovReporter adds additional information for categories, enables the category view for klov
  • fix Fix invalid counts being considered ScenarioOutline when there are failures

What's new in 3.1.0 Professional

Version 3.1.0 launched on Nov 06, 2017. Here is what's new:

  • new Adds KlovReporter (settings, demo)
  • ExtentXReporter is deprecated as of this version, use Klov instead

What's new in 3.0.5 Professional

Version 3.0.5 launched on Oct 16, 2017. Here is what's new:

  • new A brand new email template: view
  • new Support for gherkin keywords in different languages
  • fix Fix issue with AnalysisStrategy.CLASS counting incorrect tests
  • fix Fix issue for gherkin keywords with spaces causing NPEs

What's new in 3.0.4 Professional

Version 3.0.4 launched on August 23, 2017. Here is what's new:

  • new Allow base64 images using addScreenCaptureFromBase64String(..)
  • new [HTML] Adds classes for Environment and Categories boxes in dashboard view
  • new Adds ability to filter child nodes
  • fix Fix issue for Apple's Command keys not allow copy of text
  • fix Now report is maintained in the same original charset/meta
  • fix Fixes issue where config.xml scripts and styles tags were not loaded correctly to the report
  • improvement Styling improvements for nodes

What's new in 3.1.5

Version 3.1.5 launched on April 02, 2018. Here is what's new:

  • fix (#1012) Fix markup text color when label color is white
  • fix (#1016) While using BDD style reports appending reports to existing reports does not works and fails with error
  • fix (#1030) Link tag was not closed for fonts.googleapis.com/ Line No 11 and 12
  • fix (#1046) Improve Command key detection on OSX
  • new (#1002) Adds But keyword
  • improvement (f437866) Skip will have higher priority than pass for BDD, Pass has higher priority for non-BDD

What's new in 3.1.3

Version 3.1.3 launched on Feb 10, 2018. Here is what's new:

  • fix (#1021) Fix issue where the status filter for HtmlReporter contains status from tests that are removed using extent.removeTest(test);
  • new (#999) Displays full run duration in dashboard view

What's new in 3.1.2

Version 3.1.2 launched on Dec 01, 2017. Here is what's new:

  • fix (#989) Fix issue with appendExisting causing failures on parsing node timestamp
  • fix (#994) Fix issue where the parent test was marked as SKIP when not all child test skipped
  • new (#986) Shows pass percentage for Categories on Dashboard view

What's new in 3.1.1

Version 3.1.1 launched on Nov 17, 2017. Here is what's new:

What's new in 3.1.0

Version 3.1.0 launched on Nov 06, 2017. Here is what's new:

  • new Adds KlovReporter (settings, demo)
  • ExtentXReporter is deprecated as of this version, use Klov instead

What's new in 3.0.7

Version 3.0.7 launched on August 24, 2017. Here is what's new:

  • new (#939) Add ability to use a gherkin dialect
  • new (#715) Allow base64 images using addScreenCaptureFromBase64String(..)
  • fix (#947) Fix issue where a base64 image was causing NPE while uploading to ExtentX
  • fix (#945) Fix issue with AnalysisStrategy.CLASS taking invalid counts for parent and steps
  • fix (#936) Fix issue for LoadXMLConfig for scripts and styles does not working
  • fix (#926) For TestNG IReporter implementation, dashboard end time-stamp is not set correctly
  • fix (#886) Fix copy issue using COMMAND+C for Mac
  • fix (#883) Fix issue where logo shifts when view is resized to less than 992px
  • fix (#679) Fix filters to be used for both parent and nodes

What's new in 3.0.6

Version 3.0.6 launched on May 11, 2017. Here is what's new:

  • new commit Enabled MarkupHelper for extending, user can extend and create their own markup util
  • new commit Adds Author view
  • new (#874) Add ScenarioOutline and allow adding an additional level for its scenarios
  • fix (#726) Fix issue where test level snapshots were not parsed and appended when using setAppendExisting
  • fix (#837) Fix issue for setAppendExisting not able to parse child/node category on append
  • fix (#838) Allow filtering category when the category label is clicked
  • fix (#853) Category Filter doesn't work if we assign category to sub node

What's new in 3.0.5

Version 3.0.5 launched on April 05, 2017. Here is what's new:

  • fix (#840) Fixed issue with BugView not showing
  • fix (#841) Fixed the exception view which was not displaying the results properly

What's new in 3.0.4

Version 3.0.4 launched on March 30, 2017. Here is what's new:

  • new #756 Allow choosing an AnalysisStrategy for reports
  • new #794 Add ability to see the MongoDb's project and report id from ExtentXReporter
  • new #792 Show PASS, INFO and DEBUG status log messages for BDD style reports
  • new #791 Added a timestamp format configuration for HTMLReporter
    
        htmlReporter.config().setTimeStampFormat("mm/dd/yyyy hh:mm:ss a");
    		
  • new #819 All ExtentXReporter to upload media for logs to ExtentX server
  • new #832 Add ability to remove tests
  • fix (#811) Fixed the search text color in the Dark theme
  • fix (#815) End time on tests not being set correctly - total time taken incorrect for tests created using a listener
  • fix (#818) Fix filters for Category view
  • fix (#820) Fix styling for category and author labels
  • fix (#823) In case of long text parent test column distorted (Exception view)
  • fix (#835) Fixed the width for the first 2 columns specific to particular table instead of applying to all table
  • fix (#836) Fixed the exception view where it was showing only the last exception

License

View license for your relevant version here.