<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vanessa Sastre</title>
    <description>The latest articles on DEV Community by Vanessa Sastre (@vanessa_sastre).</description>
    <link>https://dev.to/vanessa_sastre</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4040790%2F6d594ddf-2719-4694-a99e-2249f9b5effa.jpeg</url>
      <title>DEV Community: Vanessa Sastre</title>
      <link>https://dev.to/vanessa_sastre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vanessa_sastre"/>
    <language>en</language>
    <item>
      <title>Playwright Trace Viewer: Debug Failed Tests Without Reproducing Them</title>
      <dc:creator>Vanessa Sastre</dc:creator>
      <pubDate>Thu, 06 Aug 2026 21:57:33 +0000</pubDate>
      <link>https://dev.to/vanessa_sastre/playwright-trace-viewer-debug-failed-tests-without-reproducing-them-39o0</link>
      <guid>https://dev.to/vanessa_sastre/playwright-trace-viewer-debug-failed-tests-without-reproducing-them-39o0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Playwright Trace Viewer&lt;/strong&gt; is a built-in GUI that records a full, replayable snapshot of a test run — every action, DOM state, network call, and console log — so you can debug a CI failure by opening a file, not by reproducing it.&lt;/p&gt;

&lt;p&gt;In practice, that makes a UI test into more than a UI recording. Every browser interaction is synchronized with the underlying API traffic, console output, and page state at the moment it happened — which gives you an API debugging layer for free, without writing a single API test.&lt;/p&gt;

&lt;p&gt;That one difference changes the economics of test debugging. This post covers what changes in your day-to-day workflow, how to turn it on, and how to justify it to the people who care about engineering time and CI spend rather than DOM snapshots.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Debugging Requires Reproducing
&lt;/h2&gt;

&lt;p&gt;CI fails → Try locally → Can't reproduce → Add debugging → Implement a fix → Push → Wait for CI → Repeat&lt;/p&gt;

&lt;p&gt;A test fails on CI. Here's what that traditionally costs you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the error message and stack trace, which often aren't clear enough on their own.&lt;/li&gt;
&lt;li&gt;Pull the branch and try to reproduce the failure locally.&lt;/li&gt;
&lt;li&gt;It passes locally. CI-only failures are common — different browser, viewport, timing, environment, or data state.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;console.log&lt;/code&gt;, screenshots, tracing, or &lt;code&gt;page.pause()&lt;/code&gt; calls and run the test again.&lt;/li&gt;
&lt;li&gt;It still doesn't reproduce. Try running headed, slow down actions, add waits, or tweak the environment until it finally fails.&lt;/li&gt;
&lt;li&gt;Eventually reproduce the issue, identify the root cause, remove the debugging code, and implement a fix.&lt;/li&gt;
&lt;li&gt;Push the fix and wait for CI to run again. If the failure persists, repeat the process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Depending on the bug, that loop can take anywhere from a few minutes to most of an afternoon — and it scales linearly with how flaky or environment-dependent the failure is. Every step exists because you're missing information that already existed at the moment the test ran, and is now gone.&lt;/p&gt;

&lt;p&gt;Trace Viewer's premise: &lt;strong&gt;stop throwing that information away.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix: Open the Trace Instead of Re-Running the Test
&lt;/h2&gt;

&lt;p&gt;With tracing enabled, every action, DOM snapshot, request, and log from the run is captured into a single &lt;code&gt;trace.zip&lt;/code&gt;. When a test fails, you open that file — locally, from CI, or in the browser — and the run replays exactly as it happened.&lt;/p&gt;

