Testing recipe · Published October 16, 2026 · 8 min read
Screenshot on Failure in Playwright, Cypress & Selenium: 5-Minute Setup
Quick answer
Playwright: set use: { screenshot: "only-on-failure" }. Cypress: nothing to do, cypress run already saves a screenshot for each failed test. Selenium and WebdriverIO: add a hook that calls the screenshot method when a test fails, before the browser is closed. Then upload the screenshot folder as a CI artifact, or it disappears with the runner.
A red build with "element not found" tells you very little. A screenshot of the page at that moment usually shows the reason in a second: a cookie banner, a login wall, an error page, a layout that wrapped. This page collects the setup for the four most common frameworks in one place, plus the part people forget: getting the image out of CI.
The matrix
| Framework | On by default? | How to enable | Where files go |
|---|---|---|---|
| Playwright Test | No (off) | screenshot: "only-on-failure" | test-results/ + HTML report |
| Cypress | Yes, in cypress run | screenshotOnRunFailure: true (default) | cypress/screenshots/ |
| Selenium (pytest, JUnit, TestNG) | No | Test framework hook | Wherever your hook writes |
| WebdriverIO | No | afterTest hook | Wherever your hook writes |
1Playwright Test
The screenshot modes are off, on, only-on-failure and on-first-failure. Pairing the screenshot with trace: "retain-on-failure" gives you a step-by-step recording next to the image, which is usually enough to fix a flaky test without rerunning it. Every screenshot option, including masks for dynamic content, is in our Playwright screenshot guide. Files land in the test output directory, typically test-results/. For Python, pytest-playwright has the same switch: pytest --screenshot only-on-failure.
2Cypress
Cypress saves failure screenshots automatically during cypress run, named {testName} (failed).png inside a folder per spec. Two details from the Cypress docs catch people out:
- Failure screenshots always use
capture: "runner", so the image includes the Command Log next to your app. That is useful for debugging but not for sharing with designers. - In interactive
cypress openmode no failure screenshot is written.
Need a clean image of the app at a specific step? Call cy.screenshot("checkout-summary", { capture: "fullPage" }) yourself.
3Selenium
The rule for every Selenium setup: take the screenshot before the driver is quit. In pytest the call report is created before fixture teardown, so the snippet above works with a normal driver fixture (we ran it against headless Chrome). For JUnit 5 use AfterTestExecutionCallback, for TestNG an ITestListener; both versions, plus full-page capture in Chrome, are in our Selenium screenshot guide.
4WebdriverIO
The afterTest hook receives passed and error in its third argument. The docs note this hook runs for Mocha and Jasmine; with Cucumber use afterScenario instead. Create the screenshots folder in your CI step or in the onPrepare hook.
Keep the screenshots after CI finishes
CI runners are thrown away after the job. Without an artifact step, every screenshot above is deleted with them.
.github/workflows/e2e.ymlif: failure() and when: on_failure keep artifact storage small: images are uploaded only when something broke.
What makes a failure screenshot useful
| Do | Why |
|---|---|
| Capture the full page | The error message is often below the fold. |
| Name files after the test, and the retry | Parallel workers and retries otherwise overwrite each other. |
| Keep the URL with the image | A screenshot of a 404 looks like a broken layout without context. |
| Attach it to the report | Nobody downloads a zip of 40 PNGs; the HTML report shows the right one. |
| Use a fixed viewport | Local and CI screenshots stay comparable. |
Takeaways
- Playwright: one config line. Cypress: already on in
cypress run. Selenium and WebdriverIO: a hook. - Capture before teardown closes the browser.
- Upload screenshots with
if: failure(), or they vanish with the runner. - Cypress empties its screenshot folder before each run by default.
Sources
FAQ
Does Playwright take screenshots on failure by default?
No. The screenshot option defaults to off. Set use: { screenshot: 'only-on-failure' } in playwright.config.ts to capture one after every failed test.
Does Cypress take screenshots on failure by default?
Yes, during cypress run. screenshotOnRunFailure defaults to true and files go to cypress/screenshots. In interactive cypress open mode no failure screenshot is saved.
Why are my Cypress failure screenshots gone after the next run?
trashAssetsBeforeRuns defaults to true, so cypress run empties the screenshots, videos and downloads folders before each run. Upload them as CI artifacts or set the option to false.
Why does my Selenium failure screenshot throw an error?
Usually the driver was already closed by teardown when the hook ran. Take the screenshot in a hook that runs before teardown, such as pytest_runtest_makereport for the call phase or JUnit 5 AfterTestExecutionCallback.
How do I keep failure screenshots in GitHub Actions?
Add an actions/upload-artifact step with if: failure() and point path at the screenshot folder, for example test-results/ for Playwright or cypress/screenshots for Cypress.
Capture production on a schedule, too
Failure screenshots tell you what broke in CI. Scheduled captures and visual diffs tell you what changed on the live site. Both work from one HTTP API. The free tier gives you 300 screenshots, no credit card required.