Skip to main content
This lesson shows how to enable JUnit test reports and publish HTML artifacts (coverage and security reports) from a Jenkins pipeline. In a previous pipeline run the coverage, dependency, and vulnerability-reporting steps were producing HTML outputs, but the publish/archive steps were commented out — so the reports never appeared in the Jenkins UI. Below are the key details, the fixed Jenkinsfile fragment (with report publishing enabled), console excerpts, and screenshots illustrating the published reports.

Problem summary

  • Unit tests, coverage, and vulnerability scans were executed, but Jenkins did not display the results because the JUnit archive and HTML publish steps were commented out.
  • Tools involved:
    • Trivy (image vulnerability scanner) — produces JSON and can convert it to HTML.
    • OWASP Dependency Check — produces XML and HTML reports.
    • Istanbul/nyc — produces coverage HTML (lcov-report).
    • Test runner — should produce JUnit XML results.
Example console excerpt from the previous run (showing Trivy converting JSON to HTML):
nodejs-22-6-0 — Use a tool from a predefined Tool Installation <1s
Fetches the environment variables for a given tool in a list of 'FOO=bar' strings suitable for the withEnv step. <1s
trivy image siddharth67/solar-system:$GIT_COMMIT --severity CRITICAL --exit-code 1 --quiet --format json -o trivy-image-CRITICAL-results.json — Shell Script 39s
trivy convert --format template --template "@/usr/local/share/trivy/templates/html.tpl" --output trivy-image-CRITICAL-results.html trivy-image-CRITICAL-results.json — Shell Script <1s

Fixed Jenkinsfile fragment

Below is a consolidated Jenkinsfile fragment with the publish/archive steps uncommented and adjusted. It focuses on Unit Tests, Code Coverage (Istanbul), Build/Push, Trivy scanning, and OWASP Dependency Check publishing.
pipeline {
    agent any
    environment {
        GIT_COMMIT = "${env.GIT_COMMIT}"
    }
    stages {
        stage('Unit Tests') {
            steps {
                sh 'npm test'
                // Archive JUnit-style test results (adjust pattern to match your test runner output)
                junit 'test-results/**/*.xml'
            }
        }

        stage('Code Coverage') {
            agent {
                docker {
                    image 'node:24'
                    args '-u root:root'
                }
            }
            steps {
                catchError(buildResult: 'SUCCESS', message: 'Oops! it will be fixed in future releases', stageResult: 'UNSTABLE') {
                    sh 'npm run coverage'
                }
                // Publish coverage HTML report produced by Istanbul/nyc
                publishHTML([
                    allowMissing: true,
                    alwaysLinkToLastBuild: true,
                    keepAll: true,
                    reportDir: 'coverage/lcov-report',
                    reportFiles: 'index.html',
                    reportName: 'Code Coverage'
                ])
            }
        }

        stage('Build Publish Image') {
            steps {
                sh 'docker build -t siddharth67/solar-system:$GIT_COMMIT .'
                withDockerRegistry(credentialsId: 'docker-hub-credentials', url: "") {
                    sh 'docker push siddharth67/solar-system:$GIT_COMMIT'
                }
            }
        }

        stage('Trivy Vulnerability Scanner') {
            steps {
                // Scan the built image and output JSON results
                sh '''
                    trivy image siddharth67/solar-system:$GIT_COMMIT --severity CRITICAL --exit-code 1 --quiet --format json -o trivy-image-CRITICAL-results.json
                '''
                // Convert JSON to an HTML report using the provided template
                sh '''
                    trivy convert --format template --template "@/usr/local/share/trivy/templates/html.tpl" --output trivy-image-CRITICAL-results.html trivy-image-CRITICAL-results.json
                '''
                publishHTML([
                    allowMissing: true,
                    alwaysLinkToLastBuild: true,
                    keepAll: true,
                    reportDir: '.',
                    reportFiles: 'trivy-image-CRITICAL-results.html',
                    reportName: 'Trivy Image Scan'
                ])
            }
        }

        stage('Security - Dependency Scans') {
            parallel {
                stage('NPM Dependency Audit') {
                    steps {
                        sh '''
                            npm audit --audit-level=critical || true
                        '''
                    }
                }

                stage('OWASP Dependency Check') {
                    steps {
                        dependencyCheck additionalArguments: """
                            --scan './'
                            --out './'
                            --format 'ALL'
                            --disableYarnAudit
                            --prettyPrint --failOnCVSS 9
                        """, nvdCredentialsId: 'owasp-dependency-check', odcInstallation: 'OWASP-DepCheck-10'
                        dependencyCheckPublisher failedTotalCritical: 1, pattern: 'dependency-check-report.xml', stopBuild: true
                        publishHTML([
                            allowMissing: true,
                            alwaysLinkToLastBuild: true,
                            keepAll: true,
                            reportDir: './',
                            reportFiles: 'dependency-check-jenkins.html',
                            reportName: 'OWASP Dependency Check'
                        ])
                    }
                }
            }
        }
    }
}
After committing these changes to the main branch, a new build was triggered and the pipeline completed successfully — including the publishing of HTML reports.
Screenshot of a Jenkins Blue Ocean pipeline run for "ci-pipeline-poll-scm" showing pipeline stages and progress. The Dependency Scanning (including OWASP) completed successfully while Code Coverage shows a warning, and a list of completed dependency-scan steps is shown below.