&lt;p&gt;Here's the same debugging tasks, before and after:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Without Trace Viewer&lt;/th&gt;
&lt;th&gt;With Trace Viewer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Find why a click hit the wrong element&lt;/td&gt;
&lt;td&gt;Re-run headed, add logs, hope it reproduces&lt;/td&gt;
&lt;td&gt;Click the action, see the before/after DOM and the exact click coordinates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grab a locator for a new assertion&lt;/td&gt;
&lt;td&gt;Re-run in debug mode, use the inspector live&lt;/td&gt;
&lt;td&gt;Open the &lt;strong&gt;Locator&lt;/strong&gt; tab on the already-captured screen and pick it — no re-run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diagnose a failed network call&lt;/td&gt;
&lt;td&gt;Add request logging, re-run, inspect devtools&lt;/td&gt;
&lt;td&gt;Open &lt;strong&gt;Network&lt;/strong&gt;, already filtered to that action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find the exact line of code behind a step&lt;/td&gt;
&lt;td&gt;Search the file, count steps manually&lt;/td&gt;
&lt;td&gt;Click the action, &lt;strong&gt;Source&lt;/strong&gt; jumps to that line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Investigate a CI-only flake&lt;/td&gt;
&lt;td&gt;SSH into CI, add debug output, wait for the next occurrence&lt;/td&gt;
&lt;td&gt;Download the trace CI already attached, open it locally&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern across every row: instead of recreating the conditions of the failure, you're looking at a recording of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup: Two Lines of Config
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Locally&lt;/strong&gt;, trace everything while you're developing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--trace&lt;/span&gt; on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use UI Mode, which traces every test automatically without the flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On CI&lt;/strong&gt;, tracing every run is wasteful — trace only what fails, on retry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// playwright.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on-first-retry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other values: &lt;code&gt;on-all-retries&lt;/code&gt;, &lt;code&gt;retain-on-failure&lt;/code&gt; (if you don't use retries), &lt;code&gt;on&lt;/code&gt; (every run — expensive), &lt;code&gt;off&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Opening a trace:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the HTML report: click &lt;strong&gt;View trace&lt;/strong&gt; on any test — it opens a local instance of Trace Viewer directly, no file hunting.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npx playwright show-report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;From the CLI, pointing at a file or a URL:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npx playwright show-trace path/to/trace.zip
  npx playwright show-trace https://example.com/trace.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In the browser, no install required: &lt;a href="https://trace.playwright.dev" rel="noopener noreferrer"&gt;trace.playwright.dev&lt;/a&gt;. Drag in a file, or pass one as a URL query parameter — handy for linking straight from a CI artifact. Everything renders locally in your browser; nothing is uploaded.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Inside a Trace
&lt;/h2&gt;

&lt;p&gt;A timeline runs across the top of the viewer — click or drag anywhere on it, and every panel below updates to that moment. You never have to click through actions one by one to find the point you care about.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tab&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Actions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every action, its locator, and its duration, with before/after DOM snapshots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Locator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The exact screen, fully replicated, with Pick Locator available on it — build a new locator without executing anything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The exact line of test code behind the action you're viewing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Call&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Timing, resolved locator, and mode (e.g. strict) for the selected action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Log&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Playwright's internal narration — waiting for visible/enabled/stable, then acting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Errors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The failure message, with a red marker on the timeline showing exactly when it happened&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser and test console logs, filterable to a single action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every request with headers/bodies, filterable to a single action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metadata&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser, viewport, duration, and run context in one place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attachments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Expected vs. actual image diffs with a slider, for visual regression tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;Network&lt;/strong&gt; tab is easy to underestimate. This is the API debugging layer mentioned at the top of this post: even if your suite only exercises the UI, every backend call made during the test is captured and synchronized with the user's actions. Instead of opening DevTools and trying to reproduce the issue, you already have a complete timeline of both the frontend behavior and the API interactions that produced it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Traces as Context for AI Agents
&lt;/h2&gt;

&lt;p&gt;Everything a trace captures — actions, locators, DOM snapshots, network calls, console output — is structured data sitting in a zip file, not just a GUI to click through manually. That makes it a good fit for AI coding agents and testing skills that need to diagnose a failure without a human first reproducing it.&lt;/p&gt;

&lt;p&gt;A few ways this shows up in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous failure triage.&lt;/strong&gt; An agent can be handed a &lt;code&gt;trace.zip&lt;/code&gt; from a CI run and asked to identify the failing action, the locator involved, and the DOM state around it, then propose a fix — without ever spinning up a browser or re-running the suite itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root-cause summaries instead of raw logs.&lt;/strong&gt; Rather than pasting a stack trace into a chat, an agent (or a skill built around one) can walk the Actions, Network, and Console data from a trace and produce a plain-language explanation of what broke and where, which is a much shorter path to a fix than reading raw output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Locator generation without execution.&lt;/strong&gt; Because the Locator tab replicates the exact screen at a given moment, an agent can use that captured state to derive or validate a selector for a new assertion, the same way a human uses Pick Locator — without needing a live browser session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feeding "video receipts" back in.&lt;/strong&gt; If you're also recording screencasts (Playwright's &lt;code&gt;page.screencast&lt;/code&gt; API) alongside traces, an agent's own verification steps can be captured the same way — giving you a reviewable artifact of what an agent did, not just a claim that it passed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI feedback loops.&lt;/strong&gt; Since traces are already CI artifacts with stable URLs, an agent watching a pipeline can fetch a failing test's trace automatically as soon as it's produced, rather than waiting on a person to attach logs or describe the failure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The underlying shift is the same one this post keeps coming back to: a trace turns "the test failed" into a self-contained record that doesn't require a human — or an agent — to reproduce the failure to understand it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Efficiency Case
&lt;/h2&gt;

