Skip to content

refactor: split canonical vs legacy-surface tests (3.0 readiness) - #236

Merged
pftg merged 5 commits into
masterfrom
refactor/v3ready-test-suite-split
Aug 23, 2026
Merged

refactor: split canonical vs legacy-surface tests (3.0 readiness)#236
pftg merged 5 commits into
masterfrom
refactor/v3ready-test-suite-split

Conversation

@pftg

@pftg pftg commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

#235 proved the gem survives the 3.0 deletion with two edits. The suite did not: ~17F/24E, every one a test still asserting the v1 surface exists.

This PR makes the split mechanical. No version or CHANGELOG changes; no lib/ changes at all — the diff is Rakefile + test/.

The mechanism: a directory, not a list

test/legacy/ holds every test whose subject is the v1 compatibility surface. They stay and stay green for the whole 2.x line; in 3.0 they are deleted by the same commit that deletes what they test:

git rm -r lib/capybara* lib/capybara_screenshot_diff.rb \
          lib/snap_diff/legacy_shims.rb lib/snap_diff/deprecation.rb \
          test/legacy
task scope
rake test everything — today's gate, unchanged
rake test:canonical everything except test/legacythe 3.0 gate
rake test:unit test/unit + test/legacy — the release gate keeps its coverage

Why a directory over a LEGACY_SURFACE_TESTS list in the Rakefile: nothing to keep in sync (a list is a second place to edit, and a stale entry fails only at 3.0), the exclusion is one FileList#exclude, and the deletion is one git rm -r instead of five paths a stranger has to look up. test/legacy is in test:unit on purpose: legacy/ marks lifetime, not kind — these are unit-sized tests and release.yml runs rake test:unit.

The money shot — scratch copy, v1 trees + test/legacy deleted, the 2 known gem edits applied

Corrected. The first version of this section was wrong, and the correction is the interesting part — see "How the first money shot lied" below. This is the real transcript.

$ rm -rf lib/capybara lib/capybara-screenshot-diff.rb lib/capybara_screenshot_diff \
         lib/capybara_screenshot_diff.rb lib/snap_diff/legacy_shims.rb \
         lib/snap_diff/deprecation.rb test/legacy
$ # edit 1: drop `require "snap_diff/legacy_shims"` from lib/snap_diff.rb
$ # edit 2: lib/snap_diff-capybara.rb -> require "snap_diff/integrations/minitest"
$ git init -q . && git add -A && git commit -qm "3.0 dry run"

$ BUNDLE_GEMFILE=$PWD/gems.rb bundle exec ruby -Ilib -e '...'      # harness gate
snap_diff.rb resolves to: .../scratchpad/v3/lib
.../scratchpad/v3/lib/snap_diff.rb
SnapDiff.start defined?      false
CapybaraScreenshotDiff?      nil
SnapDiff.compare works?      true

$ BUNDLE_GEMFILE=$PWD/gems.rb bundle exec rake test:canonical
Finished in 35.796393s, 12.7666 runs/s, 35.9813 assertions/s.

457 runs, 1288 assertions, 0 failures, 0 errors, 1 skips

Green. Identical to rake test:canonical on this branch with everything present (457/1288/0F/0E/1S), which is the point: the task is a deletion, not a fix-up.

The gate runs first on purpose — it is the check whose absence produced the wrong number the first time.

Two prerequisites for reproducing it, both learned the hard way:

  1. BUNDLE_GEMFILE must point at the scratch copy's own gems.rb. gems.rb has gemspec path: __dir__, so bundler unshifts that gemfile's directory onto $LOAD_PATHahead of -Ilib. Point it at the original checkout and every subprocess probe silently loads the un-deleted gem.
  2. The checkout must be a git working tree with at least one commit. vcs_test shells out to git show HEAD: (lib/snap_diff/vcs.rb:10), so a plain copy fails there for reasons unrelated to 3.0.

How the first money shot lied

I published 458 runs … 0 failures. Independent review got 1 failure on the same counts. The review was right about the defect and understated the harness bug: my BUNDLE_GEMFILE pointed at the worktree's gems.rb, so bundler put the worktree's lib ahead of -Ilib. Proved directly, from inside the scratch copy:

$ BUNDLE_GEMFILE=<worktree>/gems.rb bundle exec ruby -Ilib -e '...'
<worktree>/lib/snap_diff.rb            <- NOT the scratch copy
start? true                            <- from the legacy_shims.rb I had "deleted"

So it was not "0 where it should have been 1" — the deletion was never exercised at all. Every subprocess probe in that run measured the intact tree. A run that cannot distinguish the deleted tree from the intact one is not evidence, and I published it as the evidence base for the 3.0 decision. The gate line now in the transcript exists so that failure mode announces itself.

