capybara-screenshot-diff 1.7.1 → 1.8.1

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +29 -0
  3. data/capybara-screenshot-diff.gemspec +6 -3
  4. data/gems.rb +8 -2
  5. data/lib/capybara/screenshot/diff/browser_helpers.rb +29 -28
  6. data/lib/capybara/screenshot/diff/cucumber.rb +11 -0
  7. data/lib/capybara/screenshot/diff/difference.rb +63 -0
  8. data/lib/capybara/screenshot/diff/drivers/base_driver.rb +42 -0
  9. data/lib/capybara/screenshot/diff/drivers/chunky_png_driver.rb +188 -260
  10. data/lib/capybara/screenshot/diff/drivers/utils.rb +18 -0
  11. data/lib/capybara/screenshot/diff/drivers/vips_driver.rb +54 -104
  12. data/lib/capybara/screenshot/diff/drivers.rb +16 -0
  13. data/lib/capybara/screenshot/diff/image_compare.rb +125 -154
  14. data/lib/capybara/screenshot/diff/os.rb +1 -1
  15. data/lib/capybara/screenshot/diff/screenshot_matcher.rb +128 -0
  16. data/lib/capybara/screenshot/diff/screenshoter.rb +136 -0
  17. data/lib/capybara/screenshot/diff/stabilization.rb +0 -184
  18. data/lib/capybara/screenshot/diff/stable_screenshoter.rb +106 -0
  19. data/lib/capybara/screenshot/diff/test_methods.rb +51 -90
  20. data/lib/capybara/screenshot/diff/vcs.rb +44 -22
  21. data/lib/capybara/screenshot/diff/version.rb +1 -1
  22. data/lib/capybara/screenshot/diff.rb +13 -17
  23. data/sig/capybara/screenshot/diff/diff.rbs +28 -0
  24. data/sig/capybara/screenshot/diff/difference.rbs +33 -0
  25. data/sig/capybara/screenshot/diff/drivers/base_driver.rbs +63 -0
  26. data/sig/capybara/screenshot/diff/drivers/browser_helpers.rbs +36 -0
  27. data/sig/capybara/screenshot/diff/drivers/chunky_png_driver.rbs +89 -0
  28. data/sig/capybara/screenshot/diff/drivers/utils.rbs +13 -0
  29. data/sig/capybara/screenshot/diff/drivers/vips_driver.rbs +25 -0
  30. data/sig/capybara/screenshot/diff/image_compare.rbs +93 -0
  31. data/sig/capybara/screenshot/diff/os.rbs +11 -0
  32. data/sig/capybara/screenshot/diff/region.rbs +43 -0
  33. data/sig/capybara/screenshot/diff/screenshot_matcher.rbs +60 -0
  34. data/sig/capybara/screenshot/diff/screenshoter.rbs +48 -0
  35. data/sig/capybara/screenshot/diff/stable_screenshoter.rbs +29 -0
  36. data/sig/capybara/screenshot/diff/test_methods.rbs +39 -0
  37. data/sig/capybara/screenshot/diff/vcs.rbs +17 -0
  38. metadata +28 -25
  39. data/.gitattributes +0 -4
  40. data/.github/dependabot.yml +0 -8
  41. data/.github/workflows/lint.yml +0 -25
  42. data/.github/workflows/test.yml +0 -138
  43. data/.gitignore +0 -14
  44. data/.standard.yml +0 -12
  45. data/CONTRIBUTING.md +0 -24
  46. data/Dockerfile +0 -59
  47. data/README.md +0 -567
  48. data/bin/bundle +0 -114
  49. data/bin/console +0 -15
  50. data/bin/install-vips +0 -11
  51. data/bin/rake +0 -27
  52. data/bin/setup +0 -8
  53. data/bin/standardrb +0 -29
  54. data/gemfiles/rails60_gems.rb +0 -8
  55. data/gemfiles/rails61_gems.rb +0 -7
  56. data/gemfiles/rails70_gems.rb +0 -7
  57. data/tmp/.keep +0 -0
@@ -5,43 +5,41 @@ require "capybara"
5
5
  require "action_controller"
6
6
  require "action_dispatch"
7
7
  require "active_support/core_ext/string/strip"
