capybara-screenshot-diff 1.10.3 → 1.12.0

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +64 -0
  3. data/Rakefile +29 -1
  4. data/capybara-screenshot-diff.gemspec +4 -3
  5. data/docs/RELEASE_PREP.md +58 -0
  6. data/docs/UPGRADING.md +390 -0
  7. data/docs/ci-integration.md +208 -0
  8. data/docs/configuration.md +379 -0
  9. data/docs/docker-testing.md +24 -0
  10. data/docs/drivers.md +102 -0
  11. data/docs/framework-setup.md +87 -0
  12. data/docs/images/snap_diff_web_ui.png +0 -0
  13. data/docs/organization.md +226 -0
  14. data/docs/reporters.md +46 -0
  15. data/docs/thread_safety.md +97 -0
  16. data/gems.rb +2 -1
  17. data/lib/capybara/screenshot/diff/area_calculator.rb +1 -1
  18. data/lib/capybara/screenshot/diff/browser_helpers.rb +14 -1
  19. data/lib/capybara/screenshot/diff/comparison.rb +3 -0
  20. data/lib/capybara/screenshot/diff/difference.rb +40 -3
  21. data/lib/capybara/screenshot/diff/difference_finder.rb +97 -0
  22. data/lib/capybara/screenshot/diff/drivers/base_driver.rb +4 -0
  23. data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +22 -24
  24. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +40 -27
  25. data/lib/capybara/screenshot/diff/image_compare.rb +112 -123
  26. data/lib/capybara/screenshot/diff/image_preprocessor.rb +72 -0
  27. data/lib/capybara/screenshot/diff/reporters/default.rb +10 -11
  28. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +63 -36
  29. data/lib/capybara/screenshot/diff/screenshoter.rb +9 -8
  30. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +7 -9
  31. data/lib/capybara/screenshot/diff/vcs.rb +19 -52
  32. data/lib/capybara/screenshot/diff/version.rb +1 -1
  33. data/lib/capybara_screenshot_diff/backtrace_filter.rb +20 -0
  34. data/lib/capybara_screenshot_diff/cucumber.rb +2 -0
  35. data/lib/capybara_screenshot_diff/dsl.rb +102 -7
  36. data/lib/capybara_screenshot_diff/error_with_filtered_backtrace.rb +15 -0
  37. data/lib/capybara_screenshot_diff/minitest.rb +4 -2
  38. data/lib/capybara_screenshot_diff/reporters/html.rb +137 -0
  39. data/lib/capybara_screenshot_diff/reporters/templates/report.html.erb +463 -0
  40. data/lib/capybara_screenshot_diff/rspec.rb +12 -2
  41. data/lib/capybara_screenshot_diff/screenshot_assertion.rb +61 -23
  42. data/lib/capybara_screenshot_diff/screenshot_namer.rb +81 -0
  43. data/lib/capybara_screenshot_diff/snap.rb +14 -3
  44. data/lib/capybara_screenshot_diff/snap_manager.rb +10 -2
  45. data/lib/capybara_screenshot_diff/static.rb +11 -0
  46. data/lib/capybara_screenshot_diff.rb +30 -5
  47. metadata +47 -8
  48. data/lib/capybara/screenshot/diff/test_methods.rb +0 -157
