Use a Rails system test when you need a screenshot of a page rendered by a real browser. A test class derived from ApplicationSystemTestCase drives Capybara; call take_screenshot after navigation and any interactions that create the state you want to preserve. Rails also calls take_failed_screenshot during system-test teardown, so failed browser tests can leave an image for diagnosis.
This approach captures the page as the browser sees it, including JavaScript, CSS, responsive layout and user interactions. The sections below show a complete setup, viewport control, artifact handling, CI practices and fixes for common failures.
Capture a page in a Rails system test
Put the screenshot call in a system test, not in a controller or view. A system test has a browser session, so it can capture the rendered result rather than the HTML response alone.
Minimal example
require "application_system_test_case"
class UsersTest < ApplicationSystemTestCase
test "shows the users page" do
visit users_url
take_screenshot
assert_selector "h1", text: "Users"
end
end
Run the test with the command appropriate for your Rails application, for example:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
bin/rails test:system
The screenshot is taken at the exact point where take_screenshot runs. If the page requires a menu click, form submission, modal open, or other interaction, perform it first.
Capture after interactions
class CheckoutTest < ApplicationSystemTestCase
test "shows the confirmation modal" do
visit checkout_url
fill_in "Email", with: "buyer@example.test"
click_on "Continue"
click_on "Review order"
take_screenshot
assert_selector "[role='dialog']", text: "Review your order"
end
end
Assertions before the capture are useful when the screenshot is intended to document a verified state. An assertion failure stops execution before later lines, so place the call before an assertion only when you also want an image of the unexpected state.
Prepare the system-test base class
Rails configures system tests through ApplicationSystemTestCase. The browser driver and viewport belong there so every test uses a predictable environment.
Use Selenium with Chrome
require "test_helper"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
end
The current Rails guide documents Selenium with Chrome as the default system-test configuration and shows a default screen size of 1400×1400. Headless Chrome is usually the simplest choice for local and CI runs. If your application already generated this file, keep its existing require and class declaration and adjust only the driven_by line as needed.
Recommended Free Tools
Choose another browser or driver
The using: option selects the Selenium browser. Rails documents headless Chrome and Firefox configurations, and it supports driver-specific options through options:. A non-headless browser is useful while diagnosing a visual problem locally; headless execution is generally more convenient for automation.
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium,
using: :headless_firefox,
screen_size: [1280, 900]
end
Remote-browser configurations are also possible when a browser runs on another machine or service. The exact capabilities and connection settings depend on that driver, so keep them in the base class rather than scattering them through individual tests.
Rank #2
Set the viewport deliberately
A screenshot is only representative of the viewport used to create it. Set a size that matches the breakpoint or device you are documenting. Use a desktop size for a desktop layout, or a narrow width when you need to exercise the mobile breakpoint. Do not treat the 1400×1400 default as a performance measurement; it is simply Rails’ documented default screen size.
Control what the screenshot contains
Wait for asynchronous content
Capybara waits for many elements used in assertions, but a screenshot can still be taken too early if content is painted after navigation without a matching assertion. Wait for a stable, meaningful element before capturing:
Free tools Windows power users keep installed
One-click scans. No signup required.
visit dashboard_url
assert_selector "[data-testid='dashboard-ready']"
take_screenshot
For a transition or animation, prefer an assertion that reflects completion over an arbitrary sleep. If a third-party widget has no reliable marker, a short, documented wait may be necessary, but keep it as small as the rendering behavior permits.
Capture a specific state
Use normal Capybara actions to establish state: click_on, fill_in, select, check, uncheck and choose. The screenshot helper captures the current browser page; it does not rewind the test or capture a prior step.
visit settings_url
check "Enable dark mode"
click_on "Save changes"
assert_text "Settings updated"
take_screenshot
Full-page versus viewport images
Rails system-test screenshots represent the browser page at the configured viewport. If you need a single image containing an entire long page, confirm how the installed Rails version and driver implement that behavior rather than assuming that a viewport capture will include content below the fold. For visual regression, a fixed viewport is usually preferable because it makes comparisons repeatable.
Where Rails stores files
Rails’ API reference for version 7.0.8.5 identifies tmp/screenshots as the default screenshot directory. It also documents Capybara.save_path for changing the destination. These are version-specific details; check the API reference matching the Rails version installed in your application before depending on an exact path.
Choose a project-specific artifact directory
# test/application_system_test_case.rb
require "test_helper"
Capybara.save_path = Rails.root.join("tmp", "system-test-artifacts")
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
end
Create or clean this directory as part of your test workflow, and configure CI to upload it when a job fails. Keep generated images out of version control unless they are intentional fixtures.
Save HTML with the image
When an image alone does not explain a failure, save the page HTML as well. Rails 7.0.8.5 documents an html argument for the screenshot helper and the RAILS_SYSTEM_TESTING_SCREENSHOT_HTML environment variable. Because these controls can vary by Rails release, verify the supported spelling and values against your installed version before adding them to a shared pipeline.
Make screenshots useful in CI
- Use headless mode: it avoids a display-server requirement on most CI runners.
- Pin the viewport: the same width and height make failures comparable between runs.
- Keep artifacts on failure: upload the screenshot directory even when the test command exits nonzero.
- Wait on application state: capture after a stable selector or completed assertion, not merely after
visit. - Control external dependencies: third-party ads, analytics and remote widgets can change pixels or delay rendering; stub or block them where your test design permits.
- Use deterministic data: timestamps, random identifiers and user-specific content can create visual differences unrelated to a code change.
System tests are intended for scenarios that need complete user-experience verification, including JavaScript. If a test only needs to verify a response body or a presenter, a faster non-browser test is a better fit; reserve screenshots for states where the rendered browser view is the thing you need to inspect.
Automatic screenshots when a test fails
Rails includes take_failed_screenshot in system-test teardown. When a browser test fails, Rails attempts to capture the failing state automatically. This is separate from an explicit take_screenshot call, so you can keep deliberate checkpoints in a passing test and still receive a diagnostic image on failure.
If no file appears, first check that the test really ran as a system test and that the configured save directory is writable. A driver crash, browser startup failure or process termination can occur before Rails has a live page from which to capture.
Troubleshooting checklist
“ScreenshotHelper” or take_screenshot is undefined
The test is probably an ordinary ActiveSupport::TestCase instead of a class inheriting from ApplicationSystemTestCase (or the equivalent ActionDispatch::SystemTestCase). Move the browser scenario into a system-test class and require the application system-test base file.
Rank #4
The image is blank or shows the loading screen
The capture ran before the page reached its stable state. Add an assertion for a ready marker, wait for the specific content that matters, and inspect whether a JavaScript exception or failed network request prevents rendering.
Chrome or Firefox will not start in CI
Check that the browser and matching driver are installed, that the runner has the required libraries, and that the selected headless option is actually supported by the installed Selenium setup. Try the same driver locally in headless mode to separate application failures from runner configuration.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
The layout differs between local and CI
Compare browser version, viewport, device scale, fonts, locale, timezone and seeded data. Set the screen size explicitly in driven_by, install the fonts required by the application, and avoid assertions that depend on unstable external content.
The screenshot directory is empty
Confirm the effective Capybara.save_path, check write permissions and inspect the test output for a driver failure before teardown. Remember that the documented tmp/screenshots location is specific to the Rails 7.0.8.5 API reference and may differ in another release.
The capture is taken at the wrong point
Move take_screenshot below the navigation and interaction that define the desired state. For a modal, menu or validation error, assert that the state is visible immediately before the call.
When a Rails test is not the right capture tool
System tests are ideal when the screenshot is part of a browser test or must include authenticated, interactive application state. They require browser setup and test execution, however. For a public URL, a scheduled capture, a bulk set of pages or an AI-driven workflow, an HTTP screenshot API can avoid maintaining a local browser.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Best Value
Or skip the browser setup
ScreenshotNeo accepts one GET request and returns a PNG, JPEG, WebP or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
It supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets or custom viewports, retina scale, PDF paper and margin settings, custom CSS and JavaScript, click-before-capture actions, selector hiding, waits for selectors, delays or network idle, request and resource blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-rails-app.example.com -o shot.webp
See the ScreenshotNeo documentation for authentication, response formats and options. The same endpoint can be called from application code:
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://your-rails-app.example.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://your-rails-app.example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Sign up for ScreenshotNeo to start with the free allowance.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesFAQ
Can I call take_screenshot from a controller?
No. It is a system-test helper tied to a browser session. Put the call in a system test that inherits from the application system-test base class.
Does a screenshot prove that the page works for every browser?
No. It records one configured driver, browser, viewport and environment. Run separate system-test configurations when cross-browser coverage matters.
Should I commit generated screenshots?
Only when they are intentional test fixtures or approved visual baselines. Diagnostic images from ordinary runs belong in ignored artifact storage.
Frequently Asked Questions
Can I call take_screenshot from a controller?
No. It is a system-test helper tied to a browser session. Put the call in a system test that inherits from the application system-test base class.
Does a screenshot prove that the page works for every browser?
No. It records one configured driver, browser, viewport and environment. Run separate system-test configurations when cross-browser coverage matters.
Should I commit generated screenshots?
Only when they are intentional test fixtures or approved visual baselines. Diagnostic images from ordinary runs belong in ignored artifact storage.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