&lt;p&gt;Trace Viewer isn't just a developer-experience nicety — it changes measurable costs. If you need to justify adopting it (or prioritizing better trace coverage in CI), these are the levers to point at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mean time to resolution (MTTR) on test failures.&lt;/strong&gt; The reproduction loop above is where most debugging time goes. Removing the "make it fail again" step is the single biggest lever on how long a failure sits open.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engineer context-switching cost.&lt;/strong&gt; A flaky CI failure that can be diagnosed by opening a file, versus one that requires blocking time to reproduce locally, has a very different interruption cost — especially for failures that surface hours after the engineer has moved to other work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI compute spend.&lt;/strong&gt; &lt;code&gt;trace: 'on-first-retry'&lt;/code&gt; only records what actually fails, so you get debugging data without paying to trace every green run. Compare that to teams that resort to re-running whole suites repeatedly to catch a flake in the act.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flaky test triage throughput.&lt;/strong&gt; Because traces are attachable CI artifacts, failures can be triaged asynchronously by whoever has time, not only by whoever can reproduce the environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to track the impact concretely, two numbers are enough to start: &lt;strong&gt;time from failure to root cause identified&lt;/strong&gt;, and &lt;strong&gt;number of "couldn't reproduce, closed as flaky"&lt;/strong&gt; tickets. Both should move once trace review replaces manual reproduction as the default first step.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI/CD Recommendations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trace on retry, not on every run.&lt;/strong&gt; &lt;code&gt;on-first-retry&lt;/code&gt; (or &lt;code&gt;retain-on-failure&lt;/code&gt; without retries) gives full debugging data for failures at a fraction of the storage and runtime cost of &lt;code&gt;on&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link traces directly from CI output.&lt;/strong&gt; Since &lt;code&gt;show-trace&lt;/code&gt; and &lt;code&gt;trace.playwright.dev&lt;/code&gt; both accept URLs, wire your CI's artifact link straight into a one-click trace view instead of a manual download-and-unzip step.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treat "couldn't reproduce" as a process gap, not bad luck.&lt;/strong&gt; If a failure recurs without a trace attached, that's a signal to widen trace coverage, not just re-run the suite again.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Official Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev/docs/trace-viewer" rel="noopener noreferrer"&gt;Trace viewer documentation&lt;/a&gt; — full walkthrough of every tab and feature&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev/docs/api/class-testoptions#test-options-trace" rel="noopener noreferrer"&gt;testOptions.trace&lt;/a&gt; — configuration reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://trace.playwright.dev" rel="noopener noreferrer"&gt;trace.playwright.dev&lt;/a&gt; — the hosted viewer, for opening traces without the CLI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev/docs/intro" rel="noopener noreferrer"&gt;Playwright documentation&lt;/a&gt; — general guides, including CI configuration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Most test-debugging time isn't spent fixing bugs — it's spent recreating the conditions that exposed them.&lt;/strong&gt; Trace Viewer removes that step by keeping a full record of the run itself, so the question shifts from &lt;em&gt;"can I reproduce this?"&lt;/em&gt; to &lt;em&gt;"what does the trace show?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For engineering teams, the value isn't measured by prettier debugging tools — it's measured by fewer engineering hours lost to reproducing failures. Shorter debugging cycles, fewer unnecessary CI reruns, and faster root-cause analysis are improvements that show up in both developer productivity and infrastructure costs.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>ai</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Stop Debugging Blind: Playwright Screencast API for Richer Test Evidence</title>
      <dc:creator>Vanessa Sastre</dc:creator>
      <pubDate>Wed, 22 Jul 2026 22:13:02 +0000</pubDate>
      <link>https://dev.to/vanessa_sastre/stop-debugging-blind-playwright-screencast-api-for-richer-test-evidence-47l</link>
      <guid>https://dev.to/vanessa_sastre/stop-debugging-blind-playwright-screencast-api-for-richer-test-evidence-47l</guid>
      <description>&lt;p&gt;When an automated test fails, the error message alone is often insufficient to diagnose the root cause.&lt;/p&gt;

