snap_diff-capybara 2.0.0.beta2 → 2.0.0.beta4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +471 -0
  3. data/README.md +330 -0
  4. data/docs/UPGRADING.md +232 -34
  5. data/docs/architecture.md +9 -7
  6. data/docs/ci-integration.md +42 -11
  7. data/docs/configuration.md +176 -10
  8. data/docs/drivers.md +48 -5
  9. data/docs/framework-setup.md +10 -3
  10. data/docs/migration-guide.md +24 -13
  11. data/docs/reporters.md +70 -1
  12. data/docs/snapdiff.md +41 -8
  13. data/docs/thread_safety.md +77 -86
  14. data/lib/capybara/screenshot/diff/annotation_service.rb +1 -3
  15. data/lib/capybara/screenshot/diff/area_calculator.rb +1 -3
  16. data/lib/capybara/screenshot/diff/browser_helpers.rb +1 -3
  17. data/lib/capybara/screenshot/diff/config_legacy.rb +15 -60
  18. data/lib/capybara/screenshot/diff/cucumber.rb +5 -1
  19. data/lib/capybara/screenshot/diff/difference.rb +1 -4
  20. data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +3 -3
  21. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +2 -3
  22. data/lib/capybara/screenshot/diff/drivers.rb +4 -5
  23. data/lib/capybara/screenshot/diff/image_compare.rb +12 -11
  24. data/lib/capybara/screenshot/diff/image_preprocessor.rb +1 -3
  25. data/lib/capybara/screenshot/diff/os.rb +4 -11
  26. data/lib/capybara/screenshot/diff/region.rb +2 -2
  27. data/lib/capybara/screenshot/diff/reporters/default.rb +4 -3
  28. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +1 -3
  29. data/lib/capybara/screenshot/diff/screenshoter.rb +1 -3
  30. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +1 -3
  31. data/lib/capybara/screenshot/diff/utils.rb +1 -3
  32. data/lib/capybara/screenshot/diff/vcs.rb +1 -3
  33. data/lib/capybara/screenshot/diff/version.rb +8 -13
  34. data/lib/capybara-screenshot-diff.rb +10 -1
  35. data/lib/capybara_screenshot_diff/attempts_reporter.rb +1 -3
  36. data/lib/capybara_screenshot_diff/dsl.rb +5 -0
  37. data/lib/capybara_screenshot_diff/error_with_filtered_backtrace.rb +1 -4
  38. data/lib/capybara_screenshot_diff/reporters/html.rb +1 -3
  39. data/lib/capybara_screenshot_diff/screenshot_namer.rb +1 -3
  40. data/lib/capybara_screenshot_diff/snap.rb +1 -3
  41. data/lib/capybara_screenshot_diff/snap_manager.rb +1 -3
  42. data/lib/capybara_screenshot_diff/static.rb +4 -0
  43. data/lib/capybara_screenshot_diff.rb +17 -21
  44. data/lib/snap_diff/browser_helpers.rb +26 -5
  45. data/lib/snap_diff/capture/viewport.rb +2 -5
  46. data/lib/snap_diff/comparison.rb +54 -3
  47. data/lib/snap_diff/comparison_result.rb +3 -1
  48. data/lib/snap_diff/config.rb +189 -93
  49. data/lib/snap_diff/deprecation.rb +89 -28
  50. data/lib/snap_diff/driver.rb +18 -0
  51. data/lib/snap_diff/drivers/vips_driver.rb +12 -6
  52. data/lib/snap_diff/drivers.rb +94 -12
  53. data/lib/snap_diff/dsl.rb +51 -45
  54. data/lib/snap_diff/errors.rb +7 -1
  55. data/lib/snap_diff/integrations/cucumber.rb +1 -1
  56. data/lib/snap_diff/integrations/minitest.rb +45 -9
  57. data/lib/snap_diff/integrations/rspec.rb +11 -0
  58. data/lib/snap_diff/legacy_shims.rb +283 -28
  59. data/lib/snap_diff/removal.rb +159 -0
  60. data/lib/snap_diff/reporters/default.rb +86 -23
  61. data/lib/snap_diff/reporters/html.rb +29 -13
  62. data/lib/snap_diff/reporting.rb +330 -4
  63. data/lib/snap_diff/screenshot_assertion.rb +28 -23
  64. data/lib/snap_diff/screenshot_matcher.rb +121 -14
  65. data/lib/snap_diff/screenshot_namer.rb +1 -19
  66. data/lib/snap_diff/screenshoter.rb +5 -7
  67. data/lib/snap_diff/snap.rb +6 -1
  68. data/lib/snap_diff/snap_manager.rb +2 -3
  69. data/lib/snap_diff/stable_screenshoter.rb +2 -2
  70. data/lib/snap_diff/static.rb +1 -1
  71. data/lib/snap_diff/utils.rb +35 -17
  72. data/lib/snap_diff/vcs.rb +40 -7
  73. data/lib/snap_diff/version.rb +1 -1
  74. data/lib/snap_diff-capybara.rb +38 -0
  75. data/lib/snap_diff.rb +40 -39
  76. metadata +13 -10
  77. data/CODE_OF_CONDUCT.md +0 -129
  78. data/Rakefile +0 -65
  79. data/capybara-screenshot-diff.gemspec +0 -29
  80. data/docs/RELEASE_PREP.md +0 -44
  81. data/docs/docker-testing.md +0 -24
  82. data/gems.rb +0 -39
data/docs/UPGRADING.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Upgrading
2
2
 
3
- ## Upgrading to v2.0 (alpha)
3
+ ## Upgrading to v2.0
4
4
 
5
5
  ### Overview
6
6
 
7
7
  Version 2.0 introduces a new canonical namespace (`SnapDiff`) for cleaner, more discoverable code. The public DSL remains unchanged — your existing `screenshot` and `assert_matches_screenshot` calls work without modification. This guide covers the optional migration path for settings and the new namespace.
8
8
 
