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:
# ① 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:
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:
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:
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:
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:
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-failureautomate 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
- Run a test with
--tracing retain-on-failure --screenshot only-on-failure; intentionally make it fail and inspect the generatedtest-results/artifact. - Record a video with
record_video_dir; open the resulting.webmfile and note what information it reveals that screenshots cannot. - Write a visual regression test for a component; run with
--update-snapshotsto create the baseline, then introduce a CSS change and observe the pixel-diff failure.
Proceed to Chapter 12: Device Emulation and Multi-Browser.