Keeping Docs Fresh: How Ubicloud Automates Screenshot Generation with Cuprite

Article illustration 1

Documentation is notoriously hard to keep current. A single UI change can ripple through dozens of pages, and if screenshots aren’t updated, developers and users are left staring at stale images. Ubicloud’s solution is to treat screenshot capture as a first‑class automated task, mirroring the rigor of their integration tests.

“The best way to avoid outdated screenshots in documentation is to set up automatic updates for all screenshots.” – Ubicloud Blog

The Problem: Screenshots vs. Tests

Automated tests exist to catch regressions before they reach production. Screenshot generation, while seemingly a different beast, shares the same motivation: surface visual drift early. The implementation, however, differs. Traditional integration tests in Ruby use rack‑test, a lightweight HTTP simulator that runs in the same thread as the test code. Screenshots, on the other hand, require a real browser to render the DOM, meaning the test harness must spawn an external browser process.

From rack‑test to Cuprite

Rack‑test’s speed comes at the cost of visual fidelity. To capture screenshots, Ubicloud turned to Ferrum, a Ruby wrapper around Chrome’s DevTools protocol, and built on top of it is Cuprite. Cuprite offers a Capybara‑compatible API, letting developers write familiar navigation code while driving a headless Chrome instance.

# Gemfile
gem 'capybara'
gem 'cuprite'
# spec_helper.rb
Capybara.default_driver = :cuprite

Threading the Server

Because Cuprite launches a real browser, the test code must talk to a running web server. Ubicloud uses Puma, but limits it to a single thread and process for deterministic behaviour. The screenshot script starts Puma in a background thread and uses a Queue to signal readiness.

require 'puma'
require 'queue'

queue = Queue.new

Thread.new do
  Puma::Server.new(Rack::Builder.new { use Rack::Lint; run MyApp }).tap do |s|
    s.add_tcp_listener('localhost', 3000)
    s.boot
    queue << :ready
    s.start
  end
end

queue.pop # wait until the server is ready

Database Transactions Across Threads

With rack‑test, test transactions are trivial because everything runs in one thread. Cuprite’s separate browser process forces the server to run in another thread, breaking the classic transactional pattern. Ubicloud mitigates this by configuring Sequel to share a single connection across threads and wrapping screenshot generation in a transaction that rolls back on completion.

# config/sequel.rb
Sequel.connection_pool_size = 1

if ENV['SCREENSHOT_MODE']
  Sequel.extension :temporarily_release_connection
end
Sequel.transaction(requires_new: true) do
  # screenshot logic here
end

The Screenshot Engine

The heart of the system is a RegenScreenshots class that:

  1. Scans a dedicated screenshots directory.
  2. Builds a hash of expected image filenames.
  3. Navigates the app via Capybara, calling save_screenshot for each target.
  4. Removes the filename from the hash when captured.
  5. Emits a warning if any expected screenshot remains missing.
class RegenScreenshots
  def run
    expected = Dir.glob('docs/screenshots/*.png').map { |f| File.basename(f) }
    expected_hash = expected.map { |name| [name, true] }.to_h

    Capybara.visit('/login')
    Capybara.save_screenshot('docs/screenshots/login.png')
    expected_hash.delete('login.png')

    # ... more navigation

    unless expected_hash.empty?
      warn "Missing screenshots: #{expected_hash.keys.join(', ')}"
    end
  end
end

Why It Matters

By automating screenshot regeneration, Ubicloud reduces manual maintenance overhead, eliminates the risk of stale visual documentation, and ensures that developers and end‑users see an accurate reflection of the product. The approach scales: a single bundle exec rake regen_screenshots command updates every image, and the process can be hooked into CI pipelines to catch regressions before they reach production.

“This automated screenshot generation program helps Ubicloud keep its documentation up to date. This way, it is easier for viewers to use the screenshots in the documentation.” – Ubicloud Blog

The technique is broadly applicable. Any Ruby‑based web project that relies on Capybara for integration tests can adopt Cuprite to capture screenshots, adjust its Puma server for headless operation, and wrap the process in a transaction‑safe environment. The result: documentation that evolves in lockstep with the UI, without manual intervention.

Source: Ubicloud Blog – Keeping Documentation Up to Date via Automated Screenshot Generation