8
+ require "pathname"
9
+
10
+ require_relative "drivers"
8
11
  require_relative "image_compare"
9
- require_relative "stabilization"
10
12
  require_relative "vcs"
11
13
  require_relative "browser_helpers"
12
14
  require_relative "region"
13
15
 
16
+ require_relative "screenshot_matcher"
17
+
14
18
  # Add the `screenshot` method to ActionDispatch::IntegrationTest
15
19
  module Capybara
16
20
  module Screenshot
17
21
  module Diff
18
22
  module TestMethods
19
- include Stabilization
20
- include Vcs
21
- include BrowserHelpers
22
-
23
23
  def initialize(*)
24
24
  super
25
25
  @screenshot_counter = nil
26
26
  @screenshot_group = nil
27
27
  @screenshot_section = nil
28
28
  @test_screenshot_errors = nil
29
- @test_screenshots = nil
29
+ @test_screenshots = []
30
30
  end
31
31
 
32
- def group_parts
33
- parts = []
34
- parts << @screenshot_section if @screenshot_section.present?
35
- parts << @screenshot_group if @screenshot_group.present?
36
- parts
37
- end
32
+ def build_full_name(name)
33
+ if @screenshot_counter
34
+ name = format("%02i_#{name}", @screenshot_counter)
35
+ @screenshot_counter += 1
36
+ end
38
37
 
39
- def full_name(name)
40
- File.join group_parts.push(name).map(&:to_s)
38
+ File.join(*group_parts.push(name.to_s))
41
39
  end
42
40
 
43
41
  def screenshot_dir
44
- File.join [Screenshot.screenshot_area] + group_parts
42
+ File.join(*([Screenshot.screenshot_area] + group_parts))
45
43
  end
46
44
 
47
45
  def screenshot_section(name)
@@ -50,104 +48,67 @@ module Capybara
50
48
 
51
49
  def screenshot_group(name)
52
50
  @screenshot_group = name.to_s
53
- @screenshot_counter = 0
51
+ @screenshot_counter = @screenshot_group.present? ? 0 : nil
54
52
  return unless Screenshot.active? && name.present?
55
53
 
56
54
  FileUtils.rm_rf screenshot_dir
57
55
  end
58
56
 
59
- # @return [Boolean] whether a screenshot was taken
60
- def screenshot(name, skip_stack_frames: 0, **options)
61
- return false unless Screenshot.active?
62
- return false if window_size_is_wrong?
63
-
64
- driver_options = Diff.default_options.merge(options)
65
-
66
- stability_time_limit = driver_options[:stability_time_limit]
67
- wait = driver_options[:wait]
68
- crop = calculate_crop_region(driver_options)
69
-
70
- if @screenshot_counter
71
- name = "#{format("%02i", @screenshot_counter)}_#{name}"
72
- @screenshot_counter += 1
73
- end
74
- name = full_name(name)
75
- file_name = "#{Screenshot.screenshot_area_abs}/#{name}.png"
76
-
77
- create_output_directory_for(file_name)
78
-
79
- comparison = ImageCompare.new(file_name, nil, driver_options)
80
- checkout_vcs(name, comparison.old_file_name, comparison.new_file_name)
81
- take_comparison_screenshot(comparison, crop, stability_time_limit, wait)
82
-
83
- return false unless comparison.old_file_exists?
84
-
85
- # Allow nil or single or multiple areas
86
- if driver_options[:skip_area]
87
- comparison.skip_area = calculate_skip_area(driver_options[:skip_area], crop)
88
- end
89
-
90
- (@test_screenshots ||= []) << [caller[skip_stack_frames], name, comparison]
91
-
57
+ def schedule_match_job(job)
58
+ (@test_screenshots ||= []) << job
92
59
  true
93
60
  end
94
61
 
95
- def assert_image_not_changed(caller, name, comparison)
96
- return unless comparison.different?
97
-
98
- "Screenshot does not match for '#{name}' #{comparison.error_message}\nat #{caller}"
62
+ def group_parts
63
+ parts = []
64
+ parts << @screenshot_section if @screenshot_section.present?
65
+ parts << @screenshot_group if @screenshot_group.present?
66
+ parts
99
67
  end
100
68
 
