com.seleniumtests.reporter.reporters.SeleniumTestsReporter2.java Source code

Java tutorial

Introduction

Here is the source code for com.seleniumtests.reporter.reporters.SeleniumTestsReporter2.java

Source

/**
 * Orignal work: Copyright 2015 www.seleniumtests.com
 * Modified work: Copyright 2016 www.infotel.com
 *             Copyright 2017-2019 B.Hecquet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.seleniumtests.reporter.reporters;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.testng.IReporter;
import org.testng.IResultMap;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.xml.XmlSuite;

import com.seleniumtests.core.SeleniumTestsContext;
import com.seleniumtests.core.SeleniumTestsContextManager;
import com.seleniumtests.core.SeleniumTestsPageListener;
import com.seleniumtests.core.runner.SeleniumRobotTestListener;
import com.seleniumtests.driver.DriverMode;
import com.seleniumtests.driver.TestType;
import com.seleniumtests.reporter.PluginsHelper;
import com.seleniumtests.reporter.logger.TestLogging;
import com.seleniumtests.reporter.logger.TestStep;
import com.seleniumtests.util.logging.SeleniumRobotLogger;

/**
 * Class for generating test report
 * @author behe
 *
 */
public class SeleniumTestsReporter2 extends CommonReporter implements IReporter {

    private static final String STATUS = "status";
    private static final String HEADER = "header";
    private static final String APPLICATION = "application";
    private static final String APPLICATION_TYPE = "applicationType";
    private static final String METHOD_RESULT_FILE_NAME = "methodResultFileName";

    protected PrintWriter mOut;

    private String outputDirectory;
    private String resources;
    private String generationErrorMessage = null;

    /**
     * Copy resources necessary for result file
     * @throws IOException
     */
    public void copyResources() throws IOException {

        List<String> styleFiles = Arrays.asList("seleniumRobot.css", "app.min.js", "seleniumRobot_solo.css",
                "seleniumtests_test1.gif", "seleniumtests_test2.gif", "seleniumtests_test3.gif",
                "seleniumRobot.js");
        styleFiles = new ArrayList<>(styleFiles);

        if (!SeleniumTestsContextManager.getGlobalContext().getOptimizeReports()) {
            styleFiles.add("bootstrap.min.css");
            styleFiles.add("bootstrap.min.js");
            styleFiles.add("Chart.min.js");
            styleFiles.add("jQuery-2.2.0.min.js");
            styleFiles.add("AdminLTE.min.css");
            styleFiles.add("lobsterTwo.css");
            styleFiles.add("css/font-awesome.min.css");
            styleFiles.add("fonts/fontawesome-webfont.eot");
            styleFiles.add("fonts/fontawesome-webfont.svg");
            styleFiles.add("fonts/fontawesome-webfont.ttf");
            styleFiles.add("fonts/fontawesome-webfont.woff");
            styleFiles.add("fonts/fontawesome-webfont.woff2");
            styleFiles.add("fonts/FontAwesome.otf");
        }

        for (String fileName : styleFiles) {
            FileUtils.copyInputStreamToFile(
                    Thread.currentThread().getContextClassLoader()
                            .getResourceAsStream("reporter/templates/" + fileName),
                    Paths.get(outputDirectory, RESOURCES_DIR, "templates", fileName).toFile());
        }
    }

    /**
     * Completes HTML stream.
     *
     * @param  out
     */
    protected void endHtml() {
        //Add footer

        try {
            VelocityEngine ve = initVelocityEngine();

            Template t = ve.getTemplate("reporter/templates/report.part.test.footer.vm");
            StringWriter writer = new StringWriter();
            VelocityContext context = new VelocityContext();
            t.merge(context, writer);

            mOut.write(writer.toString());
            mOut.flush();
            mOut.close();
        } catch (Exception e) {
            logger.error("error writing result file end: " + e.getMessage());
        }
    }

