From ca2f05b6a41656642c35a7c9ccce1825481c6fed Mon Sep 17 00:00:00 2001 From: Paul Keen <125715+pftg@users.noreply.github.com> Date: Sun, 12 Apr 2026 23:57:50 +0200 Subject: [PATCH 1/3] feat: always print summary after tests (like SimpleCov) Add `#summary` method to HTML reporter that prints screenshot count and report path after every test run: - "3 screenshots compared, 1 failure. Report: doc/screenshots/snap_diff_report.html" - "5 screenshots compared, no failures." - Nothing printed when no screenshots were taken Follows the SimpleCov pattern: always show status so developers know the reporter is active and where to find results. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../reporters/html.rb | 17 +++++++- test/unit/reporters/html_reporter_test.rb | 43 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/capybara_screenshot_diff/reporters/html.rb b/lib/capybara_screenshot_diff/reporters/html.rb index 17b4d233..f9b7ccb5 100644 --- a/lib/capybara_screenshot_diff/reporters/html.rb +++ b/lib/capybara_screenshot_diff/reporters/html.rb @@ -68,6 +68,19 @@ def success_rate (passed.to_f / total * 100).round(1) end + def summary + return if total.zero? + + screenshots_label = (total == 1) ? "1 screenshot" : "#{total} screenshots" + + if failures.empty? + "[snap_diff] #{screenshots_label} compared, no failures." + else + failures_label = (failures.size == 1) ? "1 failure" : "#{failures.size} failures" + "[snap_diff] #{screenshots_label} compared, #{failures_label}. Report: #{output_path}" + end + end + def render ERB.new(File.read(self.class.template_path)).result(binding) end @@ -130,8 +143,8 @@ def write_report at_exit do CapybaraScreenshotDiff.reporters_mutex.synchronize { CapybaraScreenshotDiff.reporters.dup }.each do |reporter| - result = reporter.finalize - $stdout.puts "[snap_diff] HTML report: #{result}" if result.is_a?(Pathname) + reporter.finalize + $stdout.puts reporter.summary if reporter.respond_to?(:summary) && reporter.summary rescue => e warn "[snap_diff] Reporter #{reporter.class} failed (#{e.class}: #{e.message})" if ENV["DEBUG"] end diff --git a/test/unit/reporters/html_reporter_test.rb b/test/unit/reporters/html_reporter_test.rb index 293090c0..e49f4fd5 100644 --- a/test/unit/reporters/html_reporter_test.rb +++ b/test/unit/reporters/html_reporter_test.rb @@ -146,6 +146,49 @@ def synchronize assert_operator fake_mutex.synchronize_calls, :>=, 1 end + test "#finalize returns output_path when there are failures" do + reporter = HTML.new(output_path: @output_path) + reporter.record([build_failing_assertion("fail")]) + result = reporter.finalize + + assert_instance_of Pathname, result + end + + test "#finalize returns nil when no failures" do + reporter = HTML.new(output_path: @output_path) + reporter.record([build_passing_assertion("pass")]) + result = reporter.finalize + + assert_nil result + end + + test "#summary returns screenshot count and status" do + reporter = HTML.new(output_path: @output_path) + reporter.record([build_passing_assertion("ok"), build_failing_assertion("fail")]) + reporter.finalize + + summary = reporter.summary + assert_includes summary, "1 failure" + assert_includes summary, "2 screenshots" + assert_includes summary, @output_path.to_s + end + + test "#summary when all pass shows no failures" do + reporter = HTML.new(output_path: @output_path) + reporter.record([build_passing_assertion("ok")]) + reporter.finalize + + summary = reporter.summary + assert_includes summary, "1 screenshot" + assert_includes summary, "no failures" + refute_includes summary, "report" + end + + test "#summary when no screenshots recorded" do + reporter = HTML.new(output_path: @output_path) + assert_nil reporter.summary + end + test "#finalize can retry after write_report failure" do reporter = HTML.new(output_path: @output_path) reporter.record([build_failing_assertion("retry")]) From 63667048d80c56fc8a8b0aecc7e821470d629240 Mon Sep 17 00:00:00 2001 From: Paul Keen <125715+pftg@users.noreply.github.com> Date: Mon, 13 Apr 2026 00:02:08 +0200 Subject: [PATCH 2/3] Apply suggestion from @sourcery-ai[bot] Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- test/unit/reporters/html_reporter_test.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/unit/reporters/html_reporter_test.rb b/test/unit/reporters/html_reporter_test.rb index e49f4fd5..de8dd3a3 100644 --- a/test/unit/reporters/html_reporter_test.rb +++ b/test/unit/reporters/html_reporter_test.rb @@ -173,6 +173,20 @@ def synchronize assert_includes summary, @output_path.to_s end + test "#summary pluralizes failures label for multiple failures" do + reporter = HTML.new(output_path: @output_path) + reporter.record([ + build_failing_assertion("first failure"), + build_failing_assertion("second failure") + ]) + reporter.finalize + + summary = reporter.summary + assert_includes summary, "2 failures" + assert_includes summary, "2 screenshots" + assert_includes summary, @output_path.to_s + end + test "#summary when all pass shows no failures" do reporter = HTML.new(output_path: @output_path) reporter.record([build_passing_assertion("ok")]) From 19572f68864e715dd6746f40a96e22a411370a41 Mon Sep 17 00:00:00 2001 From: Paul Keen <125715+pftg@users.noreply.github.com> Date: Mon, 13 Apr 2026 00:03:41 +0200 Subject: [PATCH 3/3] refactor: simplify at_exit hook, remove dead code - Fix double summary evaluation: assign to local variable (was called twice) - Remove respond_to? guard: only HTML reporters exist, fail fast if not - Always warn on reporter errors (was silenced unless DEBUG) - Remove dead success_rate method (zero callers) - Fix fragile test assertion: check output_path instead of string "report" Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/capybara_screenshot_diff/reporters/html.rb | 11 ++++------- test/unit/reporters/html_reporter_test.rb | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/capybara_screenshot_diff/reporters/html.rb b/lib/capybara_screenshot_diff/reporters/html.rb index f9b7ccb5..84639054 100644 --- a/lib/capybara_screenshot_diff/reporters/html.rb +++ b/lib/capybara_screenshot_diff/reporters/html.rb @@ -63,11 +63,6 @@ def output_path def passed = total - failures.size def failed = failures.size - def success_rate - return 0 if total.zero? - (passed.to_f / total * 100).round(1) - end - def summary return if total.zero? @@ -144,8 +139,10 @@ def write_report at_exit do CapybaraScreenshotDiff.reporters_mutex.synchronize { CapybaraScreenshotDiff.reporters.dup }.each do |reporter| reporter.finalize - $stdout.puts reporter.summary if reporter.respond_to?(:summary) && reporter.summary + if (msg = reporter.summary) + $stdout.puts msg + end rescue => e - warn "[snap_diff] Reporter #{reporter.class} failed (#{e.class}: #{e.message})" if ENV["DEBUG"] + warn "[snap_diff] Reporter #{reporter.class} failed (#{e.class}: #{e.message})" end end diff --git a/test/unit/reporters/html_reporter_test.rb b/test/unit/reporters/html_reporter_test.rb index de8dd3a3..c34758c4 100644 --- a/test/unit/reporters/html_reporter_test.rb +++ b/test/unit/reporters/html_reporter_test.rb @@ -195,7 +195,7 @@ def synchronize summary = reporter.summary assert_includes summary, "1 screenshot" assert_includes summary, "no failures" - refute_includes summary, "report" + refute_includes summary, @output_path.to_s end test "#summary when no screenshots recorded" do