101
- private
69
+ def screenshot(name, skip_stack_frames: 0, **options)
70
+ return false unless Screenshot.active?
102
71
 
103
- def calculate_crop_region(driver_options)
104
- crop_coordinates = driver_options.delete(:crop)
105
- return nil unless crop_coordinates
72
+ screenshot_full_name = build_full_name(name)
73
+ job = build_screenshot_matches_job(screenshot_full_name, options)
106
74
 
107
- crop_coordinates = bounds_for_css(crop_coordinates).first if crop_coordinates.is_a?(String)
108
- Region.from_edge_coordinates(*crop_coordinates)
109
- end
75
+ return false unless job
110
76
 
111
- def create_output_directory_for(file_name)
112
- FileUtils.mkdir_p File.dirname(file_name)
113
- end
77
+ test_caller = caller(skip_stack_frames)
114
78
 
115
- def take_comparison_screenshot(comparison, crop, stability_time_limit, wait)
116
- blurred_input = prepare_page_for_screenshot(timeout: wait)
117
- if stability_time_limit
118
- take_stable_screenshot(
119
- comparison,
120
- crop: crop,
121
- stability_time_limit: stability_time_limit,
122
- wait: wait
123
- )
79
+ if Screenshot::Diff.delayed
80
+ schedule_match_job([test_caller] + job)
124
81
  else
125
- take_right_size_screenshot(comparison, crop: crop)
82
+ error_msg = assert_image_not_changed(job.first, job.last)
83
+ if error_msg
84
+ error = ASSERTION.new(error_msg)
85
+ error.set_backtrace(caller(2))
86
+ raise error
87
+ end
126
88
  end
127
- ensure
128
- blurred_input&.click
129
89
  end
130
90
 
131
- def calculate_skip_area(skip_area, crop)
132
- crop_region = crop && Region.new(*crop)
133
- skip_area = Array(skip_area)
134
-
135
- css_selectors, regions = skip_area.compact.partition { |region| region.is_a? String }
91
+ def assert_image_not_changed(name, comparison)
92
+ result = comparison.different?
136
93
 
137
- result = []
138
- result.concat(build_regions_for(bounds_for_css(*css_selectors))) unless css_selectors.empty?
139
- result.concat(build_regions_for(regions.flatten&.each_slice(4))) unless regions.empty?
140
- result.compact!
94
+ # Cleanup after comparisons
95
+ if !result && comparison.base_image_path.exist?
96
+ FileUtils.mv(comparison.base_image_path, comparison.image_path, force: true)
97
+ else
98
+ FileUtils.rm_rf(comparison.base_image_path)
99
+ end
141
100
 
142
- result.map! { |region| crop_region.find_relative_intersect(region) } if crop_region
101
+ return unless result
143
102
 
144
- result
103
+ "Screenshot does not match for '#{name}' #{comparison.error_message}"
145
104
  end
146
105
 
147
- def build_regions_for(coordinates)
148
- coordinates.map do |region_coordinates|
149
- Region.from_edge_coordinates(*region_coordinates)
150
- end
106
+ private
107
+
108
+ def build_screenshot_matches_job(screenshot_full_name, options)
109
+ ScreenshotMatcher
110
+ .new(screenshot_full_name, options)
111
+ .build_screenshot_matches_job
151
112
  end
152
113
  end
153
114
  end
@@ -1,42 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "os"
4
+
4
5
  module Capybara
5
6
  module Screenshot
6
7
  module Diff
7
8
  module Vcs
8
9
  SILENCE_ERRORS = Os::ON_WINDOWS ? "2>nul" : "2>/dev/null"
9
10
 
10
- def restore_git_revision(name, target_file_name)
11
- redirect_target = "#{target_file_name} #{SILENCE_ERRORS}"
12
- show_command = "git show HEAD~0:./#{Capybara::Screenshot.screenshot_area}/#{name}.png"
13
- if Capybara::Screenshot.use_lfs
14
- `#{show_command} | git lfs smudge > #{redirect_target}`
11
+ def self.restore_git_revision(screenshot_path, checkout_path)
12
+ vcs_file_path = screenshot_path.relative_path_from(Screenshot.root)
13
+
14
+ redirect_target = "#{checkout_path} #{SILENCE_ERRORS}"
15
+ show_command = "git show HEAD~0:./#{vcs_file_path}"
16
+ if Screenshot.use_lfs
17
+ `#{show_command} | git lfs smudge > #{redirect_target} ; exit ${PIPESTATUS[0]}`
15
18
  else