    /**
     * Generate result for a single test method
     * @param ve         velocity engine used to generate file
     * @param testResult   result for this test method
     */
    public void generatePanel(final VelocityEngine ve, final ITestResult testResult) {

        try {
            Template t = ve.getTemplate("reporter/templates/report.part.test.step.vm");
            VelocityContext context = new VelocityContext();

            List<TestStep> testSteps = TestLogging.getTestsSteps().get(testResult);
            if (testSteps == null) {
                return;
            }

            for (TestStep testStep : testSteps) {

                TestStep encodedTestStep = testStep.encode("html");
                // step status
                if (encodedTestStep.getFailed()) {
                    context.put(STATUS, FAILED_TEST);
                } else {
                    context.put(STATUS, PASSED_TEST);
                }

                context.put("stepName", encodedTestStep.getName());
                context.put("stepDuration", encodedTestStep.getDuration() / (double) 1000);
                context.put("step", encodedTestStep);

                StringWriter writer = new StringWriter();
                t.merge(context, writer);
                mOut.write(writer.toString());
            }

        } catch (Exception e) {
            generationErrorMessage = "generatePanel, Exception creating a singleTest:" + e.getMessage();
            logger.error("Exception creating a singleTest.", e);
        }
    }

    /**
     * Generate result for a single test method
     * @param ve         velocity engine used to generate file
     * @param testResult   result for this test method
     */
    public void generateExecutionLogs(final VelocityEngine ve, final ITestResult testResult) {

        try {
            Template t = ve.getTemplate("reporter/templates/report.part.test.logs.vm");
            VelocityContext context = new VelocityContext();

            // add logs
            String logs = SeleniumRobotLogger.getTestLogs().get(getTestName(testResult));
            if (logs == null) {
                logs = "";
            }

            // exception handling
            String[] stack = null;
            if (testResult.getThrowable() != null) {
                StringBuilder stackString = new StringBuilder();
                generateTheStackTrace(testResult.getThrowable(), testResult.getThrowable().getMessage(),
                        stackString, "html");
                stack = stackString.toString().split("\n");
            }

            // encode logs
            List<String> logLines = new ArrayList<>();
            for (String line : logs.split("\n")) {
                logLines.add(StringEscapeUtils.escapeHtml4(line));
            }
            context.put(STATUS, getTestStatus(testResult));
            context.put("stacktrace", stack);
            context.put("logs", logLines);

            StringWriter writer = new StringWriter();
            t.merge(context, writer);
            mOut.write(writer.toString());

        } catch (Exception e) {
            generationErrorMessage = "generateExecutionLogs, Exception creating execution logs:" + e.getMessage();
            logger.error("Exception creating execution logs.", e);
        }
    }

    /**
     * Returns the test status as a string
     * @param testResult
     * @return
     */
    private String getTestStatus(ITestResult testResult) {
        String testStatus = SKIPPED_TEST;
        if (testResult.getStatus() == ITestResult.SUCCESS) {
            testStatus = PASSED_TEST;
        } else if (testResult.getStatus() == ITestResult.FAILURE) {
            testStatus = FAILED_TEST;
        }
        return testStatus;
    }

    /**
     * Generate all test reports
     */
    @Override
    public void generateReport(final List<XmlSuite> xml, final List<ISuite> suites, final String outdir) {
        ITestContext testCtx = SeleniumTestsContextManager.getGlobalContext().getTestNGContext();
        if (testCtx == null) {
            logger.error("Looks like your class does not extend from SeleniumTestPlan!");
            return;
        }

        File f = new File(SeleniumTestsContextManager.getGlobalContext().getOutputDirectory());
        setOutputDirectory(f.getAbsolutePath());
        setResources(getOutputDirectory() + "/" + RESOURCES_DIR);

        // Generate general report
        Map<ITestContext, List<ITestResult>> methodResultsMap = new HashMap<>();
        try {
            mOut = createWriter(getOutputDirectory(), "SeleniumTestReport.html");
            startHtml(null, mOut, "complete");
            methodResultsMap = generateSuiteSummaryReport(suites);
            endHtml();
            mOut.flush();
            mOut.close();
            copyResources();
            logger.info("Completed Report Generation.");

        } catch (IOException e) {
            logger.error("Error writing summary report", e);
        }

        // Generate test method reports
        for (Map.Entry<ITestContext, List<ITestResult>> entry : methodResultsMap.entrySet()) {

            for (ITestResult testResult : entry.getValue()) {

                // issue #81: recreate test context from this context (due to multithreading, this context may be null if parallel testing is done
                SeleniumTestsContextManager.setThreadContextFromTestResult(entry.getKey(), getTestName(testResult),
                        getClassName(testResult), testResult);

                try {
                    mOut = createWriter(SeleniumTestsContextManager.getThreadContext().getOutputDirectory(),
                            "TestReport.html");
                    startHtml(getTestStatus(testResult), mOut, "simple");
                    generateExecutionReport(testResult);
                    endHtml();
                    logger.info("Completed Report Generation.");
                } catch (IOException e) {
                    logger.error("Error writing test report: " + getTestName(testResult), e);
                }
            }
        }

    }