The real defect it was hiding

CANONICAL_SURFACE listed start, applied to all 7 canonical entry points — and e789c8a widened it by adding snap_diff-capybara. SnapDiff.start is defined only at lib/snap_diff/legacy_shims.rb:169 and yields the two v1 config holders, so it cannot outlive them (#235 decided this); this very PR body files it under "safe to lose at 3.0". A canonical gate demanding a method 3.0 deletes goes red the day the deletion lands.

Fix: drop start from CANONICAL_SURFACE. Mutation — putting it back, in the deleted tree — reproduces the reviewer's failure exactly:

Canonical entry point(s) load only a fragment of SnapDiff:
require "snap_diff"                       -> missing: start
require "snap_diff/dsl"                   -> missing: start
require "snap_diff/integrations/minitest" -> missing: start
...  all 7 entry points

.start loses nothing: legacy_forwarders_test pins what it yields and that it applies a setting, and a new per-entry-point probe in legacy_entry_point_probe_test keeps the availability claim the canonical gate used to make — for the entries that actually keep it.

Per-category counts

# category count
1 test files quarantined into test/legacy/ 5 moved + 4 new (from splits)
2 harness files repointed test_helper.rb, system_test_case.rb + 7 support files
3 test files de-nested out of module Capybara::Screenshot(::Diff) / CapybaraScreenshotDiff 33
4 legacy constant / config-accessor / session call sites repointed 270
5 legacy require paths repointed 25
6 mixed files split (6+7) 4

Category 4 is 270, not ~330: the ~330 figure counted every CapybaraScreenshotDiff token, including the 33 module wrappers that category 3 removed outright.

Category 2: the support stubs stop reopening gem namespaces

CapybaraScreenshotDiff::DSLStub, ::DriverCoverage, ::NonMinitest, Capybara::Screenshot::ScreenshoterStub, Capybara::Screenshot::Diff::TestDoubles are all test scaffolding. They now sit at top level (DSLStub, DriverCoverage, …) and test/support/capybara_screenshot_diff/dsl_stub.rb moves to test/support/dsl_stub.rb. Defining a namespace 3.0 deletes, from a test support file, would have left a phantom CapybaraScreenshotDiff alive in the suite after the gem stopped shipping one.

Legacy-surface tests now require "capybara_screenshot_diff" themselves — the require goes with the file.

What a blind repoint would have broken

Five claims would have become tautologies (assert_same X, X) or quietly changed meaning. Each was a forwarder-identity claim about the v1 view, and each is preserved verbatim in test/legacy/:

was mechanical repoint would have produced
assert_same SnapDiff.session, CapybaraScreenshotDiff.registry assert_same SnapDiff.session, SnapDiff.session
assert_same SnapDiff::Reporting.reporters, CapybaraScreenshotDiff.reporters same-side-twice
assert_same SnapDiff::Comparison, ...::ImageCompare assert_same SnapDiff::Comparison, SnapDiff::Comparison
Capybara::Screenshot::Diff.fail_if_new = true then read via config writing and reading the same object
EAGER_USER_FACING = %w[Capybara::Screenshot::Diff::Reporters::Default ...] a list of canonical names — the eagerness claim is about the old names

Found with a scan for assertions whose two sides became textually identical, plus a read of every test whose name mentions legacy / old / mattr / alias / forwards. Four self-comparisons remain and are all genuine memoization checks (SnapDiff.session, Reporting.mutex, SnapManager.instance, Drivers.loaded); the first three were self-comparing before this PR, the fourth is the newly-ported .loaded test below.

The rspec fixtures were stubbing Capybara::Screenshot::Diff.pending_if_new, which the core stopped reading in #235 — a silently no-op stub. Now SnapDiff.config.

Unique canonical coverage found inside legacy-surface tests (and ported)

Audit rule: for every assertion moving to test/legacy/, if this file vanished at 3.0, would any canonical behaviour become untested? Two hits. Both are duplicated, not moved — the v1 originals stay put and keep guarding the v1 contract for all of 2.x.

from what it uniquely pinned now also lives in mutation
namespace_forwarding_test SnapDiff::Drivers.loaded is ONE hash mutated in place (it asserted the v1 LOADED_DRIVERS is that object, and that registering through it is visible canonically). Utils.find_driver_class_for caches through .loaded, so a copy-returning refactor breaks user driver registration silently. drivers_test".loaded is a single hash mutated in place, so registrations stick" .loaded.dup[…] = … → red: Expected: :probe_driver / Actual: nil on the second assertion (the assert_same oid mismatch fires first when the mutation is on the accessor itself)
support_load_probe_test's legacy rows an entry point defines its advertised constants when it is the ONLY require (the f89cea2 bug class) — pinned only for v1 names support_load_probe_test"every canonical entry point defines its advertised constants standalone", over snap_diff/dsl, /integrations/minitest, /integrations/rspec, snap_diff-capybara a bogus constant in the list → red, naming it

Three more gaps closed while splitting:

  • snap_diff-capybara joins CANONICAL_ENTRY_POINTS. 3.0 keeps that entry point (edit 2 repoints it), but it was covered only as a legacy entry — it would have lost all coverage.
  • errors_alias_test was mixed. The four CapybaraScreenshotDiff::* alias pairs are v1 surface; "error hierarchy is preserved" and "every error the gem defines inherits SnapDiff::Error" are not. Split verbatim into test/unit/errors_test.rb.
  • attempts_reporter_test now requires snap_diff/attempts_reporterstable_screenshoter pulls it in lazily and the v1 umbrella was what loaded it eagerly. This was the one canonical test the deletion actually broke.

And one guard moved the other way, per review: "bare require never loads the umbrella" is now in test/legacy/legacy_forwarders_test.rb. Its subject is lib/capybara_screenshot_diff.rb; once 3.0 deletes that file the $LOADED_FEATURES grep is empty by construction and the guard can never fail again. (This is the −1 that takes test:canonical from 458 to 457.)

Explicitly judged legacy-only, safe to lose at 3.0

const_missing / eager-alias semantics · deprecation warn-once, silencing, caller attribution, Deprecation.reset! · LegacyShims::CONFIG_MAPPING completeness and its agreement with Config::SETTINGS · the alias-only scan of lib/capybara* · the CapybaraScreenshotDiff session/reporter forwarders · SnapDiff.start · Capybara::Screenshot::Diff::VERSION / ::Comparison eagerness.

Every one is about a name 3.0 deletes, and every one has its canonical counterpart pinned in test/unit/active?'s full truth table, Config::SETTINGS's ivars, SnapDiff.configure, Reporting.register, SnapDiff.compare, SnapDiff.serve, the error hierarchy, Comparison::Images, Reporters::Default.

How the mixed files were split (6 + 7)

  • snap_diff_config_test+ test/legacy/legacy_config_accessors_test. Canonical keeps Config::SETTINGS↔ivars, the active? truth table (Config column), root coercion, configure, and screenshot_enabled/enabled independence. Legacy keeps CONFIG_MAPPING completeness, the mattr round trips, and the Capybara::Screenshot.active? column.
  • config_default_timing_test+ test/legacy/legacy_config_default_timing_test. Canonical keeps the whole 27-value default snapshot, require-time freezing of fail_if_new/root, and default_options[:wait] liveness — over snap_diff and snap_diff/integrations/minitest, read through SnapDiff.config only. The legacy file re-runs the canonical file's own probe scripts under the v1 entries and adds the both-surfaces-agree loop; together that is exactly what check_both asserted, with one source of truth and no drift.
  • support_load_probe_test+ test/legacy/legacy_entry_point_probe_test. Canonical keeps the support-file probe, the canonical surface probe, the dual-install guard, and the new advertised-constants probe. Legacy keeps the v1 advertised constants, the CapybaraScreenshotDiff session surface across 9 entries, SnapDiff.start's per-entry availability, and EAGER_USER_FACING / EAGER_EVERYWHERE under their old names. It reuses SupportLoadProbeTest.probe rather than keeping a second copy.

Verification

before (9822cc6) after
rake test:unit 530 runs, 1519 assertions, 0F/0E/0S 547, 1550, 0F/0E/0S
rake test 558 runs, 1564 assertions, 0F/0E/1S 575, 1595, 0F/0E/1S
rake test:canonical 457, 1288, 0F/0E/1S
rake test:canonical in the deleted tree 457, 1288, 0F/0E/1S
standardrb lib test Rakefile 148 files, no offenses
ruby mise x ruby@4.0.6

+17 runs, no assertion value changed. Attribution measured, not estimated — per-test-class counts from rake test TESTOPTS=--verbose on 9822cc6 vs this branch, grouped by the split that produced them:

split before → after net what the delta is
config_default_timing + legacy_config_default_timing 12 → 6 + 12 +6 canonical mirror entry snap_diff/integrations/minitest (×3) and the both-surfaces-agree probe per legacy entry (×3)
snap_diff_config + legacy_config_accessors 14 → 8 + 10 +4 four claims kept on both sides — active?, screenshot_enabled/enabled, root coercion, configure — canonical column and legacy column
snap_diff_testlegacy_forwarders 9 → 5 + 8 +4 4 tests relocated (alias, .start×2, umbrella guard); 4 v1 forwarder claims duplicated where the canonical file kept its own version (registry, register, reporters_mutex, serve)
support_load_probe + legacy_entry_point_probe 6 → 4 + 4 +2 the new canonical advertised-constants probe, and .start's per-entry probe moving to the legacy side
drivers_test 3 → 4 +1 ported Drivers.loaded coverage
errors_alias+ errors_test 11 → 9 + 2 0 pure split
Reporters::DefaultTestDefaultReporterTest 3 → 3 0 rename forced by de-nesting
+17

The reverse gate (core_tree_has_no_legacy_deps_test) and the alias-only gate (legacy_tree_is_alias_only_test, now in test/legacy/) are green throughout — both ALLOWED lists are still empty.

Do not merge.

🤖 Generated with Claude Code

pftg added 4 commits August 23, 2026 11:53
…anonical

The five tests whose SUBJECT is the v1 compatibility surface now live in
test/legacy/, so the 3.0 deletion is one more path on the same git rm:

  git rm -r lib/capybara* ... test/legacy

A directory rather than a list in the Rakefile: nothing to keep in sync.

- rake test           unchanged, runs everything (today's gate)
- rake test:canonical NEW, everything except test/legacy (the 3.0 gate)
- rake test:unit      test/unit + test/legacy, so the release gate keeps
                      its coverage (legacy/ marks lifetime, not kind)

errors_alias_test.rb was mixed: the four CapybaraScreenshotDiff::* alias
pairs are v1 surface, the hierarchy assertions outlive them. Split rather
than moved whole -- test/unit/errors_test.rb keeps the two canonical tests
verbatim, so no assertion is lost at 3.0.

530 runs, 1519 assertions, 0 failures (unchanged).
The suite still spoke v1 everywhere, so it would have broken on the 3.0
deletion even though the gem no longer does. Mechanical, no behaviour and
no assertion values changed:

- harness: test_helper + system_test_case load snap_diff/integrations/*
  and configure through SnapDiff.config; the support stubs (DSLStub,
  ScreenshoterStub, TestDoubles, DriverCoverage, NonMinitest) stop
  reopening gem namespaces and become plain top-level modules
- 33 test files were defined inside module Capybara::Screenshot(::Diff) /
  CapybaraScreenshotDiff -- de-nested to top-level classes, so no bare
  constant resolves into a namespace 3.0 deletes
- 270 legacy constant/accessor/session call sites repointed
  (CapybaraScreenshotDiff.registry -> SnapDiff.session, .reporters ->
  SnapDiff::Reporting.reporters, Capybara::Screenshot.root ->
  SnapDiff.config.root, ...) and 25 legacy require paths
- legacy-surface tests now require the v1 entry point themselves, since
  the shared harness no longer loads it

Three claims would have become tautologies under a blind repoint
(assert_same SnapDiff.session, SnapDiff.session and friends): they were
forwarder-identity claims about the v1 view. Preserved verbatim in the new
test/legacy/legacy_forwarders_test.rb together with SnapDiff.start, which
yields the two v1 holders and cannot outlive them.

rake test:unit 534 runs, 1526 assertions, 0F/0E (was 530/1519)
rake test      562 runs, 1571 assertions, 0F/0E/1S (was 558/1564)
+4 runs: legacy_forwarders_test keeps the v1 claim where the canonical
file also kept its own version (register-appends, reporters_mutex, serve).
Four files asserted the canonical behaviour AND the v1 view of it in one
place, so a receiver repoint turned real claims into tautologies. Each is
now two files; the v1 half is verbatim, and the canonical half stands on
its own after 3.0:

- snap_diff_config_test        -> + test/legacy/legacy_config_accessors_test
  (CONFIG_MAPPING completeness, the mattr_accessor round trips, active?
  through the legacy forwarder, SnapDiff.start)
- config_default_timing_test   -> + test/legacy/legacy_config_default_timing_test
  canonical keeps snap_diff + snap_diff/integrations/minitest and reads
  SnapDiff.config only; the legacy file re-runs the SAME probe scripts
  under the v1 entries and adds the both-surfaces-agree loop, which is
  exactly what check_both asserted -- one source of truth, no drift
- support_load_probe_test      -> + test/legacy/legacy_entry_point_probe_test
  (advertised v1 constants, the CapybaraScreenshotDiff session surface,
  EAGER_USER_FACING / EAGER_EVERYWHERE under their OLD names)
- errors_alias_test (earlier commit) -> test/unit/errors_test

snap_diff-capybara joins CANONICAL_ENTRY_POINTS: 3.0 keeps that entry
point (repointed at snap_diff/integrations/minitest), and it was covered
only as a legacy entry, so it would have lost all coverage.

Also repointed the last legacy call sites the sweep left: the rspec
fixtures stubbed Capybara::Screenshot::Diff.pending_if_new, which the core
stopped reading in #235 -- a silent no-op stub, now SnapDiff.config.

rake test:unit 544 runs, 1544 assertions, 0F/0E
rake test      572 runs, 1589 assertions, 0F/0E/1S
…inning

Audit of every assertion moving into test/legacy/, asking: if this file
vanished at 3.0, would any CANONICAL behaviour become untested? Two hits,
both now duplicated (not moved) into a canonical test -- the v1 originals
stay put, they still guard the v1 contract for all of 2.x:

- namespace_forwarding_test was the only place proving SnapDiff::Drivers
  .loaded is ONE hash mutated in place (it asserted the v1 LOADED_DRIVERS
  constant is that same object, and that registering through it shows up
  canonically). Utils.find_driver_class_for caches through .loaded, so a
  copy-returning refactor would break user driver registration silently.
  -> drivers_test ".loaded is a single hash mutated in place"
     mutation: `.loaded.dup[...] = ...` -> red, Expected :probe_driver, got nil

- the entry-point probe was the only place asserting an entry point defines
  its advertised CONSTANTS when it is the ONLY require (the f89cea2 bug
  class) -- but only for the v1 names.
  -> support_load_probe_test "every canonical entry point defines its
     advertised constants standalone", same claim over snap_diff/dsl,
     /integrations/minitest, /integrations/rspec, snap_diff-capybara.
     Entry-specific, because bare snap_diff carries neither DSL nor
     reporters by design.
     mutation: a bogus constant in the list -> red, naming it

Also: attempts_reporter_test now requires snap_diff/attempts_reporter --
stable_screenshoter pulls it in lazily and the v1 umbrella was what loaded
it eagerly, so it was the one canonical test the deletion actually broke.

Judged legacy-only and safe to lose at 3.0: const_missing/eager alias
semantics, deprecation warn-once + silencing, CONFIG_MAPPING completeness,
the alias-only scan of lib/capybara*, and the CapybaraScreenshotDiff
session/reporter forwarders -- every one is about a name 3.0 deletes, and
its canonical counterpart is pinned in test/unit/.

rake test 574 runs, 1593 assertions, 0F/0E/1S

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @pftg, your pull request is larger than the review limit of 150000 diff characters

@sourcery-ai

sourcery-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors the test suite to clearly separate legacy v1-surface coverage from canonical SnapDiff coverage via a new test/legacy directory and a test:canonical rake task, while repointing most tests and support code to canonical SnapDiff APIs and duplicating any unique canonical coverage that was previously embedded in legacy-focused tests.

Flow diagram for canonical and legacy test scopes

flowchart TD
    all["test/**/*_test.rb"] --> test[rake test]
    all --> canonicalFilter["FileList exclude test/legacy"]
    canonicalFilter --> canonical[rake test:canonical]
    unitFiles["test/unit/**/*_test.rb"] --> unit[rake test:unit]
    legacy["test/legacy/**/*_test.rb"] --> unit
    legacy -.-> deletion["3.0 deletion: remove v1 surface and test/legacy"]
    canonical --> gate["3.0 gate"]
Loading

File-Level Changes

Change Details Files
Introduce explicit canonical vs legacy test tasks and directory structure for 3.0 readiness.
  • Add LEGACY_SURFACE_TESTS FileList and new test:canonical Rake task that excludes test/legacy tests.
  • Adjust test:unit task to include both test/unit and legacy tests so release gate keeps legacy coverage.
  • Document 3.0 deletion plan and roles of test, test:canonical, and test:unit in Rakefile comments.
Rakefile
Repoint fixtures, system/integration tests, and harness from CapybaraScreenshotDiff/capybara_screenshot_diff to canonical SnapDiff APIs.
  • Change rspec fixtures to require snap_diff/integrations/rspec and use SnapDiff.config instead of Capybara::Screenshot::Diff configuration accessors.
  • Update SystemTestCase and integration/system tests to include SnapDiff::DSL and SnapDiff::Minitest::Assertions and to use SnapDiff.config/session/UnstableImage for runtime behaviour.
  • Adjust various tests to require snap_diff/* files instead of capybara_screenshot_diff/*, and to assert against SnapDiff names rather than v1 constants.
test/fixtures/rspec_after_hook_order_masking_spec.rb
test/fixtures/rspec_pending_masking_spec.rb
test/fixtures/rspec_spec.rb
test/integration/browser_screenshot_test.rb
test/integration/record_screenshot_test.rb
test/integration/rspec_after_hook_order_masking_test.rb
test/integration/rspec_pending_masking_test.rb
test/integration/rspec_test.rb
test/integration/test_methods_system_test.rb
test/system_test_case.rb
test/test_helper.rb
Quarantine legacy v1-surface tests into test/legacy and ensure they remain green through 2.x but are deletable in 3.0.
  • Move existing v1 alias and namespace forwarding tests from test/unit into test/legacy and annotate them as legacy surface.
  • Create new legacy_* tests that hold v1-only behaviour such as CONFIG_MAPPING completeness, mattr accessor round trips, and v1 entry-point probes.
  • Ensure legacy tests require capybara_screenshot_diff directly since the shared harness now only loads canonical entry points.
test/legacy/errors_alias_test.rb
test/legacy/legacy_config_accessors_test.rb
test/legacy/legacy_config_default_timing_test.rb
test/legacy/legacy_entry_point_probe_test.rb
test/legacy/legacy_forwarders_test.rb
test/legacy/legacy_namespace_deprecation_test.rb
test/legacy/legacy_tree_is_alias_only_test.rb
test/legacy/namespace_forwarding_test.rb
test/legacy/snap_diff_deprecation_test.rb
Flatten and rename test support modules to avoid reopening gem namespaces, especially v1 namespaces that will be deleted.
  • Delete test/support/capybara_screenshot_diff/dsl_stub.rb and replace it with top-level DSLStub in test/support/dsl_stub.rb.
  • Make DriverCoverage, NonMinitest::Assertions, ScreenshoterStub, and TestDoubles top-level modules/classes and update all include/require sites accordingly.
  • Update stub_test_methods.rb and test_helpers/test_data helpers to use the new top-level support modules.
test/support/capybara_screenshot_diff/dsl_stub.rb
test/support/driver_coverage.rb
test/support/dsl_stub.rb
test/support/non_minitest_assertions.rb
test/support/screenshoter_stub.rb
test/support/setup_capybara_drivers.rb
test/support/stub_test_methods.rb
test/support/test_doubles.rb
test/support/test_helpers.rb
Refactor unit tests to target canonical SnapDiff APIs and types while preserving behaviour and adding missing canonical coverage.
  • Move canonical error-hierarchy checks to new test/unit/errors_test.rb and leave alias-only checks in legacy/errors_alias_test.rb.
  • Update numerous unit tests (drivers, DSL, image compare, reporters, registry, SnapManager, config timing, etc.) to require snap_diff/* and to assert on SnapDiff namespaces instead of Capybara::Screenshot::Diff.
  • Add new unit coverage such as Drivers.loaded mutation behaviour and canonical entry-point constant provisioning via SupportLoadProbeTest, while keeping v1-only probes in legacy tests.
  • Tighten thread-local behaviour of SnapDiff.session and SnapDiff::SnapManager.instance and ensure reporters/registry APIs operate on canonical SnapDiff::Reporting and SnapDiff.session.
test/unit/errors_test.rb
test/unit/annotation_service_test.rb
test/unit/area_calculator_test.rb
test/unit/attempts_reporter_test.rb
test/unit/backtrace_filter_test.rb
test/unit/capture/viewport_test.rb
test/unit/compare_api_test.rb
test/unit/config_default_timing_test.rb
test/unit/core_tree_has_no_legacy_deps_test.rb
test/unit/diff_test.rb
test/unit/difference_test.rb
test/unit/driver_coverage_test.rb
test/unit/drivers/chunky_png_driver_test.rb
test/unit/drivers/utils_test.rb
test/unit/drivers/vips_driver_test.rb
test/unit/drivers_test.rb
test/unit/dsl_test.rb
test/unit/image_compare_test.rb
test/unit/image_preprocessor_test.rb
test/unit/minitest_assertions_test.rb
test/unit/pending_screenshots_message_test.rb
test/unit/region_test.rb
test/unit/registry_concurrency_test.rb
test/unit/reporter_interplay_test.rb
test/unit/reporters/default_test.rb
test/unit/reporters/html_reporter_test.rb
test/unit/reporters_mutex_test.rb
test/unit/screenshot_assertion_test.rb
test/unit/screenshot_matcher_test.rb
test/unit/screenshot_namer_test.rb
test/unit/screenshot_test.rb
test/unit/screenshoter_test.rb
test/unit/snap_diff_config_test.rb
test/unit/snap_diff_test.rb
test/unit/snap_manager_cleanup_test.rb
test/unit/snap_manager_test.rb
test/unit/stable_screenshoter_test.rb
test/unit/static_test.rb
test/unit/support_load_probe_test.rb
test/unit/vcs_test.rb

Possibly linked issues

  • Create CODE_OF_CONDUCT.md #4: Links to ADR-004 Phase 3 readiness: canonical tests survive legacy namespace deletion, though library code remains unchanged.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5c4b5b0e-b899-42de-8365-dd23b3322aee

📥 Commits

Reviewing files that changed from the base of the PR and between 9822cc6 and 17225cc.

📒 Files selected for processing (70)
  • Rakefile
  • test/fixtures/rspec_after_hook_order_masking_spec.rb
  • test/fixtures/rspec_pending_masking_spec.rb
  • test/fixtures/rspec_spec.rb
  • test/integration/browser_screenshot_test.rb
  • test/integration/record_screenshot_test.rb
  • test/integration/rspec_after_hook_order_masking_test.rb
  • test/integration/rspec_pending_masking_test.rb
  • test/integration/rspec_test.rb
  • test/integration/test_methods_system_test.rb
  • test/legacy/errors_alias_test.rb
  • test/legacy/legacy_config_accessors_test.rb
  • test/legacy/legacy_config_default_timing_test.rb
  • test/legacy/legacy_entry_point_probe_test.rb
  • test/legacy/legacy_forwarders_test.rb
  • test/legacy/legacy_namespace_deprecation_test.rb
  • test/legacy/legacy_tree_is_alias_only_test.rb
  • test/legacy/namespace_forwarding_test.rb
  • test/legacy/snap_diff_deprecation_test.rb
  • test/support/capybara_screenshot_diff/dsl_stub.rb
  • test/support/driver_coverage.rb
  • test/support/dsl_stub.rb
  • test/support/non_minitest_assertions.rb
  • test/support/screenshoter_stub.rb
  • test/support/setup_capybara_drivers.rb
  • test/support/stub_test_methods.rb
  • test/support/test_doubles.rb
  • test/support/test_helpers.rb
  • test/system_test_case.rb
  • test/test_helper.rb
  • test/unit/annotation_service_test.rb
  • test/unit/area_calculator_test.rb
  • test/unit/attempts_reporter_test.rb
  • test/unit/backtrace_filter_test.rb
  • test/unit/capture/viewport_test.rb
  • test/unit/compare_api_test.rb
  • test/unit/config_default_timing_test.rb
  • test/unit/core_tree_has_no_legacy_deps_test.rb
  • test/unit/diff_test.rb
  • test/unit/difference_test.rb
  • test/unit/driver_coverage_test.rb
  • test/unit/drivers/chunky_png_driver_test.rb
  • test/unit/drivers/utils_test.rb
  • test/unit/drivers/vips_driver_test.rb
  • test/unit/drivers_test.rb
  • test/unit/dsl_test.rb
  • test/unit/errors_test.rb
  • test/unit/image_compare_test.rb
  • test/unit/image_preprocessor_test.rb
  • test/unit/minitest_assertions_test.rb
  • test/unit/pending_screenshots_message_test.rb
  • test/unit/region_test.rb
  • test/unit/registry_concurrency_test.rb
  • test/unit/reporter_interplay_test.rb
  • test/unit/reporters/default_test.rb
  • test/unit/reporters/html_reporter_test.rb
  • test/unit/reporters_mutex_test.rb
  • test/unit/screenshot_assertion_test.rb
  • test/unit/screenshot_matcher_test.rb
  • test/unit/screenshot_namer_test.rb
  • test/unit/screenshot_test.rb
  • test/unit/screenshoter_test.rb
  • test/unit/snap_diff_config_test.rb
  • test/unit/snap_diff_test.rb
  • test/unit/snap_manager_cleanup_test.rb
  • test/unit/snap_manager_test.rb
  • test/unit/stable_screenshoter_test.rb
  • test/unit/static_test.rb
  • test/unit/support_load_probe_test.rb
  • test/unit/vcs_test.rb
💤 Files with no reviewable changes (1)
  • test/support/capybara_screenshot_diff/dsl_stub.rb

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The test suite now separates canonical SnapDiff tests from legacy compatibility tests. Test support, integrations, fixtures, configuration checks, namespaces, reporters, sessions, and error assertions use canonical SnapDiff APIs.

Changes

SnapDiff test migration

Layer / File(s) Summary
Test execution split
Rakefile
Adds test:canonical, defines the legacy test pattern, and includes legacy tests in test:unit.
Integration and RSpec migration
test/fixtures/*, test/integration/*
Migrates fixtures and integration tests to SnapDiff integrations, configuration, sessions, and exceptions.
Legacy compatibility coverage
test/legacy/*
Adds coverage for legacy entry points, configuration forwarding, timing, reporters, sessions, and planned 3.0 removal boundaries.
Canonical test support
test/support/*, test/system_test_case.rb, test/test_helper.rb
Moves helpers, doubles, assertions, driver setup, and system-test configuration to canonical namespaces and APIs.
Canonical unit coverage
test/unit/*
Migrates unit tests to SnapDiff APIs and top-level test classes while preserving existing behavioral coverage.
Canonical loading and configuration checks
test/unit/config_default_timing_test.rb, test/unit/snap_diff_config_test.rb, test/unit/support_load_probe_test.rb, test/unit/errors_test.rb
Validates canonical configuration, advertised constants, entry-point loading, driver state, and error hierarchy.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 17225

This test-only reorganization adds a canonical gate while preserving the existing suites; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: separating canonical and legacy-surface tests for 3.0 readiness.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/v3ready-test-suite-split

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Caught by independent review: `rake test:canonical` in the deleted tree is
1F, not the 0F I published.

CANONICAL_SURFACE listed `start`, applied to all 7 canonical entry points.
SnapDiff.start is defined only in lib/snap_diff/legacy_shims.rb:169 and
yields the two v1 config holders, so it cannot outlive them (#235 decided
this). A canonical gate demanding a method 3.0 deletes is a gate that goes
red the day the deletion lands -- and I widened it in e789c8a by adding
snap_diff-capybara. My own PR body filed .start under "safe to lose at 3.0".

  mutation (start put back, deleted tree):
    require "snap_diff"                       -> missing: start
    require "snap_diff/dsl"                   -> missing: start
    require "snap_diff/integrations/minitest" -> missing: start
    ... all 7 entry points

.start keeps full coverage on the legacy side: legacy_forwarders_test pins
what it yields and that it applies a setting, and a new per-entry-point
probe in legacy_entry_point_probe_test pins the availability claim the
canonical gate used to make -- for the entries that actually keep it.

Also, per review:

- "bare require never loads the umbrella" moves to legacy_forwarders_test.
  Its subject is lib/capybara_screenshot_diff.rb; once 3.0 deletes that
  file the $LOADED_FEATURES grep is empty by construction and the guard can
  never fail again. (-1 canonical run: 458 -> 457.)
- backtrace_filter_test built synthetic paths under lib/capybara_screenshot_diff/.
  Pure string inputs to a prefix matcher, so no assertion changes -- but one
  of them named the real file the filter defaults to, which 3.0 deletes.

rake test:unit      547 runs, 1550 assertions, 0F/0E/0S
rake test           575 runs, 1595 assertions, 0F/0E/1S
rake test:canonical 457 runs, 1288 assertions, 0F/0E/1S
  ... and 457/1288/0F/0E/1S in the deleted tree, identical.
@pftg
pftg merged commit bbf724f into master Aug 23, 2026
8 checks passed
@pftg
pftg deleted the refactor/v3ready-test-suite-split branch August 23, 2026 10:43
pftg added a commit that referenced this pull request Aug 23, 2026
`rake test:canonical` is defined as "exactly what must still pass once
test/legacy/ and the v1 trees are gone". Three times in one day a test
asserting LEGACY behaviour was written into test/unit/, i.e. into that
suite: a canonical surface table demanding the shim-only SnapDiff.start
(#236), three legacy-constant probes in a canonical file (#237), and a
pre-existing umbrella guard #236 had to relocate. Each would have failed
the day the deletion landed, long after its author moved on. Reviews
caught all three; the fourth would ship.

The test-tree twin of core_tree_has_no_legacy_deps_test.rb: no file under
test/unit/ or test/integration/ may require a doomed path, name a v1
namespace constant, or use a shim-only name (SnapDiff.start,
.silence_deprecations, SnapDiff::Deprecation, suppress_migration_notice!).
test/legacy/ is deliberately not policed -- exercising the legacy surface
is its job.

Same conventions as the twin: file:line: reason -- `code`, whole-line
comments ignored, a vacuity guard, and a sub-test that fails on stale
allowlist entries. The allowlist holds two entries, both gates rather
than tests of behaviour (deletion_3_0_test.rb, which names the deletion
set by construction, and the twin gate's own pattern literal). A third
entry means canonical tests are still entangled and needs a decision, not
a green build.

This file cannot scan itself: a line-level allowlist has to quote the
lines it blesses, and every quote is itself an offence -- no fixed point
exists.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant