惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
The GitHub Blog
The GitHub Blog
美团技术团队
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
The Register - Security
The Register - Security
Stack Overflow Blog
Stack Overflow Blog
Attack and Defense Labs
Attack and Defense Labs
G
Google Developers Blog
I
InfoQ
博客园 - 司徒正美
T
Troy Hunt's Blog
Google DeepMind News
Google DeepMind News
J
Java Code Geeks
MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
S
Security Affairs
M
MIT News - Artificial intelligence
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Tailwind CSS Blog
量子位
Vercel News
Vercel News
月光博客
月光博客
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
L
LangChain Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
LINUX DO - 最新话题
F
Full Disclosure
The Hacker News
The Hacker News
Hacker News: Ask HN
Hacker News: Ask HN
T
Tor Project blog
A
Arctic Wolf
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Forbes - Security
Forbes - Security
IT之家
IT之家
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
GbyAI
GbyAI
B
Blog RSS Feed
V
Visual Studio Blog
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs

Giant Robots Smashing Into Other Giant Robots

Join us: Building Secure Healthcare Systems Upcase has retired, but the learning continues The Bike Shed Ep 506: The Muppet Software Team Migrating to native stack navigation, with a surprise from iOS 26 Past and present thoughtbotters at LRUG this Monday The Bike Shed Ep 505: What is a “principal” or “staff” engineer? Your vibe coded website is going to get you fined Roux’s New Component Library Why we're choosing stewardship over an exit The Bike Shed Ep 503: Seeing the Graph for the Trees Announcing Shoulda Matchers 8.0: validate multiple attributes in one line AI's "overnight" solution for our flaky tests took two weeks to adopt Meet thoughtbot at Brighton Ruby 2026 614: AI Code Audits The mistake I didn't realise I was making when designing workshops AI crawlers are inflating your view counts 502: Apps That Make Our Work Go Toast: the 2-minute test that reveals how you think about building products How I Built a Chrome Extension Wrapper (and Everything That Tried to Stop Me) Enforcing Your Ruby Style Guide on AI-Generated Code Copy as Markdown: AI-friendly blog posts 501: What makes for good technical writing? The Four Signals of AI Observability Can you really launch a tech business with a no-code app builder? 612: Do fish drink? This week in #dev (May 15, 2026) Lost, forgotten, and unfamiliar HTML 500: Celebrating with past hosts Why Duck Typer? Biometrics authentication for your mobile app
The Playwright debugging tool Rails devs aren't using
Justin Toniazzo · 2026-06-19 · via Giant Robots Smashing Into Other Giant Robots

Playwright ships with a black-box recorder that records every detail of every test, giving you a treasure trove of information for debugging flaky tests. Most Rails apps I’ve worked on don’t use it.

It’s called the Trace Viewer, and if you’re running Capybara with Playwright via playwright-ruby-client, turning it on takes a single block in rails_helper.rb.

What’s in a Playwright trace?

When Playwright records a trace, you get back a .zip you can replay in the Trace Viewer. The viewer is essentially a DVR for your test run. For every action Playwright took, every click, fill_in, and navigation, you get:

  • A before-and-after screenshot, so you can see what the page looked like at each moment
  • The DOM at that point in time, fully inspectable like in your devtools
  • The network requests in flight, with status codes, body, and timing
  • The JavaScript console output
  • A timeline of everything that happened

plawright trace viewer

Compare that to what you usually get when a test fails on CI: A screenshot, and maybe a stack trace pointing at expected to find "Saved". With a trace, you can scrub through the test like a video, and pinpoint exactly where things went wrong.

Enable Playwright traces in your Rails app

Drop this into your rails_helper.rb. It assumes you’re using playwright-ruby-client.

You may need to change type: :system to type: :feature if that’s how you set up your browser-based tests.