    /**
     * Generate summary report for all test methods
     * @param suites
     * @param suiteName
     * @param map
     * @return   map containing test results
     */
    public Map<ITestContext, List<ITestResult>> generateSuiteSummaryReport(final List<ISuite> suites) {

        // build result list for each TestNG test
        Map<ITestContext, List<ITestResult>> methodResultsMap = new LinkedHashMap<>();

        for (ISuite suite : suites) {
            Map<String, ISuiteResult> tests = suite.getResults();
            for (ISuiteResult r : tests.values()) {
                ITestContext context = r.getTestContext();
                List<ITestResult> resultList = new ArrayList<>();

                Collection<ITestResult> methodResults = new ArrayList<>();
                methodResults.addAll(context.getFailedTests().getAllResults());
                methodResults.addAll(context.getPassedTests().getAllResults());
                methodResults.addAll(context.getSkippedTests().getAllResults());

                methodResults = methodResults.stream()
                        .sorted((r1, r2) -> Long.compare(r1.getStartMillis(), r2.getStartMillis()))
                        .collect(Collectors.toList());

                for (ITestResult result : methodResults) {
                    SeleniumTestsContext testContext = (SeleniumTestsContext) result
                            .getAttribute(SeleniumRobotTestListener.TEST_CONTEXT);

                    String fileName;
                    if (testContext != null) {
                        fileName = testContext.getRelativeOutputDir() + "/TestReport.html";
                    } else {
                        fileName = getTestName(result) + "/TestReport.html";
                    }
                    result.setAttribute(METHOD_RESULT_FILE_NAME, fileName);
                    result.setAttribute(SeleniumRobotLogger.UNIQUE_METHOD_NAME, getTestName(result));

                }
                resultList.addAll(methodResults);

                methodResultsMap.put(context, resultList);
            }
        }

        try {
            VelocityEngine ve = initVelocityEngine();

            Template t = ve.getTemplate("/reporter/templates/report.part.suiteSummary.vm");
            VelocityContext context = new VelocityContext();

            context.put("tests", methodResultsMap);
            context.put("steps", TestLogging.getTestsSteps());

            StringWriter writer = new StringWriter();
            t.merge(context, writer);
            mOut.write(writer.toString());

        } catch (Exception e) {
            generationErrorMessage = "generateSuiteSummaryReport error:" + e.getMessage();
            logger.error("generateSuiteSummaryReport error: ", e);
        }

        return methodResultsMap;

    }

    /**
     * @param   tests
     *
     * @return
     */
    protected Collection<ITestResult> getResultSet(final IResultMap tests, final ITestNGMethod method) {
        Set<ITestResult> r = new TreeSet<>();
        for (ITestResult result : tests.getAllResults()) {
            if (result.getMethod().getMethodName().equals(method.getMethodName())) {
                r.add(result);
            }
        }

        return r;
    }

    public void setOutputDirectory(final String outtimestamped) {
        this.outputDirectory = outtimestamped;
    }

    public void setResources(final String resources) {
        this.resources = resources;
    }

    public String getOutputDirectory() {
        return outputDirectory;
    }

    public String getResources() {
        return resources;
    }

