Skip to content

Latest commit

 

History

History
199 lines (142 loc) · 8.07 KB

File metadata and controls

199 lines (142 loc) · 8.07 KB

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/snap-diff/snap_diff-capybara. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

Development Setup

Prerequisites

  • Ruby 3.2+ (the project tests against 3.2–4.0)
  • libvips 8.9+ (optional, for the VIPS driver). Install with:
    • macOS: brew install vips
    • Ubuntu: sudo apt-get install libvips-dev
  • Chrome (for integration tests with selenium_chrome_headless or cuprite)

First-time setup

bin/setup

This installs gem dependencies and prepares the development environment.

Running tests

# All tests
rake test

# Unit tests only (faster, no browser required)
rake test:unit

# Integration tests (require a browser)
rake test:integration

# Run specific test file
ruby -Ilib:test test/unit/image_compare_test.rb

# Run with a specific screenshot driver
SCREENSHOT_DRIVER=vips rake test

# Run with a specific Capybara driver (integration tests)
CAPYBARA_DRIVER=cuprite rake test:integration

# Record new baseline screenshots (integration tests)
RECORD_SCREENSHOTS=1 bin/dtest

Docker testing

Use Docker for reproducible CI-matching test runs:

bin/dtest                     # Run all tests in Docker
bin/dtest test/integration/   # Run specific directory

See docs/docker-testing.md for details.

Linting

# Ruby linting (Standard Ruby)
bundle exec standardrb

# Auto-fix
bundle exec standardrb --fix

CI: a green PR does not always mean a green master

The full Test Ruby & Rails matrix does not run on every PR. It is off by default to stay inside free-tier Actions minutes, which means the job that breaks master can be a job that never ran on your PR.

A pull request runs only the critical jobs — Lint, Functional Test and the minimal-setup check. The version matrix is the master gate; it also runs on manual dispatch, the weekly cron, and any PR carrying the full-ci label.

Add full-ci by hand when a change is version-sensitive and you want the answer before merging rather than after: anything conditional on defined?, respond_to? or RUBY_VERSION, anything touching subprocess or environment handling, and anything you would be surprised to see break on JRuby.

The matrix is a spanning set, not a cross product — every Ruby appears at least once and every Rails appears at least once. A cross product re-proves the same facts: TestsWithoutAssertions being Rails 7.2+ shows up on every Ruby running 7.1, and JRuby lacking Kernel#fork shows up on every Rails. The combinations outside the span are not run anywhere; that is the trade for the matrix costing ~46 min instead of ~130.

Two things about reading CI results here:

  • cancelled is not a pass. It means a later push superseded the run, or fail-fast killed the cell before it reported. It occupies the same slot as a verdict while carrying none — a JRuby lane sat broken for 15 consecutive runs looking exactly like this.
  • After merging, check master. gh run list --branch master --workflow Test --limit 1. A red master blocks the next merge. Pass --workflow Test: without it you get whichever workflow ran last, which has already reported a Dependabot success while Test was failing on the same commit.

Coding Conventions

Style

This project uses Standard Ruby for consistent formatting. Run bundle exec standardrb --fix before committing.

Ruby version compatibility

The gem supports Ruby 3.2 through 4.0 (including JRuby). When adding features:

  • Avoid syntax or APIs that are only available in newer Ruby versions
  • Test with the full matrix (see .github/workflows/test.yml)
  • Be mindful of JRuby compatibility (no C extensions, avoid platform-specific code)

Naming conventions

  • Classes/modules: CamelCase (Ruby convention)
  • Methods: snake_case
  • Test files: snake_case_test.rb (matching the class they test)
  • Screenshot names: descriptive, kebab-case or snake_case

Architecture patterns

  • Value objects — immutable data carriers (e.g., Difference, Comparison, Region)
  • Strategy pattern — interchangeable algorithms (e.g., VipsDriver/ChunkyPNGDriver)
  • Layered comparison — fast-then-slow strategy in ImageCompare (byte → pixel → region)
  • Thread safety — thread-local state for per-test data, mutex for shared state
  • Test doubles — use TestDoubles::TestDriver and TestDoubles::TestPath (see test/support/test_doubles.rb)

What to avoid

  • Service objects — this codebase prefers explicit method calls over service object wrappers
  • Global state mutation at runtime — configuration should be set once before tests
  • Monkey-patching — prefer composition over patching Capybara internals

Pull Request Guidelines

Before submitting

  1. Run the full test suiterake test should pass
  2. Run the linterbundle exec standardrb should pass
  3. Add tests for new functionality or bug fixes
  4. Update docs if changing behavior or adding features
  5. Update CHANGELOG.md under the [Unreleased] section

PR description

Include:

  • What changed (one-line summary)
  • Why it changed (motivation, related issue)
  • How it was tested
  • Screenshots if the change affects visual output

Review process

  1. A maintainer will review your PR
  2. Address review feedback with additional commits (no force-pushing)
  3. Once approved, a maintainer will merge

Testing guidelines

  • Unit tests go in test/unit/ and test a single class in isolation. Use test doubles from test/support/test_doubles.rb rather than testing with real image files or browsers.
  • Integration tests go in test/integration/ and exercise the full capture → compare → report pipeline with a real browser. These are slower and require Chrome.
  • Driver contract tests (test/support/driver_contract_tests.rb) verify that all image processing drivers meet the same interface. Add a contract test when adding a new driver method.
  • Test environment isolation: each unit test snapshots and restores Capybara::Screenshot and Capybara::Screenshot::Diff global state. Don't mutate globals outside of setup/teardown.

Adding a New Driver

  1. Create lib/capybara/screenshot/diff/drivers/new_driver.rb inheriting from BaseDriver
  2. Implement required methods: load_images, from_file, save_image_to, same_pixels?, find_difference_region, crop, add_black_box, draw_rectangles, resize_image_to
  3. Register in Utils.detect_available_drivers and Utils.find_driver_class_for
  4. Add driver contract tests in test/unit/drivers/new_driver_test.rb
  5. Add integration tests exercising the new driver

Adding a New Reporter

  1. Create lib/capybara_screenshot_diff/reporters/new_reporter.rb
  2. Implement record(assertions) and finalize methods
  3. Register with CapybaraScreenshotDiff.reporters << MyReporter.new
  4. Add tests in test/unit/reporters/

Releasing

To release a new version:

  1. Update the version number in lib/snap_diff/version.rb — the only place it lives; the gemspec and the legacy lib/capybara/screenshot/diff/version.rb both read it
  2. Update CHANGELOG.md with the new version and date
  3. Go to Actions → Release, click Run workflow and enter the version number. The workflow verifies the version against lib/, tests, tags, publishes both gem names, and creates the GitHub Release.

Do not use rake release. It is inherited from bundler/gem_tasks and publishes only capybara-screenshot-diff, skipping the snap_diff-capybara mirror — the two gems must never diverge in version. The full runbook, including the trusted-publishing prerequisites and what to do when a run fails halfway, is in docs/RELEASE_PREP.md.