What Jenkins published

  • JUnit test results were archived and shown in the Tests page (Blue Ocean and classic UI).
  • Istanbul (nyc) coverage HTML report was published under coverage/lcov-report/index.html.
  • Trivy produced a JSON results file that was converted into an HTML report and published.
  • OWASP Dependency Check HTML report was published and made available under Artifacts.
Console excerpt for the Code Coverage stage:
Code Coverage - 19s
> Check out from version control
> Checks if running on a Unix-like node
> docker inspect -f . "$JD_TO_RUN" — Shell Script
> nodejs-22-6-0 — Use a tool from a predefined Tool Installation
> Fetches the environment variables for a given tool in a list of 'FOO=bar' strings suitable for the withEnv step.
> npm run coverage — Shell Script
> Publish HTML reports
Open the Tests page (Blue Ocean) or the classic UI to view detailed test results (test counts, durations, and per-test metadata). For this run all tests passed (11 tests total).
A screenshot of the Jenkins CI web interface showing a test results page marked "Passed" with a left-hand navigation menu of reports and pipeline options. The main panel displays a test case about checking a liveness endpoint and a button to add a description.

Published artifacts (example locations)

Report typeFile / PathDescription
OWASP Dependency Check (HTML)dependency-check-jenkins.htmlFull dependency scanning report and vulnerability summary.
Code Coverage (Istanbul)coverage/lcov-report/index.htmlPer-file code coverage with highlighted missed lines/branches.
Trivy (image scan)trivy-image-CRITICAL-results.htmlImage vulnerability summary converted from Trivy JSON output.
JUnit test resultstest-results/**/*.xmlJUnit-style XML results used by Jenkins to display test summaries.
A Jenkins Dependency-Check HTML report for project "ci-pipeline-poll-scm #27" showing scan information, totals for dependencies and vulnerabilities, and a summary table. It lists a vulnerable package (formidable@2.1.2) with details like severity, CVE counts and file path.
The OWASP Dependency Check report in this run highlighted some medium severity findings. Because the pipeline is configured with --failOnCVSS 9 (or the corresponding publisher threshold), the build only fails on critical CVSS >= 9 — medium findings do not fail the pipeline.
A browser screenshot of a code coverage report (Istanbul) showing coverage for app.js, with statements ~79.54%, branches 33.33%, functions 70% and lines ~79.06%. The page lists file metrics in a table under "All files."
The Istanbul coverage report provides file-level details (for example, app.js shown at ~80% coverage) with marked statements and branches that need additional tests. Console excerpt for the Trivy stage showing JSON output and conversion to HTML:
Trivy Vulnerability Scanner - 12s
> nodejs-22-6-0 — Use a tool from a predefined Tool Installation <1s
> Fetches the environment variables for a given tool in a list of 'FOO=bar' strings suitable for the withEnv step. <1s
> trivy image siddharth67/solar-system:$GIT_COMMIT --severity CRITICAL --exit-code 1 --quiet --format json -o trivy-image-CRITICAL-results.json — Shell Script 11s
> trivy convert --format template --template "@/usr/local/share/trivy/templates/html.tpl" --output trivy-image-CRITICAL-results.html trivy-image-CRITICAL-results.json — Shell Script <1s
> Publish HTML reports <1s

Tips and best practices

  • Ensure your test runner produces JUnit XML. Configure the junit step to match the test output path (e.g. test-results/**/*.xml).
  • For HTML publishing, always confirm reportDir and reportFiles point to the generated files. Use allowMissing: true to avoid breaking builds when a report is not generated.
  • Set security thresholds deliberately. In CI you may want to fail only on high/critical severity and publish informational reports for lower severities.
  • Consider archiving both raw JSON/XML outputs and the converted HTML so you can reprocess raw outputs later.
When publishing HTML reports from Jenkins, use allowMissing: true to avoid failing the build if a report is not produced. Also verify reportDir and reportFiles paths match the files generated by your tools.
These published artifacts improve visibility into test outcomes, coverage gaps, and security issues directly from the Jenkins build. If you plan to migrate to GitHub Actions, the GitHub Actions Importer can help transfer these stages and artifacts to workflows.

Watch Video

Practice Lab