Project

perchfall

0.0
No release in over 3 years
Run headless browser checks against a URL using Playwright and receive a structured, immutable Ruby report object — framework-agnostic, no persistence.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 3.13
~> 1.70
~> 0.22

Runtime

>= 2.0
 Project Readme

Perchfall

CI Playwright smoke check Gem Version

Synthetic browser monitoring for Ruby. Give it a URL; get back a structured report of what a real Chromium browser saw — HTTP status, broken assets, JavaScript errors, and load time. No framework required.

report = Perchfall.run(url: "https://example.com")

report.ok?             # => true
report.http_status     # => 200
report.duration_ms     # => 834
report.network_errors  # => []
report.console_errors  # => []
report.to_json         # => '{"status":"ok","url":"https://example.com",...}'

Why Perchfall

Uptime monitoring tells you a server is responding. Perchfall tells you the page actually works.

  • A 200 OK doesn't mean your JavaScript loaded.
  • An APM trace doesn't capture a missing CDN asset.
  • A health check endpoint doesn't know your checkout flow is broken.

Perchfall runs a headless Chromium browser against your URL and gives you back everything it found: the HTTP status, every failed network request, every JavaScript error logged to the console, and how long it took. The result is an immutable Ruby value object you can store, log, or alert on — no database schema imposed, no framework lock-in.

Drop it into a Sidekiq job, a Rake task, a CI step, or a plain Ruby script. It works anywhere Ruby runs.


Requirements

Dependency Version
Ruby ≥ 3.2
Node ≥ 18
Playwright installed via npm

Installation

# 1. Add the gem
bundle add perchfall

# 2. Install Playwright (once per machine)
npm install playwright
npx playwright install chromium

Quickstart

require "perchfall"

report = Perchfall.run(url: "https://example.com")

if report.ok?
  puts "#{report.url} loaded in #{report.duration_ms}ms"
else
  puts "Page failed: #{report.network_errors.map(&:failure).join(", ")}"
end

Detect broken assets and JS errors

report = Perchfall.run(url: "https://example.com")

report.network_errors.each do |e|
  puts "#{e.http_method} #{e.url}#{e.failure}"
end
# GET https://example.com/assets/app.js — HTTP 404
# GET https://cdn.example.com/font.woff — net::ERR_NAME_NOT_RESOLVED

report.console_errors.each do |e|
  puts "#{e.type}: #{e.text}"
end
# error: Uncaught ReferenceError: Stripe is not defined

A page can be ok: true (it loaded) and still have broken sub-resources. Perchfall captures both.

Handle page load failures

begin
  report = Perchfall.run(url: "https://example.com", timeout_ms: 10_000)
rescue Perchfall::Errors::PageLoadError => e
  # Page couldn't load at all — a partial report is always attached.
  store_report(e.report.to_json)
end

Use in a background job

class SyntheticCheckJob
  include Sidekiq::Job

  def perform(url)
    report = Perchfall.run(url: url)
    SyntheticResult.create!(ok: report.ok?, payload: report.to_json)
  rescue Perchfall::Errors::PageLoadError => e
    SyntheticResult.create!(ok: false, payload: e.report.to_json)
  end
end

What's in a report

Every check returns a Perchfall::Report:

Field Type Description
ok? Boolean true if the page loaded successfully
http_status Integer / nil HTTP response code
duration_ms Integer Total time from navigation start to load event
url String The URL checked
timestamp Time When the check ran (UTC)
network_errors Array Failed or errored network requests
console_errors Array JavaScript errors logged to the browser console
to_json String Full report as JSON

Full report schema and JSON reference


Errors

Exception When
ArgumentError URL is invalid (bad scheme, internal address)
Perchfall::Errors::PageLoadError Page couldn't load; partial report attached at e.report
Perchfall::Errors::ConcurrencyLimitError All browser slots are busy; back off and retry
Perchfall::Errors::InvocationError Node isn't installed or not in PATH
Perchfall::Errors::Error Base class — catches any Perchfall error

Full error handling guide


Configuration

Perchfall.run(
  url:           "https://example.com",
  timeout_ms:    10_000,          # default 30_000, max 60_000
  wait_until:    "domcontentloaded",  # default "load"
  scenario_name: "homepage_smoke" # included in report JSON
)

All options and wait_until strategies


Further reading


Development

bundle install
bundle exec rspec    # ~0.4s, no browser or Node required
bin/console          # IRB with perchfall loaded

License

MIT