&lt;p&gt;You're usually left asking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What actions happened before the failure?&lt;/li&gt;
&lt;li&gt;Which element was being interacted with?&lt;/li&gt;
&lt;li&gt;Where exactly did the app start behaving differently than expected?&lt;/li&gt;
&lt;li&gt;Can a teammate understand the failure without re-running the test themselves?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Playwright 1.59 introduced &lt;code&gt;page.screencast&lt;/code&gt;, a programmatic recording API that answers these questions by turning a test run into a narrated, annotated video — instead of just a pass/fail line in a report.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With screencast-based reporting, a test can produce:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A recording of the full execution&lt;/li&gt;
&lt;li&gt;Visual annotations on every interacted element&lt;/li&gt;
&lt;li&gt;Chapter markers that break the run into named stages&lt;/li&gt;
&lt;li&gt;Custom HTML overlays for context or metadata&lt;/li&gt;
&lt;li&gt;Real-time JPEG frame capture for external tooling&lt;/li&gt;
&lt;li&gt;"Video receipts" for AI-driven test and coding agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple: &lt;strong&gt;make failures faster to understand, and make passing runs easier to trust.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmmpfawrn2nflpwo3hm5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgmmpfawrn2nflpwo3hm5.gif" alt="Screencast example showing annotated Playwright test execution" width="500" height="312"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Screencasts Matter
&lt;/h2&gt;

&lt;p&gt;Traditional artifacts already carry useful information:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Artifact&lt;/th&gt;
&lt;th&gt;What it tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What the framework reported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Screenshots&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The app's state at one moment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DOM snapshots, network activity, console output&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A screencast adds a fourth layer that none of these provide well on their own: &lt;strong&gt;the full user journey, as a video.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of staring at this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected: "Order completed"
Received: "Payment failed"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...you watch the actual sequence: checkout opens, a product is added, payment details are entered, submit is clicked, and the error appears, all annotated in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screencast vs. Playwright Trace
&lt;/h2&gt;

&lt;p&gt;These two tools solve different problems, and a mature framework should use both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trace Viewer&lt;/strong&gt; is built for deep technical debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network activity&lt;/li&gt;
&lt;li&gt;DOM snapshots&lt;/li&gt;
&lt;li&gt;Console messages&lt;/li&gt;
&lt;li&gt;Step-by-step execution inspection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Screencast&lt;/strong&gt; is built for fast, visual understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A timeline of what a user (or agent) actually did&lt;/li&gt;
&lt;li&gt;Visible, annotated interactions&lt;/li&gt;
&lt;li&gt;Context that non-technical stakeholders can follow without opening dev tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice: reach for &lt;strong&gt;traces&lt;/strong&gt; when you need to diagnose &lt;em&gt;why&lt;/em&gt; something broke, and for &lt;strong&gt;screencasts&lt;/strong&gt; when you need to show &lt;em&gt;what happened&lt;/em&gt; quickly, and to a wider audience than just engineers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up Screencast Recording
&lt;/h2&gt;

&lt;p&gt;The cleanest way to roll this out across a suite is a shared fixture, so every test gets recording, annotations, and reporting for free — no boilerplate in individual test files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// fixtures.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outputPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screencast.webm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;videoPath&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showActions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Screencast&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;videoPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/webm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every test that imports &lt;code&gt;test&lt;/code&gt; from this file now automatically records a screencast and attaches it to the report, including in CI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Narration Inside a Test
&lt;/h3&gt;

&lt;p&gt;Once the fixture is in place, use &lt;code&gt;showChapter()&lt;/code&gt; to label the stages of a test as it runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./fixtures&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showChapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User authentication flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...login steps&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showChapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checkout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Complete purchase flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// ...checkout steps&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting video reads like a labeled walkthrough rather than raw, unexplained footage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Screencast API Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;page.screencast.start(options)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Starts a screencast session. Pass &lt;code&gt;path&lt;/code&gt; to save a WebM recording, or &lt;code&gt;onFrame&lt;/code&gt; to receive JPEG frames in real time — both can be used together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;execution.webm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because it's called explicitly, you control exactly when recording starts and stops, unlike the older, always-on &lt;code&gt;video&lt;/code&gt; config option, which records for the entire test lifecycle regardless of what you actually need.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;page.screencast.stop()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Stops the active session and finalizes the video file at the path given to &lt;code&gt;start()&lt;/code&gt;. Typically called right after the test body finishes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;page.screencast.showActions(options)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Annotates interacted elements as they're clicked, filled, or hovered, so a reviewer can see exactly which element was touched and when.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showActions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// ms each annotation stays visible&lt;/span&gt;
  &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// animates a cursor between actions; use 'none' to disable&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid &lt;code&gt;position&lt;/code&gt; values: &lt;code&gt;top-left&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;top-right&lt;/code&gt;, &lt;code&gt;bottom-left&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;bottom-right&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To turn annotations off mid-test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hideActions&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Suite-wide alternative:&lt;/strong&gt; if you want action annotations everywhere without touching a fixture, enable them directly in &lt;code&gt;playwright.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;video&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;page.screencast.showChapter(title, options)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Displays a centered, blurred-backdrop overlay to mark a new stage of the recording. Disappears automatically after &lt;code&gt;duration&lt;/code&gt; (defaults to a couple of seconds).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showChapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Payment validation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submitting card details and confirming charge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chaining a few of these gives you a scannable timeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Login → Search product → Add to cart → Checkout → Payment validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reviewers can jump straight to the stage they care about instead of scrubbing a long recording blindly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;page.screencast.showOverlay(html, options)&lt;/code&gt; / &lt;code&gt;showOverlays()&lt;/code&gt; / &lt;code&gt;hideOverlays()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Adds arbitrary HTML on top of the recording — handy for environment info, build numbers, or debug messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;div style="color:red"&amp;gt;Running checkout validation&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Omit &lt;code&gt;duration&lt;/code&gt; and the overlay stays until you explicitly hide or remove it. &lt;code&gt;showOverlays()&lt;/code&gt; / &lt;code&gt;hideOverlays()&lt;/code&gt; toggle visibility of overlays already on screen without discarding them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Frame Capture