16
19
  `#{show_command} > #{redirect_target}`
17
20
  end
18
- FileUtils.rm_f(target_file_name) unless $CHILD_STATUS == 0
19
- end
20
21
 
21
- def checkout_vcs(name, old_file_name, new_file_name)
22
- svn_file_name = "#{Capybara::Screenshot.screenshot_area_abs}/.svn/text-base/#{name}.png.svn-base"
22
+ if $CHILD_STATUS != 0
23
+ FileUtils.rm_f(checkout_path)
24
+ false
25
+ else
26
+ true
27
+ end
28
+ end
23
29
 
24
- if File.exist?(svn_file_name)
25
- committed_file_name = svn_file_name
26
- FileUtils.cp committed_file_name, old_file_name
30
+ def self.checkout_vcs(screenshot_path, checkout_path)
31
+ if svn?
32
+ restore_svn_revision(screenshot_path, checkout_path)
27
33
  else
28
- svn_info = `svn info #{new_file_name} #{SILENCE_ERRORS}`
29
- if svn_info.present?
30
- wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
31
- checksum = svn_info.slice(/(?<=Checksum: ).*$/)
32
- if checksum
33
- committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
34
- FileUtils.cp committed_file_name, old_file_name
35
- end
36
- else
37
- restore_git_revision(name, old_file_name)
34
+ restore_git_revision(screenshot_path, checkout_path)
35
+ end
36
+ end
37
+
38
+ def self.restore_svn_revision(screenshot_path, checkout_path)
39
+ committed_file_name = screenshot_path + "../.svn/text-base/" + "#{screenshot_path.basename}.svn-base"
40
+ if committed_file_name.exist?
41
+ FileUtils.cp(committed_file_name, checkout_path)
42
+ return true
43
+ end
44
+
45
+ svn_info = `svn info #{screenshot_path} #{SILENCE_ERRORS}`
46
+ if svn_info.present?
47
+ wc_root = svn_info.slice(/(?<=Working Copy Root Path: ).*$/)
48
+ checksum = svn_info.slice(/(?<=Checksum: ).*$/)
49
+
50
+ if checksum
51
+ committed_file_name = "#{wc_root}/.svn/pristine/#{checksum[0..1]}/#{checksum}.svn-base"
52
+ FileUtils.cp(committed_file_name, checkout_path)
53
+ return true
38
54
  end
39
55
  end
56
+
57
+ false
58
+ end
59
+
60
+ def self.svn?
61
+ (Screenshot.screenshot_area_abs / ".svn").exist?
40
62
  end
41
63
  end
42
64
  end
@@ -3,7 +3,7 @@
3
3
  module Capybara
4
4
  module Screenshot
5
5
  module Diff
6
- VERSION = "1.7.1"
6
+ VERSION = "1.8.1"
7
7
  end
8
8
  end
9
9
  end
@@ -5,10 +5,10 @@ require "capybara/screenshot/diff/version"
5
5
  require "capybara/screenshot/diff/drivers/utils"
6
6
  require "capybara/screenshot/diff/image_compare"
7
7
  require "capybara/screenshot/diff/test_methods"
8
+ require "capybara/screenshot/diff/screenshoter"
8
9
 
9
10
  module Capybara
10
11
  module Screenshot
11
- extend Os
12
12
  mattr_accessor :add_driver_path
13
13
  mattr_accessor :add_os_path
14
14
  mattr_accessor :blur_active_element
@@ -30,10 +30,10 @@ module Capybara
30
30
  end
31
31
 
32
32
  def screenshot_area
33
- parts = [Capybara::Screenshot.save_path]
34
- parts << Capybara.current_driver.to_s if Capybara::Screenshot.add_driver_path
35
- parts << os_name if Capybara::Screenshot.add_os_path
36
- File.join parts
33
+ parts = [Screenshot.save_path]
34
+ parts << Capybara.current_driver.to_s if Screenshot.add_driver_path
35
+ parts << Os.name if Screenshot.add_os_path
36
+ File.join(*parts)
37
37
  end
