snap_diff-capybara 0.0.1 → 2.0.0.alpha1

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +167 -0
  3. data/CODE_OF_CONDUCT.md +129 -0
  4. data/LICENSE.txt +21 -0
  5. data/Rakefile +65 -0
  6. data/capybara-screenshot-diff.gemspec +29 -0
  7. data/docs/RELEASE_PREP.md +44 -0
  8. data/docs/UPGRADING.md +475 -0
  9. data/docs/architecture.md +267 -0
  10. data/docs/ci-integration.md +238 -0
  11. data/docs/configuration.md +395 -0
  12. data/docs/docker-testing.md +24 -0
  13. data/docs/drivers.md +102 -0
  14. data/docs/framework-setup.md +87 -0
  15. data/docs/images/snap_diff_annotated.png +0 -0
  16. data/docs/images/snap_diff_web_ui.png +0 -0
  17. data/docs/migration-guide.md +286 -0
  18. data/docs/organization.md +204 -0
  19. data/docs/reporters.md +46 -0
  20. data/docs/thread_safety.md +112 -0
  21. data/gems.rb +39 -0
  22. data/lib/capybara/screenshot/diff/annotation_service.rb +7 -0
  23. data/lib/capybara/screenshot/diff/area_calculator.rb +7 -0
  24. data/lib/capybara/screenshot/diff/browser_helpers.rb +7 -0
  25. data/lib/capybara/screenshot/diff/config_legacy.rb +113 -0
  26. data/lib/capybara/screenshot/diff/cucumber.rb +3 -0
  27. data/lib/capybara/screenshot/diff/difference.rb +8 -0
  28. data/lib/capybara/screenshot/diff/drivers/base_driver.rb +9 -0
  29. data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +7 -0
  30. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +7 -0
  31. data/lib/capybara/screenshot/diff/drivers.rb +9 -0
  32. data/lib/capybara/screenshot/diff/image_compare.rb +12 -0
  33. data/lib/capybara/screenshot/diff/image_preprocessor.rb +7 -0
  34. data/lib/capybara/screenshot/diff/os.rb +14 -0
  35. data/lib/capybara/screenshot/diff/region.rb +107 -0
  36. data/lib/capybara/screenshot/diff/reporters/default.rb +109 -0
  37. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +7 -0
  38. data/lib/capybara/screenshot/diff/screenshoter.rb +7 -0
  39. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +7 -0
  40. data/lib/capybara/screenshot/diff/utils.rb +7 -0
  41. data/lib/capybara/screenshot/diff/vcs.rb +7 -0
  42. data/lib/capybara/screenshot/diff/version.rb +15 -0
  43. data/lib/capybara/screenshot/diff.rb +3 -0
  44. data/lib/capybara-screenshot-diff.rb +3 -0
  45. data/lib/capybara_screenshot_diff/attempts_reporter.rb +7 -0
  46. data/lib/capybara_screenshot_diff/cucumber.rb +8 -0
  47. data/lib/capybara_screenshot_diff/dsl.rb +12 -0
  48. data/lib/capybara_screenshot_diff/error_with_filtered_backtrace.rb +8 -0
  49. data/lib/capybara_screenshot_diff/minitest.rb +14 -0
  50. data/lib/capybara_screenshot_diff/reporters/html.rb +7 -0
  51. data/lib/capybara_screenshot_diff/rspec.rb +8 -0
  52. data/lib/capybara_screenshot_diff/screenshot_assertion.rb +71 -0
  53. data/lib/capybara_screenshot_diff/screenshot_namer.rb +7 -0
  54. data/lib/capybara_screenshot_diff/snap.rb +7 -0
  55. data/lib/capybara_screenshot_diff/snap_manager.rb +7 -0
  56. data/lib/capybara_screenshot_diff/static.rb +9 -0
  57. data/lib/capybara_screenshot_diff.rb +50 -0
  58. data/lib/snap_diff/annotation_service.rb +84 -0
  59. data/lib/snap_diff/area_calculator.rb +56 -0
  60. data/lib/snap_diff/attempts_reporter.rb +51 -0
  61. data/lib/snap_diff/browser_helpers.rb +121 -0
  62. data/lib/snap_diff/capture/viewport.rb +40 -0
  63. data/lib/snap_diff/comparison.rb +238 -0
  64. data/lib/snap_diff/comparison_result.rb +104 -0
  65. data/lib/snap_diff/config.rb +78 -0
  66. data/lib/snap_diff/deprecation.rb +84 -0
  67. data/lib/snap_diff/driver.rb +37 -0
  68. data/lib/snap_diff/drivers/chunky_png_driver.rb +298 -0
  69. data/lib/snap_diff/drivers/vips_driver.rb +171 -0
  70. data/lib/snap_diff/drivers.rb +14 -0
  71. data/lib/snap_diff/dsl.rb +143 -0
  72. data/lib/snap_diff/error_with_filtered_backtrace.rb +32 -0
  73. data/lib/snap_diff/image_preprocessor.rb +68 -0
  74. data/lib/snap_diff/integrations/cucumber.rb +22 -0
  75. data/lib/snap_diff/integrations/minitest.rb +70 -0
  76. data/lib/snap_diff/integrations/rspec.rb +68 -0
  77. data/lib/snap_diff/legacy_shims.rb +99 -0
  78. data/lib/snap_diff/os.rb +17 -0
  79. data/lib/snap_diff/reporters/html.rb +143 -0
  80. data/lib/snap_diff/reporters/templates/report.html.erb +463 -0
  81. data/lib/snap_diff/reporting.rb +53 -0
  82. data/lib/snap_diff/screenshot_assertion.rb +143 -0
  83. data/lib/snap_diff/screenshot_matcher.rb +113 -0
  84. data/lib/snap_diff/screenshot_namer.rb +81 -0
  85. data/lib/snap_diff/screenshoter.rb +129 -0
  86. data/lib/snap_diff/snap.rb +66 -0
  87. data/lib/snap_diff/snap_manager.rb +125 -0
  88. data/lib/snap_diff/stable_screenshoter.rb +103 -0
  89. data/lib/snap_diff/static.rb +11 -0
  90. data/lib/snap_diff/utils.rb +38 -0
  91. data/lib/snap_diff/vcs.rb +35 -0
  92. data/lib/snap_diff/version.rb +5 -0
  93. data/lib/snap_diff.rb +65 -0
  94. metadata +160 -10
  95. data/README.md +0 -3
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::BrowserHelpers now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::BrowserHelpers.
6
+ require "snap_diff/browser_helpers"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Legacy Capybara::Screenshot / Capybara::Screenshot::Diff mattr_accessor
4
+ # config module, extracted out of capybara_screenshot_diff.rb so it is a
5
+ # leaf: it can be required by both capybara_screenshot_diff.rb (the old
6
+ # umbrella entry point) and snap_diff.rb (the canonical entry point)
7
+ # without either of *those* requiring the other back.
8
+ #
9
+ # Why this file has to exist at all: several lib/snap_diff/* units
10
+ # (Screenshoter, SnapManager, Utils) are referenced here at class-body
11
+ # eval time (mattr_accessor default blocks, AVAILABLE_DRIVERS) rather
12
+ # than at call time, so they must be real, already-loaded classes before
13
+ # this module body runs. Since v2 step 6 they are pulled in and referenced
14
+ # under their canonical SnapDiff names: the old-name aliases are lazy
15
+ # const_missing shims that emit deprecation warnings, and the gem's own
16
+ # code must stay warning-free. Everything else this module references
17
+ # (Os, Comparison) is referenced only inside method bodies, resolved
18
+ # lazily at call time by whichever entry point loaded them.
19
+ require "snap_diff/screenshoter"
20
+ require "snap_diff/snap_manager"
21
+ require "snap_diff/utils"
22
+
23
+ module Capybara
24
+ module Screenshot
25
+ mattr_accessor :add_driver_path
26
+ mattr_accessor :add_os_path
27
+ mattr_accessor(:blur_active_element) { true }
28
+ mattr_accessor :enabled
29
+ mattr_accessor(:hide_caret) { true }
30
+ mattr_accessor :disable_animations
31
+ mattr_reader(:root) { (defined?(Rails) && defined?(Rails.root) && Rails.root) || Pathname(".").expand_path }
32
+ mattr_accessor :stability_time_limit
33
+ mattr_accessor :window_size
34
+ mattr_accessor(:save_path) { "doc/screenshots" }
35
+ mattr_accessor(:use_lfs)
36
+ mattr_accessor(:screenshot_format) { "png" }
37
+ mattr_accessor(:capybara_screenshot_options) { {} }
38
+
39
+ class << self
40
+ def root=(path)
41
+ @@root = Pathname(path).expand_path
42
+ end
43
+
44
+ def active?
45
+ enabled || (enabled.nil? && Diff.enabled)
46
+ end
47
+
48
+ def screenshot_area
49
+ parts = [Screenshot.save_path]
50
+ parts << SnapDiff::Os.name if Screenshot.add_os_path
51
+ parts << Capybara.current_driver.to_s if Screenshot.add_driver_path
52
+ File.join(*parts)
53
+ end
54
+
55
+ def screenshot_area_abs
56
+ root / screenshot_area
57
+ end
58
+ end
59
+
60
+ # Module to track screenshot changes
61
+ module Diff
62
+ mattr_accessor(:delayed) { true }
63
+ mattr_accessor :area_size_limit
64
+ mattr_accessor(:fail_if_new) { !ENV["CI"].nil? && !ENV["CI"].empty? }
65
+ mattr_accessor(:pending_if_new) { false }
66
+ mattr_accessor(:fail_on_difference) { true }
67
+ mattr_accessor :color_distance_limit
68
+ mattr_accessor(:enabled) { true }
69
+ mattr_accessor :shift_distance_limit
70
+ mattr_accessor :skip_area
71
+ mattr_accessor(:driver) { :auto }
72
+ mattr_accessor :tolerance
73
+ mattr_accessor :perceptual_threshold
74
+
75
+ mattr_accessor(:screenshoter) { SnapDiff::Screenshoter }
76
+ mattr_accessor(:manager) { SnapDiff::SnapManager }
77
+
78
+ AVAILABLE_DRIVERS = SnapDiff::Utils.detect_available_drivers.freeze
79
+
80
+ # Configure screenshot and diff settings in one block.
81
+ #
82
+ # Capybara::Screenshot::Diff.configure do |screenshot, diff|
83
+ # screenshot.window_size = [1280, 1024]
84
+ # screenshot.stability_time_limit = 1
85
+ # diff.driver = :vips
86
+ # diff.tolerance = 0.0005
87
+ # end
88
+ def self.configure
89
+ yield Screenshot, self
90
+ end
91
+
92
+ def self.compare(baseline_path, current_path, **options)
93
+ SnapDiff::Comparison.new(current_path, baseline_path, default_options.merge(options))
94
+ end
95
+
96
+ def self.default_options
97
+ {
98
+ area_size_limit: area_size_limit,
99
+ color_distance_limit: color_distance_limit,
100
+ driver: driver,
101
+ screenshot_format: Screenshot.screenshot_format,
102
+ capybara_screenshot_options: Screenshot.capybara_screenshot_options,
103
+ perceptual_threshold: perceptual_threshold,
104
+ shift_distance_limit: shift_distance_limit,
105
+ skip_area: skip_area,
106
+ stability_time_limit: Screenshot.stability_time_limit,
107
+ tolerance: tolerance || ((driver == :vips) ? 0.001 : nil),
108
+ wait: Capybara.default_max_wait_time
109
+ }
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "snap_diff/integrations/cucumber"
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): the comparison-result value object lives at
4
+ # SnapDiff::ComparisonResult (ex-Difference); the old name now resolves
5
+ # lazily via snap_diff/legacy_shims' const_missing, with a deprecation
6
+ # warning.
7
+ require "snap_diff/comparison_result"
8
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # BaseDriver was dissolved into the SnapDiff::Driver mixin (ADR-004 v2
4
+ # step 4); since step 6 the old name resolves lazily via
5
+ # snap_diff/legacy_shims' const_missing, with a deprecation warning.
6
+ # Note it is now a module -- `class MyDriver < BaseDriver` becomes
7
+ # `include SnapDiff::Driver`.
8
+ require "snap_diff/driver"
9
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 4): ChunkyPNGDriver lives in SnapDiff::Drivers
4
+ # now; the module alias in capybara/screenshot/diff/drivers.rb makes it
5
+ # reachable as Capybara::Screenshot::Diff::Drivers::ChunkyPNGDriver.
6
+ require "capybara/screenshot/diff/drivers"
7
+ require "snap_diff/drivers/chunky_png_driver"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 4): VipsDriver lives in SnapDiff::Drivers now;
4
+ # the module alias in capybara/screenshot/diff/drivers.rb makes it reachable
5
+ # as Capybara::Screenshot::Diff::Drivers::VipsDriver.
6
+ require "capybara/screenshot/diff/drivers"
7
+ require "snap_diff/drivers/vips_driver"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Diff::Drivers now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::Drivers. The forwarded module is
6
+ # the same object, so Drivers.for and the Drivers::VipsDriver /
7
+ # Drivers::ChunkyPNGDriver constants keep resolving through the old name.
8
+ require "snap_diff/drivers"
9
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): the comparison class lives at
4
+ # SnapDiff::Comparison (ex-ImageCompare); the old name now resolves lazily
5
+ # via snap_diff/legacy_shims' const_missing, with a deprecation warning.
6
+ # snap_diff/comparison itself pulls in the ComparisonResult and Drivers
7
+ # units, and the shims keep the old ::Difference / ::Drivers names
8
+ # resolvable, so this path still provides everything the pre-move
9
+ # image_compare.rb did. The internal Comparison struct and LOADED_DRIVERS
10
+ # keep their legacy names and are defined by snap_diff/comparison.rb itself.
11
+ require "snap_diff/comparison"
12
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Diff::ImagePreprocessor
4
+ # now resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::ImagePreprocessor.
6
+ require "snap_diff/image_preprocessor"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "snap_diff/os"
4
+
5
+ # Deliberately EAGER and silent (v2 step 6 exception): Os is an advertised
6
+ # entry-point constant probed with Object.const_defined? by
7
+ # support_load_probe_test.rb, and const_defined? never triggers
8
+ # const_missing -- a lazy shim would break that contract. See
9
+ # snap_diff/legacy_shims.rb for the full exception list.
10
+ module Capybara
11
+ module Screenshot
12
+ Os = SnapDiff::Os
13
+ end
14
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Region
4
+ attr_accessor :x, :y, :width, :height
5
+
6
+ def initialize(x, y, width, height)
7
+ @x, @y, @width, @height = x, y, width, height
8
+ end
9
+
10
+ def self.from_edge_coordinates(left, top, right, bottom)
11
+ return nil unless left && top && right && bottom
12
+ return nil if right < left || bottom < top
13
+
14
+ Region.new(left, top, right - left, bottom - top)
15
+ end
16
+
17
+ def to_edge_coordinates
18
+ [left, top, right, bottom]
19
+ end
20
+
21
+ def to_top_left_corner_coordinates
22
+ [x, y, width, height]
23
+ end
24
+
25
+ def top
26
+ y
27
+ end
28
+
29
+ def bottom
30
+ y + height
31
+ end
32
+
33
+ def left
34
+ x
35
+ end
36
+
37
+ def right
38
+ x + width
39
+ end
40
+
41
+ def size
42
+ return 0 if width < 0 || height < 0
43
+
44
+ result = width * height
45
+ result.zero? ? 1 : result
46
+ end
47
+
48
+ def to_a
49
+ [@x, @y, @width, @height]
50
+ end
51
+
52
+ def find_intersect_with(region)
53
+ return nil unless intersect?(region)
54
+
55
+ new_left = [x, region.x].max
56
+ new_top = [y, region.y].max
57
+
58
+ Region.new(new_left, new_top, [right, region.right].min - new_left, [bottom, region.bottom].min - new_top)
59
+ end
60
+
61
+ def intersect?(region)
62
+ left <= region.right && right >= region.left && top <= region.bottom && bottom >= region.top
63
+ end
64
+
65
+ def move_by(right_by, down_by)
66
+ Region.new(x + right_by, y + down_by, width, height)
67
+ end
68
+
69
+ def find_relative_intersect(region)
70
+ intersect = find_intersect_with(region)
71
+ return nil unless intersect
72
+
73
+ intersect.move_by(-x, -y)
74
+ end
75
+
76
+ def cover?(x, y)
77
+ x.between?(left, right) && y.between?(top, bottom)
78
+ end
79
+
80
+ def empty?
81
+ width.zero? || height.zero?
82
+ end
83
+
84
+ def blank?
85
+ empty?
86
+ end
87
+
88
+ def present?
89
+ !empty?
90
+ end
91
+
92
+ def inspect
93
+ "Region(x: #{x}, y: #{y}, width: #{width}, height: #{height})"
94
+ end
95
+
96
+ # need to add this method to make it work with assert_equal
97
+ def ==(other)
98
+ case other
99
+ when Region
100
+ x == other.x && y == other.y && width == other.width && height == other.height
101
+ when Array
102
+ to_a == other
103
+ else
104
+ false
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "snap_diff/annotation_service"
4
+ # Defines the Capybara::Screenshot::Diff namespace this file reopens.
5
+ require "snap_diff/legacy_shims"
6
+
7
+ module Capybara::Screenshot::Diff
8
+ module Reporters
9
+ class Default
10
+ attr_reader :difference
11
+
12
+ def initialize(difference)
13
+ @difference = difference
14
+ @annotation_service = SnapDiff::AnnotationService.new(difference)
15
+ end
16
+
17
+ def annotated_image_path
18
+ annotation_service.annotated_image_path
19
+ end
20
+
21
+ def annotated_base_image_path
22
+ annotation_service.annotated_base_image_path
23
+ end
24
+
25
+ def heatmap_diff_path
26
+ annotation_service.heatmap_diff_path
27
+ end
28
+
29
+ def generate
30
+ if difference.equal?
31
+ # NOTE: Delete previous run runtime files
32
+ clean_tmp_files
33
+ return nil
34
+ end
35
+
36
+ if difference.failed? && difference.failed_by[:different_dimensions]
37
+ return build_error_for_different_dimensions
38
+ end
39
+
40
+ annotate_and_save_images
41
+ build_error_message
42
+ end
43
+
44
+ def clean_tmp_files
45
+ annotation_service.clean_tmp_files
46
+ end
47
+
48
+ def annotate_and_save_images
49
+ annotation_service.annotate_and_save_images
50
+ end
51
+
52
+ def save_annotation_for(image, image_path)
53
+ annotation_service.save_annotation_for(image, image_path)
54
+ end
55
+
56
+ def annotate_difference(image, region)
57
+ annotation_service.annotate_difference(image, region)
58
+ end
59
+
60
+ def annotate_skip_areas(image, skip_areas)
61
+ annotation_service.annotate_skip_areas(image, skip_areas)
62
+ end
63
+
64
+ def save(image, image_path)
65
+ annotation_service.save(image, image_path)
66
+ end
67
+
68
+ def build_error_for_different_dimensions
69
+ change_msg = [comparison.base_image, comparison.new_image]
70
+ .map { |image| driver.dimension(image).join("x") }
71
+ .join(" => ")
72
+
73
+ "Dimensions have changed: #{change_msg}\n#{base_image_path.to_path}\n#{image_path.to_path}"
74
+ end
75
+
76
+ NEW_LINE = "\n"
77
+
78
+ def build_error_message
79
+ [
80
+ "(#{difference.to_h.to_json})",
81
+ image_path.to_path,
82
+ annotated_base_image_path.to_path,
83
+ annotated_image_path.to_path,
84
+ heatmap_diff_path.to_path
85
+ ].join(NEW_LINE)
86
+ end
87
+
88
+ private
89
+
90
+ attr_reader :annotation_service
91
+
92
+ def base_image_path
93
+ comparison.base_image_path
94
+ end
95
+
96
+ def image_path
97
+ comparison.new_image_path
98
+ end
99
+
100
+ def driver
101
+ @_driver ||= comparison.driver
102
+ end
103
+
104
+ def comparison
105
+ @_comparison ||= difference.comparison
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Diff::ScreenshotMatcher
4
+ # now resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::ScreenshotMatcher.
6
+ require "snap_diff/screenshot_matcher"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Screenshoter now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::Screenshoter.
6
+ require "snap_diff/screenshoter"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Diff::StableScreenshoter
4
+ # now resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::StableScreenshoter.
6
+ require "snap_diff/stable_screenshoter"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Diff::Utils now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::Utils.
6
+ require "snap_diff/utils"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): Capybara::Screenshot::Diff::Vcs now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::Vcs.
6
+ require "snap_diff/vcs"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "snap_diff/version"
4
+
5
+ # Deliberately EAGER and silent (v2 step 6 exception): the gemspec resolves
6
+ # Capybara::Screenshot::Diff::VERSION at build time, so a lazy warning shim
7
+ # would make every `gem build` warn. See snap_diff/legacy_shims.rb for the
8
+ # full exception list.
9
+ module Capybara
10
+ module Screenshot
11
+ module Diff
12
+ VERSION = SnapDiff::VERSION
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "capybara-screenshot-diff"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "capybara_screenshot_diff/minitest"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): CapybaraScreenshotDiff::AttemptsReporter now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::AttemptsReporter.
6
+ require "snap_diff/attempts_reporter"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Legacy entry point: pre-move, this path transitively loaded the whole gem
4
+ # (via dsl -> umbrella). Preserve that contract for consumers who require
5
+ # only this file.
6
+ require "capybara_screenshot_diff"
7
+
8
+ require "snap_diff/integrations/cucumber"
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "snap_diff/dsl"
4
+
5
+ # Deliberately EAGER and silent (v2 step 6 exception): DSL is an advertised
6
+ # entry-point constant probed with Object.const_defined? by
7
+ # support_load_probe_test.rb, and const_defined? never triggers
8
+ # const_missing -- a lazy shim would break that contract. See
9
+ # snap_diff/legacy_shims.rb for the full exception list.
10
+ module CapybaraScreenshotDiff
11
+ DSL = SnapDiff::DSL
12
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): CapybaraScreenshotDiff::BacktraceFilter and
4
+ # CapybaraScreenshotDiff::ErrorWithFilteredBacktrace now resolve lazily via
5
+ # snap_diff/legacy_shims' const_missing, with deprecation warnings pointing
6
+ # at their SnapDiff:: replacements.
7
+ require "snap_diff/error_with_filtered_backtrace"
8
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Legacy entry point: pre-move, this path transitively loaded the whole gem
4
+ # (via dsl -> umbrella). Preserve that contract for consumers who require
5
+ # only this file.
6
+ require "capybara_screenshot_diff"
7
+
8
+ require "snap_diff/integrations/minitest"
9
+
10
+ module CapybaraScreenshotDiff
11
+ module Minitest
12
+ Assertions = SnapDiff::Minitest::Assertions
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): CapybaraScreenshotDiff::Reporters::HTML now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::Reporters::HTML.
6
+ require "snap_diff/reporters/html"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Legacy entry point: pre-move, this path transitively loaded the whole gem
4
+ # (via dsl -> umbrella). Preserve that contract for consumers who require
5
+ # only this file.
6
+ require "capybara_screenshot_diff"
7
+
8
+ require "snap_diff/integrations/rspec"
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "snap_diff/screenshot_assertion"
4
+ require "snap_diff/reporting"
5
+ # ScreenshotAssertion / AssertionRegistry forward lazily (with deprecation
6
+ # warnings) via snap_diff/legacy_shims' const_missing since v2 step 6.
7
+ require "snap_diff/legacy_shims"
8
+
9
+ # CapybaraScreenshotDiff's module methods cover two lifecycles with
10
+ # different scopes, kept separate below:
11
+ #
12
+ # - Session lifecycle: thread-local, per-test. The AssertionRegistry holds
13
+ # the assertions and screenshot names of the test running on this thread;
14
+ # `reset` clears it between tests.
15
+ # - Reporter lifecycle: process-global, suite-long. Owned by
16
+ # SnapDiff::Reporting; the methods here are thin public shims over it.
17
+ #
18
+ # `reset` is the one deliberate bridge between the two: a finished test's
19
+ # assertions are handed to the reporters before the registry is cleared.
20
+ module CapybaraScreenshotDiff
21
+ class << self
22
+ require "forwardable"
23
+ extend Forwardable
24
+
25
+ # --- Session lifecycle (thread-local, per-test) ---
26
+
27
+ def registry
28
+ Thread.current[:capybara_screenshot_diff_registry] ||= SnapDiff::AssertionRegistry.new
29
+ end
30
+
31
+ def_delegators :registry, :add_assertion, :assertions, :assertions_present?,
32
+ :failed_assertions, :record_new_screenshot, :new_screenshots,
33
+ :new_screenshots_present?, :screenshot_namer, :verify
34
+
35
+ def reset
36
+ notify_reporters(registry.assertions)
37
+ registry.reset
38
+ end
39
+
40
+ # Message to skip the test with when a new screenshot has no baseline yet
41
+ # and `pending_if_new` is enabled. Adapters call this after verifying
42
+ # screenshots, and skip the test with the returned message when present.
43
+ #
44
+ # @return [String, nil] the pending message, or nil when there is nothing to report
45
+ def pending_screenshots_message
46
+ return unless ::Capybara::Screenshot::Diff.pending_if_new && new_screenshots_present?
47
+
48
+ "No baseline for: #{new_screenshots.join(", ")}. Commit the captured screenshots to record them."
49
+ end
50
+
51
+ # --- Reporter lifecycle (process-global, suite-long) ---
52
+
53
+ def reporters
54
+ SnapDiff::Reporting.reporters
55
+ end
56
+
57
+ def reporters_mutex
58
+ SnapDiff::Reporting.mutex
59
+ end
60
+
61
+ def finalize_reporters!
62
+ SnapDiff::Reporting.finalize!
63
+ end
64
+
65
+ private
66
+
67
+ def notify_reporters(assertions)
68
+ SnapDiff::Reporting.notify(assertions)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): CapybaraScreenshotDiff::ScreenshotNamer now
4
+ # resolves lazily via snap_diff/legacy_shims' const_missing, with a
5
+ # deprecation warning pointing at SnapDiff::ScreenshotNamer.
6
+ require "snap_diff/screenshot_namer"
7
+ require "snap_diff/legacy_shims"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Forwarder (ADR-004 v2 step 6): CapybaraScreenshotDiff::Snap now resolves
4
+ # lazily via snap_diff/legacy_shims' const_missing, with a deprecation
5
+ # warning pointing at SnapDiff::Snap.
6
+ require "snap_diff/snap"
7
+ require "snap_diff/legacy_shims"