@@ -0,0 +1,208 @@
1
+ # CI & Non-Rails Integration
2
+
3
+ ## Non-Rails Projects (Hugo, Jekyll, Static Sites)
4
+
5
+ ```ruby
6
+ # test/test_helper.rb
7
+ require 'capybara_screenshot_diff/static'
8
+
9
+ CapybaraScreenshotDiff.serve("_site") # or "public", "build", "dist"
10
+ ```
11
+
12
+ This sets up Capybara to serve static files and configures screenshot paths automatically.
13
+
14
+ ## .gitignore Setup
15
+
16
+ Add these patterns to your `.gitignore` — diff artifacts are generated at runtime and should not be committed:
17
+
18
+ ```gitignore
19
+ # Screenshot diff artifacts (generated, not committed)
20
+ *.diff.png
21
+ *.base.png
22
+ *.diff.webp
23
+ *.base.webp
24
+ snap_diff_report.html
25
+ ```
26
+
27
+ Only commit the baseline screenshots (e.g., `homepage.png`). The `.base.png`, `.diff.png`, `.heatmap.diff.png`, and report files are regenerated on every test run.
28
+
29
+ ## GitHub Actions Integration
30
+
31
+ ### 1. Enable the HTML report
32
+
33
+ Add to your test helper:
34
+
35
+ ```ruby
36
+ require 'capybara_screenshot_diff/reporters/html'
37
+ ```
38
+
39
+ ### 2. Upload artifacts (manual setup)
40
+
41
+ This is the full YAML so you understand what each step does:
42
+
43
+ ```yaml
44
+ # .github/workflows/test.yml
45
+ jobs:
46
+ test:
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ - uses: actions/checkout@v6
50
+
51
+ - uses: ruby/setup-ruby@v1
52
+ with:
53
+ bundler-cache: true
54
+
55
+ # Install libvips for the :vips driver (optional — skip if using :chunky_png)
56
+ - name: Install libvips
57
+ run: sudo apt-get install -y libvips-dev
58
+
59
+ - name: Run tests
60
+ run: bundle exec rake test
61
+
62
+ # Upload HTML report — renders inline in Actions UI (no download needed)
63
+ - name: Upload screenshot report
64
+ if: failure()
65
+ uses: actions/upload-artifact@v7
66
+ with:
67
+ name: screenshot-report
68
+ path: doc/screenshots/snap_diff_report.html
69
+ archive: false
70
+ retention-days: 2
71
+
72
+ # Upload full report with images (for offline review)
73
+ - name: Upload full screenshot report
74
+ if: failure()
75
+ uses: actions/upload-artifact@v7
76
+ with:
77
+ name: screenshot-report-full
78
+ path: doc/screenshots/
79
+ retention-days: 2
80
+ ```
81
+
82
+ ### 3. Or use the reusable action (one line)
83
+
84
+ Instead of the manual upload steps above, reference our composite action directly:
85
+
86
+ ```yaml
87
+ - name: Run tests
88
+ run: bundle exec rake test
89
+
90
+ - name: Upload screenshot reports
91
+ if: failure()
92
+ uses: snap-diff/snap_diff-capybara/.github/actions/upload-screenshots@master
93
+ with:
94
+ name: screenshots
95
+ ```
96
+
97
+ This uploads diffs, Capybara failure screenshots, and the HTML report (inline + full) in one step.
98
+
99
+ **Inputs:**
100
+
101
+ | Input | Default | Description |
102
+ |-------|---------|-------------|
103
+ | `name` | (required) | Artifact name prefix |
104
+ | `report-path` | `doc/screenshots` | Path to HTML report directory |
105
+ | `retention-days` | `2` | Days to retain artifacts |
106
+
107
+ ### 4. PR comment with link to report (optional)
108
+
109
+ Automatically comment on the PR pointing to the artifact. Uses `find-comment` to update existing comments instead of creating duplicates:
110
+
111
+ ```yaml
112
+ - name: Find existing comment
113
+ if: failure() && github.event_name == 'pull_request'
114
+ uses: peter-evans/find-comment@v3
115
+ id: find-comment
116
+ with:
117
+ issue-number: ${{ github.event.pull_request.number }}
118
+ comment-author: 'github-actions[bot]'
119
+ body-includes: 'Screenshot diffs detected'
120
+
121
+ - name: Comment PR with report link
122
+ if: failure() && github.event_name == 'pull_request'
123
+ uses: peter-evans/create-or-update-comment@v5
124
+ with:
125
+ comment-id: ${{ steps.find-comment.outputs.comment-id }}
126
+ issue-number: ${{ github.event.pull_request.number }}
127
+ edit-mode: replace
128
+ body: |
129
+ ### Screenshot diffs detected
130
+ [View report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts)
131
+ ```
132
+
133
+ **Required:** Add permissions to the job for PR commenting to work:
134
+
135
+ ```yaml
136
+ jobs:
137
+ test:
138
+ permissions:
139
+ contents: read
140
+ pull-requests: write
141
+ ```
142
+
143
+ ## Update Baselines in CI
144
+
145
+ When intentional UI changes are made, baselines need to be re-recorded. You can do this locally:
146
+
147
+ ```bash
148
+ RECORD_SCREENSHOTS=1 bundle exec rake test
149
+ git add test/fixtures/screenshots/
150
+ git commit -m "chore: update screenshot baselines"
151
+ ```
152
+
153
+ Or add a workflow that maintainers can trigger manually:
154
+
155
+ ```yaml
156
+ # .github/workflows/update-baselines.yml
157
+ name: Update Screenshot Baselines
158
+
159
+ on:
160
+ workflow_dispatch:
161
+ inputs:
162
+ branch:
163
+ description: 'Branch to update baselines on'
164
+ required: true
165
+ default: 'main'
166
+
167
+ permissions:
168
+ contents: write
169
+
170
+ jobs:
171
+ update:
172
+ runs-on: ubuntu-latest
173
+ steps:
174
+ - uses: actions/checkout@v6
175
+ with:
176
+ ref: ${{ inputs.branch }}
177
+
178
+ - uses: ruby/setup-ruby@v1
179
+ with:
180
+ bundler-cache: true
181
+
182
+ - name: Install libvips
183
+ run: sudo apt-get install -y libvips-dev
184
+
185
+ - name: Record new baselines
186
+ run: RECORD_SCREENSHOTS=1 bundle exec rake test
187
+ continue-on-error: true
188
+
189
+ - name: Commit updated baselines
190
+ run: |
191
+ git config user.name "github-actions[bot]"
192
+ git config user.email "github-actions[bot]@users.noreply.github.com"
193
+ git add test/fixtures/ doc/screenshots/
194
+ git diff --staged --quiet || git commit -m "chore: update screenshot baselines"
195
+ git push
196
+ ```
197
+
198
+ **How it works:**
199
+ 1. Go to Actions → "Update Screenshot Baselines" → "Run workflow"
200
+ 2. Enter the branch name (e.g. your PR branch)
201
+ 3. The workflow records new baselines, commits, and pushes
202
+
203
+ **Safety:**
204
+ - Only maintainers with write access can trigger `workflow_dispatch`
205
+ - The commit uses `git diff --staged --quiet ||` to skip empty commits
206
+ - `GITHUB_TOKEN` pushes don't trigger subsequent CI runs ([by design](https://docs.github.com/actions/using-workflows/events-that-trigger-workflows))
207
+
208
+ [← Back to README](../README.md)
@@ -0,0 +1,379 @@
1
+ # Configuration Reference
2
+
3
+ ## Quick Setup
4
+
5
+ Configure all settings in one place using the `configure` helper:
6
+
7
+ ```ruby
8
+ # In test_helper.rb or rails_helper.rb
9
+ Capybara::Screenshot::Diff.configure do |screenshot, diff|
10
+ screenshot.window_size = [1280, 1024]
11
+ screenshot.stability_time_limit = 1
12
+ screenshot.blur_active_element = true
13
+ screenshot.hide_caret = true
14
+ diff.driver = :vips
15
+ diff.tolerance = 0.0005
16
+ diff.color_distance_limit = 15
17
+ end
18
+ ```
19
+
20
+ **Note:** `fail_if_new` defaults to `true` in CI environments (when `ENV['CI']` is set). New screenshots are allowed locally but rejected in CI — no configuration needed.
21
+
22
+ **Note:** Setting `Capybara::Screenshot.enabled = false` is sufficient to disable all screenshots. There is no need to define no-op modules or monkey-patch the gem.
23
+
24
+ ## Recommended tolerance values
25
+
26
+ | Use Case | VIPS `tolerance` | ChunkyPNG `color_distance_limit` | `stability_time_limit` |
27
+ |----------|-----------------|--------------------------------|----------------------|
28
+ | Animated/complex pages | 0.01 | 30 | 2s |
29
+ | Standard Rails apps | 0.001 (default) | 15 | 1s |
30
+ | Pixel-perfect design tests | 0.0001 | 5 | 1s |
31
+
32
+ **Note:** VIPS defaults to `tolerance: 0.001` (allows 0.1% pixel difference). ChunkyPNG has no default tolerance.
33
+
34
+ ## Choosing the Right Color Comparison Method
35
+
36
+ **Important:** `perceptual_threshold`, `color_distance_limit`, and `tolerance` serve different purposes. Use this decision tree:
37
+
38
+ ### Step 1: Choose color comparison method (pick ONE)
39
+
40
+ | Method | Scale | Driver | Best for |
41
+ |--------|-------|--------|----------|
42
+ | `perceptual_threshold` | 0-100+ (dE00) | VIPS only | Cross-OS/browser font rendering, anti-aliasing |
43
+ | `color_distance_limit` | 0-510 (RGBA Euclidean) | VIPS, ChunkyPNG | Legacy setups, fine-grained RGB control |
44
+
45
+ **Recommendation:** Use `perceptual_threshold: 2.0` for most cases. It matches human perception and needs less tuning.
46
+
47
+ **⚠️ Color comparison methods are exclusive:** `perceptual_threshold` and `color_distance_limit` cannot both be active — if you set both, `perceptual_threshold` wins and `color_distance_limit` is ignored. However, `tolerance` works with **both** methods and is applied by default for VIPS (0.001). This means even with `perceptual_threshold: 2.0`, the `tolerance: 0.001` default still filters results.
48
+
49
+ ### Step 2: Set tolerance (optional, independent)
50
+
51
+ | Setting | What it does | Scale |
52
+ |---------|--------------|-------|
53
+ | `tolerance` | Maximum allowed *ratio* of different pixels (VIPS) or diff bounding box (ChunkyPNG) | 0.0-1.0 |
54
+
55
+ **Example:** `tolerance: 0.001` allows 0.1% of the image to differ (e.g., 125 pixels in a 1280×1024 screenshot).
56
+
57
+ **Key difference:**
58
+ - `perceptual_threshold` / `color_distance_limit` → **"how different can a pixel be?"**
59
+ - `tolerance` → **"how many pixels can differ?"**
60
+
61
+ **⚠️ Driver difference:** VIPS counts actual different pixels. ChunkyPNG counts the bounding box area around differences — a single pixel diff creates a box, and the entire box area counts against tolerance. This makes ChunkyPNG stricter with the same tolerance value.
62
+
63
+ ### Quick start
64
+
65
+ ```ruby
66
+ # Modern approach (recommended)
67
+ screenshot 'dashboard', perceptual_threshold: 2.0
68
+
69
+ # Allow small noise regions
70
+ screenshot 'dashboard', perceptual_threshold: 2.0, tolerance: 0.001
71
+
72
+ # Legacy ChunkyPNG setup
73
+ screenshot 'dashboard', color_distance_limit: 15
74
+ ```
75
+
76
+ ## Configuration Tiers
77
+
78
+ **Tier 1 — Zero config (works immediately):**
79
+ `blur_active_element`, `hide_caret`, and `fail_if_new` (in CI) are enabled by default.
80
+ Just `require 'capybara_screenshot_diff/minitest'` and call `screenshot`.
81
+
82
+ **Tier 2 — Set when tests are flaky:**
83
+
84
+ | Setting | When to use |
85
+ |---------|-------------|
86
+ | `window_size` | Screenshots differ between machines due to different browser sizes |
87
+ | `tolerance` | Sub-pixel rendering differences cause false positives |
88
+ | `skip_area` | Dynamic content (timestamps, ads) changes between runs |
89
+ | `stability_time_limit` | Animations or loading states cause inconsistent captures |
90
+
91
+ **Tier 3 — Advanced tuning:**
92
+
93
+ | Setting | When to use |
94
+ |---------|-------------|
95
+ | `perceptual_threshold` | Anti-aliasing false positives across OS/browser versions |
96
+ | `shift_distance_limit` | Content shifts by a few pixels (ChunkyPNG only) |
97
+ | `area_size_limit` | Allow small diff regions below a pixel count |
98
+ | `color_distance_limit` | Fine-tune raw RGB channel tolerance |
99
+ | `median_filter_window_size` | Smooth noise before comparison (VIPS only) |
100
+
101
+ ---
102
+
103
+ ## Common Options
104
+
105
+ ### Screen size
106
+
107
+ You can specify the desired screen size using
108
+
109
+ ```ruby
110
+ Capybara::Screenshot.window_size = [1024, 768]
111
+ ```
112
+
113
+ This will force the screen shots to the given size, and skip taking screen shots
114
+ unless the desired window size can be achieved.
115
+
116
+ ### Disabling screen shots
117
+
118
+ If you want to skip taking screen shots, set
119
+
120
+ ```ruby
121
+ Capybara::Screenshot.enabled = false
122
+ ```
123
+
124
+ You can of course set this by an environment variable
125
+
126
+ ```ruby
127
+ Capybara::Screenshot.enabled = ENV['TAKE_SCREENSHOTS']
128
+ ```
129
+
130
+ ### Disabling diff
131
+
132
+ If you want to skip the assertion for change in the screen shot, set
133
+
134
+ ```ruby
135
+ Capybara::Screenshot::Diff.enabled = false
136
+ ```
137
+
138
+ Using an environment variable
139
+
140
+ ```ruby
141
+ Capybara::Screenshot::Diff.enabled = ENV['COMPARE_SCREENSHOTS']
142
+ ```
143
+
144
+ ### Tolerate screenshot differences
145
+
146
+ To allow screenshot differences, but still fail on functional errors, you can set the following option:
147
+
148
+ ```ruby
149
+ Capybara::Screenshot::Diff.fail_on_difference = false
150
+ ```
151
+
152
+ It defaults to `true`. This can be useful in continuous integration to a generate a screenshot difference
153
+ report while still reporting functional errors.
154
+
155
+ ### Does not tolerate new screenshots
156
+
157
+ To fail the test if a new screenshot is taken, set the following option:
158
+
159
+ ```ruby
160
+ Capybara::Screenshot::Diff.fail_if_new = true
161
+ ```
162
+
163
+ If `fail_if_new` is set to `true`, the test will fail if a new screenshot is taken
164
+ that does not have a corresponding previous image to compare against.
165
+ This can be useful in situations where you want to ensure
166
+ that every screenshot taken by your tests corresponds to an expected state of your application.
167
+
168
+ ### Screen shot save path
169
+
170
+ By default, `Capybara::Screenshot::Diff` saves screenshots to a
171
+ `doc/screenshots` folder, relative to either `Rails.root` (if you're in Rails),
172
+ or your current directory otherwise.
173
+
174
+ If you want to change where screenshots are saved to, then there are two
175
+ configuration options that that are relevant.
176
+
177
+ The most likely one you'll want to modify is ...
178
+
179
+ ```ruby
180
+ Capybara::Screenshot.save_path = "other/path"
181
+ ```
182
+
183
+ The `save_path` option is relative to `Capybara::Screenshot.root`.
184
+
185
+ `Capybara::Screenshot.root` defaults to either `Rails.root` (if you're in
186
+ Rails) or your current directory. You can change it to something entirely
187
+ different if necessary, such as when using an alternative web framework.
188
+
189
+ ```ruby
190
+ Capybara::Screenshot.root = Hanami.root
191
+ ```
192
+
193
+ ### Screen shot stability
194
+
195
+ To ensure that animations are finished before saving a screen shot, you can add
196
+ a stability time limit. If the stability time limit is set, a second screen
197
+ shot will be taken and compared to the first. This is repeated until two
198
+ subsequent screen shots are identical.
199
+
200
+ ```ruby
201
+ Capybara::Screenshot.stability_time_limit = 0.1
202
+ ```
203
+
204
+ This can be overridden on a single screenshot:
205
+
206
+ ```ruby
207
+ test 'stability_time_limit' do
208
+ visit '/'
209
+ screenshot 'index', stability_time_limit: 0.5
210
+ end
211
+ ```
212
+
213
+ ### Maximum wait limit
214
+
215
+ When the `stability_time_limit` is set, but no stable screenshot can be taken, a timeout occurs.
216
+ The timeout occurs after `Capybara.default_max_wait_time`, but can be overridden by an option.
217
+
218
+ ```ruby
219
+ test 'max wait time' do
220
+ visit '/'
221
+ screenshot 'index', wait: 20.seconds
222
+ end
223
+ ```
224
+
225
+ ### Hiding the caret for active input elements
226
+
227
+ In Chrome the screenshot includes the blinking input cursor. This can make it impossible to get a
228
+ stable screenshot. To get around this you can set the `hide caret` option:
229
+
230
+ ```ruby
231
+ Capybara::Screenshot.hide_caret = true
232
+ ```
233
+
234
+ This will make the cursor (caret) transparent (invisible), so the blinking does not delay the screen shot.
235
+
236
+
237
+ ### Removing focus from the active element
238
+
239
+ Another way to avoid the cursor blinking is to set the `blur_active_element` option:
240
+
241
+ ```ruby
242
+ Capybara::Screenshot.blur_active_element = true
243
+ ```
244
+
245
+ This will remove the focus from the active element, removing the blinking cursor.
246
+
247
+
248
+
249
+ ### Allowed color distance
250
+
251
+ Sometimes you want to allow small differences in the images. For example, Chrome renders the same
252
+ page slightly differently sometimes. You can set set the color difference threshold for the
253
+ comparison using the `color_distance_limit` option to the `screenshot` method:
254
+
255
+ ```ruby
256
+ test 'color threshold' do
257
+ visit '/'
258
+ screenshot 'index', color_distance_limit: 30
259
+ end
260
+ ```
261
+
262
+ The difference is calculated as the euclidean distance. You can also set this globally:
263
+
264
+ ```ruby
265
+ Capybara::Screenshot::Diff.color_distance_limit = 42
266
+ ```
267
+
268
+
269
+ ### Allowed shift distance
270
+
271
+ Sometimes you want to allow small movements in the images. For example, jquery-tablesorter
272
+ renders the same table slightly differently sometimes. You can set set the shift distance
273
+ threshold for the comparison using the `shift_distance_limit` option to the `screenshot`
274
+ method:
275
+
276
+ ```ruby
277
+ test 'color threshold' do
278
+ visit '/'
279
+ screenshot 'index', shift_distance_limit: 2
280
+ end
281
+ ```
282
+
283
+ The difference is calculated as maximum distance in either the X or the Y axis.
284
+ You can also set this globally:
285
+
286
+ ```ruby
287
+ Capybara::Screenshot::Diff.shift_distance_limit = 1
288
+ ```
289
+
290
+ **Note:** For each increase in `shift_distance_limit` more pixels are searched for a matching color value, and
291
+ this will impact performance **severely** if a match cannot be found.
292
+
293
+ If `shift_distance_limit` is `nil` shift distance is not measured. If `shift_distance_limit` is set,
294
+ even to `0`, shift distance is measured and reported on image differences.
295
+
296
+ ### Allowed difference size
297
+
298
+ You can set set a threshold for the differing area size for the comparison
299
+ using the `area_size_limit` option to the `screenshot` method:
300
+
301
+ ```ruby
302
+ test 'area threshold' do
303
+ visit '/'
304
+ screenshot 'index', area_size_limit: 17
305
+ end
306
+ ```
307
+
308
+ The difference is calculated as `width * height`. You can also set this globally:
309
+
310
+ ```ruby
311
+ Capybara::Screenshot::Diff.area_size_limit = 42
312
+ ```
313
+
314
+
315
+ ### Skipping an area
316
+
317
+ Sometimes you have expected change that you want to ignore.
318
+ You can use the `skip_area` option with `[left, top, right, bottom]`
319
+ or css selector like `'#footer'` or `'.container .skipped_element'` to the `screenshot` method to ignore an area.
320
+ Be aware that if the selector is not in the page then the library will wait the default wait time for it to appear.
321
+ Therefore, it is best to only use css selectors for skip_areas you know will be in the page:
322
+
323
+ ```ruby
324
+ test 'unstable area' do
325
+ visit '/'
326
+ screenshot 'index', skip_area: [[17, 6, 27, 16], '.container .skipped_element', '#footer']
327
+ end
328
+ ```
329
+
330
+ The arguments are `[left, top, right, bottom]` for the area you want to ignore. You can also set this globally:
331
+
332
+ ```ruby
333
+ Capybara::Screenshot::Diff.skip_area = [0, 0, 64, 48]
334
+ ```
335
+
336
+ If you need to ignore multiple areas:
337
+
338
+ ```ruby
339
+ screenshot 'index', skip_area: [[0, 0, 64, 48], [17, 6, 27, 16], 'css_selector .element']
340
+ ```
341
+
342
+ ### Skipping stack frames in the error output
343
+
344
+ If you would like to override the `screenshot` method or for some other reason would like to skip stack
345
+ frames when reporting image differences, you can use the `skip_stack_frames` option:
346
+
347
+ ```ruby
348
+ test 'test visiting the index' do
349
+ visit root_path
350
+ screenshot :index
351
+ end
352
+
353
+ private
354
+
355
+ def screenshot(name, **options)
356
+ super(name, skip_stack_frames: 1, **options)
357
+ end
358
+ ```
359
+
360
+ ### Screenshot Format
361
+
362
+ You can specify the format of the screenshots taken by setting the `screenshot_format` option. By default, the format is set to `"png"`. However, you can change this to any format supported by your image processing driver. For example, to set the format to `"webp"`, you can do the following:
363
+
364
+ ```ruby
365
+ Capybara::Screenshot.screenshot_format = "webp"
366
+ ```
367
+
368
+ ### Customize Capybara#screenshot options
369
+
370
+ Allow to bypass screenshot options to Capybara driver.
371
+
372
+ ```ruby
373
+ # To create full page screenshots for Selenium
374
+ Capybara::Screenshot.capybara_screenshot_options[:full_page] = true
375
+
376
+ screenshot('index', median_filter_window_size: 2, capybara_screenshot_options: {full_page: false})
377
+ ```
378
+
379
+ [← Back to README](../README.md)
@@ -0,0 +1,24 @@
1
+ # Docker Testing
2
+
3
+ ## Running tests in Docker
4
+
5
+ Screenshot tests depend on exact browser rendering, which varies across OS and browser versions. Use `bin/dtest` to run tests inside Docker for consistent, reproducible results matching CI:
6
+
7
+ ```bash
8
+ bin/dtest # Run all tests with all drivers
9
+ bin/dtest test/integration/ # Run specific test directory
10
+ ```
11
+
12
+ This builds a Docker image with Chrome and runs the test suite against three Capybara drivers: `cuprite`, `selenium_chrome_headless`, and `selenium_headless`.
13
+
14
+ ## Recording baseline screenshots
15
+
16
+ Screenshot baselines are committed to the repo and compared against during tests. When you set up the project for the first time, or after upgrading the browser/driver, you need to re-record them:
17
+
18
+ ```bash
19
+ RECORD_SCREENSHOTS=1 bin/dtest
20
+ ```
21
+
22
+ This skips screenshot comparisons and saves new baselines instead. Without this step, tests will fail because your local browser renders pixels differently from the previously committed baselines.
23
+
24
+ [← Back to README](../README.md)