38
38
 
39
39
  def screenshot_area_abs
@@ -44,8 +44,8 @@ module Capybara
44
44
  # Module to track screen shot changes
45
45
  module Diff
46
46
  include Capybara::DSL
47
- include Capybara::Screenshot::Os
48
47
 
48
+ mattr_accessor(:delayed) { true }
49
49
  mattr_accessor :area_size_limit
50
50
  mattr_accessor :color_distance_limit
51
51
  mattr_accessor(:enabled) { true }
@@ -54,6 +54,8 @@ module Capybara
54
54
  mattr_accessor(:driver) { :auto }
55
55
  mattr_accessor :tolerance
56
56
 
57
+ mattr_accessor(:screenshoter) { Screenshoter }
58
+
57
59
  AVAILABLE_DRIVERS = Utils.detect_available_drivers.freeze
58
60
  ASSERTION = Utils.detect_test_framework_assert
59
61
 
@@ -65,7 +67,7 @@ module Capybara
65
67
  shift_distance_limit: shift_distance_limit,
66
68
  skip_area: skip_area,
67
69
  stability_time_limit: Screenshot.stability_time_limit,
68
- tolerance: tolerance || (driver == :vips ? 0.001 : nil),
70
+ tolerance: tolerance || ((driver == :vips) ? 0.001 : nil),
69
71
  wait: Capybara.default_max_wait_time
70
72
  }
71
73
  end
@@ -73,19 +75,13 @@ module Capybara
73
75
  def self.included(klass)
74
76
  klass.include TestMethods
75
77
  klass.setup do
76
- if Capybara::Screenshot.window_size
77
- if page.driver.respond_to?(:resize)
78
- page.driver.resize(*Capybara::Screenshot.window_size)
79
- elsif selenium?
80
- page.driver.browser.manage.window.resize_to(*Capybara::Screenshot.window_size)
81
- end
82
- end
78
+ BrowserHelpers.resize_to(Screenshot.window_size) if Screenshot.window_size
83
79
  end
84
80
 
85
81
  klass.teardown do
86
- if Capybara::Screenshot.active? && @test_screenshots
82
+ if Screenshot.active? && @test_screenshots.present?
87
83
  track_failures(@test_screenshots, caller)
88
- @test_screenshots = nil
84
+ @test_screenshots.clear
89
85
  end
90
86
  end
91
87
  end
@@ -96,7 +92,7 @@ module Capybara
96
92
 
97
93
  def track_failures(screenshots, original_caller)
98
94
  test_screenshot_errors = screenshots.map do |caller, name, compare|
99
- assert_image_not_changed(caller, name, compare)
95
+ assert_image_not_changed(name, compare)
100
96
  end
101
97
 
102
98
  test_screenshot_errors.compact!
