pytest-html's report lost its styling
The suite finishes, report.html exists, and the copy someone opens from
Slack, email or a CI artifact looks like a 1996 web page — or has a summary
line and no test rows at all. Two different causes; one fix covers both.
Cause 1: the assets/ folder stayed behind
pytest --html=report.html writes two things:
report.html
assets/style.css
The report links the stylesheet by relative path. Move, attach or upload
report.html alone and that path points at nothing. The browser falls back
to default styles: black serif text, borderless tables, no pass/fail colors.
The fix is one flag:
pytest --html=report.html --self-contained-html
The CSS is inlined, and so is anything you pass with --css custom.css. The
file stands on its own.
Cause 2: the table needs JavaScript (pytest-html 4+)
pytest-html 4.0 rewrote the report. The results table is no longer static HTML. The test data is embedded in the page as JSON, and a script builds the rows, filters and sort order when the page loads. That makes the report smaller and faster for big suites. It also means no script, no rows.
| Where it's opened | What blocks the script | What you see |
|---|---|---|
| Jenkins HTML Publisher | Default CSP: sandbox; default-src 'none'; ... |
No styles, no rows |
| Outlook / Gmail preview | Mail clients never run scripts | Summary only, or a download |
| Jira / Confluence / Drive file preview | Rendered as text or in a script-less sandbox | Source code or an empty shell |
| GitHub Actions artifact | Zip download; fine once unzipped and opened | Works locally, not as a link |
Your own <iframe sandbox> |
No allow-scripts |
Header and environment, no rows |
If you pinned pytest-html<4 to get a static table back, that works, but you
give up the v4 fixes. Better to open the report somewhere that runs its
scripts.
Jenkins specifically
Jenkins serves archived files with a strict Content-Security-Policy to stop
one job's HTML from attacking another user's session. It blocks the inline
<style> and <script> that pytest-html relies on. Your options, best first:
- Set a Resource Root URL (Manage Jenkins → System). Reports are then served from a separate origin where the CSP isn't applied.
- Publish the report outside Jenkins and link it from the build.
- Relax the CSP with
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")in the script console. It works, but it turns off the protection for every job on the controller, and it resets on restart unless you set it as a JVM flag.
More on that trade-off in Jenkins HTML reports.
Screenshots and attachments
Selenium and Playwright screenshots are the other thing that disappears.
# Linked — breaks when the report moves
extras.append(pytest_html.extras.image("screenshots/login.png"))
# Embedded — survives --self-contained-html
extras.append(pytest_html.extras.png(driver.get_screenshot_as_base64()))
When a path-based extra is left over, pytest-html warns
Self-contained HTML report includes link to external resource. That warning
is the list of things that will 404 for whoever opens the file. The same
goes for videos and log files. Embed them, or host them next to the report.
See images not showing in a sent HTML file for
the general case.
Checklist
-
--self-contained-htmlon the pytest command line (or inaddopts) - Screenshots added as base64, not file paths
- No
Self-contained HTML report includes link to external resourcewarnings in the run output - Opened somewhere that runs JavaScript, over
https://
# pytest.ini — so nobody forgets the flag
[pytest]
addopts = --html=report.html --self-contained-html
Getting it to people who don't have Jenkins
A self-contained report is one file, which makes it easy to put behind a
link. Comma stores the HTML as-is and renders it in a sandboxed frame with
scripts allowed. The v4 table, filters and collapsible tracebacks work. QA
leads, PMs and clients open one URL with no CI login. They can comment on a
failing test, and the next run lands as a new revision of the same report.
CI can publish it with one curl — see publish from CI.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.
Related
- Share a pytest report
- My HTML report lost its CSS
- Jenkins HTML reports
- JUnit XML viewer — for
pytest --junitxml=junit.xmlinstead - Playwright report is blank