Skip to content
End To End Tester

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

typescript
// 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.

javascript
// 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:

typescript
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

typescript
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.

yaml
- 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 waste

Set a short retention. Nobody opens last week's video.

#Mobile

typescript
// 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 ↗

Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.

Was this page useful?