RSpec.configure do |config|
  config.before(:each, type: :system) do |example|
    driver = Capybara.current_session.driver
    next unless driver.respond_to?(:start_tracing)

    driver.start_tracing(
      screenshots: true,
      snapshots: true,
      sources: true,
      title: example.full_description
    )
    example.metadata[:playwright_tracing] = true
  end

  config.after(:each, type: :system) do |example|
    next unless example.metadata[:playwright_tracing]

    path = trace_path(example)
    Capybara.current_session.driver.stop_tracing(path:)
    if example.exception
      output = RSpec.configuration.output_stream
      output.puts("Playwright trace: #{path}")
    end
  rescue Playwright::Error => e
    raise unless e.message.start_with?("Must start tracing before stopping")
  end

  def trace_path(example)
    if example.exception
      Rails.root.join(
        "tmp",
        "playwright-traces",
        [
          example.full_description.parameterize[0, 150],
          "-#{Time.current.to_i}",
          ".zip"
        ].join
      )
    else
      File::NULL
    end
  end
end

This starts a trace before every feature spec and only saves it if the test failed. When that happens, you’ll see a line like this printed:

Playwright trace: /Users/jutonz/code/thoughtbot/testing/tmp/playwright-traces/it-works-1775576941.zip

A few things in the snippet might catch your eye (the before(:each) instead of around, the rescue, the conditional path). They’re like that for a reason. Copy as-is, or expand below for the rationale.

Why the snippet looks the way it does
  • before(:each) over around. Capybara only swaps from RackTest to Playwright after it sees the spec’s :js (or js: true) metadata. That swap happens after the around block, so a hook running there would still see the RackTest driver and respond_to?(:start_tracing) would always be false.
  • rescue Playwright::Error. If start_tracing failed for any reason, e.g. if a test modifies some low level driver config, stop_tracing will throw a “Must start tracing before stopping” error. Catching it won’t fail an otherwise passing test because of a tracing issue.
  • Conditional trace_path / File::NULL. Playwright requires you to write a trace somewhere once you’ve started one. Since we don’t care about traces for passing tests, we discard them by writing them to the /dev/null null device.

Download traces from CI for debugging

You’ll want to keep failed traces around for inspection. In GitHub Actions, add an upload step that runs even when the suite fails:

- name: Upload Playwright traces
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: playwright-traces
    path: tmp/playwright-traces

Now you can download traces after a failed test.

Open a trace

Once you’ve downloaded a trace zip, open it with:

npx playwright show-trace path/to/trace.zip

That spins up the Trace Viewer in a local browser. The Playwright docs cover the UI in detail.

Gotchas

A couple of things worth knowing once traces are on.

Traces only show Playwright actions, not Capybara or RSpec.

A click_on("Save") call will appear as Click in the trace, since that’s the underlying Playwright action that’s performed. Usually it’s easy enough to map the Playwright action to the Capybara version, but it won’t be exactly the same.

The trace recorder is also capable of capturing Playwright-based assertions, but if you’re using Capybara, these won’t show up. Capybara does assertions itself, rather than delegating to the driver. This is because Selenium and other drivers don’t support built-in assertions.

There’s a small I/O cost.

Recording is mostly disk-bound. In my experience the cost is negligible, but if your CI is I/O-constrained, you might notice it as new flakiness. If that happens, you can store traces in a tmpfs or ramdisk by passing tracesDir to the driver:

Capybara::Playwright::Driver.new(
  app,
  # ... existing options ...
  tracesDir: ENV["CI"].present? ? "/mnt/ramdisk/playwright-traces" : nil,
)

CircleCI exposes /mnt/ramdisk by default. On other providers you can create a tmpfs directory manually.

Debug, don’t guess

Before traces, my approach to flaky CI was guess, re-run, and hope. Now I open the artifact, scrub through the test, and see the actual problem. The difference is night and day, and the setup is one block of code.

If you’re running Playwright through Capybara, you’ve already got everything you need. Turn it on.