Skip to content

Chapter 11 · Screenshots, Video, and Trace Viewer ​

Chapter goals: Master element/full-page screenshots and visual regression assertions, learn to record test videos, and use Trace Viewer to replay every millisecond of a failed test like a "dashcam."

11.1 Screenshots: From Archival to Assertion ​

Screenshots come in three tiers, each with a different purpose:

python
# ① Viewport screenshot (default): captures only the visible area
page.screenshot(path="viewport.png")

# ② Full-page screenshot: scrolls and stitches the entire scrollable page
page.screenshot(path="full.png", full_page=True)

# ③ Element-level screenshot: captures just one component — ideal for visual baselines
page.locator(".header").screenshot(path="header.png")

# ④ No file I/O: get raw bytes for custom processing
png_bytes = page.screenshot()

Full-page screenshots work by "virtually enlarging the viewport", so lazy-loaded pages need a scroll trigger first; element screenshots auto-wait for the element to be stably visible.

11.2 Visual Regression Assertions ​

expect(locator).to_have_screenshot() turns screenshots into retryable assertions: the first run generates a baseline image stored in the snapshot directory, and subsequent runs do pixel-by-pixel comparison — mismatches fail the test. With pytest you can refresh the baseline at runtime with --update-snapshots:

python
from playwright.sync_api import Page, expect

def test_header_visual(page: Page):
    page.goto("https://demo.playwright.dev/todomvc")
    expect(page.locator(".header")).to_have_screenshot("header.png")

Beware of "environment noise" in visual comparisons: font rendering varies by OS, animations cause pixel jitter, and dynamic timestamps always fail comparison. In practice: lock the viewport and device_scale_factor, freeze animations (animation: none in CSS), and exclude volatile regions from the screenshot area.

11.3 Recording Test Videos ​

Enable recording at the context level — the video is written to disk only when the context closes — so don't forget to close manually-created contexts:

python
context = browser.new_context(
    record_video_dir="videos/",
    record_video_size={"width": 640, "height": 480},   # default scales to fit within 800x800
)
page = context.new_page()
page.goto("https://demo.playwright.dev/todomvc")
# ... test steps ...
context.close()                       # video only available after this

path = page.video.path()              # access the file path afterward via page.video
print("Video saved at:", path)

Videos are valuable for showing non-technical stakeholders "what the test actually did"; but they are large and low information density — in CI keep them only for failing cases.

11.4 Trace Viewer: The Black Box for Failed Tests ​

Trace is Playwright's flagship debugging tool: it records DOM snapshots, network requests, and console logs before and after every action, and lets you time-travel to any step to see what the page looked like at that moment. Three lines at the API level:

python
browser = chromium.launch()
context = browser.new_context()

# Start recording: DOM snapshots + screenshots + source maps
context.tracing.start(screenshots=True, snapshots=True, sources=True)

page = context.new_page()
page.goto("https://playwright.dev")
# ... test steps ...

# Stop and export as a single zip
context.tracing.stop(path="trace.zip")

Two ways to open a trace:

bash
playwright show-trace trace.zip          # Local GUI viewer
# Or drag the zip into https://trace.playwright.dev in a browser
# This site resolves everything client-side; data never leaves your machine. Also accepts URLs to open remote traces.

11.5 Automated Failure Forensics in pytest ​

The pytest-playwright plugin turns all of the above into CLI flags — no handwritten tracing code needed:

bash
pytest --tracing retain-on-failure   # Keep trace only on failure, auto-delete on success
pytest --video retain-on-failure     # Same for video
pytest --screenshot only-on-failure  # Auto-screenshot on failure

--tracing accepts three values: on (record everything), off (default, no recording), retain-on-failure (keep only failures). Artifacts land in the test-results/ directory. The production CI standard config is the retain-on-failure trio: upload the entire directory as build artifacts on failure — no more guessing what went wrong.

Cost trade-off

Trace recording DOM snapshots has noticeable performance overhead, so keeping it off by default and enabling only when needed is the right call. Temporarily switch to --tracing on locally when reproducing stubborn bugs.

11.6 Chapter Summary ​

  • Three screenshot tiers: viewport / full-page / element-level;
  • to_have_screenshot() makes visual comparison a retryable assertion; lock viewport and freeze animations to avoid noise;
  • Video records at context level; only written on context.close() — remember to close manually created contexts;
  • Trace Viewer is the ultimate debug tool: DOM snapshots + screenshots + source maps, time-travel capable;
  • pytest CLI flags --tracing retain-on-failure, --video retain-on-failure, --screenshot only-on-failure automate forensics.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. When does a context-recorded video become available on disk?

2. What does --tracing retain-on-failure do in pytest?

3. Why should you freeze animations when doing visual regression testing?

4. Which approach gives the richest debug information for a flaky failure?

🛠️ Hands-On Exercises ​

  1. Run a test with --tracing retain-on-failure --screenshot only-on-failure; intentionally make it fail and inspect the generated test-results/ artifact.
  2. Record a video with record_video_dir; open the resulting .webm file and note what information it reveals that screenshots cannot.
  3. Write a visual regression test for a component; run with --update-snapshots to create the baseline, then introduce a CSS change and observe the pixel-diff failure.

Proceed to Chapter 12: Device Emulation and Multi-Browser.