&lt;/h3&gt;

&lt;p&gt;Pass &lt;code&gt;onFrame&lt;/code&gt; to &lt;code&gt;start()&lt;/code&gt; to stream JPEG-encoded frames while the test runs, instead of (or alongside) writing a video file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;onFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewportWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewportHeight&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;processFrame&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewportWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewportHeight&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This opens the door to live dashboards, vision-model analysis, or custom monitoring pipelines that react to what's on screen &lt;em&gt;during&lt;/em&gt; execution, not after.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agentic Video Receipts
&lt;/h2&gt;

&lt;p&gt;Screencasts are also a good fit for AI-driven testing and coding agents, where a plain text summary ("verified checkout — passed") is much less convincing than a video showing exactly what the agent did.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;receipt.webm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showActions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top-right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showChapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verifying checkout flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Validating payment completion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Agent performs its verification steps here&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#coupon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SAVE20&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#apply-coupon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.discount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toContainText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;20%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showChapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checkout validation completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screencast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a self-contained "receipt": chapter titles explain intent, action annotations show exactly what was clicked or filled, and a human can review the whole thing in the time it takes to watch a short clip, no re-running the agent required.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI/CD Recommendations
&lt;/h2&gt;

&lt;p&gt;Recording every single test run adds up fast in storage and CI time. A few practical guardrails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Record failures, not everything.&lt;/strong&gt; Attach the screencast only when a test doesn't match its expected status:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expectedStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;testInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Screencast&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;videoPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video/webm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Record critical paths deliberately.&lt;/strong&gt; Reserve always-on recording for your highest-value flows (checkout, sign-up, payment) rather than every spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up old artifacts&lt;/strong&gt; on a schedule so recordings don't silently pile up in CI storage.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Official Resources
&lt;/h2&gt;

&lt;p&gt;This post covers the common patterns, but the official docs are the source of truth for full option lists, edge cases, and future updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev/docs/api/class-screencast" rel="noopener noreferrer"&gt;Screencast API reference&lt;/a&gt; — full method and options list&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/playwright/releases" rel="noopener noreferrer"&gt;Playwright release notes&lt;/a&gt; — see the v1.59 entry for the original &lt;code&gt;page.screencast&lt;/code&gt; announcement, and later releases for additions like the &lt;code&gt;cursor&lt;/code&gt; option&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://playwright.dev/docs/intro" rel="noopener noreferrer"&gt;Playwright documentation&lt;/a&gt; — general guides, including video/trace configuration
If you hit something this post doesn't cover, those are the pages to check first.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Test automation is moving past a simple pass/fail line. Screencast-based reporting gives teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster failure diagnosis&lt;/li&gt;
&lt;li&gt;A visual record of execution flow anyone can follow, technical or not&lt;/li&gt;
&lt;li&gt;Better handoffs between QA and development&lt;/li&gt;
&lt;li&gt;A foundation for reviewing AI-driven testing and coding agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A mature framework shouldn't just report &lt;em&gt;that&lt;/em&gt; a test failed, it should show &lt;strong&gt;what happened, where it happened, and give you what you need to fix it.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>testing</category>
      <category>tutorial</category>
      <category>documentation</category>
    </item>
  </channel>
</rss>
