Screen Recordings
Video of a test run — when it is worth the storage, how to configure it so it costs nothing on green runs, and why a trace is usually better.
2 min read · updated 19 September 2026
A video shows what happened, in order, at the speed it happened. For a flaky test that is sometimes exactly what you need — the thing that failed was a transition, a double-render, or a control that appeared and moved.
For most failures a trace is better, because it has the DOM, the network and the console alongside the picture. Treat video as a supplement.
#Configuration
// playwright.config.ts
use: {
video: {
mode: 'retain-on-failure', // 'on' | 'off' | 'on-first-retry' | 'retain-on-failure'
size: { width: 1280, height: 720 } // smaller than the viewport is fine
}
}The modes matter:
| Mode | Behaviour |
|---|---|
off |
no recording (default) |
on |
every test, kept — expensive |
retain-on-failure |
recorded always, deleted on pass |
on-first-retry |
recorded only on the retry — cheapest useful option |
on-first-retry is the right default for a suite with retries enabled: the
green path costs nothing, and the recording exists exactly when the test was
behaving unpredictably.
// Cypress: video is on by default in run mode and is usually worth turning off,
// because the screenshots and the command log are more useful.
module.exports = defineConfig({
video: false,
videoCompression: 32, // if you do keep it
videoUploadOnPasses: false
});#Recording only part of a test
Occasionally you want video of one section of a long journey:
test('the long checkout journey', async ({ browser }) => {
const context = await browser.newContext({
recordVideo: { dir: 'videos/', size: { width: 1280, height: 720 } }
});
const page = await context.newPage();
// ... the flow ...
// The file is finalised on close. Without this, the tail is missing.
await context.close();
console.log('video at', await page.video()?.path());
});That last point is the most common complaint about test video: the recording
stops mid-action because the process was killed. Close the context; do not
SIGKILL the runner in CI.
#Attaching it to the report
test.afterEach(async ({ page }, testInfo) => {
const video = page.video();
if (video && testInfo.status !== testInfo.expectedStatus) {
await testInfo.attach('video', { path: await video.path(), contentType: 'video/webm' });
}
});An attached video appears in the HTML report next to the failure, which is where someone will actually find it. A video in a directory in an artefact zip is a video nobody watches.
#Storage, honestly
A 30-second WebM at 1280×720 is roughly 1–3MB. A 400-test suite recording everything is over a gigabyte per run, uploaded on every build.
- uses: actions/upload-artifact@v4
if: failure()
with:
name: videos-${{ matrix.shard }}
path: test-results/**/*.webm
retention-days: 3 # a three-day-old video of a fixed failure is wasteSet a short retention. Nobody opens last week's video.
#Mobile
// Appium records the device screen natively.
await driver.startRecordingScreen({ videoType: 'mpeg4', timeLimit: 180 });
// ... the test ...
const base64 = await driver.stopRecordingScreen();
fs.writeFileSync(`artifacts/${testName}.mp4`, Buffer.from(base64, 'base64'));On mobile, video is relatively more valuable than on the web, because Appium has no trace equivalent and the accessibility-tree dump is much harder to read than a DOM snapshot.
#Where video genuinely wins
Timing and animation. A trace shows states; video shows the transition between them. A test failing because an element moved while being clicked is obvious on video and subtle in a trace.
Communication. A 20-second clip of the bug is something you can put in front of a designer, a product owner or a support engineer. A trace file requires the tooling and the vocabulary.
Mobile and desktop, where no trace exists.
Everywhere else, open the trace first.
Common questions
- Should I record video of every test run?
- No. Video on every test is large, slow to upload and rarely watched. Record on failure or on retry only, and prefer a trace where one is available.
- Video or trace?
- Trace, for almost every debugging task — it has the DOM, the network and the console alongside the visual, and you can step through it. Video is better for showing timing and animation, and for showing someone who does not use the test tooling.
- Why is my video missing the last few seconds?
- The recording is finalised when the browser context closes. If the process is killed, or the test ends abruptly, the tail is lost. Make sure the runner closes contexts cleanly, and do not SIGKILL the test process in CI.
Runnable samples for this page
last test results ↗- TypeScript (Playwright)
browser/tests/diagnostics/screen-recordings
Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.
Was this page useful?
Related topics
- ScreenshotsCapturing the screen at the moment of failure, and using screenshot comparison as visual regression testing — configuration, masking and the flakiness to design out.
- Traces and Debugging CI FailuresA trace records every action, request, console message and DOM snapshot of a run — the single artefact that turns an unreproducible CI failure into a five-minute diagnosis.
- PlaywrightThe default browser automation tool in 2026 — auto-waiting locators, tracing, sharding and fixtures — with the configuration that matters and the mistakes that still cause flakiness.
- CypressIn-browser test execution, automatic retry-ability and time-travel debugging — what Cypress's architecture buys, and the constraints that come with it.
- Flaky TestsWhy tests fail intermittently, the six root causes and how to fix each one, how to detect flakiness deliberately, and what to do with a test you cannot fix today.
- End-to-End TestingWhat belongs in an end-to-end suite and what does not, how many journeys are enough, and the practices that keep a browser suite from becoming the thing everyone ignores.