Skip to content

Commit fc8dbaa

Browse files
pftgclaude
andauthored
fix: use Minitest.after_run for report summary (#182)
* fix: use Minitest.after_run for report summary, not at_exit Root cause: at_exit hooks run in LIFO order. Our at_exit was registered after minitest's autorun, so it fired before tests finished — reporter had total=0 because no assertions had been processed yet. Fix: Use Minitest.after_run (runs after all tests complete) when minitest is available, falling back to at_exit for RSpec/Cucumber. Also remove screenshots&.clear from verify_screenshots! ensure block — clearing now happens in registry.reset after notify_reporters, so assertions are available for the reporter before being cleared. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: keep base image when screenshots differ for reporter Previously, base_image_path was deleted after comparison when images differed. This meant the HTML reporter couldn't show the baseline image — it appeared as a broken "Base" alt text. The base image is a VCS checkout that gets re-created on next run, so keeping it has no side effects. The reporter now shows both baseline and current images side-by-side. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs: document .gitignore patterns for diff artifacts - Add .gitignore block to README Quick Start - Add .gitignore section to CI integration docs - Add generated artifacts table to organization docs - Add *.base.png to snap_diff:clean rake task - Add *.base.png to test fixtures README Files to gitignore: *.diff.png, *.base.png, *.diff.webp, *.base.webp, snap_diff_report.html. Only commit baseline screenshots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: register both at_exit and Minitest.after_run hooks Always register at_exit (works for RSpec/Cucumber) AND additionally Minitest.after_run when Minitest is loaded (correct ordering for Minitest). The @Finalized guard in #finalize prevents double execution. Previously the else branch meant RSpec/Cucumber users got no at_exit hook when Minitest was also loaded in the same process. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: avoid double finalize call for Minitest Restore if/else — Minitest.after_run when Minitest is loaded, at_exit otherwise. Nobody runs Minitest and RSpec in the same process, so the @Finalized double-execution guard is unnecessary overhead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: register framework-native hooks for RSpec and Cucumber - Minitest: Minitest.after_run (runs after all tests) - RSpec: RSpec.configure { |c| c.after(:suite) } (runs after suite) - Cucumber/other: at_exit fallback at_exit fires in LIFO order, so it runs BEFORE the test framework finishes when registered after it. Framework-native hooks guarantee correct ordering — reporter has data when finalize runs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: move finalize_reporters! to framework adapters Extract finalize_reporters! into CapybaraScreenshotDiff module and call it from each framework's native "after suite" hook: - Minitest: Minitest.after_run - RSpec: config.after(:suite) - Cucumber: AfterAll html.rb no longer detects frameworks — it just registers at_exit as fallback for unknown frameworks. Each adapter owns its hook. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d7153ab commit fc8dbaa

11 files changed

Lines changed: 78 additions & 22 deletions

File tree

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ doc/screenshots/
6060
homepage.png <- your baseline (commit this)
6161
```
6262

63+
Add diff artifacts to `.gitignore` — these are generated at runtime and should not be committed:
64+
```gitignore
65+
# Screenshot diff artifacts (generated, not committed)
66+
*.diff.png
67+
*.base.png
68+
*.diff.webp
69+
*.base.webp
70+
snap_diff_report.html
71+
```
72+
6373
If you skip Step 2 and push to CI, the build will fail — `fail_if_new` is `true` by default in CI.
6474

6575
For RSpec, Cucumber, or non-Rails setup, see [Framework Setup](docs/framework-setup.md).

Rakefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ end
3737

3838
desc "Remove screenshot diff artifacts (keeps baselines)"
3939
task "snap_diff:clean" do
40-
patterns = ["**/*.diff.png", "**/*.base.diff.png", "**/*.heatmap.diff.png",
41-
"**/*.diff.webp", "**/*.base.diff.webp", "**/*.heatmap.diff.webp",
40+
patterns = ["**/*.diff.png", "**/*.base.png", "**/*.base.diff.png", "**/*.heatmap.diff.png",
41+
"**/*.diff.webp", "**/*.base.webp", "**/*.base.diff.webp", "**/*.heatmap.diff.webp",
4242
"**/snap_diff_report.html"]
4343
removed = patterns.flat_map { |p| Dir.glob("tmp/#{p}") + Dir.glob("doc/screenshots/#{p}") }.uniq
4444
removed.each { |f| FileUtils.rm_f(f) }

docs/ci-integration.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ CapybaraScreenshotDiff.serve("_site") # or "public", "build", "dist"
1111

1212
This sets up Capybara to serve static files and configures screenshot paths automatically.
1313

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+
1429
## GitHub Actions Integration
1530

1631
### 1. Enable the HTML report

docs/organization.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,34 @@ doc
2828
welcome_index
2929
```
3030

31-
To store the screen shot history, add the `doc/screenshots` directory to your
32-
version control system (git, svn, etc).
31+
To store the screenshot history, add the `doc/screenshots` directory to your
32+
version control system (git).
3333

34-
Screen shots are compared to the previously COMMITTED version of the same screen shot.
34+
Screenshots are compared to the previously COMMITTED version of the same screenshot.
35+
36+
### Generated artifacts (do not commit)
37+
38+
When a screenshot differs, the gem generates temporary diff files alongside the baseline:
39+
40+
| Pattern | Description |
41+
|---------|-------------|
42+
| `*.base.png` | VCS checkout of the committed baseline |
43+
| `*.diff.png` | Annotated diff with changes highlighted |
44+
| `*.base.diff.png` | Annotated baseline with diff region marked |
45+
| `*.heatmap.diff.png` | Heatmap of pixel differences |
46+
| `snap_diff_report.html` | Interactive Web UI report |
47+
48+
Add these to `.gitignore`:
49+
50+
```gitignore
51+
*.diff.png
52+
*.base.png
53+
*.diff.webp
54+
*.base.webp
55+
snap_diff_report.html
56+
```
57+
58+
Clean up artifacts with `rake snap_diff:clean`.
3559

3660
## Screenshot groups
3761

lib/capybara_screenshot_diff/cucumber.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
Capybara::Screenshot::Diff.delayed = false
99
Capybara::Screenshot::BrowserHelpers.resize_window_if_needed
1010
end
11+
12+
AfterAll { CapybaraScreenshotDiff.finalize_reporters! }

lib/capybara_screenshot_diff/minitest.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,5 @@ def before_teardown
4747
end
4848
end
4949
end
50+
51+
::Minitest.after_run { CapybaraScreenshotDiff.finalize_reporters! } if ::Minitest.respond_to?(:after_run)

lib/capybara_screenshot_diff/reporters/html.rb

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,6 @@ def write_report
136136
CapybaraScreenshotDiff.reporters << CapybaraScreenshotDiff::Reporters::HTML.new(embed_images: !!ENV["CI"])
137137
end
138138

139-
at_exit do
140-
CapybaraScreenshotDiff.reporters_mutex.synchronize { CapybaraScreenshotDiff.reporters.dup }.each do |reporter|
141-
reporter.finalize
142-
if (msg = reporter.summary)
143-
$stdout.puts msg
144-
end
145-
rescue => e
146-
warn "[snap_diff] Reporter #{reporter.class} failed (#{e.class}: #{e.message})"
147-
end
148-
end
139+
# Fallback for frameworks without explicit integration (e.g., Cucumber).
140+
# Minitest and RSpec adapters call finalize_reporters! via their native hooks.
141+
at_exit { CapybaraScreenshotDiff.finalize_reporters! }

lib/capybara_screenshot_diff/rspec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,6 @@
4141
end
4242
end
4343
end
44+
45+
config.after(:suite) { CapybaraScreenshotDiff.finalize_reporters! }
4446
end

lib/capybara_screenshot_diff/screenshot_assertion.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ def self.verify_screenshots!(screenshots)
4141
test_screenshot_errors.compact!
4242

4343
test_screenshot_errors.empty? ? nil : test_screenshot_errors
44-
ensure
45-
screenshots&.clear
4644
end
4745

4846
# Asserts that an image has not changed compared to its baseline.
@@ -58,8 +56,6 @@ def self.assert_image_not_changed(backtrace, name, comparison)
5856
# Cleanup after comparisons
5957
if !result && comparison.base_image_path.exist?
6058
FileUtils.mv(comparison.base_image_path, comparison.image_path, force: true)
61-
elsif !comparison.dimensions_changed?
62-
FileUtils.rm_rf(comparison.base_image_path)
6359
end
6460

6561
return unless result
@@ -135,6 +131,17 @@ def reporters
135131

136132
attr_reader :reporters_mutex
137133

134+
def finalize_reporters!
135+
reporters_mutex.synchronize { reporters.dup }.each do |reporter|
136+
reporter.finalize
137+
if (msg = reporter.summary)
138+
$stdout.puts msg
139+
end
140+
rescue => e
141+
warn "[snap_diff] Reporter #{reporter.class} failed (#{e.class}: #{e.message})"
142+
end
143+
end
144+
138145
def_delegator :registry, :screenshot_namer
139146
def_delegator :registry, :verify
140147

test/fixtures/images/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ These files are generated during test runs and should NOT be committed:
1919

2020
| Pattern | Description |
2121
|---------|-------------|
22+
| `*.base.png` | VCS checkout of committed baseline |
2223
| `*.diff.png` | Annotated diff overlay |
23-
| `*.base.diff.png` | Annotated base image |
24+
| `*.base.diff.png` | Annotated base image with diff region |
2425
| `*.heatmap.diff.png` | Heatmap of pixel differences |

0 commit comments

Comments
 (0)