Jest's HTML report is missing, blank, or says nothing useful
There are two popular packages with almost the same name, and most of the confusion starts there:
| Package | Output by default | Single file? |
|---|---|---|
jest-html-reporter (singular) |
./test-report.html |
Yes, CSS inlined |
jest-html-reporters (plural) |
./jest_html_reporters.html plus a *-attach folder |
Only with inlineSource: true |
Work out which one is in package.json before reading further. The fixes
differ.
Symptom → cause
| What you see | Most likely cause |
|---|---|
| No HTML file at all | Reporter never ran: CLI --reporters, projects, wrong config |
| HTML file exists, terminal output vanished | reporters array without "default" |
| File is there locally, missing from the CI artifact | outputPath relative to a different working directory |
| Plural package: blank page once moved or uploaded | -attach folder left behind; inlineSource is off |
| Singular package: "failed" with no message | includeFailureMsg defaults to false |
Vitest html/index.html blank |
Page fetches its data; needs an HTTP server |
| Report only from the last CI step / partial suite | Job killed (timeout, OOM, process.exit) before Jest finished |
1. The reporter never ran
Jest calls reporters at the end of the run. If nothing was written, Jest didn't load the reporter. Three ways that happens:
The command line replaced it. --reporters on the CLI overrides the
reporters key in your config, it doesn't add to it. A CI script with
jest --ci --reporters=default --reporters=jest-junit silently drops the
HTML reporter you configured.
It's inside projects. reporters is a global option. In a monorepo
config with projects: [...], a reporters key inside a project entry is
ignored. Put it at the top level of the root config.
Jest read a different config. A jest key in package.json and a
jest.config.js in the same folder is an error (Multiple configurations found). A --config flag in CI, or running from a subfolder, can pick up
a file you didn't edit.
Check what Jest actually resolved:
npx jest --showConfig | grep -A5 '"reporters"'
If your reporter isn't in that list, nothing below matters yet.
2. You lost the terminal output
Setting reporters replaces Jest's defaults rather than adding to them.
Keep the console summary with "default":
// jest.config.js
module.exports = {
reporters: [
"default",
[
"jest-html-reporter",
{
pageTitle: "Test report",
outputPath: "reports/test-report.html",
includeFailureMsg: true,
includeSuiteFailure: true,
},
],
],
};
Older setups used testResultsProcessor: "./node_modules/jest-html-reporter".
That still exists, but the reporters form is the one to use; don't
configure both.
3. The file is local-only
outputPath is resolved from the directory Jest runs in. In a monorepo
where CI runs npm test -w packages/web, the report lands in
packages/web/reports/, while the artifact step uploads reports/ at the
repo root and finds nothing. Use an absolute path in CI, or point the
upload at the package folder. jest-html-reporter also reads environment
variables such as JEST_HTML_REPORTER_OUTPUT_PATH, which is handy when
only CI needs a different location.
4. jest-html-reporters opens blank
The plural package writes the page and a sibling folder (*-attach) that
holds the run data and any attachments. The page loads that folder when it
opens. Email the HTML, upload it alone, or open it from inside a zip
preview, and you get the app shell with no tests in it.
reporters: [
"default",
[
"jest-html-reporters",
{
publicPath: "./reports",
filename: "jest-report.html",
inlineSource: true, // one file, no -attach folder needed for the data
expand: false,
},
],
];
Attachments added with addAttach (screenshots from Puppeteer or
Playwright-in-Jest) are still files on disk. If you need them in the shared
copy, pass them as base64 or keep the folder with the report. The general
rule is the same one behind most broken reports: a report is the
directory, not the file.
5. "Failed" with no reason
In jest-html-reporter, includeFailureMsg defaults to false. The
report shows a red row and no assertion message, so whoever opens it still
has to go and find the CI log. Turn it on, along with:
includeSuiteFailure: true— suites that fail to even load (a syntax error, a bad import) otherwise show up as an empty suite.includeConsoleLog: true—console.logoutput per suite.executionTimeWarningThreshold— highlights slow tests, useful when hunting flaky tests.
6. Vitest's HTML report is blank
vitest --reporter=html (it needs @vitest/ui installed) writes an
html/ directory. Its index.html fetches the results at view time, so
opening it from disk hits the file:// origin rules
and renders an empty UI. The Vitest docs serve it instead:
npx vite preview --outDir ./html
That works on your machine but not as something to send. For a shareable view of a Vitest run, emit JUnit XML and render it:
npx vitest run --reporter=default --reporter=junit --outputFile.junit=junit.xml
Paste junit.xml into the JUnit XML viewer to
get a static HTML report with failures grouped by suite — one file, no
fetches.
7. Half a report, or none, after a CI timeout
Reporters write when the run completes. A job killed by the CI step
timeout, the OOM killer, or a test calling process.exit() never gets
there. Signs: the HTML file is missing only on the runs where the job was
red for "cancelled" or "exit code 137". Give the job more headroom,
shard with --shard=1/3, or use --maxWorkers=50% on small runners.
And make the upload step unconditional (if: always() in GitHub Actions),
or the report never leaves the runner on exactly the runs that failed.
Checklist
- Reporter shows up in
npx jest --showConfig -
"default"is still inreporters - Single file: singular package, or
inlineSource: trueon the plural -
includeFailureMsg: true -
outputPathmatches the path your artifact step uploads - Upload step runs on failure
Sending it to someone
Once the report is one file, it can live behind a link. Comma stores the
HTML as-is and renders it in a sandboxed frame with scripts allowed, so the
jest-html-reporters React UI works when it's built with inlineSource: true. Teammates open one URL without a CI login; commenting needs a free
sign-in. Each run can land as a new revision of the same report with one
curl from CI — see publish from CI. One limit to know: a
report that loads its data from sibling files at view time (the default
plural setup, Vitest html) won't load it inside Comma either. Make it a
single file first.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.
Related
- Share a Jest or Vitest report
- Test reporter — what a reporter is, and running more than one
- JUnit XML viewer
- pytest-html report has no CSS · Playwright report is blank
- Cypress mochawesome shows only the last spec