9
- **Status:** `2.0.0.beta2` is an opt-in prerelease. RubyGems never installs prereleases by default resolution normal `bundle update` keeps you on the 1.x line. The final 2.0.0 ships only after adopter feedback; please report anything surprising on [#166](https://github.com/snap-diff/snap_diff-capybara/issues/166).
9
+ **Status:** 2.0 is the **transitional** release — the v1 API and the canonical `SnapDiff` API both work. **2.1 removes** everything 2.0 warns about (the legacy namespaces, the ChunkyPNG driver, `shift_distance_limit`, the `driver:` setting and the driver abstraction, and the `fail_if_new` / `pending_if_new` / `fail_on_difference` booleans that `record` replaces). There is no 3.0. Migrating on 2.0 is optional; doing it before 2.1 is not.
10
10
 
11
11
  **Estimated upgrade time:** 5–15 minutes (most users need only the Gemfile pin)
12
12
 
@@ -14,22 +14,37 @@ Version 2.0 introduces a new canonical namespace (`SnapDiff`) for cleaner, more
14
14
  [SnapDiff — the canonical API](snapdiff.md): the same setup, configuration, and extension points
15
15
  with canonical names only, no legacy shapes to unlearn.
16
16
 
17
- **Breaking changes:** None for the DSL; deprecation warnings if you reference legacy constants (suppressible), plus two known alpha caveats (see below)
17
+ **Breaking changes:** None for the DSL; one migration notice per process plus a deprecation warning per legacy constant you reference (both suppressible), plus the [known caveats](#known-caveats) below
18
18
 
19
19
  ---
20
20
 
21
21
  ### The Short Version (Most Users)
22
22
 
23
23
  ```ruby
24
- # In your Gemfile — the exact prerelease version is required to opt in
25
- gem "capybara-screenshot-diff", "2.0.0.beta2" # or the latest 2.0.0 prerelease
24
+ # In your Gemfile
25
+ gem "capybara-screenshot-diff", "2.0.0.beta4" # current 2.0 prerelease; 2.0.0 final is not out yet
26
26
  ```
27
27
 
28
+ **Pin the exact prerelease.** An unpinned `gem "capybara-screenshot-diff"` resolves to the 1.x
29
+ line, and `"~> 2.0"` does not resolve at all — Bundler never picks a prerelease from a plain
30
+ requirement, so it fails with `Could not find gem 'capybara-screenshot-diff (~> 2.0)'`. Once
31
+ 2.0.0 ships, `"~> 2.0"` is the pin to use.
32
+
33
+ The same content is also published as `snap_diff-capybara` from 2.0.0 on, but that name's
34
+ only earlier non-prerelease is a `0.0.1` placeholder with no Ruby files in it — unpinned, it
35
+ installs an empty gem and raises `LoadError`. Stay on `capybara-screenshot-diff`, and install
36
+ **one** name: with both in a Gemfile the gem raises `SnapDiff::DualInstallError` at require
37
+ time.
38
+
28
39
  ```bash
29
40
  bundle install
30
- bundle exec rake test
41
+ bin/rails test:system # the task that actually runs your screenshot tests
31
42
  ```
32
43
 
44
+ > **Not `rake test` / `rails test`.** In a Rails app those skip `test/system/`, so you get
45
+ > `0 runs, 0 assertions, 0 failures` — a clean-looking pass that compared nothing. Outside
46
+ > Rails, run whatever task loads your Capybara tests.
47
+
33
48
  **That's it.** Your existing code works unchanged. The old namespaces (`Capybara::Screenshot::Diff`, `CapybaraScreenshotDiff`) are shimmed with deprecation warnings; the new one (`SnapDiff`) is available if you want to modernize.
34
49
 
35
50
  ---
@@ -38,13 +53,14 @@ bundle exec rake test
38
53
 
39
54
  #### 1. New Canonical Namespace: `SnapDiff`
40
55
 
41
- The implementation now lives in `lib/snap_diff/` under the `SnapDiff` namespace. Every legacy constant still resolves — lazily, to the *same object* but emits a one-time-per-constant deprecation warning. The main renames:
56
+ The implementation now lives in `lib/snap_diff/` under the `SnapDiff` namespace. Every legacy constant still resolves to the *same object*. Most do so lazily and warn once each; a documented set stays eagerly defined and silent — see [Deprecation Warnings](#deprecation-warnings) for exactly which. The main renames:
42
57
 
43
58
  | Legacy name | v2 canonical name |
44
59
  |-------------|-------------------|
45
60
  | `Capybara::Screenshot::Diff::ImageCompare` | `SnapDiff::Comparison` |
46
61
  | `Capybara::Screenshot::Diff::Difference` | `SnapDiff::ComparisonResult` |
47
62
  | `Capybara::Screenshot::Diff::Drivers::BaseDriver` | `SnapDiff::Driver` (now a mixin — see below) |
63
+ | `Capybara::Screenshot::Os` | `SnapDiff::Os` |
48
64
  | `CapybaraScreenshotDiff::SnapManager` / `::Snap` | `SnapDiff::SnapManager` / `SnapDiff::Snap` |
49
65
  | `CapybaraScreenshotDiff::RED_RGBA` / `::ORANGE_RGBA` | `SnapDiff::RED_RGBA` / `SnapDiff::ORANGE_RGBA` |
50
66
  | `CapybaraScreenshotDiff::Minitest::Assertions` | `SnapDiff::Minitest::Assertions` |
@@ -98,7 +114,7 @@ The most commonly-used settings and how to update them:
98
114
  | `save_path` | `Capybara::Screenshot.save_path = "doc/screenshots"` | `SnapDiff.config.save_path = "doc/screenshots"` | Where baseline screenshots are stored |
99
115
  | `window_size` | `Capybara::Screenshot.window_size = [1280, 1024]` | `SnapDiff.config.window_size = [1280, 1024]` | Browser viewport size for consistent screenshots |
100
116
 
101
- **All 27 settings** from both legacy namespaces are available via `SnapDiff.config.<attr_name>` — see the [Configuration Reference](configuration.md) for the full list. One rename to note: `Capybara::Screenshot.enabled` becomes `SnapDiff.config.screenshot_enabled` (it would otherwise collide with `Capybara::Screenshot::Diff.enabled`, which keeps the bare `enabled` name).
117
+ **All 28 settings** from both legacy namespaces are available via `SnapDiff.config.<attr_name>` — see the [Configuration Reference](configuration.md) for the full list. One rename to note: `Capybara::Screenshot.enabled` becomes `SnapDiff.config.screenshot_enabled` (it would otherwise collide with `Capybara::Screenshot::Diff.enabled`, which keeps the bare `enabled` name).
102
118
 
103
119
  ---
104
120
 
@@ -114,7 +130,6 @@ Capybara::Screenshot::Diff.configure do |screenshot, diff|
114
130
  screenshot.window_size = [1280, 1024]
115
131
  screenshot.blur_active_element = false
116
132
  diff.tolerance = 0.0005
117
- diff.driver = :vips
118
133
  end
119
134
  ```
120
135
 
@@ -125,7 +140,6 @@ SnapDiff.start do |screenshot, diff|
125
140
  screenshot.window_size = [1280, 1024]
126
141
  screenshot.blur_active_element = false
127
142
  diff.tolerance = 0.0005
128
- diff.driver = :vips
129
143
  end
130
144
  ```
131
145
 
@@ -136,7 +150,6 @@ SnapDiff.configure do |config|
136
150
  config.window_size = [1280, 1024]
137
151
  config.blur_active_element = false
138
152
  config.tolerance = 0.0005
139
- config.driver = :vips
140
153
  end
141
154
  ```
142
155
 
@@ -159,19 +172,187 @@ This means you can migrate your codebase incrementally **now**, before opting in
159
172
 
160
173
  ### Deprecation Warnings
161
174
 
162
- In v2.0, resolving a legacy *constant* emits one deprecation warning per constant per process:
175
+ v2.0 emits five different things, and it is worth knowing which is which. The first two are
176
+ about the old namespaces; the third is about the driver features 2.1 removes; the fourth is
177
+ about options that never did anything; the fifth is about the new-screenshot booleans that
178
+ `record` replaces.
179
+
180
+ Everything 2.1 removes warns in 2.0, and every warning names 2.1. Nothing you can still write
181
+ in 2.0 does nothing quietly — if a setting is on its way out, or was never read at all, you
182
+ hear about it once per process.
183
+
184
+ #### 1. The migration notice — one line per process
185
+
186
+ The first time a process touches *any* hookable legacy API, you get a single line:
187
+
188
+ ```
189
+ [snap_diff deprecation] This process uses the v1 `Capybara::Screenshot*` / `CapybaraScreenshotDiff*` API. It still works in 2.0 and is REMOVED in 2.1 -- see docs/UPGRADING.md for the SnapDiff replacements. Silence with `SnapDiff.silence_deprecations = true` or SNAP_DIFF_SILENCE_DEPRECATIONS=1. (shown once per process)
190
+ ```
191
+
192
+ It fires once and never again, whichever door you came through:
193
+
194
+ - **requiring a v1-named entry point** — `require "capybara/screenshot/diff"`,
195
+ `"capybara-screenshot-diff"`, `"capybara_screenshot_diff"`, or any of
196
+ `capybara_screenshot_diff/{dsl,minitest,rspec,cucumber}`. With `gem "capybara-screenshot-diff"`
197
+ in the Gemfile, `Bundler.require` opens this door for you at boot.
198
+ - a legacy config accessor — `Capybara::Screenshot.window_size = ...`, `Capybara::Screenshot::Diff.tolerance`
199
+ - a lazily shimmed legacy constant (see below)
200
+ - `include Capybara::Screenshot` / `include Capybara::Screenshot::Diff`
201
+
202
+ It exists because most of the v1 surface **cannot** warn per use, so without it a 2.x app could
203
+ be entirely silent right up to the bare `NameError` it would get on 2.1. The require door is
204
+ what makes the rest of the list a safety net rather than the only mechanism: a suite that
205
+ requires the gem and only calls `screenshot` touches none of the other four.
206
+
207
+ The canonical entry points — `require "snap_diff"`, `"snap_diff-capybara"`, `"snap_diff/dsl"`,
208
+ `"snap_diff/integrations/*"` — never fire it, even though `snap_diff-capybara` loads the v1
209
+ compatibility files internally.
210
+
211
+ > **Silencing a require-time notice needs the env var.** `SnapDiff.silence_deprecations = true`
212
+ > only takes effect from the line that sets it, and you cannot set it before the require that
213
+ > defines `SnapDiff`. Under `Bundler.require` there is no earlier moment at all. Use
214
+ > `SNAP_DIFF_SILENCE_DEPRECATIONS=1` in the environment, or
215
+ > `require "snap_diff/deprecation"; SnapDiff.silence_deprecations = true` ahead of everything
216
+ > else.
217
+
218
+ #### 2. Per-constant warnings — one line per lazily shimmed constant
219
+
220
+ Resolving a legacy constant that is shimmed through `const_missing` also warns, once per constant
221
+ per process:
163
222
 
164
223
  ```
165
224
  [snap_diff deprecation] `Capybara::Screenshot::Diff::ImageCompare` is deprecated (constant); use `SnapDiff::Comparison` instead.
166
225
  ```
167
226
 
168
- **Warnings appear for:** legacy constant access — `Capybara::Screenshot::Diff::ImageCompare`, `::Difference`, `::Drivers`, `CapybaraScreenshotDiff::SnapManager`, etc.
227
+ **These appear for:** `Capybara::Screenshot::{BrowserHelpers, Screenshoter}`;
228
+ `Capybara::Screenshot::Diff::{Vcs, StableScreenshoter, ImagePreprocessor, AreaCalculator,
229
+ AnnotationService, Utils, ScreenshotMatcher, Drivers, ImageCompare, Difference}`;
230
+ `Capybara::Screenshot::Diff::Drivers::BaseDriver`; `CapybaraScreenshotDiff::{RED_RGBA,
231
+ ORANGE_RGBA, SnapManager, Snap, ScreenshotNamer, AttemptsReporter, BacktraceFilter,
232
+ ErrorWithFilteredBacktrace, ScreenshotAssertion, AssertionRegistry}`;
233
+ `CapybaraScreenshotDiff::Reporters::HTML`.
234
+
235
+ `CapybaraScreenshotDiff::DSL` and `::Minitest::Assertions` are shimmed this way **only under a
236
+ canonical `snap_diff*` require**. Under the v1 entry points — what an unmigrated app actually
237
+ uses — they are eagerly defined and silent, like everything in the next section.
238
+
239
+ #### 3. Removal warnings — the driver half, removed in 2.1
240
+
241
+ The warnings above are about *names*. These are about *features*: 2.1 makes **libvips the only
242
+ image backend** and deletes the rest of the driver machinery. 2.0 still supports all of it and
243
+ warns once per process per subject, through the same channel and the same silencing switches.
244
+
245
+ | You will see it when you… | Removed in 2.1 | Do this instead |
246
+ |---|---|---|
247
+ | select the ChunkyPNG driver — `driver: :chunky_png`, `SnapDiff.config.driver = :chunky_png`, or the legacy `Capybara::Screenshot::Diff.driver =` | the `:chunky_png` driver | add `gem "ruby-vips"` (plus the libvips system package) and drop the option |
248
+ | run on `driver: :auto` **without `ruby-vips` installed** | the `:auto` fallback to ChunkyPNG | same — install libvips + `ruby-vips`. This is the case worth reading twice: nothing in your setup says `chunky_png`, so the warning is the only sign that 2.1 will break this process |
249
+ | set `shift_distance_limit` — globally or per screenshot | `shift_distance_limit` (ChunkyPNG-only) | `median_filter_window_size`, `tolerance`, or `color_distance_limit` — see [Configuration](configuration.md#allowed-shift-distance) |
250
+ | read `SnapDiff::Drivers.loaded` (the custom-driver registry) | the registry | nothing — custom drivers are removed, see below |
251
+ | read `SnapDiff::Drivers.available` | driver detection | require `ruby-vips` instead of branching on a detected list |
252
+ | `include SnapDiff::Driver` in your own driver class | the driver mixin | nothing — see below |
253
+ | set a driver **at all** — `SnapDiff.config.driver =`, the legacy `Capybara::Screenshot::Diff.driver =`, or `screenshot "index", driver: …` | the `driver` setting and the `driver:` option | delete the line. With libvips the only backend there is nothing to select |
254
+
255
+ > **`driver: :vips` warns too, and that is deliberate.** The warning is not about the *value*
256
+ > you picked — it is about the setting existing. `SnapDiff.config.driver = :vips` raises
257
+ > `NoMethodError: undefined method 'driver='` in 2.1, at config time before any test runs, and
258
+ > `screenshot "index", driver: :vips` becomes an `ArgumentError` for an unknown option. Both
259
+ > are lines to delete, not lines to change. `driver: :auto` on a machine that *has* libvips is
260
+ > the one place the option still says something — and the `:auto` row above covers the case
261
+ > that matters, where `:auto` silently lands on ChunkyPNG.
262
+
263
+ ```
264
+ [snap_diff deprecation] `driver: :auto` selected chunky_png because libvips is not available in this process. The chunky_png driver is REMOVED in 2.1, when libvips (the `ruby-vips` gem) becomes required -- install it now, or this setup stops comparing on 2.1. See docs/drivers.md. Silence with `SnapDiff.silence_deprecations = true` or SNAP_DIFF_SILENCE_DEPRECATIONS=1. (shown once per process) (called from /app/test/test_helper.rb:12)
265
+ ```
266
+
267
+ **Custom drivers have no migration path.** The whole abstraction goes: the `SnapDiff::Driver`
268
+ mixin, the `SnapDiff::Drivers.loaded` registry, `SnapDiff::Drivers.available` /
269
+ `SnapDiff::Utils.detect_available_drivers`, and selecting a driver by name. Nothing replaces
270
+ them, and this guide is not going to pretend otherwise — if you maintain a third-party driver,
271
+ say so on [the issue tracker](https://github.com/snap-diff/snap_diff-capybara/issues) before 2.1 ships.
272
+
273
+ Three spots on the same chopping block stay silent: the legacy
274
+ `Capybara::Screenshot::Diff::LOADED_DRIVERS` / `::AVAILABLE_DRIVERS` aliases are plain constants
275
+ with nothing to hook (use `SnapDiff::Drivers.loaded` / `.available` to hear the warning);
276
+ `SnapDiff::Drivers.for` is not warned on at all — the gem itself calls it for every comparison,
277
+ so warning there would fire on setups that are not affected by anything on this list; and
278
+ detection (`SnapDiff::Drivers.detect_available` / `SnapDiff::Utils.detect_available_drivers`)
279
+ runs at load, before any user code.
280
+
281
+ #### 4. Unknown screenshot options — warned in 2.0, raised in 2.1
282
+
283
+ Per-screenshot options used to be a free-form hash: anything the gem did not read was frozen,
284
+ carried around, and ignored. A misspelt `tolerence:` bought you a green suite that compared
285
+ nothing and never said so. 2.0 warns once per unknown key; 2.1 raises `ArgumentError`.
286
+
287
+ ```
288
+ [snap_diff deprecation] `:tolerence` is not a recognised screenshot option, so it does nothing. 2.1 raises ArgumentError for it. Check the spelling against the option list in docs/configuration.md. Silence with `SnapDiff.silence_deprecations = true` or SNAP_DIFF_SILENCE_DEPRECATIONS=1. (shown once per process) (called from /app/test/features/home_test.rb:14)
289
+ ```
290
+
291
+ It applies to every route into a comparison — `screenshot`, `assert_matches_screenshot`,
292
+ `capture_screenshot` and `SnapDiff.compare`. Recognised keys are `area_size_limit`,
293
+ `capybara_screenshot_options`, `color_distance_limit`, `crop`, `delayed`, `driver`,
294
+ `median_filter_window_size`, `perceptual_threshold`, `screenshot_format`,
295
+ `shift_distance_limit`, `skip_area`, `stability_time_limit`, `tolerance` and `wait`.
296
+
297
+ #### 5. The new-screenshot booleans — superseded by `record`, removed in 2.1
298
+
299
+ `fail_if_new`, `pending_if_new` and `fail_on_difference` each answered part of "what happens when
300
+ there is no baseline, or when there is a difference" — and none of them named the action people
301
+ actually want, which is *accept this change*. 2.0 adds the verb:
302
+
303
+ ```ruby
304
+ SnapDiff.config.record = :once # default. Record a screenshot that has no baseline.
305
+ SnapDiff.config.record = :none # strict. A missing baseline always fails.
306
+ SnapDiff.config.record = :all # re-record everything. THE BULK-ACCEPT MODE.
307
+ ```
308
+
309
+ | Old | New | Note |
310
+ |-----|-----|------|
311
+ | `fail_if_new = true` | `record = :none` | the mode means the same thing on CI and off it |
312
+ | `fail_if_new = false` | `record = :once` | |
313
+ | `pending_if_new = true` | `record = :none`, or `:once` | `:none` fails with the `git add` command attached; `:once` records and lists it in the end-of-run summary |
314
+ | `fail_on_difference = false` | `record = :all` | to *accept* the new rendering rather than ignore the difference |
169
315
 
170
- **Warnings do NOT appear for:**
171
- - Requiring the gem: `require "capybara_screenshot_diff/minitest"` etc. is not deprecated
172
- - The DSL: `screenshot`, `assert_matches_screenshot`, `capture_screenshot` are never deprecated
173
- - Settings access: `Capybara::Screenshot.blur_active_element`, `Capybara::Screenshot::Diff.tolerance=`, and the `Diff.configure` block stay silent — they remain the canonical storage that `SnapDiff.config` forwards to
174
- - A few advertised entry-point constants that stay eagerly defined by design: `Capybara::Screenshot::Os`, `CapybaraScreenshotDiff::DSL`, `Capybara::Screenshot::Diff::VERSION`, and the driver leaf classes (`Drivers::VipsDriver`, `Drivers::ChunkyPNGDriver`)
316
+ All three keep working for the whole 2.x line (2.0 deletes nothing) and each warns once per
317
+ process, from the point you set it:
318
+
319
+ ```
320
+ [snap_diff deprecation] `fail_if_new` is REMOVED in 2.1: the record modes replace it. `SnapDiff.config.record = :none` is `fail_if_new = true`, `= :once` is `fail_if_new = false` -- and unlike the boolean, a mode means the same thing on CI and off it. See docs/configuration.md. Silence with `SnapDiff.silence_deprecations = true` or SNAP_DIFF_SILENCE_DEPRECATIONS=1. (shown once per process) (called from /app/test/test_helper.rb:9)
321
+ ```
322
+
323
+ **Nothing changes if you set none of them.** With no `record` line, `record` reads back as `:none`
324
+ under CI and `:once` off it — exactly what `fail_if_new` already did, sniff and all. The
325
+ missing-baseline default is deliberately unchanged; `:none` is how you opt into strictness
326
+ explicitly. **Precedence:** an explicitly set mode outranks `fail_if_new`, and `fail_if_new`
327
+ decides only when no mode was set — the same rule `fail_if_new` itself has over the `CI` sniff.
328
+
329
+ `record = :all` **refuses to run under CI**: it accepts every rendering by design, so left in a
330
+ committed config file it would be a build that compares nothing and passes forever. See
331
+ [Record modes](configuration.md#record-modes--accepting-changes).
332
+
333
+ #### Silent by design
334
+
335
+ Some legacy names never warn individually, and that is deliberate — the migration notice above is
336
+ the signal for all of them:
337
+
338
+ - **The DSL.** `screenshot`, `assert_matches_screenshot`, `capture_screenshot` are never deprecated.
339
+ - **Settings access.** `Capybara::Screenshot.blur_active_element`, `Capybara::Screenshot::Diff.tolerance=`
340
+ and the `Diff.configure` block are plain delegators onto `SnapDiff.config`. There is no
341
+ `const_missing` to hook, so they cannot warn per call without adding one on every read.
342
+ - **Eagerly defined constants.** `Capybara::Screenshot::Os`, `Capybara::Screenshot::Diff::VERSION`,
343
+ `::Comparison`, `::LOADED_DRIVERS`, `::AVAILABLE_DRIVERS`, `::Reporters::Default`, the top-level
344
+ `Region`, the `CapybaraScreenshotDiff` error classes, and — under the v1 entry points —
345
+ `CapybaraScreenshotDiff::DSL` / `::Minitest::Assertions`. `const_defined?` never triggers
346
+ `const_missing`, so these have to be real constants for adopter feature detection and `rescue`
347
+ clauses to keep working — which means nothing is left to hook.
348
+ - **The driver leaf classes.** `Drivers::VipsDriver` / `Drivers::ChunkyPNGDriver` are autoloaded on
349
+ `SnapDiff::Drivers`, so the leaf name itself never warns. Reaching them through the old path still
350
+ warns once for `Capybara::Screenshot::Diff::Drivers` — that part is a `const_missing` shim.
351
+ Each leaf is only declared when its gem is actually installed, so
352
+ `defined?(...Drivers::VipsDriver)` stays `nil` without `ruby-vips`, exactly as in v1.
353
+
354
+ Every one of those names resolves under a canonical `snap_diff*` require too, so migrating your
355
+ `require` line first (as this guide recommends) never breaks a constant you have not renamed yet.
175
356
 
176
357
  Warnings go through `Kernel#warn`, so test suites that hook `Warning.warn` (e.g. raise-on-warning setups) see them like any other Ruby warning.
177
358
 
@@ -191,14 +372,28 @@ export SNAP_DIFF_SILENCE_DEPRECATIONS=1
191
372
 
192
373
  ---
193
374
 
194
- ### Known Alpha Caveats
375
+ ### Known Caveats
195
376
 
196
- Two deliberate consequences of the lazy shim design both flagged for feedback on [#166](https://github.com/snap-diff/snap_diff-capybara/issues/166):
377
+ Two deliberate consequences of the lazy shim design. Both go away at 2.1, when the shimmed
378
+ names go away with them:
197
379
 
198
- 1. **`defined?` / `const_defined?` on lazily-shimmed legacy names returns `false`/`nil`.** The shims resolve via `const_missing`, which those checks never trigger. Feature detection like `defined?(Capybara::Screenshot::Diff::ImageCompare)` must move to the `SnapDiff::` name. Rescuing legacy *error classes* is unaffected — they remain eagerly defined.
380
+ 1. **`defined?` / `const_defined?` on lazily-shimmed legacy names returns `false`/`nil`.** The shims resolve via `const_missing`, which those checks never trigger. Feature detection like `defined?(Capybara::Screenshot::Diff::ImageCompare)` must move to the `SnapDiff::` name. Everything in [Silent by design](#silent-by-design) is unaffected — those names are real constants, so `defined?`, `const_defined?` and `rescue` all behave as they always did.
199
381
 
200
382
  2. **Reopening `module Capybara::Screenshot::Diff::Drivers` shadows the shim.** The historical custom-driver monkey-patch pattern defines a fresh, empty `Drivers` module instead of reaching the real one. Define custom drivers under `SnapDiff::Drivers` instead — and note `BaseDriver` is gone as a superclass: `class MyDriver < BaseDriver` becomes `include SnapDiff::Driver` (it's a mixin now).
201
383
 
384
+ #### Two moves that fail *silently* if you miss them
385
+
386
+ **Stubbing the detected-drivers list.** The value moved to `SnapDiff::Drivers::AVAILABLE_DRIVERS`, and `Capybara::Screenshot::Diff::AVAILABLE_DRIVERS` is now an eager alias of it. *Reading* either is identical, but **stubbing the legacy name only rebinds the alias** — the gem keeps reading the canonical constant, so a test that stubs it to `[]` no longer exercises the no-drivers path and just passes for the wrong reason:
387
+
388
+ ```ruby
389
+ # before
390
+ Capybara::Screenshot::Diff.stub_const(:AVAILABLE_DRIVERS, []) { ... }
391
+ # now
392
+ SnapDiff::Drivers.stub_const(:AVAILABLE_DRIVERS, []) { ... }
393
+ ```
394
+
395
+ **`SnapDiff::Config::MAPPING` is gone.** It split in two: `SnapDiff::Config::SETTINGS` (the setting names, no legacy knowledge) and `SnapDiff::LegacyShims::CONFIG_MAPPING` (which legacy holder each name hangs off). If you referenced `MAPPING` — iterating settings in a test helper, say — use `SETTINGS`; `CONFIG_MAPPING` is `@api private` and disappears in 2.1 with the rest of the v1 surface.
396
+
202
397
  ---
203
398
 
204
399
  ### FAQ
@@ -233,12 +428,15 @@ All settings and baselines are compatible with v1.x. Simply pin your Gemfile bac
233
428
 
234
429
  ### Summary Checklist
235
430
 
236
- - [ ] Pin `gem "capybara-screenshot-diff", "2.0.0.beta2"` (or the latest 2.0.0 prerelease) in your Gemfile
431
+ - [ ] Pin `gem "capybara-screenshot-diff", "2.0.0.beta4"` in your Gemfile (`"~> 2.0"` once 2.0.0 ships)
237
432
  - [ ] Run `bundle install`
238
- - [ ] Run your test suite to verify no regressions
239
- - [ ] (Optional) Migrate config to the `SnapDiff` namespace
433
+ - [ ] Run your system tests (`bin/rails test:system`, not `rake test`) to verify no regressions
434
+ - [ ] Read the warnings it prints each one names something 2.1 removes
435
+ - [ ] Add `gem "ruby-vips"` if you are not already on it (2.1 makes libvips the only backend)
436
+ - [ ] Drop `driver:` from your config and your `screenshot` calls — 2.0 warns about it, 2.1 removes it
437
+ - [ ] (Optional, but do it before 2.1) Migrate config and constants to the `SnapDiff` namespace
240
438
  - [ ] (Optional) Silence deprecation warnings if not ready to migrate
241
- - [ ] Report anything surprising on [#166](https://github.com/snap-diff/snap_diff-capybara/issues/166)
439
+ - [ ] Report anything surprising on [the issue tracker](https://github.com/snap-diff/snap_diff-capybara/issues)
242
440
 
243
441
  ---
244
442
 
@@ -261,7 +459,7 @@ gem 'capybara-screenshot-diff', '~> 1.13.0'
261
459
 
262
460
  ```bash
263
461
  bundle update capybara-screenshot-diff
264
- bundle exec rake test
462
+ bin/rails test:system # `rake test` skips test/system/ — 0 runs, no comparison
265
463
  ```
266
464
 
267
465
  **That's it!** Existing `screenshot` calls work unchanged. New methods available if needed.
@@ -346,7 +544,7 @@ gem 'capybara-screenshot-diff', '~> 1.12.0'
346
544
 
347
545
  ```bash
348
546
  bundle update capybara-screenshot-diff
349
- bundle exec rake test # Verify tests still pass
547
+ bin/rails test:system # Verify tests still pass (`rake test` skips test/system/)
350
548
  ```
351
549
 
352
550
  **That's it!** The zero-config setup still works out of the box. Your existing screenshot comparisons will continue to work with v1.12.0.
@@ -551,7 +749,6 @@ Use the new `Diff.configure` block:
551
749
  Capybara::Screenshot::Diff.configure do |screenshot, diff|
552
750
  screenshot.window_size = [1280, 1024]
553
751
  screenshot.stability_time_limit = 1
554
- diff.driver = :vips
555
752
  diff.tolerance = 0.0005
556
753
  end
557
754
  ```
@@ -602,7 +799,7 @@ bundle update capybara-screenshot-diff
602
799
  ### Step 3: Run Tests
603
800
 
604
801
  ```bash
605
- bundle exec rake test
802
+ bin/rails test:system # `rake test` skips test/system/ — 0 runs, no comparison
606
803
  ```
607
804
 
608
805
  ### Step 4: Check for New Screenshot Failures
@@ -656,13 +853,14 @@ Capybara::Screenshot.hide_caret = false
656
853
  Then re-record baselines with the new defaults (recommended):
657
854
 
658
855
  ```bash
659
- # Delete old baselines
660
- rm doc/screenshots/*.png
856
+ # Do NOT delete the baselines — they are read from git (`git show HEAD:<path>`), so
857
+ # removing the files changes nothing. Re-recording is a commit.
661
858
 
662
- # Run tests to generate new baselines
663
- bundle exec rake test
859
+ # The run fails and rewrites every changed baseline in place
860
+ bin/rails test:system
664
861
 
665
- # Commit new baselines
862
+ # Review the diffs, then commit the new baselines
863
+ git status
666
864
  git add doc/screenshots/
667
865
  git commit -m "Re-record baselines with v1.12.0 defaults"
668
866
  ```
data/docs/architecture.md CHANGED
@@ -125,9 +125,9 @@ Drivers abstract image processing operations. Shared default behavior lives in t
125
125
  | `merge` | Composite images | Not applicable |
126
126
  | `highlight_mask` | Conditional color overlay | Not applicable |
127
127
 
128
- **Auto-detection:** `Utils.detect_available_drivers` tries to load `:vips` first (via `ruby-vips` gem), then `:chunky_png`. The `:auto` driver mode picks the first available.
128
+ **Auto-detection:** `SnapDiff::Drivers.detect_available` tries to load `:vips` first (via `ruby-vips` gem), then `:chunky_png`. The `:auto` driver mode picks the first available. `Utils.detect_available_drivers` is the older name and one-lines into it.
129
129
 
130
- **Registry (ADR-008 step 5b):** `SnapDiff::Drivers.loaded` is the canonical driver-class cache — a `name => class` hash filled lazily by `Utils.find_driver_class_for`, and the registration point for custom drivers (the legacy `Capybara::Screenshot::Diff::LOADED_DRIVERS` is an eager same-object alias, so registrations through either land in the same hash). `SnapDiff::Drivers.available` is the canonical read API for the detected list; the value itself still lives on `Capybara::Screenshot::Diff::AVAILABLE_DRIVERS`, which stays the published stubbing point. `SnapDiff::Drivers.for` resolves an options hash to a driver instance. See [Custom drivers](snapdiff.md#custom-drivers).
130
+ **Registry (ADR-008 step 5b):** `SnapDiff::Drivers.loaded` is the canonical driver-class cache — a `name => class` hash filled lazily by `Utils.find_driver_class_for`, and the registration point for custom drivers (the legacy `Capybara::Screenshot::Diff::LOADED_DRIVERS` is an eager same-object alias, so registrations through either land in the same hash). `SnapDiff::Drivers.available` is the canonical read API for the detected list, and since the 2.1-readiness pass the value lives with it, as `SnapDiff::Drivers::AVAILABLE_DRIVERS` — that constant is now the published stubbing point, and the legacy `Capybara::Screenshot::Diff::AVAILABLE_DRIVERS` is an eager same-object alias of it. `SnapDiff::Drivers.for` resolves an options hash to a driver instance. See [Custom drivers](snapdiff.md#custom-drivers).
131
131
 
132
132
  ### 6. Difference Region Detection
133
133
 
@@ -221,7 +221,9 @@ Test begins
221
221
 
222
222
  Since ADR-008 step 1 the storage ownership is inverted from the original v2 consolidation: **`SnapDiff::Config` (`lib/snap_diff/config.rb`) IS the storage** — one eagerly-created instance, reachable as `SnapDiff.config`, holding every setting as a plain `attr_accessor`. It is the leaf of the config require graph and requires nothing that leads back to either entry point.
223
223
 
224
- The legacy `Capybara::Screenshot.*` / `Capybara::Screenshot::Diff.*` accessors are thin delegators generated from `Config::MAPPING` (both singleton and instance methods, matching what `mattr_accessor` used to define) that forward to that one object. One storage, two views — a write through either surface is visible through the other structurally, not by synchronization. `lib/capybara/screenshot/diff/config_legacy.rb` remains at the old path, but it now installs the delegating surface rather than owning the state.
224
+ The legacy `Capybara::Screenshot.*` / `Capybara::Screenshot::Diff.*` accessors are thin delegators generated from `SnapDiff::LegacyShims::CONFIG_MAPPING` (both singleton and instance methods, matching what `mattr_accessor` used to define) that forward to that one object. One storage, two views — a write through either surface is visible through the other structurally, not by synchronization.
225
+
226
+ Since the 2.1-readiness pass, `lib/snap_diff/legacy_shims.rb` is the single file that holds the v1 surface as code: the `const_missing` forwarders, `CONFIG_MAPPING` and its generator, the derived forwarders (`Screenshot.active?`, `Diff.configure`, `Diff.default_options`, …) and `SnapDiff.start`. `Config` itself names nothing from the v1 namespaces — it declares its settings in `Config::SETTINGS`, and `LegacyShims::CONFIG_MAPPING` says which legacy holder each one is exposed on (an invariant pinned by `snap_diff_config_test.rb`). `lib/capybara/screenshot/diff/config_legacy.rb` remains at the old path as a pair of requires.
225
227
 
226
228
  The two legacy views are organized into two namespaces:
227
229
 
@@ -232,13 +234,13 @@ The two legacy views are organized into two namespaces:
232
234
 
233
235
  **`Capybara::Screenshot::Diff`** — comparison settings:
234
236
  - `driver`, `tolerance`, `color_distance_limit`, `perceptual_threshold`, `shift_distance_limit`
235
- - `area_size_limit`, `skip_area`, `fail_if_new`, `fail_on_difference`, `delayed`
237
+ - `area_size_limit`, `skip_area`, `record`, `fail_if_new`, `fail_on_difference`, `delayed`
236
238
 
237
- The canonical way in is `SnapDiff.configure { |config| ... }` (all 27 settings flat on one object). `SnapDiff.start` and `Capybara::Screenshot::Diff.configure` are the two-holder block shape over the same storage — since ADR-008 step 7b, `Diff.configure` forwards to `SnapDiff.start` rather than the other way round.
239
+ The canonical way in is `SnapDiff.configure { |config| ... }` (all 28 settings flat on one object). `SnapDiff.start` and `Capybara::Screenshot::Diff.configure` are the two-holder block shape over the same storage — since ADR-008 step 7b, `Diff.configure` forwards to `SnapDiff.start` rather than the other way round.
238
240
 
239
241
  `Config` also owns the derived values that used to live on the legacy modules: `active?` (ex `Capybara::Screenshot.active?`), `screenshot_area` / `screenshot_area_abs`, and `default_options` (ex `Capybara::Screenshot::Diff.default_options`, the option hash handed to `SnapDiff::Comparison`). The legacy module methods one-line forward here.
240
242
 
241
- **Default timing contract:** every default is evaluated once, in `Config#initialize`, which runs at require time of `config.rb` — the same load moment the old `mattr_accessor` default blocks evaluated at. `fail_if_new` (from `ENV["CI"]`) and `root` (from `Rails.root`) must never become lazy read-time defaults. The one deliberately live value is `default_options[:wait]`, a method-body read of `Capybara.default_max_wait_time`.
243
+ **Default timing contract:** every *stored* default is evaluated once, in `Config#initialize`, which runs at require time of `config.rb` — the same load moment the old `mattr_accessor` default blocks evaluated at. `root` (from `Rails.root`) must never become a lazy read-time default. Two values are deliberately live: `default_options[:wait]`, a method-body read of `Capybara.default_max_wait_time`, and `fail_if_new`, whose reader falls back to `ENV["CI"]` whenever nothing explicit was set — an explicit setting outranks the environment, so the sniff cannot be frozen into storage. `record` is the third: its reader falls back to `fail_if_new` (`:none` when it is on, `:once` when it is off), which is what makes the record modes a rename of the existing behaviour rather than a change to it — the matcher branches on the mode alone and a suite with no `record` line takes exactly the branches it always took.
242
244
 
243
245
  ## File Layout
244
246
 
@@ -247,7 +249,7 @@ lib/
247
249
  snap_diff.rb # SnapDiff module: compare/start/configure/config
248
250
  snap_diff/ # Canonical implementation (v2)
249
251
  dsl.rb # screenshot(), screenshot_group(), etc.
250
- config.rb # SnapDiff::Config — THE storage for all 27 settings
252
+ config.rb # SnapDiff::Config — THE storage for all 28 settings
251
253
  errors.rb # Error / ExpectationNotMet / UnstableImage / WindowSizeMismatchError
252
254
  region.rb # SnapDiff::Region — bounding box (+ eager top-level ::Region alias)
253
255
  deprecation.rb # Warn-once-per-constant machinery
@@ -58,7 +58,7 @@ jobs:
58
58
  bundler-cache: true
59
59
 
60
60
  - name: Run tests
61
- run: bundle exec rake test
61
+ run: bin/rails test:system # not `rake test` — it skips test/system/
62
62
 
63
63
  - name: Upload screenshot reports
64
64
  if: failure()
@@ -68,6 +68,10 @@ jobs:
68
68
  pr-comment: 'true'
69
69
  ```
70
70
 
71
+ The three workflows on this page all run `bin/rails test:system`, because `rake test` and
72
+ `rails test` skip `test/system/` and report `0 runs` — a green CI job that compared nothing.
73
+ Not on Rails? Substitute whatever task loads your Capybara tests.
74
+
71
75
  That's it. On failure, this will:
72
76
  - Upload diff images + HTML report as artifacts
73
77
  - Post a PR comment with links to the inline report and full artifact download
@@ -129,7 +133,7 @@ jobs:
129
133
  ruby-version: '4.0'
130
134
  cache-apt-packages: true
131
135
 
132
- - run: bundle exec rake test
136
+ - run: bin/rails test:system # not `rake test` — it skips test/system/
133
137
 
134
138
  - uses: snap-diff/snap_diff-capybara/.github/actions/upload-screenshots@master
135
139
  if: failure()
@@ -160,7 +164,7 @@ jobs:
160
164
  run: sudo apt-get install -y libvips-dev
161
165
 
162
166
  - name: Run tests
163
- run: bundle exec rake test
167
+ run: bin/rails test:system # not `rake test` — it skips test/system/
164
168
 
165
169
  - name: Upload screenshot report
166
170
  if: failure()
@@ -184,14 +188,21 @@ jobs:
184
188
 
185
189
  ## Update Baselines in CI
186
190
 
187
- When intentional UI changes are made, baselines need to be re-recorded. You can do this locally:
191
+ When intentional UI changes are made, baselines need to be re-recorded. Baselines are
192
+ read from git, so accepting a change is a commit — the failing run has already written
193
+ the new capture to the baseline path:
188
194
 
189
195
  ```bash
190
- RECORD_SCREENSHOTS=1 bundle exec rake test
191
- git add test/fixtures/screenshots/
196
+ bin/rails test:system # fails, and rewrites the changed baselines
197
+ git status # review what moved
198
+ git add doc/screenshots/ # the default save_path; adjust if you changed it
192
199
  git commit -m "chore: update screenshot baselines"
193
200
  ```
194
201
 
202
+ With the [recommended `.gitignore`](../README.md#quick-start-5-minutes) in place, `git add
203
+ doc/screenshots/` stages only the baselines — the `.diff.png` / `.base.png` artifacts are
204
+ ignored.
205
+
195
206
  Or add a workflow that maintainers can trigger manually:
196
207
 
197
208
  <details>
@@ -225,24 +236,44 @@ jobs:
225
236
  ruby-version: '4.0'
226
237
  cache-apt-packages: true
227
238
 
228
- - name: Record new baselines
229
- run: RECORD_SCREENSHOTS=1 bundle exec rake test
230
- continue-on-error: true
239
+ - name: Record baselines
240
+ run: bin/rails test:system
241
+ continue-on-error: true # the run fails by design; it rewrites the baselines
242
+ env:
243
+ CI: "" # see below — without this, a NEW screenshot is never written
231
244
 
232
245
  - name: Commit updated baselines
233
246
  run: |
234
247
  git config user.name "github-actions[bot]"
235
248
  git config user.email "github-actions[bot]@users.noreply.github.com"
236
- git add test/fixtures/ doc/screenshots/
249
+ git add doc/screenshots/
237
250
  git diff --staged --quiet || git commit -m "chore: update screenshot baselines"
238
251
  git push
239
252
  ```
240
253
 
241
254
  </details>
242
255
 
256
+ > **Set the record mode, or clear `CI`, to let this job record a *new* baseline.** With
257
+ > nothing set, a missing baseline fails whenever `ENV["CI"]` is set and non-empty — so on a
258
+ > stock GitHub Actions runner a screenshot with no committed baseline raises `No existing
259
+ > screenshot found for …`. `SnapDiff.config.record = :once`
260
+ > (or clearing `CI` for this one step, or the older `SnapDiff.config.fail_if_new = false`)
261
+ > records new baselines instead. *Changed* baselines are rewritten either way; only new ones
262
+ > need this. See [Record modes](configuration.md#record-modes--accepting-changes).
263
+ >
264
+ > The screenshot itself **is** written before the raise ([`screenshot_matcher.rb`](https://github.com/snap-diff/snap_diff-capybara/blob/master/lib/snap_diff/screenshot_matcher.rb)
265
+ > — `capture_screenshot` precedes `fail_if_new_screenshot`), so the `git add` the message names
266
+ > is a command you can actually run. But the raise still fails the test it happened in, and a red
267
+ > job usually never reaches the commit step — which is why a recording job sets the mode rather
268
+ > than relying on the files being there.
269
+ >
270
+ > **`record = :all` is not the mode for this job** — it refuses to run under CI, because it
271
+ > would accept every *changed* rendering unreviewed as well. `:once` records what is new and
272
+ > keeps comparing everything that already has a baseline.
273
+
243
274
  **How it works:**
244
275
  1. Go to Actions → "Update Screenshot Baselines" → "Run workflow"
245
276
  2. Enter the branch name (e.g. your PR branch)
246
- 3. The workflow records new baselines, commits, and pushes
277
+ 3. The workflow records new and changed baselines, commits, and pushes
247
278
 
248
279
  [← Back to README](../README.md)