    /**
     * Begin HTML file
     * @param testPassed   true if test is OK, false if test is KO, null if test is skipped
     * @param out
     * @param type
     */
    protected void startHtml(final String testStatus, final PrintWriter out, final String type) {
        try {
            VelocityEngine ve = initVelocityEngine();

            Template t = ve.getTemplate("/reporter/templates/report.part.header.vm");
            VelocityContext context = new VelocityContext();

            String userName = System.getProperty("user.name");
            context.put("userName", userName);
            context.put("staticPathPrefix", "complete".equals(type) ? "" : "../");

            // optimize reports means that resources are get from internet
            context.put("localResources", !SeleniumTestsContextManager.getGlobalContext().getOptimizeReports());
            context.put("currentDate", new Date().toString());

            DriverMode mode = SeleniumTestsContextManager.getGlobalContext().getRunMode();
            List<String> hubUrls = SeleniumTestsContextManager.getGlobalContext().getWebDriverGrid();
            String hubLink = "";
            for (String hubUrl : hubUrls) {
                hubLink += "<a href='" + hubUrl + "' target=hub>" + hubUrl + "</a>";
            }
            context.put("gridHub", hubLink);

            context.put("mode", mode.toString());

            StringBuilder sbGroups = new StringBuilder();
            sbGroups.append("envt,test");

            List<SeleniumTestsPageListener> pageListenerList = PluginsHelper.getInstance().getPageListeners();
            if (pageListenerList != null && !pageListenerList.isEmpty()) {
                for (SeleniumTestsPageListener abstractPageListener : pageListenerList) {
                    sbGroups.append(",").append(abstractPageListener.getClass().getSimpleName());
                }
            }
            context.put("groups", sbGroups.toString());
            context.put("report", type);

            if (type == "simple") {
                context.put(HEADER, testStatus);
            }
            StringWriter writer = new StringWriter();
            t.merge(context, writer);
            out.write(writer.toString());

        } catch (Exception e) {
            generationErrorMessage = "startHtml error:" + e.getMessage();
            logger.error("startHtml error:", e);
        }

    }

    public String getGenerationErrorMessage() {
        return generationErrorMessage;
    }

    /**
     * Fill velocity context with test context
     * @param velocityContext
     */
    private void fillContextWithTestParams(VelocityContext velocityContext) {
        SeleniumTestsContext selTestContext = SeleniumTestsContextManager.getThreadContext();

        if (selTestContext != null) {
            String browser = selTestContext.getBrowser().getBrowserType();

            String app = selTestContext.getApp();
            String appPackage = selTestContext.getAppPackage();
            TestType testType = selTestContext.getTestType();

            if (browser != null) {
                browser = browser.replace("*", "");
            }

            String browserVersion = selTestContext.getWebBrowserVersion();
            if (browserVersion != null) {
                browser = browser + browserVersion;
            }
            velocityContext.put(APPLICATION, "");

            if (testType == null) {
                velocityContext.put(APPLICATION_TYPE, "Error in initialization");

                // Log URL for web test and app info for app test
            } else if (testType.family().equals(TestType.WEB)) {
                velocityContext.put(APPLICATION_TYPE, "Browser :");
                velocityContext.put(APPLICATION, browser);
            } else if (testType.family().equals(TestType.APP)) {

                // Either app Or app package and app activity is specified to run test on app
                if (StringUtils.isNotBlank(appPackage)) {
                    velocityContext.put(APPLICATION_TYPE, "App Package :");
                    velocityContext.put(APPLICATION, appPackage);
                } else if (StringUtils.isNotBlank(app)) {
                    velocityContext.put(APPLICATION_TYPE, "App :");
                    velocityContext.put(APPLICATION, app);
                }
            } else if (testType.family().equals(TestType.NON_GUI)) {
                velocityContext.put(APPLICATION_TYPE, "");

            } else {
                velocityContext.put(APPLICATION_TYPE, "Invalid Test type");
            }
        }
    }

    /**
     * Method for generating a report for test method
     * @param suite         suite this test belongs to
     * @param testContext
     */
    public void generateExecutionReport(ITestResult testResult) {
        try {
            VelocityEngine ve = initVelocityEngine();
            Template t = ve.getTemplate("reporter/templates/report.part.test.vm");

            // create a context and add data
            VelocityContext velocityContext = new VelocityContext();

            Object[] testParameters = testResult.getParameters();
            StringBuilder testName = new StringBuilder(getTestName(testResult));

            // issue #163: add test parameter to test name
            if (testParameters.length > 0) {
                testName.append(" with params: (");

                int i = 0;

                for (Object param : testParameters) {
                    testName.append(param.toString());
                    if (i < testParameters.length - 1) {
                        testName.append(",");
                    }
                    i++;
                }
                testName.append(")");
            }

            velocityContext.put("testName", testName);
            velocityContext.put("description", testResult.getMethod().getDescription());

            // Application information
            fillContextWithTestParams(velocityContext);

            // write file
            StringWriter writer = new StringWriter();
            t.merge(velocityContext, writer);
            mOut.write(writer.toString());

            generatePanel(ve, testResult);
            generateExecutionLogs(ve, testResult);

        } catch (Exception e) {
            logger.error("Error generating execution report: " + e.getMessage());
        }
    }
}