@@ -0,0 +1,28 @@
1
+ module Capybara
2
+ module Screenshot
3
+ def self.root=: ((String | Pathname) path) -> Pathname
4
+
5
+ def self.root: -> Pathname
6
+
7
+ def self.active?: () -> boolish
8
+
9
+ def self.screenshot_area: () -> String
10
+
11
+ def self.screenshot_area_abs: () -> Pathname
12
+
13
+ # Module to track screen shot changes
14
+ module Diff
15
+ AVAILABLE_DRIVERS: Array[(:vips | :chunky_png)]
16
+
17
+ ASSERTION: (top | RuntimeError)
18
+
19
+ def self.default_options: () -> ScreenshotMatcher::input_options
20
+
21
+ def self.included: (top klass) -> void
22
+
23
+ private
24
+
25
+ def track_failures: (Array[untyped] screenshots, (Array[String] | String) original_caller) -> void
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Capybara
2
+ module Screenshot
3
+ module Diff
4
+ class Difference
5
+ attr_reader comparison: ImageCompare::Comparison
6
+
7
+ def different?: () -> bool
8
+
9
+ def base_image: () -> top
10
+
11
+ def options: () -> Drivers::BaseDriver::options_entity
12
+
13
+ def tolerance: () -> Numeric?
14
+
15
+ def area_size_limit: () -> Numeric?
16
+
17
+ def blank?: () -> bool
18
+
19
+ def region_area_size: () -> Numeric
20
+
21
+ def ratio: () -> Numeric?
22
+
23
+ def to_h: () -> Hash[Symbol, untyped]
24
+
25
+ def coordinates: () -> Region::raw_region_entity
26
+
27
+ def inspect: () -> String
28
+
29
+ def tolerable?: () -> bool
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,63 @@
1
+ module Capybara
2
+ module Screenshot
3
+ module Diff
4
+ module Drivers
5
+ class BaseDriver[ImageEntity]
6
+
7
+ type images_entity[out T] = [T, T]
8
+ type dimension_entity = [Numeric, Numeric]
9
+
10
+ type options_entity = {
11
+ area_size_limit?: Numeric?,
12
+ color_distance_limit?: Numeric?,
13
+ driver: (:auto | :vips | :chunky_png | VipsDriver | ChunkyPNGDriver)?,
14
+ dimensions: dimension_entity?,
15
+ median_filter_window_size: Numeric?,
16
+ shift_distance_limit?: Numeric?,
17
+ skip_area?: Array[Region]?,
18
+ stability_time_limit?: Numeric?,
19
+ tolerance?: Numeric?,
20
+ wait?: Numeric?
21
+ }
22
+
23
+ type color = [Integer, Integer, Integer, Integer]
24
+
25
+ def find_difference_region: (ImageEntity new_image, ImageEntity old_image, Numeric color_distance_limit, Numeric _shift_distance_limit, Integer _area_size_limit, ?fast_fail: bool) -> Difference
26
+
27
+ def inscribed?: (dimension_entity dimensions, ImageEntity i) -> boolish
28
+
29
+ def crop: (Region region, ImageEntity i) -> ImageEntity
30
+
31
+ def filter_image_with_median: (ImageEntity image, Numeric median_filter_window_size) -> ImageEntity
32
+
33
+ def add_black_box: (ImageEntity memo, Region region) -> void
34
+
35
+ def difference_level: (ImageEntity diff_mask, ImageEntity old_img, Region _region) -> Float
36
+
37
+ def image_area_size: (ImageEntity old_img) -> Integer
38
+
39
+ def height_for: (ImageEntity image) -> Integer
40
+
41
+ def width_for: (ImageEntity image) -> Integer
42
+
43
+ # Vips could not work with the same file. Per each process we require to create new file
44
+ def save_image_to: (ImageEntity image, String filename) -> void
45
+
46
+ def resize_image_to: (ImageEntity image, Integer new_width, Integer new_height) -> ImageEntity
47
+
48
+ def load_images: (String old_file_name, String new_file_name) -> images_entity[ImageEntity]
49
+
50
+ def from_file: (TestMethods::path_entity filename) -> ImageEntity
51
+
52
+ def dimension: (ImageEntity image) -> dimension_entity
53
+
54
+ def draw_rectangles: (images_entity[ImageEntity] images, Region region, color rgba, Integer offset) -> void
55
+
56
+ def same_pixels?: () -> boolish
57
+ end
58
+
59
+ def self.for: (ScreenshotMatcher::input_options) -> (VipsDriver | ChunkyPNGDriver)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ module Capybara
2
+ class Result
3
+ def map: () -> Result
4
+ end
5
+ module Screenshot
6
+ module BrowserHelpers
7
+ def self.current_capybara_driver_class: () -> top
8
+
9
+ def self.selenium?: () -> boolish?
10
+
11
+ def self.window_size_is_wrong?: () -> boolish
12
+
13
+ def self.bounds_for_css: (*String css_selectors) -> Array[Region::raw_region_entity]
14
+
15
+ IMAGE_WAIT_SCRIPT: String
16
+
17
+ def self.pending_image_to_load: () -> top?
18
+
19
+ HIDE_CARET_SCRIPT: String
20
+
21
+ def self.hide_caret: () -> void
22
+
23
+ FIND_ACTIVE_ELEMENT_SCRIPT: String
24
+
25
+ type capybara_element = top
26
+
27
+ def self.blur_from_focused_element: () -> capybara_element?
28
+
29
+ GET_BOUNDING_CLIENT_RECT_SCRIPT: String
30
+
31
+ def self.all_visible_regions_for: (String selector) -> Array[Region::raw_region_entity]
32
+
33
+ def self.region_for: (Result element) -> Region::raw_region_entity
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,89 @@
1
+ module ChunkyPNG
2
+ class Canvas
3
+ end
4
+
5
+ class Image
6
+ def self.from_blob: (String str) -> Image
7
+
8
+ def self.from_file: (String filename) -> Image
9
+ end
10
+ end
11
+
12
+ module Capybara
13
+ module Screenshot
14
+ module Diff
15
+ module Drivers
16
+ class ChunkyPNGDriver < BaseDriver[ChunkyPNG::Canvas]
17
+
18
+ def _load_images: (String old_file, String new_file) -> [ChunkyPNG::Image, ChunkyPNG::Image]
19
+
20
+ class DifferenceRegionFinder
21
+ def find_diff_rectangle: (
22
+ ChunkyPNG::Image org_img,
23
+ ChunkyPNG::Image new_img,
24
+ (Region | Region::raw_region_entity) area_coordinates,
25
+ cache: ImageCompare::cache_entity
26
+ ) -> Region?
27
+
28
+ def find_top: (
29
+ ChunkyPNG::Image old_img,
30
+ ChunkyPNG::Image new_img,
31
+ cache: ImageCompare::cache_entity
32
+ ) -> Region::raw_region_entity?
33
+
34
+ def find_left_right_and_top: (
35
+ ChunkyPNG::Image old_img,
36
+ ChunkyPNG::Image new_img,
37
+ (Region | Region::raw_region_entity) region,
38
+ cache: ImageCompare::cache_entity
39
+ ) -> Region::raw_region_entity
40
+
41
+ def find_bottom: (
42
+ ChunkyPNG::Image old_img,
43
+ ChunkyPNG::Image new_img,
44
+ Integer left,
45
+ Integer right,
46
+ Integer bottom,
47
+ cache: ImageCompare::cache_entity
48
+ ) -> Integer
49
+
50
+ def same_color?: (
51
+ ChunkyPNG::Image old_img,
52
+ ChunkyPNG::Image new_img,
53
+ Integer x,
54
+ Integer y,
55
+ ?cache: ImageCompare::cache_entity
56
+ ) -> boolish
57
+
58
+ def skipped_region?: (Integer x, Integer y) -> boolish
59
+
60
+ def color_distance_at: (
61
+ ChunkyPNG::Image new_img,
62
+ ChunkyPNG::Image old_img,
63
+ Integer x,
64
+ Integer y,
65
+ shift_distance_limit: Numeric?
66
+ ) -> Float
67
+
68
+ def shift_distance_at: (
69
+ ChunkyPNG::Image new_img,
70
+ ChunkyPNG::Image old_img,
71
+ Integer x,
72
+ Integer y,
73
+ color_distance_limit: Numeric?
74
+ ) -> Numeric
75
+
76
+ def color_matches: (
77
+ ChunkyPNG::Image new_img,
78
+ Integer org_color,
79
+ Integer x,
80
+ Integer y,
81
+ Numeric? color_distance_limit
82
+ ) -> boolish
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,13 @@
1
+ module Capybara
2
+ module Screenshot
3
+ module Diff
4
+ module Utils
5
+ def self.detect_available_drivers: () -> Array[(:vips | :chunky_png)]
6
+
7
+ def self.find_driver_class_for: [T] (Symbol driver) -> T
8
+
9
+ def self.detect_test_framework_assert: [T < Exception] () -> T
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module ::Vips
2
+ class Image
3
+ def self.new_from_file: (String filename) -> Image
4
+ end
5
+ end
6
+
7
+ module Capybara
8
+ module Screenshot
9
+ module Diff
10
+ module Drivers
11
+ class VipsDriver < BaseDriver[Vips::Image]
12
+ class VipsUtil
13
+ def self.difference_area: (Vips::Image old_image, Vips::Image new_image, ?color_distance: ::Integer) -> Numeric
14
+
15
+ def self.difference_area_size_by: (Vips::Image difference_mask) -> Numeric
16
+
17
+ def self.difference_mask: (Vips::Image, Vips::Image, ?Numeric? color_distance) -> Vips::Image
18
+
19
+ def self.difference_region_by: (Vips::Image diff_mask) -> Region?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end